diff --git a/.github/scripts/deploy/README.md b/.github/scripts/deploy/README.md index 1589b767a3..fa21abd36c 100644 --- a/.github/scripts/deploy/README.md +++ b/.github/scripts/deploy/README.md @@ -81,7 +81,25 @@ btcli multisig pending -w # pending sudo-multisig ops; also lists pending Do not sign a WASM you were simply handed. srtool builds are deterministic: identical source at the same toolchain produces a byte-identical runtime. The -pre-release tag *is* the code being deployed, so: +pre-release tag *is* the code being deployed. + +The short version is one command (requires docker). Run it from a checkout +you already trust — reviewed `main`, not the proposal tag, so the proposal +cannot supply the verifier that vouches for it: + +``` +git fetch origin && git checkout origin/main +./scripts/verify-upgrade.sh +``` + +It downloads the release manifest and wasm, fetches the proposal commit and +rebuilds it from a pristine clone in the same srtool container CI used, +byte-compares the result against the released runtime, runs +`btcli upgrade check` against the chain if btcli is installed, and prints +the `btcli upgrade sign` command pinned to your own build. It never submits +anything. + +The manual recipe, if you prefer to run each step yourself: ``` git fetch origin && git checkout v diff --git a/.github/workflows/cargo-audit.yml b/.github/workflows/cargo-audit.yml index 12ee8e123e..765a4a2c90 100644 --- a/.github/workflows/cargo-audit.yml +++ b/.github/workflows/cargo-audit.yml @@ -35,7 +35,7 @@ jobs: sccache-writer-secret-access-key: ${{ secrets.SCCACHE_R2_WRITE_SECRET_ACCESS_KEY }} - name: Install cargo-audit - run: cargo install --force cargo-audit + run: cargo install --force --locked cargo-audit - name: cargo audit # Each ignore is a known, accepted advisory; revisit when bumping diff --git a/.github/workflows/check-bittensor-e2e-tests.yml b/.github/workflows/check-bittensor-e2e-tests.yml index c3c475f4af..ef54b75e17 100644 --- a/.github/workflows/check-bittensor-e2e-tests.yml +++ b/.github/workflows/check-bittensor-e2e-tests.yml @@ -125,7 +125,7 @@ jobs: run: | set -euo pipefail manifest=sdk/bittensor-core/tests/e2e-manifest.json - jq -e 'length == 113' "$manifest" >/dev/null + jq -e 'length == 112' "$manifest" >/dev/null jq -e 'map(.test) | length == (unique | length)' "$manifest" >/dev/null jq -e 'all(.[]; (.test | startswith("intent_")) or (.test | startswith("test_")))' "$manifest" >/dev/null test_matrix=$(jq -c . "$manifest") @@ -314,7 +314,27 @@ jobs: password: ${{ secrets.GITHUB_TOKEN }} - name: Push Docker Image to GHCR - run: docker push "$LOCALNET_IMAGE_CI" + # GHCR intermittently returns `unknown blob` mid-push (dropped upload + # session). Push is idempotent for a content-addressed tag, so retry. + run: | + for i in 1 2 3 4; do + if out=$(docker push "$LOCALNET_IMAGE_CI" 2>&1); then + printf '%s\n' "$out" + exit 0 + fi + printf '%s\n' "$out" + if [ "$i" -eq 4 ]; then + echo "::error::docker push failed after 4 attempts" + exit 1 + fi + if printf '%s\n' "$out" | grep -qiE 'unknown blob|blob unknown|blob upload|timeout|EOF|connection reset|5[0-9]{2}'; then + echo "::warning::transient GHCR push error (attempt $i/4); retrying in $((i * 20))s" + sleep $((i * 20)) + continue + fi + echo "::error::non-transient docker push failure; not retrying" + exit 1 + done run-rust-e2e-tests: needs: diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml index a7c247019c..1297b49947 100644 --- a/.github/workflows/deploy-docs.yml +++ b/.github/workflows/deploy-docs.yml @@ -63,6 +63,17 @@ jobs: npx --yes vercel build --token "$VERCEL_TOKEN" url=$(npx --yes vercel deploy --prebuilt --token "$VERCEL_TOKEN") echo "Deployed: $url" - npx --yes vercel alias set "$url" "staging.$DOCS_DOMAIN" --token "$VERCEL_TOKEN" + # `vercel alias set` only accepts domains in the team-level domain + # registry; staging.$DOCS_DOMAIN is attached (and verified) at the + # project level, so the CLI rejects it with "you don't have access + # to the domain". The deployments API honors project domains. + dep_id=$(curl -sf -H "Authorization: Bearer $VERCEL_TOKEN" \ + "https://api.vercel.com/v13/deployments/${url#https://}?teamId=$VERCEL_ORG_ID" \ + | node -pe 'JSON.parse(require("fs").readFileSync(0, "utf8")).id') + curl -sf -X POST \ + -H "Authorization: Bearer $VERCEL_TOKEN" \ + -H "Content-Type: application/json" \ + -d "{\"alias\":\"staging.$DOCS_DOMAIN\"}" \ + "https://api.vercel.com/v2/deployments/$dep_id/aliases?teamId=$VERCEL_ORG_ID" > /dev/null echo "Aliased to https://staging.$DOCS_DOMAIN" >> $GITHUB_STEP_SUMMARY fi diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index be842703ed..2132bc0829 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -1,13 +1,16 @@ name: Publish Docker Image -# Node images publish on runtime releases and on demand. Releases cut by -# watch-mainnet-release.yml use the default GITHUB_TOKEN, which never emits -# `release: published`; the watcher therefore dispatches this workflow -# directly with the release tag. Release-version tags (vN) move :latest. +# Node images publish when a network mirror moves, on runtime releases, and on +# demand. Releases cut by watch-mainnet-release.yml use the default +# GITHUB_TOKEN, which never emits `release: published`; the watcher therefore +# dispatches this workflow directly with the release tag. Release-version tags +# (vN) move :latest; network mirror tags do not. on: release: types: [published] + push: + branches: [devnet, testnet] workflow_dispatch: inputs: tag: @@ -31,7 +34,9 @@ jobs: steps: - uses: actions/checkout@v4 with: - ref: ${{ github.event.inputs.tag || github.ref_name }} + # Push/release events carry the immutable promoted commit. Manual + # dispatches may override it with a branch or tag. + ref: ${{ github.event.inputs.tag || github.sha }} - name: Resolve immutable source revision id: ref @@ -116,18 +121,15 @@ jobs: runs-on: [self-hosted, fireactions-turbo-8] timeout-minutes: 30 steps: - - name: Determine tag, ref, and image name + - name: Determine tag and image name env: INPUT_TAG: ${{ github.event.inputs.tag || github.ref_name }} run: | - ref="$INPUT_TAG" - # `ref` is what we check out (a real branch/tag, may contain '/'). # Docker tags cannot contain '/', so derive the tag by replacing any # disallowed characters — otherwise a ref like `feat/x` fails tag # validation at push time. - tag="${ref//[^a-zA-Z0-9._-]/-}" + tag="${INPUT_TAG//[^a-zA-Z0-9._-]/-}" echo "tag=$tag" >> $GITHUB_ENV - echo "ref=$ref" >> $GITHUB_ENV # Move :latest for release events and for dispatched release-version # tags (the watcher dispatches with tag=vN because its GITHUB_TOKEN # release cannot fire the `release` trigger). diff --git a/.github/workflows/publish-sdk-dev.yml b/.github/workflows/publish-sdk-dev.yml index 81a339000e..53bfec9322 100644 --- a/.github/workflows/publish-sdk-dev.yml +++ b/.github/workflows/publish-sdk-dev.yml @@ -53,6 +53,12 @@ jobs: working-directory: sdk/python run: uv build --out-dir ../../dist + # PEP 740 provenance, same as the rc/stable publish jobs. Running it on + # the dev channel means a broken attestation step fails here on every + # push to main instead of for the first time during a release. + - name: Generate PEP 740 attestations + uses: astral-sh/attest-action@f589a42a7efb6fe400b4f400de60b4bc90390027 # v0.0.6 + - name: Publish to TestPyPI run: | uv publish --trusted-publishing always \ diff --git a/.github/workflows/release-train.yml b/.github/workflows/release-train.yml index 95d69bd113..ce9885b7a2 100644 --- a/.github/workflows/release-train.yml +++ b/.github/workflows/release-train.yml @@ -360,6 +360,12 @@ jobs: - name: Build SDK wheel and sdist working-directory: sdk/python run: uv build --out-dir ../../dist + # PEP 740 provenance: sign every dist with this job's OIDC identity + # (must match the trusted publisher, so it runs here rather than in + # build-core-wheels.yml). uv publish uploads the resulting + # *.publish.attestation files alongside each dist. + - name: Generate PEP 740 attestations + uses: astral-sh/attest-action@f589a42a7efb6fe400b4f400de60b4bc90390027 # v0.0.6 # --check-url makes a re-run after a partial upload idempotent: files # already on the index are skipped instead of failing with a 400 # duplicate (e.g. the SDK wheel landed but bittensor-core didn't). diff --git a/.github/workflows/watch-mainnet-release.yml b/.github/workflows/watch-mainnet-release.yml index 22f8bac488..3ee09e50a9 100644 --- a/.github/workflows/watch-mainnet-release.yml +++ b/.github/workflows/watch-mainnet-release.yml @@ -254,6 +254,14 @@ jobs: working-directory: sdk/python run: uv build --out-dir ../../dist + # PEP 740 provenance: sign every dist with this job's OIDC identity. + # Must happen here, not in build-core-wheels.yml — PyPI only accepts + # attestations whose Sigstore identity matches the trusted publisher + # doing the upload. uv publish picks up the *.publish.attestation + # files from dist/ automatically. + - name: Generate PEP 740 attestations + uses: astral-sh/attest-action@f589a42a7efb6fe400b4f400de60b4bc90390027 # v0.0.6 + # --check-url makes a re-run after a partial upload idempotent: files # already on the index are skipped instead of failing with a 400 # duplicate (e.g. the SDK wheel landed but bittensor-core didn't). diff --git a/Cargo.lock b/Cargo.lock index 97661d59cc..debfc4538a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8754,6 +8754,8 @@ dependencies = [ "subtensor-custom-rpc-runtime-api", "subtensor-macros", "subtensor-runtime-common", + "tikv-jemalloc-ctl", + "tikv-jemallocator", "tokio", ] @@ -19552,6 +19554,16 @@ dependencies = [ "libc", ] +[[package]] +name = "tikv-jemallocator" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "965fe0c26be5c56c94e38ba547249074803efd52adfb66de62107d95aab3eaca" +dependencies = [ + "libc", + "tikv-jemalloc-sys", +] + [[package]] name = "time" version = "0.3.53" diff --git a/README.md b/README.md index 8f28f98106..a280f52ac2 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ contracts alongside native Substrate extrinsics. | Shared Rust | [`common/`](./common/), [`primitives/`](./primitives/), [`support/`](./support/) | Shared types, math primitives, lints, macros, weight tooling. | | Client core | [`sdk/bittensor-core/`](./sdk/bittensor-core/) | The Rust core for Bittensor clients (keypairs, keyfiles, SCALE codec, extrinsic assembly, RFC-0078 digest, drand timelock, ML-KEM) — built against the same crate revisions as the chain; Python bindings in [`sdk/bittensor-core-py/`](./sdk/bittensor-core-py/). | | Python SDK | [`sdk/python/`](./sdk/python/) | The `bittensor` package and `btcli` CLI. Generated from chain metadata so reads, writes, and docs cannot drift. | -| Documentation | [`docs/`](./docs/) | Source for bittensor.com/docs. `docs/tx/`, `docs/query/`, and `docs/errors.mdx` are generated — do not hand-edit. | +| Documentation | [`docs/`](./docs/) | Source for bittensor.com/docs. `docs/tx/`, `docs/query/`, and `docs/errors/` are generated — do not hand-edit. | | Website | [`website/`](./website/) | Yarn + Turbo monorepo; `apps/bittensor-website` renders marketing pages and these docs. | | Tests | [`ts-tests/`](./ts-tests/), [`clones/`](./clones/), [`eco-tests/`](./eco-tests/) | Moonwall/zombienet integration tests, mainnet-clone regression harness, indexer contract tests. | | Operations | [`chainspecs/`](./chainspecs/), [`scripts/`](./scripts/), [`Dockerfile`](./Dockerfile) | Chain specs, CI/dev scripts, production and localnet Docker images. | diff --git a/chain-extensions/src/mock.rs b/chain-extensions/src/mock.rs index 93eeaf684c..67103fd00a 100644 --- a/chain-extensions/src/mock.rs +++ b/chain-extensions/src/mock.rs @@ -345,7 +345,7 @@ parameter_types! { pub const InitialColdkeySwapAnnouncementDelay: u64 = 50; pub const InitialColdkeySwapReannouncementDelay: u64 = 10; pub const InitialDissolveNetworkScheduleDuration: u64 = 5 * 24 * 60 * 60 / 12; // Default as 5 days - pub const InitialTaoWeight: u64 = 0; // 100% global weight. + pub const InitialTaoWeight: u64 = 0; // 0% TAO weight. pub const InitialEmaPriceHalvingPeriod: u64 = 201_600_u64; // 4 weeks pub const InitialStartCallDelay: u64 = 7 * 24 * 60 * 60 / 12; // Default as 7 days pub const InitialKeySwapOnSubnetCost: TaoBalance = TaoBalance::new(10_000_000); diff --git a/chain-extensions/src/types.rs b/chain-extensions/src/types.rs index 001965cc90..46a2e9fde5 100644 --- a/chain-extensions/src/types.rs +++ b/chain-extensions/src/types.rs @@ -140,6 +140,10 @@ impl From for Output { Some("SlippageTooHigh") => Output::SlippageTooHigh, Some("SubnetNotExists") => Output::SubnetNotExists, Some("SameAutoStakeHotkeyAlreadySet") => Output::SameAutoStakeHotkeyAlreadySet, + Some("InsufficientTaoBalance") => Output::InsufficientBalance, + Some("InsufficientAlphaBalance") => Output::InsufficientBalance, + // Other pallets (e.g. swap, crowdloan) still emit their own + // `InsufficientBalance`; keep mapping it to the same ABI code. Some("InsufficientBalance") => Output::InsufficientBalance, Some("AmountTooLow") => Output::AmountTooLow, Some("InsufficientLiquidity") => Output::InsufficientLiquidity, diff --git a/docs/concepts/meta.json b/docs/concepts/meta.json index 75f401ca98..01f5780fc0 100644 --- a/docs/concepts/meta.json +++ b/docs/concepts/meta.json @@ -1,4 +1,4 @@ { "title": "Concepts", - "pages": ["network", "emissions", "wallets", "money", "client", "transactions", "advanced"] + "pages": ["network", "emissions", "wallets", "money", "staking-pools", "client", "transactions", "advanced"] } diff --git a/docs/concepts/money.mdx b/docs/concepts/money.mdx index 99ad8fbe5e..d0e03972a8 100644 --- a/docs/concepts/money.mdx +++ b/docs/concepts/money.mdx @@ -49,7 +49,9 @@ slippage. ## Slippage -Staking and unstaking are swaps against a pool, so the trade itself moves the +Staking and unstaking are swaps against a pool +([staking and pools](/docs/concepts/staking-pools) explains the pool +mechanics with interactive charts), so the trade itself moves the price: your order consumes reserves as it fills, and the gap between what the spot price promised and what you actually receive grows with the trade's size relative to the pool's liquidity. Strictly, that self-inflicted gap is diff --git a/docs/concepts/staking-pools.mdx b/docs/concepts/staking-pools.mdx new file mode 100644 index 0000000000..d7302fd8d5 --- /dev/null +++ b/docs/concepts/staking-pools.mdx @@ -0,0 +1,170 @@ +--- +title: Staking and pools +description: The per-subnet balancer pools, what a stake operation actually does, price impact, swap fees, and MEV-shielded submission. +--- + +Staking on a subnet is not a deposit — it is a **swap**. Every subnet has a +liquidity pool holding TAO and the subnet's alpha; staking sells TAO into +that pool for alpha, unstaking sells alpha back for TAO. Everything that +follows — price impact, swap fees, limit orders, MEV — falls out of that one +fact. This page explains the machinery; the +[staking guide](/docs/guides/staking) covers the commands, and +[how money moves](/docs/concepts/money) covers units and valuation. + +## The pool + +Each subnet's pool is a **weighted balancer pool** (a generalization of the +constant-product AMM): two reserves, TAO (`SubnetTAO`) and alpha +(`SubnetAlphaIn`), plus a pair of weights $w_1 + w_2 = 1$. The spot price is + +$$ +p = \frac{w_1 \cdot \text{TAO reserve}}{w_2 \cdot \text{alpha reserve}} +$$ + +With the default equal weights $w_1 = w_2 = 0.5$ this reduces to the +familiar reserve ratio `TAO / alpha`. Unlike Uniswap-style pools, the weights +let the protocol add liquidity *disproportionally* to price without moving +the price — the weights absorb the imbalance instead (they are re-solved on +every injection and bounded so they stay near 0.5 in practice). Only the +protocol adds liquidity this way; user staking always trades against the +pool. + +A swap moves along the weighted invariant +$L = x^{w_1} \cdot y^{w_2}$. Selling TAO into the pool (staking), the alpha +you receive for `Δy` TAO is + +$$ +\Delta x = x \cdot \left(1 - \left(\tfrac{y}{y+\Delta y}\right)^{w_2/w_1}\right) +$$ + +which is why the execution price curves away from spot as the trade grows — +each slice of your order fills at a slightly worse price than the last. + + + +The math lives in `pallets/swap/src/pallet/balancer.rs`; quotes over RPC use +exactly the same path ([`quote-stake`](/docs/query/quote-stake), +[`quote-unstake`](/docs/query/quote-unstake)), so a quote is a faithful +simulation, not an estimate. + +## What a stake operation does + +[`add-stake`](/docs/tx/add-stake) is a coldkey-signed extrinsic that moves +TAO out of your free balance, swaps it through the subnet's pool, and +credits the resulting alpha to the *(hotkey, coldkey, netuid)* stake +position. Step through it: + + + +Points worth pinning down: + +- **The fee comes off the input side first.** Amount × `FeeRate`/65535 + (default 33 ≈ 0.05%, per-subnet) is deducted before the swap and paid to + the block author — TAO when staking, alpha when unstaking. +- **The swap updates both reserves.** Your TAO enters `SubnetTAO`, alpha + leaves the pool's side (`SubnetAlphaIn`) and joins the staked total + (`SubnetAlphaOut`). The price after your trade is the new reserve ratio — + your own trade moved it. +- **The position is alpha, not TAO.** From the moment the swap settles, the + position's TAO value floats with the pool price, and it earns the + validator's staking emissions (minus its take) every epoch. +- **Unstaking is the mirror image** ([`remove-stake`](/docs/tx/remove-stake)): + alpha leaves the position, the fee is taken in alpha, the swap pays out + TAO. There is no unbonding period — the TAO is spendable immediately. + +Moving a position between hotkeys on the same subnet +([`move-stake`](/docs/tx/move-stake)) is *not* a swap — no fee, no price +impact. Cross-subnet moves run two swaps (alpha → TAO → alpha) and pay the +fee once. + +## Price impact, slippage, and limit orders + +Two different things eat your execution price, and they have different +defenses: + +- **Price impact** — self-inflicted: your own order consumes reserves as it + fills. Deterministic, included in every quote. +- **Slippage** — external: other transactions land before yours and move the + price between quote and execution. Not knowable in advance. + +The limit variants ([`add-stake-limit`](/docs/tx/add-stake-limit), +[`remove-stake-limit`](/docs/tx/remove-stake-limit)) defend against both by +bounding the execution price itself: the chain computes exactly how much can +fill before the pool price reaches your limit, fills up to that point, and +either stops there (`allow_partial=true`) or rejects the whole order +(`allow_partial=false`). + + + +To turn a tolerance into a `limit_price`: staking, ceiling = spot × (1 + +tolerance); unstaking, floor = spot × (1 − tolerance). Read spot with +[`alpha-price`](/docs/query/alpha-price). + +## MEV and shielded submission + +A large stake order sitting in the public mempool is legible to everyone — +including a front-runner who can buy alpha before your order executes, let +your price impact reprice the pool, and sell back after. The sandwich's +profit is carved out of your execution price. + +[MEV-shielded submission](/docs/concepts/advanced#mev-shielded-submission) +closes the window: the SDK signs your call as a complete inner extrinsic, +encrypts it to the chain's rotating ML-KEM-768 key, and submits only the +ciphertext. The mempool sees an opaque blob; the block author decrypts it at +block-build time and includes the inner extrinsic in the block it builds. +There is no interval where the order is both public and pending. + + + +```python +await client.submit_shielded( + sub.AddStakeLimit(hotkey_ss58="5F...", netuid=1, amount_tao=5_000, + limit_price_rao=int(spot["price_rao"] * 1.01), + allow_partial=False), + wallet, +) +``` + +Shield *and* bound the price — they protect against different things. +Shielding hides the order from front-runners; the limit caps damage from +whatever moves the price anyway. What is worth shielding follows the same +economics as price impact: a large swap with a loose limit is the prime +target, while small swaps (well under ~1 TAO) and root-network staking (no +pool, nothing to front-run) gain nothing from it. + +## Root staking: the exception + +Staking on the root network (netuid 0) is not a pool swap. TAO stays +TAO-denominated and converts 1:1 — no swap fee, no price impact, no MEV +exposure. The `limit_price` machinery degenerates accordingly: any limit ≥ +1 TAO/TAO fills fully, anything lower fills nothing. See +[root stake and dividends](/docs/guides/staking#root-stake-and-dividends) +for how root positions earn. + +## Where the liquidity comes from + +Nobody LPs these pools. Every block, the coinbase injects each subnet's +share of TAO emission into its pool's TAO reserve, alongside newly issued +alpha into the alpha reserve (`alpha_in ≈ tao_in / price`, capped by the +subnet's root proportion) — see +[emissions](/docs/concepts/emissions). Because the protocol may inject the +two sides disproportionally to price, the balancer weights absorb the +imbalance; this is the reason the pools are weighted at all. Reserves only +grow through injection and trading — stake swaps in TAO, unstake swaps it +back out — and the pool refuses trades that would drain a reserve below a +minimum, and rejects single swaps larger than 1,000× the TAO reserve +(`InsufficientLiquidity`). + +## Reading the pool + +```bash +btcli query alpha-price --netuid 1 --json # spot price now +btcli query quote-stake --netuid 1 --amount-tao 100 --json # simulate entry +btcli query quote-unstake --netuid 1 --amount-alpha 50 --json # simulate exit +``` + +Spot valuation of a position (`alpha × price`, +[`stake-value-for-coldkey`](/docs/query/stake-value-for-coldkey)) marks to +the current reserve ratio and ignores what your own exit would do to it. For +what you would actually receive, quote the exit — the quote runs the real +swap math against the real reserves. diff --git a/docs/concepts/wallets.mdx b/docs/concepts/wallets.mdx index 6b45127d0a..a6a6009461 100644 --- a/docs/concepts/wallets.mdx +++ b/docs/concepts/wallets.mdx @@ -98,17 +98,16 @@ Match each key to the least-trusted machine that needs it: 3. **Coldkey workstation** — a dedicated, clean machine for coldkey-signed operations, ideally holding only a scoped proxy key rather than the real coldkey. -4. **Primary coldkey in a hardware wallet** — Ledger works through - Talisman, Nova Wallet, or SubWallet; Polkadot Vault turns a permanently - offline phone into an air-gapped signer that can sign any extrinsic via QR - codes (Ledger apps cover a narrower operation set). - -This CLI and SDK cannot sign with hardware wallets. That is what the proxy -pattern is for: the hardware-held primary coldkey creates a single delayed -`NonTransfer` [proxy](/docs/concepts/advanced), and that proxy creates and -revokes narrower scoped proxies (`Staking`, `Registration`, ...) for daily -work. Only two operations ever strictly require the primary coldkey: the first -add-proxy, and coldkey rotation. +4. **Primary coldkey on a hardware or air-gapped signer** — a + [Ledger](/docs/guides/ledger) can sign CLI and SDK transactions through the + Polkadot generic app; Polkadot Vault signs by QR code from a permanently + offline phone. + +Keep the hardware-held key for recovery and high-impact operations. Use it to +create a delayed `NonTransfer` [proxy](/docs/guides/proxies), which can create +and revoke narrower proxies (`Staking`, `Registration`, ...) for routine work — +the full layering is in the proxy guide's +[recommended architecture](/docs/guides/proxies#a-recommended-architecture). ### Supply-chain risk @@ -189,9 +188,14 @@ swap is a two-step, delayed process: window the wallet is locked — no transfers or staking; the coldkey can only execute or dispute. 2. [`swap-coldkey-announced`](/docs/tx/swap-coldkey-announced) executes after - the delay, moving everything — balance, stake, hotkeys, registrations, - subnet ownership — to the destination, which must be an unused coldkey with - no existing stake, registrations, or child hotkeys. + the delay, moving the coldkey's Subtensor-managed state — transferable TAO, + stake, conviction locks (including accrued conviction), hotkeys, + registrations, and subnet ownership — to the destination. Proxy grants and + reserved balances do not move. Before announcing, remove proxies whose + deposits you need to recover and leave enough transferable TAO to recreate + the proxies you still need. Use a verified destination that is not a hotkey + and has no stake or active locked mass; recreate required proxies after the + swap. Check a pending announcement with the [`coldkey-swap-announcement`](/docs/query/coldkey-swap-announcement) read. An diff --git a/docs/errors.mdx b/docs/errors.mdx deleted file mode 100644 index 99ce8dc505..0000000000 --- a/docs/errors.mdx +++ /dev/null @@ -1,442 +0,0 @@ ---- -title: "Errors" -description: "Every failure carries a machine-readable code and a remediation hint." ---- - -{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} - -A failed `execute` returns an `ExtrinsicResult` whose `error` has a semantic `code` (branch on it) and a `remediation` hint (what to try next). Every chain error name is deliberately classified — a CI gate keeps the mapping complete in both directions. The machine-readable version is at [`/catalog/errors.json`](/catalog/errors.json). - -## Error codes - -### `insufficient_balance` - -Fund the signing account or reduce the amount; check with `btcli wallet balance` - -### `insufficient_liquidity` - -The pool cannot absorb this trade; reduce the amount or split it into smaller steps - -### `rate_limited` - -Wait for the rate-limit window to pass, then retry - -### `not_registered` - -Register the hotkey on this subnet first with `btcli subnets register` - -### `not_authorized` - -Sign with the key or origin that owns the target object, then retry - -### `already_exists` - -The object or state already exists; treat the goal as met or pick a different target - -### `not_found` - -The referenced object is not on-chain; check the identifier - -### `subnet_not_exists` - -Use an existing netuid; `btcli subnets list` shows valid ones - -### `subtoken_disabled` - -The subnet is not active yet; wait for start_call - -### `disabled` - -This call or feature is switched off on this network - -### `too_early` - -The required window has not opened yet; wait some blocks and retry - -### `expired` - -The window has closed; restart the flow with fresh state - -### `limit_exceeded` - -A chain-side capacity limit was hit; reduce the size or count of the request - -### `unit_mismatch` - -Match the Balance netuid to the operation's currency - -### `invalid_argument` - -Check the argument values against the operation schema - -### `policy_violation` - -The action exceeds a configured safety policy - -### `internal` - -A chain-side invariant failed; nothing to fix client-side — report it if it persists - -### `unknown` - -Inspect the message for details - -## Chain error classification - -The exact chain error name (from the extrinsic receipt) maps to a code: - -| Chain error | Code | -| --- | --- | -| `AccountNotAllowedCommit` | `not_authorized` | -| `AccountRejectsLockedAlpha` | `invalid_argument` | -| `ActiveLockExists` | `already_exists` | -| `ActivityCutoffFactorMilliOutOfBounds` | `invalid_argument` | -| `ActivityCutoffTooLow` | `invalid_argument` | -| `AddStakeBurnRateLimitExceeded` | `rate_limited` | -| `AdminActionProhibitedDuringWeightsWindow` | `too_early` | -| `AllNetworksInImmunity` | `too_early` | -| `AlphaHighTooLow` | `invalid_argument` | -| `AlphaLowOutOfRange` | `invalid_argument` | -| `AlreadyApproved` | `already_exists` | -| `AlreadyDeposited` | `already_exists` | -| `AlreadyFinalized` | `already_exists` | -| `AlreadyFinalizing` | `already_exists` | -| `AlreadyNoted` | `already_exists` | -| `AlreadyStored` | `already_exists` | -| `AmountTooLow` | `invalid_argument` | -| `AnnouncedColdkeyHashDoesNotMatch` | `invalid_argument` | -| `AnnouncementDepositInvariantViolated` | `internal` | -| `ArithmeticOverflow` | `internal` | -| `AutoEpochAlreadyImminent` | `already_exists` | -| `BadEncKeyLen` | `invalid_argument` | -| `BalanceLow` | `insufficient_balance` | -| `BalanceWithdrawalError` | `insufficient_balance` | -| `BeneficiaryDoesNotOwnHotkey` | `not_authorized` | -| `BlockDurationTooLong` | `invalid_argument` | -| `BlockDurationTooShort` | `invalid_argument` | -| `BondsMovingAverageMaxReached` | `limit_exceeded` | -| `CallDisabled` | `disabled` | -| `CallFiltered` | `not_authorized` | -| `CallUnavailable` | `not_found` | -| `CanNotSetRootNetworkWeights` | `invalid_argument` | -| `CannotAddSelfAsDelegateDependency` | `invalid_argument` | -| `CannotAffordLockCost` | `insufficient_balance` | -| `CannotBurnOrRecycleOnRootSubnet` | `invalid_argument` | -| `CannotEndInPast` | `invalid_argument` | -| `CannotReleaseYet` | `too_early` | -| `CannotUseSystemAccount` | `not_authorized` | -| `CapNotRaised` | `too_early` | -| `CapRaised` | `limit_exceeded` | -| `CapTooLow` | `invalid_argument` | -| `ChainIdMismatch` | `invalid_argument` | -| `ChangePending` | `already_exists` | -| `ChildParentInconsistency` | `invalid_argument` | -| `CodeInUse` | `invalid_argument` | -| `CodeInfoNotFound` | `not_found` | -| `CodeNotFound` | `not_found` | -| `CodeRejected` | `invalid_argument` | -| `CodeTooLarge` | `limit_exceeded` | -| `ColdKeyAlreadyAssociated` | `already_exists` | -| `ColdkeySwapAlreadyDisputed` | `already_exists` | -| `ColdkeySwapAnnounced` | `already_exists` | -| `ColdkeySwapAnnouncementNotFound` | `not_found` | -| `ColdkeySwapClearTooEarly` | `too_early` | -| `ColdkeySwapDisputed` | `not_authorized` | -| `ColdkeySwapReannouncedTooEarly` | `too_early` | -| `ColdkeySwapTooEarly` | `too_early` | -| `CommitRevealDisabled` | `disabled` | -| `CommitRevealEnabled` | `disabled` | -| `CommittingWeightsTooFast` | `rate_limited` | -| `ContractNotFound` | `not_found` | -| `ContractReverted` | `internal` | -| `ContractTrapped` | `internal` | -| `ContributionPeriodEnded` | `expired` | -| `ContributionPeriodNotEnded` | `too_early` | -| `ContributionTooLow` | `invalid_argument` | -| `CreateOriginNotAllowed` | `not_authorized` | -| `CurrencyError` | `internal` | -| `DeadAccount` | `not_found` | -| `DecodingFailed` | `invalid_argument` | -| `DelegateDependencyAlreadyExists` | `already_exists` | -| `DelegateDependencyNotFound` | `not_found` | -| `DelegateTakeTooHigh` | `invalid_argument` | -| `DelegateTakeTooLow` | `invalid_argument` | -| `DelegateTxRateLimitExceeded` | `rate_limited` | -| `DeltaZero` | `invalid_argument` | -| `DepositCannotBeWithdrawn` | `invalid_argument` | -| `DepositTooLow` | `invalid_argument` | -| `Deprecated` | `disabled` | -| `DisabledTemporarily` | `disabled` | -| `DrandConnectionFailure` | `internal` | -| `Duplicate` | `already_exists` | -| `DuplicateChild` | `invalid_argument` | -| `DuplicateContract` | `already_exists` | -| `DuplicateOffenceReport` | `already_exists` | -| `DuplicateOrderInBatch` | `invalid_argument` | -| `DuplicateUids` | `invalid_argument` | -| `DynamicTempoBlockedByCommitReveal` | `disabled` | -| `Entered` | `already_exists` | -| `EpochTriggerAlreadyPending` | `already_exists` | -| `EvmKeyAssociateRateLimitExceeded` | `rate_limited` | -| `EvmKeyAssociationLimitExceeded` | `limit_exceeded` | -| `ExistentialDeposit` | `insufficient_balance` | -| `ExistingVestingSchedule` | `already_exists` | -| `Exited` | `already_exists` | -| `ExpectedBeneficiaryOrigin` | `not_authorized` | -| `Expendability` | `insufficient_balance` | -| `ExpiredWeightCommit` | `expired` | -| `FailedToExtractRuntimeVersion` | `invalid_argument` | -| `FailedToSchedule` | `internal` | -| `FaucetDisabled` | `disabled` | -| `FeeOverflow` | `internal` | -| `FeeRateTooHigh` | `invalid_argument` | -| `FirstEmissionBlockNumberAlreadySet` | `already_exists` | -| `GasLimitTooHigh` | `invalid_argument` | -| `GasLimitTooLow` | `invalid_argument` | -| `GasPriceTooLow` | `invalid_argument` | -| `HotKeyAccountNotExists` | `not_registered` | -| `HotKeyAlreadyDelegate` | `already_exists` | -| `HotKeyAlreadyRegisteredInSubNet` | `already_exists` | -| `HotKeyNotRegisteredInNetwork` | `not_registered` | -| `HotKeyNotRegisteredInSubNet` | `not_registered` | -| `HotKeySetTxRateLimitExceeded` | `rate_limited` | -| `HotKeySwapOnSubnetIntervalNotPassed` | `rate_limited` | -| `IncorrectCommitRevealVersion` | `invalid_argument` | -| `IncorrectPartialFillAmount` | `invalid_argument` | -| `IncorrectWeightVersionKey` | `invalid_argument` | -| `Indeterministic` | `invalid_argument` | -| `InputForwarded` | `invalid_argument` | -| `InputLengthsUnequal` | `invalid_argument` | -| `InsufficientBalance` | `insufficient_balance` | -| `InsufficientInputAmount` | `invalid_argument` | -| `InsufficientLiquidity` | `insufficient_liquidity` | -| `InsufficientStakeForLock` | `insufficient_balance` | -| `InvalidCallFlags` | `invalid_argument` | -| `InvalidChainId` | `invalid_argument` | -| `InvalidChild` | `invalid_argument` | -| `InvalidChildkeyTake` | `invalid_argument` | -| `InvalidCrowdloanId` | `not_found` | -| `InvalidDerivedAccount` | `invalid_argument` | -| `InvalidDerivedAccountId` | `invalid_argument` | -| `InvalidDifficulty` | `invalid_argument` | -| `InvalidEquivocationProof` | `invalid_argument` | -| `InvalidFinalizationConfig` | `invalid_argument` | -| `InvalidIdentity` | `invalid_argument` | -| `InvalidIpAddress` | `invalid_argument` | -| `InvalidIpType` | `invalid_argument` | -| `InvalidKeyOwnershipProof` | `invalid_argument` | -| `InvalidLeaseBeneficiary` | `invalid_argument` | -| `InvalidLiquidityValue` | `invalid_argument` | -| `InvalidNonce` | `invalid_argument` | -| `InvalidNumRootClaim` | `invalid_argument` | -| `InvalidOrigin` | `not_authorized` | -| `InvalidPort` | `invalid_argument` | -| `InvalidRecoveredPublicKey` | `invalid_argument` | -| `InvalidRevealCommitHashNotMatch` | `invalid_argument` | -| `InvalidRevealRound` | `expired` | -| `InvalidRootClaimThreshold` | `invalid_argument` | -| `InvalidRoundNumber` | `invalid_argument` | -| `InvalidSchedule` | `invalid_argument` | -| `InvalidSeal` | `invalid_argument` | -| `InvalidSignature` | `invalid_argument` | -| `InvalidSpecName` | `invalid_argument` | -| `InvalidSubnetNumber` | `invalid_argument` | -| `InvalidTickRange` | `invalid_argument` | -| `InvalidValue` | `invalid_argument` | -| `InvalidVotingPowerEmaAlpha` | `invalid_argument` | -| `InvalidWorkBlock` | `invalid_argument` | -| `IssuanceDeactivated` | `disabled` | -| `LeaseCannotEndInThePast` | `invalid_argument` | -| `LeaseDoesNotExist` | `not_found` | -| `LeaseHasNoEndBlock` | `invalid_argument` | -| `LeaseHasNotEnded` | `too_early` | -| `LeaseNetuidNotFound` | `not_found` | -| `LimitOrdersDisabled` | `disabled` | -| `LiquidAlphaDisabled` | `disabled` | -| `LiquidityRestrictions` | `insufficient_balance` | -| `LockHotkeyMismatch` | `invalid_argument` | -| `LockIdOverFlow` | `limit_exceeded` | -| `MaxAllowedUIdsLessThanCurrentUIds` | `invalid_argument` | -| `MaxAllowedUidsGreaterThanDefaultMaxAllowedUids` | `invalid_argument` | -| `MaxAllowedUidsLessThanMinAllowedUids` | `invalid_argument` | -| `MaxCallDepthReached` | `limit_exceeded` | -| `MaxContributionReached` | `limit_exceeded` | -| `MaxContributorsReached` | `limit_exceeded` | -| `MaxDelegateDependenciesReached` | `limit_exceeded` | -| `MaxValidatorsLargerThanMaxUIds` | `invalid_argument` | -| `MaxWeightExceeded` | `limit_exceeded` | -| `MaxWeightTooLow` | `invalid_argument` | -| `MaximumContributionTooLow` | `invalid_argument` | -| `MechanismDoesNotExist` | `subnet_not_exists` | -| `MigrationInProgress` | `too_early` | -| `MinAllowedUidsGreaterThanCurrentUids` | `invalid_argument` | -| `MinAllowedUidsGreaterThanMaxAllowedUids` | `invalid_argument` | -| `MinimumContributionTooHigh` | `invalid_argument` | -| `MinimumContributionTooLow` | `invalid_argument` | -| `MinimumThreshold` | `invalid_argument` | -| `MultiBlockMigrationsOngoing` | `too_early` | -| `Named` | `invalid_argument` | -| `NeedWaitingMoreBlocksToStarCall` | `too_early` | -| `NegativeSigmoidSteepness` | `invalid_argument` | -| `NetworkDissolveAlreadyQueued` | `already_exists` | -| `NetworkTxRateLimitExceeded` | `rate_limited` | -| `NeuronNoValidatorPermit` | `not_authorized` | -| `NewColdKeyIsHotkey` | `invalid_argument` | -| `NewHotKeyIsSameWithOld` | `invalid_argument` | -| `NewHotKeyNotCleanForRootSwap` | `invalid_argument` | -| `NoApprovalsNeeded` | `invalid_argument` | -| `NoChainExtension` | `disabled` | -| `NoContribution` | `not_found` | -| `NoDeposit` | `not_found` | -| `NoExistingLock` | `not_found` | -| `NoMigrationPerformed` | `invalid_argument` | -| `NoNeuronIdAvailable` | `limit_exceeded` | -| `NoPermission` | `not_authorized` | -| `NoSelfProxy` | `invalid_argument` | -| `NoTimepoint` | `invalid_argument` | -| `NoWeightsCommitFound` | `not_found` | -| `NonAssociatedColdKey` | `not_authorized` | -| `NonDefaultComposite` | `invalid_argument` | -| `NonZeroRefCount` | `invalid_argument` | -| `NoneValue` | `not_found` | -| `NotAllowed` | `not_authorized` | -| `NotAuthorized` | `not_authorized` | -| `NotConfigured` | `disabled` | -| `NotEnoughAlphaOutToRecycle` | `insufficient_liquidity` | -| `NotEnoughBalanceToPaySwapColdKey` | `insufficient_balance` | -| `NotEnoughBalanceToPaySwapHotKey` | `insufficient_balance` | -| `NotEnoughBalanceToStake` | `insufficient_balance` | -| `NotEnoughStake` | `insufficient_balance` | -| `NotEnoughStakeToSetChildkeys` | `insufficient_balance` | -| `NotEnoughStakeToSetWeights` | `insufficient_balance` | -| `NotEnoughStakeToWithdraw` | `insufficient_balance` | -| `NotFound` | `not_found` | -| `NotNoted` | `not_found` | -| `NotOwner` | `not_authorized` | -| `NotPermittedOnRootSubnet` | `invalid_argument` | -| `NotProxy` | `not_authorized` | -| `NotReadyToDissolve` | `too_early` | -| `NotRequested` | `not_found` | -| `NotRootSubnet` | `invalid_argument` | -| `NotSubnetOwner` | `not_authorized` | -| `NothingAuthorized` | `not_found` | -| `OrderAlreadyProcessed` | `already_exists` | -| `OrderCancelled` | `expired` | -| `OrderExpired` | `expired` | -| `OrderNetUidMismatch` | `invalid_argument` | -| `OutOfBounds` | `invalid_argument` | -| `OutOfGas` | `limit_exceeded` | -| `OutOfTransientStorage` | `limit_exceeded` | -| `OutputBufferTooSmall` | `invalid_argument` | -| `Overflow` | `internal` | -| `POWRegistrationDisabled` | `disabled` | -| `PalletHotkeyNotRegistered` | `not_registered` | -| `PartialFillsNotEnabled` | `disabled` | -| `PauseFailed` | `invalid_argument` | -| `PaymentOverflow` | `internal` | -| `PreLogExists` | `invalid_argument` | -| `PriceConditionNotMet` | `too_early` | -| `PriceLimitExceeded` | `insufficient_liquidity` | -| `ProportionOverflow` | `invalid_argument` | -| `PulseVerificationError` | `invalid_argument` | -| `RandomSubjectTooLong` | `limit_exceeded` | -| `ReentranceDenied` | `invalid_argument` | -| `Reentrancy` | `internal` | -| `RegistrationNotPermittedOnRootSubnet` | `invalid_argument` | -| `RegistrationPriceLimitExceeded` | `limit_exceeded` | -| `RelayerMissMatch` | `invalid_argument` | -| `RelayerRequiredForPartialFill` | `invalid_argument` | -| `Requested` | `invalid_argument` | -| `RequireSudo` | `not_authorized` | -| `RescheduleNoChange` | `invalid_argument` | -| `ReservesOutOfBalance` | `insufficient_liquidity` | -| `ReservesTooLow` | `insufficient_liquidity` | -| `ResumeFailed` | `invalid_argument` | -| `RevealPeriodTooLarge` | `invalid_argument` | -| `RevealPeriodTooSmall` | `invalid_argument` | -| `RevealTooEarly` | `too_early` | -| `RootNetUidNotAllowed` | `invalid_argument` | -| `RootNetworkDoesNotExist` | `subnet_not_exists` | -| `SameAutoStakeHotkeyAlreadySet` | `already_exists` | -| `SameNetuid` | `invalid_argument` | -| `SenderInSignatories` | `invalid_argument` | -| `ServingRateLimitExceeded` | `rate_limited` | -| `SettingWeightsTooFast` | `rate_limited` | -| `SignatoriesOutOfOrder` | `invalid_argument` | -| `SlippageTooHigh` | `insufficient_liquidity` | -| `SpaceLimitExceeded` | `rate_limited` | -| `SpecVersionNeedsToIncrease` | `invalid_argument` | -| `StakeTooLowForRoot` | `insufficient_balance` | -| `StakeUnavailable` | `insufficient_balance` | -| `StakingRateLimitExceeded` | `rate_limited` | -| `StartCallNotReady` | `too_early` | -| `StateChangeDenied` | `not_authorized` | -| `StorageDepositLimitExhausted` | `limit_exceeded` | -| `StorageDepositNotEnoughFunds` | `insufficient_balance` | -| `StorageOverflow` | `internal` | -| `SubNetRegistrationDisabled` | `disabled` | -| `SubnetBuybackRateLimitExceeded` | `rate_limited` | -| `SubnetDoesNotExist` | `subnet_not_exists` | -| `SubnetLimitReached` | `limit_exceeded` | -| `SubnetNotExists` | `subnet_not_exists` | -| `SubtokenDisabled` | `subtoken_disabled` | -| `SwapInputTooLarge` | `insufficient_liquidity` | -| `SwapReturnedZero` | `insufficient_liquidity` | -| `SymbolAlreadyInUse` | `already_exists` | -| `SymbolDoesNotExist` | `not_found` | -| `TargetBlockNumberInPast` | `invalid_argument` | -| `TempoOutOfBounds` | `invalid_argument` | -| `TerminatedInConstructor` | `internal` | -| `TerminatedWhileReentrant` | `invalid_argument` | -| `TooBig` | `limit_exceeded` | -| `TooFew` | `invalid_argument` | -| `TooFewSignatories` | `invalid_argument` | -| `TooMany` | `limit_exceeded` | -| `TooManyCalls` | `limit_exceeded` | -| `TooManyChildren` | `limit_exceeded` | -| `TooManyFieldsInCommitmentInfo` | `limit_exceeded` | -| `TooManyFreezes` | `limit_exceeded` | -| `TooManyHolds` | `limit_exceeded` | -| `TooManyPendingExtrinsics` | `limit_exceeded` | -| `TooManyRegistrationsThisBlock` | `rate_limited` | -| `TooManyRegistrationsThisInterval` | `rate_limited` | -| `TooManyReserves` | `limit_exceeded` | -| `TooManySignatories` | `limit_exceeded` | -| `TooManyTopics` | `limit_exceeded` | -| `TooManyUIDsPerMechanism` | `limit_exceeded` | -| `TooManyUnrevealedCommits` | `limit_exceeded` | -| `TooSoon` | `rate_limited` | -| `TransactionMustComeFromEOA` | `not_authorized` | -| `TransactorAccountShouldBeHotKey` | `not_authorized` | -| `TransferDisallowed` | `disabled` | -| `TransferFailed` | `insufficient_balance` | -| `TrimmingWouldExceedMaxImmunePercentage` | `limit_exceeded` | -| `TxChildkeyTakeRateLimitExceeded` | `rate_limited` | -| `TxRateLimitExceeded` | `rate_limited` | -| `UidMapCouldNotBeCleared` | `internal` | -| `UidVecContainInvalidOne` | `invalid_argument` | -| `UidsLengthExceedUidsInSubNet` | `limit_exceeded` | -| `UnableToRecoverPublicKey` | `invalid_argument` | -| `Unannounced` | `too_early` | -| `Unauthorized` | `not_authorized` | -| `Undefined` | `internal` | -| `Underflow` | `internal` | -| `UnexpectedTimepoint` | `invalid_argument` | -| `UnexpectedUnreserveLeftover` | `internal` | -| `UnlockAmountTooHigh` | `insufficient_balance` | -| `Unproxyable` | `not_authorized` | -| `Unreachable` | `internal` | -| `UnverifiedPulse` | `invalid_argument` | -| `ValueNotInBounds` | `invalid_argument` | -| `ValueTooLarge` | `limit_exceeded` | -| `VestingBalance` | `insufficient_balance` | -| `VotingPowerTrackingNotEnabled` | `disabled` | -| `WaitingForDissolvedSubnetCleanup` | `too_early` | -| `WeightExceedsAbsoluteMax` | `limit_exceeded` | -| `WeightVecLengthIsLow` | `invalid_argument` | -| `WeightVecNotEqualSize` | `invalid_argument` | -| `WithdrawFailed` | `insufficient_balance` | -| `WrongTimepoint` | `invalid_argument` | -| `XCMDecodeFailed` | `invalid_argument` | -| `ZeroBalanceAfterWithdrawn` | `insufficient_balance` | -| `ZeroShareInBatch` | `invalid_argument` | diff --git a/docs/errors/already-exists.mdx b/docs/errors/already-exists.mdx new file mode 100644 index 0000000000..0073d3c33b --- /dev/null +++ b/docs/errors/already-exists.mdx @@ -0,0 +1,50 @@ +--- +title: "already_exists" +description: "The object or state already exists; treat the goal as met or pick a different target" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The state the call would create already exists: the hotkey is already registered on the subnet, an announcement or commit is already pending, the identity/symbol is already taken, or the approval was already given. + +This is often good news — the goal may already be met, so check the current state before retrying. if you meant to create something new, pick a different identifier or wait for the pending object to clear. + +## Remediation + +The object or state already exists; treat the goal as met or pick a different target + +## Chain errors + +The exact chain error names (from the extrinsic receipt) that classify to `already_exists`; the description says what triggered the failure and where to check. Each name has its own page: + +| Chain error | Description | +| --- | --- | +| [`ActiveLockExists`](/docs/errors/chain/ActiveLockExists) | The destination coldkey already holds a lock with nonzero locked mass on that subnet, so a new or transferred lock cannot be created there. Inspect the `Lock` storage for the (coldkey, netuid, hotkey) triple and wait for the existing lock to unlock or remove it first. | +| [`AlreadyApproved`](/docs/errors/chain/AlreadyApproved) | The sender has already approved this multisig call, so a repeat approval is redundant. Check the `Multisigs` entry for the call hash: the sender's account already appears in its `approvals` list. | +| [`AlreadyDeposited`](/docs/errors/chain/AlreadyDeposited) | The account calling safe-mode `enter` or `extend` already has a safe-mode deposit on hold, so another cannot be placed. Check the account's `Deposits` entries and its balance held under the `EnterOrExtend` hold reason. | +| [`AlreadyFinalized`](/docs/errors/chain/AlreadyFinalized) | The crowdloan's `finalized` flag is already true, so withdraw, finalize, refund, dissolve, and the update calls are all rejected. Check the `finalized` field of the `Crowdloans` entry for the given `crowdloan_id`. | +| [`AlreadyFinalizing`](/docs/errors/chain/AlreadyFinalizing) | A `finalize` call was made while another finalization is still in progress, i.e. the dispatched call from a previous finalize has not cleared. Check that the `CurrentCrowdloanId` storage value is empty before retrying. | +| [`AlreadyNoted`](/docs/errors/chain/AlreadyNoted) | The preimage for this hash has already been noted on-chain, so `note_preimage` has nothing to add. Check `RequestStatusFor` (or legacy `StatusFor`) for the hash before submitting the bytes again. | +| [`AlreadyStored`](/docs/errors/chain/AlreadyStored) | The call data supplied for storage is already stored on-chain for this multisig operation. Check whether the call bytes were previously stored for this call hash before submitting them again. | +| [`AutoEpochAlreadyImminent`](/docs/errors/chain/AutoEpochAlreadyImminent) | `trigger_epoch` was called when the next automatic epoch is closer than the `AdminFreezeWindow`, so a manual trigger would have no effect. Check the subnet's tempo and blocks until the next epoch, and simply wait for it to fire. | +| [`ChangePending`](/docs/errors/chain/ChangePending) | A GRANDPA authority-set change has already been signalled and is still pending, so a new change cannot be scheduled. Check the Grandpa `PendingChange` and `State` storage and wait for the pending change to be applied first. | +| [`ColdKeyAlreadyAssociated`](/docs/errors/chain/ColdKeyAlreadyAssociated) | The destination coldkey of a coldkey swap already has staking hotkeys associated with it, so it cannot receive the swapped identity. Check the `StakingHotkeys` storage for the new coldkey and swap to a fresh, unused coldkey instead. | +| [`ColdkeySwapAlreadyDisputed`](/docs/errors/chain/ColdkeySwapAlreadyDisputed) | `dispute_coldkey_swap` was called for a coldkey whose pending swap announcement is already under dispute. Check the `ColdkeySwapDisputes` storage for the coldkey; no further dispute action is needed. | +| [`ColdkeySwapAnnounced`](/docs/errors/chain/ColdkeySwapAnnounced) | The coldkey has a pending swap announcement, so all but a small allow-list of extrinsics are blocked until the swap completes or is cleared. Check the `ColdkeySwapAnnouncements` storage for the coldkey and either finish the swap with `coldkey_swap` or clear the announcement. | +| [`DelegateDependencyAlreadyExists`](/docs/errors/chain/DelegateDependencyAlreadyExists) | The contract called `lock_delegate_dependency` for a code hash it has already locked. Check the contract's recorded delegate dependencies before adding, and unlock the old entry first if replacing it. | +| [`Duplicate`](/docs/errors/chain/Duplicate) | This delegate is already registered as a proxy for the delegator with the same proxy type and delay. Check the delegator's `Proxies` entry before calling `add_proxy` with the same (delegate, proxy type, delay) tuple. | +| [`DuplicateContract`](/docs/errors/chain/DuplicateContract) | Instantiation would create a contract at an address already occupied by an existing contract. Check the derived contract address and vary the `salt` argument to obtain a fresh address. | +| [`DuplicateOffenceReport`](/docs/errors/chain/DuplicateOffenceReport) | The equivocation proof is valid but this offence has already been reported and recorded. Check whether an equivocation report for the same offender, session, and round was previously submitted before reporting again. | +| [`Entered`](/docs/errors/chain/Entered) | Safe-mode is currently active, so `enter` or `force_enter` cannot activate it again and `release_deposit` is blocked until it exits. Check `EnteredUntil` for the block at which safe-mode disengages. | +| [`EpochTriggerAlreadyPending`](/docs/errors/chain/EpochTriggerAlreadyPending) | `trigger_epoch` was called while a previously triggered epoch is still queued for this subnet. Check the `PendingEpochAt` storage for the netuid and wait for the pending epoch to fire before triggering again. | +| [`ExistingVestingSchedule`](/docs/errors/chain/ExistingVestingSchedule) | A vesting schedule already exists for the target account and this call cannot add another. Check the account's `Vesting` storage entry before attempting to set a new vested transfer or schedule. | +| [`Exited`](/docs/errors/chain/Exited) | Safe-mode is not currently active, so `extend`, `force_extend`, or `force_exit` have nothing to act on. Check that `EnteredUntil` contains a value before extending or exiting. | +| [`FirstEmissionBlockNumberAlreadySet`](/docs/errors/chain/FirstEmissionBlockNumberAlreadySet) | `start_call` was issued for a subnet whose emissions have already been started. Check the `FirstEmissionBlockNumber` storage for the netuid; a non-empty value means the subnet is already emitting and no action is needed. | +| [`HotKeyAlreadyDelegate`](/docs/errors/chain/HotKeyAlreadyDelegate) | `become_delegate` was called for a hotkey that is already a delegate. Check the `Delegates` storage for the hotkey; if it has a take entry it is already delegating and no action is needed. | +| [`HotKeyAlreadyRegisteredInSubNet`](/docs/errors/chain/HotKeyAlreadyRegisteredInSubNet) | A registration or hotkey swap targeted a hotkey that already holds a UID on the subnet (or, for a swap without a netuid, on any subnet). Check the `Uids` storage for the (netuid, hotkey) pair or `btcli wallet overview`; use a different hotkey or netuid. | +| [`NetworkDissolveAlreadyQueued`](/docs/errors/chain/NetworkDissolveAlreadyQueued) | The subnet is already in the dissolve cleanup queue, so it cannot be queued for dissolution again. Check the `DissolveCleanupQueue` storage value for the netuid before submitting another dissolve request. | +| [`OrderAlreadyProcessed`](/docs/errors/chain/OrderAlreadyProcessed) | The order id already has a terminal status: execution found it fulfilled, or `cancel_order` found any existing status for it. Check the `Orders` storage map under the blake2-256 hash of the SCALE-encoded `VersionedOrder`. | +| [`SameAutoStakeHotkeyAlreadySet`](/docs/errors/chain/SameAutoStakeHotkeyAlreadySet) | The coldkey tried to set its auto-stake destination on a subnet to the hotkey that is already configured. Read `AutoStakeDestination` for the coldkey and netuid before calling; only a different hotkey is accepted. | +| [`SymbolAlreadyInUse`](/docs/errors/chain/SymbolAlreadyInUse) | The token symbol requested for the subnet is already assigned to another subnet. Scan `TokenSymbol` across netuids and pick a symbol that is not taken. | + +The same explanation is available in the terminal: `btcli explain already_exists` (or `btcli explain ` for one exact chain error). diff --git a/docs/errors/chain/AccountNotAllowedCommit.mdx b/docs/errors/chain/AccountNotAllowedCommit.mdx new file mode 100644 index 0000000000..56c0aefa72 --- /dev/null +++ b/docs/errors/chain/AccountNotAllowedCommit.mdx @@ -0,0 +1,16 @@ +--- +title: "AccountNotAllowedCommit" +description: "Sign with the key or origin that owns the target object, then retry" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +Raised by `set_commitment` when the runtime commit check fails: the subnet must exist and the signing hotkey must be registered on it. Verify the `netuid` and that the hotkey has a UID on that subnet. + +Declared by the `Commitments` pallet; it classifies to the semantic code [`not_authorized`](/docs/errors/not-authorized). + +## Remediation + +Sign with the key or origin that owns the target object, then retry + +The same explanation is available in the terminal: `btcli explain AccountNotAllowedCommit`. diff --git a/docs/errors/chain/AccountRejectsLockedAlpha.mdx b/docs/errors/chain/AccountRejectsLockedAlpha.mdx new file mode 100644 index 0000000000..ec1b3042a2 --- /dev/null +++ b/docs/errors/chain/AccountRejectsLockedAlpha.mdx @@ -0,0 +1,16 @@ +--- +title: "AccountRejectsLockedAlpha" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +Locked alpha was being transferred to a coldkey whose `AccountFlags` do not have the accept-locked-alpha bit set, e.g. during a lock transfer or coldkey swap of locks. Check the destination coldkey's `AccountFlags` storage and have the recipient opt in to receiving locked alpha before retrying. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain AccountRejectsLockedAlpha`. diff --git a/docs/errors/chain/ActiveLockExists.mdx b/docs/errors/chain/ActiveLockExists.mdx new file mode 100644 index 0000000000..659a93687b --- /dev/null +++ b/docs/errors/chain/ActiveLockExists.mdx @@ -0,0 +1,16 @@ +--- +title: "ActiveLockExists" +description: "The object or state already exists; treat the goal as met or pick a different target" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The destination coldkey already holds a lock with nonzero locked mass on that subnet, so a new or transferred lock cannot be created there. Inspect the `Lock` storage for the (coldkey, netuid, hotkey) triple and wait for the existing lock to unlock or remove it first. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`already_exists`](/docs/errors/already-exists). + +## Remediation + +The object or state already exists; treat the goal as met or pick a different target + +The same explanation is available in the terminal: `btcli explain ActiveLockExists`. diff --git a/docs/errors/chain/ActivityCutoffFactorMilliOutOfBounds.mdx b/docs/errors/chain/ActivityCutoffFactorMilliOutOfBounds.mdx new file mode 100644 index 0000000000..050ca06b28 --- /dev/null +++ b/docs/errors/chain/ActivityCutoffFactorMilliOutOfBounds.mdx @@ -0,0 +1,16 @@ +--- +title: "ActivityCutoffFactorMilliOutOfBounds" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The `factor_milli` argument to set the activity-cutoff factor was outside the allowed 1000-50000 per-mille range (1 to 50 tempos). Adjust the argument to fall within those bounds before resubmitting. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain ActivityCutoffFactorMilliOutOfBounds`. diff --git a/docs/errors/chain/ActivityCutoffTooLow.mdx b/docs/errors/chain/ActivityCutoffTooLow.mdx new file mode 100644 index 0000000000..da2faf6ebe --- /dev/null +++ b/docs/errors/chain/ActivityCutoffTooLow.mdx @@ -0,0 +1,16 @@ +--- +title: "ActivityCutoffTooLow" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +An admin tried to set the subnet's activity cutoff below the chain-wide minimum. Compare the requested value against the `MinActivityCutoff` storage item and current `activity_cutoff` in `btcli sudo get --netuid `. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain ActivityCutoffTooLow`. diff --git a/docs/errors/chain/AddStakeBurnRateLimitExceeded.mdx b/docs/errors/chain/AddStakeBurnRateLimitExceeded.mdx new file mode 100644 index 0000000000..a8b56bf290 --- /dev/null +++ b/docs/errors/chain/AddStakeBurnRateLimitExceeded.mdx @@ -0,0 +1,16 @@ +--- +title: "AddStakeBurnRateLimitExceeded" +description: "Wait for the rate-limit window to pass, then retry" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The add-stake-and-burn operation was submitted again before its per-key rate-limit window elapsed. Wait some blocks and retry; no active raise site exists in current code, so this mainly appears on older runtimes. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`rate_limited`](/docs/errors/rate-limited). + +## Remediation + +Wait for the rate-limit window to pass, then retry + +The same explanation is available in the terminal: `btcli explain AddStakeBurnRateLimitExceeded`. diff --git a/docs/errors/chain/AdminActionProhibitedDuringWeightsWindow.mdx b/docs/errors/chain/AdminActionProhibitedDuringWeightsWindow.mdx new file mode 100644 index 0000000000..adf62f8f11 --- /dev/null +++ b/docs/errors/chain/AdminActionProhibitedDuringWeightsWindow.mdx @@ -0,0 +1,16 @@ +--- +title: "AdminActionProhibitedDuringWeightsWindow" +description: "The required window has not opened yet; wait some blocks and retry" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +An owner or admin hyperparameter change was attempted inside the protected freeze window just before the subnet's epoch runs. Check `AdminFreezeWindow` and the blocks remaining until the next epoch (subnet tempo), then retry after the epoch fires. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`too_early`](/docs/errors/too-early). + +## Remediation + +The required window has not opened yet; wait some blocks and retry + +The same explanation is available in the terminal: `btcli explain AdminActionProhibitedDuringWeightsWindow`. diff --git a/docs/errors/chain/AllNetworksInImmunity.mdx b/docs/errors/chain/AllNetworksInImmunity.mdx new file mode 100644 index 0000000000..2f36d1c6d7 --- /dev/null +++ b/docs/errors/chain/AllNetworksInImmunity.mdx @@ -0,0 +1,16 @@ +--- +title: "AllNetworksInImmunity" +description: "The required window has not opened yet; wait some blocks and retry" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +Creating a new subnet required pruning an existing one, but every candidate subnet is still inside its network immunity period so none can be dissolved. Check `NetworkImmunityPeriod` and each subnet's `NetworkRegisteredAt`, and retry once a subnet leaves immunity. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`too_early`](/docs/errors/too-early). + +## Remediation + +The required window has not opened yet; wait some blocks and retry + +The same explanation is available in the terminal: `btcli explain AllNetworksInImmunity`. diff --git a/docs/errors/chain/AlphaHighTooLow.mdx b/docs/errors/chain/AlphaHighTooLow.mdx new file mode 100644 index 0000000000..96edb9a4fa --- /dev/null +++ b/docs/errors/chain/AlphaHighTooLow.mdx @@ -0,0 +1,16 @@ +--- +title: "AlphaHighTooLow" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The `alpha_high` argument to set liquid-alpha values was below the minimum of roughly 0.025 (1638/65535 in u16 units). Raise `alpha_high` in the `sudo_set_alpha_values` call; current values are in the `AlphaValues` storage per netuid. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain AlphaHighTooLow`. diff --git a/docs/errors/chain/AlphaLowOutOfRange.mdx b/docs/errors/chain/AlphaLowOutOfRange.mdx new file mode 100644 index 0000000000..9a8fbb62be --- /dev/null +++ b/docs/errors/chain/AlphaLowOutOfRange.mdx @@ -0,0 +1,16 @@ +--- +title: "AlphaLowOutOfRange" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The `alpha_low` argument to set liquid-alpha values was below the ~0.025 minimum (1638/65535) or greater than `alpha_high`. Choose alpha_low within that range and not exceeding alpha_high; current settings are in the `AlphaValues` storage for the netuid. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain AlphaLowOutOfRange`. diff --git a/docs/errors/chain/AlreadyApproved.mdx b/docs/errors/chain/AlreadyApproved.mdx new file mode 100644 index 0000000000..034262353b --- /dev/null +++ b/docs/errors/chain/AlreadyApproved.mdx @@ -0,0 +1,16 @@ +--- +title: "AlreadyApproved" +description: "The object or state already exists; treat the goal as met or pick a different target" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The sender has already approved this multisig call, so a repeat approval is redundant. Check the `Multisigs` entry for the call hash: the sender's account already appears in its `approvals` list. + +Declared by the `Multisig` pallet; it classifies to the semantic code [`already_exists`](/docs/errors/already-exists). + +## Remediation + +The object or state already exists; treat the goal as met or pick a different target + +The same explanation is available in the terminal: `btcli explain AlreadyApproved`. diff --git a/docs/errors/chain/AlreadyDeposited.mdx b/docs/errors/chain/AlreadyDeposited.mdx new file mode 100644 index 0000000000..c768a7e019 --- /dev/null +++ b/docs/errors/chain/AlreadyDeposited.mdx @@ -0,0 +1,16 @@ +--- +title: "AlreadyDeposited" +description: "The object or state already exists; treat the goal as met or pick a different target" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The account calling safe-mode `enter` or `extend` already has a safe-mode deposit on hold, so another cannot be placed. Check the account's `Deposits` entries and its balance held under the `EnterOrExtend` hold reason. + +Declared by the `SafeMode` pallet; it classifies to the semantic code [`already_exists`](/docs/errors/already-exists). + +## Remediation + +The object or state already exists; treat the goal as met or pick a different target + +The same explanation is available in the terminal: `btcli explain AlreadyDeposited`. diff --git a/docs/errors/chain/AlreadyFinalized.mdx b/docs/errors/chain/AlreadyFinalized.mdx new file mode 100644 index 0000000000..41551a0ec4 --- /dev/null +++ b/docs/errors/chain/AlreadyFinalized.mdx @@ -0,0 +1,16 @@ +--- +title: "AlreadyFinalized" +description: "The object or state already exists; treat the goal as met or pick a different target" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The crowdloan's `finalized` flag is already true, so withdraw, finalize, refund, dissolve, and the update calls are all rejected. Check the `finalized` field of the `Crowdloans` entry for the given `crowdloan_id`. + +Declared by the `Crowdloan` pallet; it classifies to the semantic code [`already_exists`](/docs/errors/already-exists). + +## Remediation + +The object or state already exists; treat the goal as met or pick a different target + +The same explanation is available in the terminal: `btcli explain AlreadyFinalized`. diff --git a/docs/errors/chain/AlreadyFinalizing.mdx b/docs/errors/chain/AlreadyFinalizing.mdx new file mode 100644 index 0000000000..36e22406c4 --- /dev/null +++ b/docs/errors/chain/AlreadyFinalizing.mdx @@ -0,0 +1,16 @@ +--- +title: "AlreadyFinalizing" +description: "The object or state already exists; treat the goal as met or pick a different target" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +A `finalize` call was made while another finalization is still in progress, i.e. the dispatched call from a previous finalize has not cleared. Check that the `CurrentCrowdloanId` storage value is empty before retrying. + +Declared by the `Crowdloan` pallet; it classifies to the semantic code [`already_exists`](/docs/errors/already-exists). + +## Remediation + +The object or state already exists; treat the goal as met or pick a different target + +The same explanation is available in the terminal: `btcli explain AlreadyFinalizing`. diff --git a/docs/errors/chain/AlreadyNoted.mdx b/docs/errors/chain/AlreadyNoted.mdx new file mode 100644 index 0000000000..b8057c48db --- /dev/null +++ b/docs/errors/chain/AlreadyNoted.mdx @@ -0,0 +1,16 @@ +--- +title: "AlreadyNoted" +description: "The object or state already exists; treat the goal as met or pick a different target" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The preimage for this hash has already been noted on-chain, so `note_preimage` has nothing to add. Check `RequestStatusFor` (or legacy `StatusFor`) for the hash before submitting the bytes again. + +Declared by the `Preimage` pallet; it classifies to the semantic code [`already_exists`](/docs/errors/already-exists). + +## Remediation + +The object or state already exists; treat the goal as met or pick a different target + +The same explanation is available in the terminal: `btcli explain AlreadyNoted`. diff --git a/docs/errors/chain/AlreadyStored.mdx b/docs/errors/chain/AlreadyStored.mdx new file mode 100644 index 0000000000..2d18086103 --- /dev/null +++ b/docs/errors/chain/AlreadyStored.mdx @@ -0,0 +1,16 @@ +--- +title: "AlreadyStored" +description: "The object or state already exists; treat the goal as met or pick a different target" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The call data supplied for storage is already stored on-chain for this multisig operation. Check whether the call bytes were previously stored for this call hash before submitting them again. + +Declared by the `Multisig` pallet; it classifies to the semantic code [`already_exists`](/docs/errors/already-exists). + +## Remediation + +The object or state already exists; treat the goal as met or pick a different target + +The same explanation is available in the terminal: `btcli explain AlreadyStored`. diff --git a/docs/errors/chain/AmountTooLow.mdx b/docs/errors/chain/AmountTooLow.mdx new file mode 100644 index 0000000000..d9127f4f25 --- /dev/null +++ b/docs/errors/chain/AmountTooLow.mdx @@ -0,0 +1,16 @@ +--- +title: "AmountTooLow" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +A stake, unstake, move or swap amount was zero or its TAO equivalent fell below the minimum stake threshold after fees and slippage. Compare the amount against the `DefaultMinStake` storage item and the subnet's alpha price before retrying with a larger amount. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain AmountTooLow`. diff --git a/docs/errors/chain/AnnouncedColdkeyHashDoesNotMatch.mdx b/docs/errors/chain/AnnouncedColdkeyHashDoesNotMatch.mdx new file mode 100644 index 0000000000..94c274e2ce --- /dev/null +++ b/docs/errors/chain/AnnouncedColdkeyHashDoesNotMatch.mdx @@ -0,0 +1,16 @@ +--- +title: "AnnouncedColdkeyHashDoesNotMatch" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The `new_coldkey` passed to `coldkey_swap` hashes to a different value than the hash committed in the earlier `announce_coldkey_swap`. Verify the announced hash in the `ColdkeySwapAnnouncements` storage matches the BlakeTwo256 hash of the coldkey you are swapping to. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain AnnouncedColdkeyHashDoesNotMatch`. diff --git a/docs/errors/chain/AnnouncementDepositInvariantViolated.mdx b/docs/errors/chain/AnnouncementDepositInvariantViolated.mdx new file mode 100644 index 0000000000..3ae5236d6e --- /dev/null +++ b/docs/errors/chain/AnnouncementDepositInvariantViolated.mdx @@ -0,0 +1,16 @@ +--- +title: "AnnouncementDepositInvariantViolated" +description: "A chain-side invariant failed; nothing to fix client-side \u2014 report it if it persists" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +Internal invariant failure in `announce`: recomputing the announcement deposit returned nothing after the pending announcements were updated. Inspect the caller's `Announcements` entry and the announcement deposit constants; this indicates a pallet bug rather than bad input. + +Declared by the `Proxy` pallet; it classifies to the semantic code [`internal`](/docs/errors/internal). + +## Remediation + +A chain-side invariant failed; nothing to fix client-side — report it if it persists + +The same explanation is available in the terminal: `btcli explain AnnouncementDepositInvariantViolated`. diff --git a/docs/errors/chain/ArithmeticOverflow.mdx b/docs/errors/chain/ArithmeticOverflow.mdx new file mode 100644 index 0000000000..0d40cf1a75 --- /dev/null +++ b/docs/errors/chain/ArithmeticOverflow.mdx @@ -0,0 +1,16 @@ +--- +title: "ArithmeticOverflow" +description: "A chain-side invariant failed; nothing to fix client-side \u2014 report it if it persists" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +Converting a TAO amount to alpha during batched order execution overflowed the fixed-point range, typically when the pool price is tiny relative to the batch's total buy TAO. Check the subnet's current alpha price against the batch's aggregate buy amounts. + +Declared by the `LimitOrders` pallet; it classifies to the semantic code [`internal`](/docs/errors/internal). + +## Remediation + +A chain-side invariant failed; nothing to fix client-side — report it if it persists + +The same explanation is available in the terminal: `btcli explain ArithmeticOverflow`. diff --git a/docs/errors/chain/AutoEpochAlreadyImminent.mdx b/docs/errors/chain/AutoEpochAlreadyImminent.mdx new file mode 100644 index 0000000000..b4c9e046fe --- /dev/null +++ b/docs/errors/chain/AutoEpochAlreadyImminent.mdx @@ -0,0 +1,16 @@ +--- +title: "AutoEpochAlreadyImminent" +description: "The object or state already exists; treat the goal as met or pick a different target" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +`trigger_epoch` was called when the next automatic epoch is closer than the `AdminFreezeWindow`, so a manual trigger would have no effect. Check the subnet's tempo and blocks until the next epoch, and simply wait for it to fire. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`already_exists`](/docs/errors/already-exists). + +## Remediation + +The object or state already exists; treat the goal as met or pick a different target + +The same explanation is available in the terminal: `btcli explain AutoEpochAlreadyImminent`. diff --git a/docs/errors/chain/BadEncKeyLen.mdx b/docs/errors/chain/BadEncKeyLen.mdx new file mode 100644 index 0000000000..bb4992c96c --- /dev/null +++ b/docs/errors/chain/BadEncKeyLen.mdx @@ -0,0 +1,16 @@ +--- +title: "BadEncKeyLen" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The `enc_key` passed to `announce_next_key` is not the exact ML-KEM-768 encapsulation key length (1184 bytes). Check the byte length of the `enc_key` argument before announcing. + +Declared by the `MevShield` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain BadEncKeyLen`. diff --git a/docs/errors/chain/BalanceLow.mdx b/docs/errors/chain/BalanceLow.mdx new file mode 100644 index 0000000000..b9157e3f51 --- /dev/null +++ b/docs/errors/chain/BalanceLow.mdx @@ -0,0 +1,16 @@ +--- +title: "BalanceLow" +description: "Fund the signing account or reduce the amount; check with `btcli wallet balance`" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The sender's mapped account cannot cover the transaction's value plus maximum gas fee, detected during validation or when withdrawing the fee. Check the account balance against `value` plus `gas_limit` times the effective gas price. + +Declared by the `EVM` pallet; it classifies to the semantic code [`insufficient_balance`](/docs/errors/insufficient-balance). + +## Remediation + +Fund the signing account or reduce the amount; check with `btcli wallet balance` + +The same explanation is available in the terminal: `btcli explain BalanceLow`. diff --git a/docs/errors/chain/BalanceWithdrawalError.mdx b/docs/errors/chain/BalanceWithdrawalError.mdx new file mode 100644 index 0000000000..7ddc407dfc --- /dev/null +++ b/docs/errors/chain/BalanceWithdrawalError.mdx @@ -0,0 +1,16 @@ +--- +title: "BalanceWithdrawalError" +description: "Fund the signing account or reduce the amount; check with `btcli wallet balance`" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The requested TAO could not be withdrawn from the coldkey's free balance, typically due to insufficient funds, the existential deposit, or frozen/reserved balance. Check the coldkey's balance with `btcli wallet balance` and reduce the amount or top up. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`insufficient_balance`](/docs/errors/insufficient-balance). + +## Remediation + +Fund the signing account or reduce the amount; check with `btcli wallet balance` + +The same explanation is available in the terminal: `btcli explain BalanceWithdrawalError`. diff --git a/docs/errors/chain/BeneficiaryDoesNotOwnHotkey.mdx b/docs/errors/chain/BeneficiaryDoesNotOwnHotkey.mdx new file mode 100644 index 0000000000..325e63f27f --- /dev/null +++ b/docs/errors/chain/BeneficiaryDoesNotOwnHotkey.mdx @@ -0,0 +1,16 @@ +--- +title: "BeneficiaryDoesNotOwnHotkey" +description: "Sign with the key or origin that owns the target object, then retry" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +When ending a subnet lease, the hotkey passed for the ownership handover is not owned by the lease's beneficiary coldkey. Check the `Owner` storage for that hotkey and pass a hotkey the beneficiary coldkey actually owns. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`not_authorized`](/docs/errors/not-authorized). + +## Remediation + +Sign with the key or origin that owns the target object, then retry + +The same explanation is available in the terminal: `btcli explain BeneficiaryDoesNotOwnHotkey`. diff --git a/docs/errors/chain/BlockDurationTooLong.mdx b/docs/errors/chain/BlockDurationTooLong.mdx new file mode 100644 index 0000000000..8d8864ef45 --- /dev/null +++ b/docs/errors/chain/BlockDurationTooLong.mdx @@ -0,0 +1,16 @@ +--- +title: "BlockDurationTooLong" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The requested `end` block is more than `MaximumBlockDuration` blocks after the current block. Compare `end` minus the current block number against the `MaximumBlockDuration` pallet constant when calling `create` or `update_end`. + +Declared by the `Crowdloan` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain BlockDurationTooLong`. diff --git a/docs/errors/chain/BlockDurationTooShort.mdx b/docs/errors/chain/BlockDurationTooShort.mdx new file mode 100644 index 0000000000..a672feeab6 --- /dev/null +++ b/docs/errors/chain/BlockDurationTooShort.mdx @@ -0,0 +1,16 @@ +--- +title: "BlockDurationTooShort" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The requested `end` block is fewer than `MinimumBlockDuration` blocks after the current block. Compare `end` minus the current block number against the `MinimumBlockDuration` pallet constant when calling `create` or `update_end`. + +Declared by the `Crowdloan` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain BlockDurationTooShort`. diff --git a/docs/errors/chain/BondsMovingAverageMaxReached.mdx b/docs/errors/chain/BondsMovingAverageMaxReached.mdx new file mode 100644 index 0000000000..1eec0c8e63 --- /dev/null +++ b/docs/errors/chain/BondsMovingAverageMaxReached.mdx @@ -0,0 +1,16 @@ +--- +title: "BondsMovingAverageMaxReached" +description: "A chain-side capacity limit was hit; reduce the size or count of the request" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +A subnet owner called `sudo_set_bonds_moving_average` with a value above 975000, the cap for owner-set values; root is exempt. Lower the `bonds_moving_average` argument or submit the call as root. + +Declared by the `AdminUtils` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). + +## Remediation + +A chain-side capacity limit was hit; reduce the size or count of the request + +The same explanation is available in the terminal: `btcli explain BondsMovingAverageMaxReached`. diff --git a/docs/errors/chain/CallDisabled.mdx b/docs/errors/chain/CallDisabled.mdx new file mode 100644 index 0000000000..1cd26e77a6 --- /dev/null +++ b/docs/errors/chain/CallDisabled.mdx @@ -0,0 +1,16 @@ +--- +title: "CallDisabled" +description: "This call or feature is switched off on this network" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The extrinsic has been switched off in the current runtime and cannot be dispatched. There is no active raise site in current code; if seen, check release notes for whether the call was re-enabled in a newer runtime version. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`disabled`](/docs/errors/disabled). + +## Remediation + +This call or feature is switched off on this network + +The same explanation is available in the terminal: `btcli explain CallDisabled`. diff --git a/docs/errors/chain/CallFiltered.mdx b/docs/errors/chain/CallFiltered.mdx new file mode 100644 index 0000000000..2eae02ae59 --- /dev/null +++ b/docs/errors/chain/CallFiltered.mdx @@ -0,0 +1,16 @@ +--- +title: "CallFiltered" +description: "Sign with the key or origin that owns the target object, then retry" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The runtime's origin call filter (e.g. `BaseCallFilter` or a restricted origin) rejected this call before dispatch. Check whether the specific call is permitted for the origin you used, including any proxy or safe-mode filtering in effect. + +Declared by the `System` pallet; it classifies to the semantic code [`not_authorized`](/docs/errors/not-authorized). + +## Remediation + +Sign with the key or origin that owns the target object, then retry + +The same explanation is available in the terminal: `btcli explain CallFiltered`. diff --git a/docs/errors/chain/CallUnavailable.mdx b/docs/errors/chain/CallUnavailable.mdx new file mode 100644 index 0000000000..1b862efd18 --- /dev/null +++ b/docs/errors/chain/CallUnavailable.mdx @@ -0,0 +1,16 @@ +--- +title: "CallUnavailable" +description: "The referenced object is not on-chain; check the identifier" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +During `finalize` the crowdloan's stored call could not be fetched from preimage storage, so nothing was dispatched. Check that the preimage referenced by the `call` field of the `Crowdloans` entry still exists in the preimage pallet. + +Declared by the `Crowdloan` pallet; it classifies to the semantic code [`not_found`](/docs/errors/not-found). + +## Remediation + +The referenced object is not on-chain; check the identifier + +The same explanation is available in the terminal: `btcli explain CallUnavailable`. diff --git a/docs/errors/chain/CanNotSetRootNetworkWeights.mdx b/docs/errors/chain/CanNotSetRootNetworkWeights.mdx new file mode 100644 index 0000000000..154248049d --- /dev/null +++ b/docs/errors/chain/CanNotSetRootNetworkWeights.mdx @@ -0,0 +1,16 @@ +--- +title: "CanNotSetRootNetworkWeights" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +`set_weights` was called with netuid 0, the root network, where normal weight setting is not allowed. Use a non-root `netuid` argument; root weights are handled by a separate mechanism. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain CanNotSetRootNetworkWeights`. diff --git a/docs/errors/chain/CannotAddSelfAsDelegateDependency.mdx b/docs/errors/chain/CannotAddSelfAsDelegateDependency.mdx new file mode 100644 index 0000000000..5283401b9f --- /dev/null +++ b/docs/errors/chain/CannotAddSelfAsDelegateDependency.mdx @@ -0,0 +1,16 @@ +--- +title: "CannotAddSelfAsDelegateDependency" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +A contract called `lock_delegate_dependency` with its own code hash, which is not permitted. Check the code hash argument passed to the delegate dependency API against the contract's own code hash. + +Declared by the `Contracts` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain CannotAddSelfAsDelegateDependency`. diff --git a/docs/errors/chain/CannotAffordLockCost.mdx b/docs/errors/chain/CannotAffordLockCost.mdx new file mode 100644 index 0000000000..e601abb024 --- /dev/null +++ b/docs/errors/chain/CannotAffordLockCost.mdx @@ -0,0 +1,16 @@ +--- +title: "CannotAffordLockCost" +description: "Fund the signing account or reduce the amount; check with `btcli wallet balance`" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The coldkey's free balance cannot cover the current dynamic subnet-creation lock cost. Compare `btcli subnets create-cost` (or `btcli query subnet-registration-cost`) against the coldkey balance from `btcli wallet balance` before registering a subnet. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`insufficient_balance`](/docs/errors/insufficient-balance). + +## Remediation + +Fund the signing account or reduce the amount; check with `btcli wallet balance` + +The same explanation is available in the terminal: `btcli explain CannotAffordLockCost`. diff --git a/docs/errors/chain/CannotBurnOrRecycleOnRootSubnet.mdx b/docs/errors/chain/CannotBurnOrRecycleOnRootSubnet.mdx new file mode 100644 index 0000000000..7e86f9b560 --- /dev/null +++ b/docs/errors/chain/CannotBurnOrRecycleOnRootSubnet.mdx @@ -0,0 +1,16 @@ +--- +title: "CannotBurnOrRecycleOnRootSubnet" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +`recycle_alpha` or `burn_alpha` was called with netuid 0, and TAO on the root subnet cannot be burned or recycled. Pass a non-root `netuid` argument for the subnet whose alpha you want to recycle or burn. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain CannotBurnOrRecycleOnRootSubnet`. diff --git a/docs/errors/chain/CannotEndInPast.mdx b/docs/errors/chain/CannotEndInPast.mdx new file mode 100644 index 0000000000..c001b5e819 --- /dev/null +++ b/docs/errors/chain/CannotEndInPast.mdx @@ -0,0 +1,16 @@ +--- +title: "CannotEndInPast" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The `end` block passed to `create` or `update_end` is not after the current block. Compare the `end` argument against the current block number; it must be strictly greater. + +Declared by the `Crowdloan` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain CannotEndInPast`. diff --git a/docs/errors/chain/CannotReleaseYet.mdx b/docs/errors/chain/CannotReleaseYet.mdx new file mode 100644 index 0000000000..e033f8c6ad --- /dev/null +++ b/docs/errors/chain/CannotReleaseYet.mdx @@ -0,0 +1,16 @@ +--- +title: "CannotReleaseYet" +description: "The required window has not opened yet; wait some blocks and retry" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +`release_deposit` was called too early: the current block must exceed the deposit's block plus `ReleaseDelay`, and safe-mode must be exited. Check the block key of the entry in `Deposits` against the `ReleaseDelay` config. + +Declared by the `SafeMode` pallet; it classifies to the semantic code [`too_early`](/docs/errors/too-early). + +## Remediation + +The required window has not opened yet; wait some blocks and retry + +The same explanation is available in the terminal: `btcli explain CannotReleaseYet`. diff --git a/docs/errors/chain/CannotUseSystemAccount.mdx b/docs/errors/chain/CannotUseSystemAccount.mdx new file mode 100644 index 0000000000..d16ae1a8c3 --- /dev/null +++ b/docs/errors/chain/CannotUseSystemAccount.mdx @@ -0,0 +1,16 @@ +--- +title: "CannotUseSystemAccount" +description: "Sign with the key or origin that owns the target object, then retry" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The hotkey supplied for registration, hotkey swap, or subnet-owner-hotkey assignment is a reserved subnet system account. Use a regular user-generated hotkey instead; system accounts are derived per-subnet and rejected by `is_subnet_account_id`. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`not_authorized`](/docs/errors/not-authorized). + +## Remediation + +Sign with the key or origin that owns the target object, then retry + +The same explanation is available in the terminal: `btcli explain CannotUseSystemAccount`. diff --git a/docs/errors/chain/CapNotRaised.mdx b/docs/errors/chain/CapNotRaised.mdx new file mode 100644 index 0000000000..7c38c3e9e4 --- /dev/null +++ b/docs/errors/chain/CapNotRaised.mdx @@ -0,0 +1,16 @@ +--- +title: "CapNotRaised" +description: "The required window has not opened yet; wait some blocks and retry" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +`finalize` was called before the crowdloan's `raised` amount equals its `cap`. Compare the `raised` and `cap` fields of the `Crowdloans` entry; contribute the remainder or lower the cap with `update_cap` before finalizing. + +Declared by the `Crowdloan` pallet; it classifies to the semantic code [`too_early`](/docs/errors/too-early). + +## Remediation + +The required window has not opened yet; wait some blocks and retry + +The same explanation is available in the terminal: `btcli explain CapNotRaised`. diff --git a/docs/errors/chain/CapRaised.mdx b/docs/errors/chain/CapRaised.mdx new file mode 100644 index 0000000000..410c9f055b --- /dev/null +++ b/docs/errors/chain/CapRaised.mdx @@ -0,0 +1,16 @@ +--- +title: "CapRaised" +description: "A chain-side capacity limit was hit; reduce the size or count of the request" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +A contribution was attempted on a crowdloan whose `raised` amount has already reached its `cap`, so no further contributions are accepted. Compare the `raised` and `cap` fields of the `Crowdloans` entry for the `crowdloan_id`. + +Declared by the `Crowdloan` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). + +## Remediation + +A chain-side capacity limit was hit; reduce the size or count of the request + +The same explanation is available in the terminal: `btcli explain CapRaised`. diff --git a/docs/errors/chain/CapTooLow.mdx b/docs/errors/chain/CapTooLow.mdx new file mode 100644 index 0000000000..42d0c51bfe --- /dev/null +++ b/docs/errors/chain/CapTooLow.mdx @@ -0,0 +1,16 @@ +--- +title: "CapTooLow" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +On `create` the `cap` is not strictly greater than the initial `deposit`, or on `update_cap` the new cap is below the amount already raised. Compare the cap argument against the `deposit` or the `raised` field of the `Crowdloans` entry. + +Declared by the `Crowdloan` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain CapTooLow`. diff --git a/docs/errors/chain/ChainIdMismatch.mdx b/docs/errors/chain/ChainIdMismatch.mdx new file mode 100644 index 0000000000..da5f9b7fa6 --- /dev/null +++ b/docs/errors/chain/ChainIdMismatch.mdx @@ -0,0 +1,16 @@ +--- +title: "ChainIdMismatch" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The order payload's `chain_id` field differs from this chain's configured EVM chain id, e.g. an order signed for testnet was submitted to mainnet. Compare the order's `chain_id` with the runtime's `pallet_evm_chain_id` value and re-sign if needed. + +Declared by the `LimitOrders` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain ChainIdMismatch`. diff --git a/docs/errors/chain/ChangePending.mdx b/docs/errors/chain/ChangePending.mdx new file mode 100644 index 0000000000..08a1550ee5 --- /dev/null +++ b/docs/errors/chain/ChangePending.mdx @@ -0,0 +1,16 @@ +--- +title: "ChangePending" +description: "The object or state already exists; treat the goal as met or pick a different target" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +A GRANDPA authority-set change has already been signalled and is still pending, so a new change cannot be scheduled. Check the Grandpa `PendingChange` and `State` storage and wait for the pending change to be applied first. + +Declared by the `Grandpa` pallet; it classifies to the semantic code [`already_exists`](/docs/errors/already-exists). + +## Remediation + +The object or state already exists; treat the goal as met or pick a different target + +The same explanation is available in the terminal: `btcli explain ChangePending`. diff --git a/docs/errors/chain/ChildParentInconsistency.mdx b/docs/errors/chain/ChildParentInconsistency.mdx new file mode 100644 index 0000000000..f8a09f790e --- /dev/null +++ b/docs/errors/chain/ChildParentInconsistency.mdx @@ -0,0 +1,16 @@ +--- +title: "ChildParentInconsistency" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +A `set_children` or parent-delegation call would make the same hotkey appear as both a child and a parent, or referenced a child missing from the proposed mapping. Inspect the `ChildKeys` and `ParentKeys` storage for the hotkeys involved and remove the overlap. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain ChildParentInconsistency`. diff --git a/docs/errors/chain/CodeInUse.mdx b/docs/errors/chain/CodeInUse.mdx new file mode 100644 index 0000000000..a060517b50 --- /dev/null +++ b/docs/errors/chain/CodeInUse.mdx @@ -0,0 +1,16 @@ +--- +title: "CodeInUse" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +`remove_code` was refused because at least one contract instance still references the code hash. Check the code's reference count and terminate or `set_code` the contracts using it before removal. + +Declared by the `Contracts` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain CodeInUse`. diff --git a/docs/errors/chain/CodeInfoNotFound.mdx b/docs/errors/chain/CodeInfoNotFound.mdx new file mode 100644 index 0000000000..f7f2beeb4d --- /dev/null +++ b/docs/errors/chain/CodeInfoNotFound.mdx @@ -0,0 +1,16 @@ +--- +title: "CodeInfoNotFound" +description: "The referenced object is not on-chain; check the identifier" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +No `CodeInfoOf` entry exists for the supplied code hash, so its owner and deposit metadata cannot be read. Verify the code hash argument and that the code was uploaded and not since removed. + +Declared by the `Contracts` pallet; it classifies to the semantic code [`not_found`](/docs/errors/not-found). + +## Remediation + +The referenced object is not on-chain; check the identifier + +The same explanation is available in the terminal: `btcli explain CodeInfoNotFound`. diff --git a/docs/errors/chain/CodeNotFound.mdx b/docs/errors/chain/CodeNotFound.mdx new file mode 100644 index 0000000000..0f0203e9a2 --- /dev/null +++ b/docs/errors/chain/CodeNotFound.mdx @@ -0,0 +1,16 @@ +--- +title: "CodeNotFound" +description: "The referenced object is not on-chain; check the identifier" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +No uploaded WASM binary exists under the supplied code hash. Verify the `code_hash` argument used in instantiation, `set_code`, or a delegate call, and that the code was uploaded via `upload_code` and not removed. + +Declared by the `Contracts` pallet; it classifies to the semantic code [`not_found`](/docs/errors/not-found). + +## Remediation + +The referenced object is not on-chain; check the identifier + +The same explanation is available in the terminal: `btcli explain CodeNotFound`. diff --git a/docs/errors/chain/CodeRejected.mdx b/docs/errors/chain/CodeRejected.mdx new file mode 100644 index 0000000000..2e8cc00e24 --- /dev/null +++ b/docs/errors/chain/CodeRejected.mdx @@ -0,0 +1,16 @@ +--- +title: "CodeRejected" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The uploaded WASM failed validation, most often because it imports a host API the node does not support, e.g. newer ink! against an older node. Rerun the node with `-lruntime::contracts=debug` to see the detailed rejection reason. + +Declared by the `Contracts` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain CodeRejected`. diff --git a/docs/errors/chain/CodeTooLarge.mdx b/docs/errors/chain/CodeTooLarge.mdx new file mode 100644 index 0000000000..25aef781d7 --- /dev/null +++ b/docs/errors/chain/CodeTooLarge.mdx @@ -0,0 +1,16 @@ +--- +title: "CodeTooLarge" +description: "A chain-side capacity limit was hit; reduce the size or count of the request" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The code blob passed to `instantiate_with_code` or `upload_code` exceeds the maximum code length in the pallet's schedule. Compare the WASM binary size against the schedule's code length limit and shrink the contract. + +Declared by the `Contracts` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). + +## Remediation + +A chain-side capacity limit was hit; reduce the size or count of the request + +The same explanation is available in the terminal: `btcli explain CodeTooLarge`. diff --git a/docs/errors/chain/ColdKeyAlreadyAssociated.mdx b/docs/errors/chain/ColdKeyAlreadyAssociated.mdx new file mode 100644 index 0000000000..c44c420974 --- /dev/null +++ b/docs/errors/chain/ColdKeyAlreadyAssociated.mdx @@ -0,0 +1,16 @@ +--- +title: "ColdKeyAlreadyAssociated" +description: "The object or state already exists; treat the goal as met or pick a different target" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The destination coldkey of a coldkey swap already has staking hotkeys associated with it, so it cannot receive the swapped identity. Check the `StakingHotkeys` storage for the new coldkey and swap to a fresh, unused coldkey instead. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`already_exists`](/docs/errors/already-exists). + +## Remediation + +The object or state already exists; treat the goal as met or pick a different target + +The same explanation is available in the terminal: `btcli explain ColdKeyAlreadyAssociated`. diff --git a/docs/errors/chain/ColdkeySwapAlreadyDisputed.mdx b/docs/errors/chain/ColdkeySwapAlreadyDisputed.mdx new file mode 100644 index 0000000000..9ae9d775e7 --- /dev/null +++ b/docs/errors/chain/ColdkeySwapAlreadyDisputed.mdx @@ -0,0 +1,16 @@ +--- +title: "ColdkeySwapAlreadyDisputed" +description: "The object or state already exists; treat the goal as met or pick a different target" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +`dispute_coldkey_swap` was called for a coldkey whose pending swap announcement is already under dispute. Check the `ColdkeySwapDisputes` storage for the coldkey; no further dispute action is needed. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`already_exists`](/docs/errors/already-exists). + +## Remediation + +The object or state already exists; treat the goal as met or pick a different target + +The same explanation is available in the terminal: `btcli explain ColdkeySwapAlreadyDisputed`. diff --git a/docs/errors/chain/ColdkeySwapAnnounced.mdx b/docs/errors/chain/ColdkeySwapAnnounced.mdx new file mode 100644 index 0000000000..132a542099 --- /dev/null +++ b/docs/errors/chain/ColdkeySwapAnnounced.mdx @@ -0,0 +1,16 @@ +--- +title: "ColdkeySwapAnnounced" +description: "The object or state already exists; treat the goal as met or pick a different target" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The coldkey has a pending swap announcement, so all but a small allow-list of extrinsics are blocked until the swap completes or is cleared. Check the `ColdkeySwapAnnouncements` storage for the coldkey and either finish the swap with `coldkey_swap` or clear the announcement. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`already_exists`](/docs/errors/already-exists). + +## Remediation + +The object or state already exists; treat the goal as met or pick a different target + +The same explanation is available in the terminal: `btcli explain ColdkeySwapAnnounced`. diff --git a/docs/errors/chain/ColdkeySwapAnnouncementNotFound.mdx b/docs/errors/chain/ColdkeySwapAnnouncementNotFound.mdx new file mode 100644 index 0000000000..aa075341ae --- /dev/null +++ b/docs/errors/chain/ColdkeySwapAnnouncementNotFound.mdx @@ -0,0 +1,16 @@ +--- +title: "ColdkeySwapAnnouncementNotFound" +description: "The referenced object is not on-chain; check the identifier" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +`coldkey_swap`, `dispute_coldkey_swap`, or `clear_coldkey_swap_announcement` was called for a coldkey with no pending announcement. Check the `ColdkeySwapAnnouncements` storage; you must call `announce_coldkey_swap` first. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`not_found`](/docs/errors/not-found). + +## Remediation + +The referenced object is not on-chain; check the identifier + +The same explanation is available in the terminal: `btcli explain ColdkeySwapAnnouncementNotFound`. diff --git a/docs/errors/chain/ColdkeySwapClearTooEarly.mdx b/docs/errors/chain/ColdkeySwapClearTooEarly.mdx new file mode 100644 index 0000000000..d73693ad7f --- /dev/null +++ b/docs/errors/chain/ColdkeySwapClearTooEarly.mdx @@ -0,0 +1,16 @@ +--- +title: "ColdkeySwapClearTooEarly" +description: "The required window has not opened yet; wait some blocks and retry" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The swap announcement cannot be cleared until the reannouncement delay after the announcement's execution block has passed. Compare the current block with the `when` stored in `ColdkeySwapAnnouncements` plus `ColdkeySwapReannouncementDelay` and retry later. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`too_early`](/docs/errors/too-early). + +## Remediation + +The required window has not opened yet; wait some blocks and retry + +The same explanation is available in the terminal: `btcli explain ColdkeySwapClearTooEarly`. diff --git a/docs/errors/chain/ColdkeySwapDisputed.mdx b/docs/errors/chain/ColdkeySwapDisputed.mdx new file mode 100644 index 0000000000..262ecc40de --- /dev/null +++ b/docs/errors/chain/ColdkeySwapDisputed.mdx @@ -0,0 +1,16 @@ +--- +title: "ColdkeySwapDisputed" +description: "Sign with the key or origin that owns the target object, then retry" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +All extrinsics from this coldkey are blocked because its pending coldkey swap is under dispute. Check the `ColdkeySwapDisputes` storage for the coldkey; the dispute must be resolved by root before the account can transact. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`not_authorized`](/docs/errors/not-authorized). + +## Remediation + +Sign with the key or origin that owns the target object, then retry + +The same explanation is available in the terminal: `btcli explain ColdkeySwapDisputed`. diff --git a/docs/errors/chain/ColdkeySwapReannouncedTooEarly.mdx b/docs/errors/chain/ColdkeySwapReannouncedTooEarly.mdx new file mode 100644 index 0000000000..49082e3169 --- /dev/null +++ b/docs/errors/chain/ColdkeySwapReannouncedTooEarly.mdx @@ -0,0 +1,16 @@ +--- +title: "ColdkeySwapReannouncedTooEarly" +description: "The required window has not opened yet; wait some blocks and retry" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +`announce_coldkey_swap` was called again before the reannouncement delay after the previous announcement's execution block elapsed. Compare the current block with the stored announcement time plus `ColdkeySwapReannouncementDelay` and retry later. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`too_early`](/docs/errors/too-early). + +## Remediation + +The required window has not opened yet; wait some blocks and retry + +The same explanation is available in the terminal: `btcli explain ColdkeySwapReannouncedTooEarly`. diff --git a/docs/errors/chain/ColdkeySwapTooEarly.mdx b/docs/errors/chain/ColdkeySwapTooEarly.mdx new file mode 100644 index 0000000000..5ee6a08a80 --- /dev/null +++ b/docs/errors/chain/ColdkeySwapTooEarly.mdx @@ -0,0 +1,16 @@ +--- +title: "ColdkeySwapTooEarly" +description: "The required window has not opened yet; wait some blocks and retry" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +`coldkey_swap` was executed before the announcement delay had elapsed since `announce_coldkey_swap`. Check the execution block stored in `ColdkeySwapAnnouncements` (announcement time plus `ColdkeySwapAnnouncementDelay`) and wait until then. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`too_early`](/docs/errors/too-early). + +## Remediation + +The required window has not opened yet; wait some blocks and retry + +The same explanation is available in the terminal: `btcli explain ColdkeySwapTooEarly`. diff --git a/docs/errors/chain/CommitRevealDisabled.mdx b/docs/errors/chain/CommitRevealDisabled.mdx new file mode 100644 index 0000000000..8d2fc08d7d --- /dev/null +++ b/docs/errors/chain/CommitRevealDisabled.mdx @@ -0,0 +1,16 @@ +--- +title: "CommitRevealDisabled" +description: "This call or feature is switched off on this network" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +A weight commit or reveal was submitted on a subnet where commit-reveal is turned off. Check the `commit_reveal_weights_enabled` hyperparameter for the netuid (`btcli sudo get --netuid `); use plain `set_weights` instead when it is disabled. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`disabled`](/docs/errors/disabled). + +## Remediation + +This call or feature is switched off on this network + +The same explanation is available in the terminal: `btcli explain CommitRevealDisabled`. diff --git a/docs/errors/chain/CommitRevealEnabled.mdx b/docs/errors/chain/CommitRevealEnabled.mdx new file mode 100644 index 0000000000..4be0f9155d --- /dev/null +++ b/docs/errors/chain/CommitRevealEnabled.mdx @@ -0,0 +1,16 @@ +--- +title: "CommitRevealEnabled" +description: "This call or feature is switched off on this network" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +Plain `set_weights` was called on a subnet where commit-reveal is enabled, which requires the commit/reveal flow instead. Check the `commit_reveal_weights_enabled` hyperparameter for the netuid and switch to `commit_weights`/`reveal_weights`. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`disabled`](/docs/errors/disabled). + +## Remediation + +This call or feature is switched off on this network + +The same explanation is available in the terminal: `btcli explain CommitRevealEnabled`. diff --git a/docs/errors/chain/CommittingWeightsTooFast.mdx b/docs/errors/chain/CommittingWeightsTooFast.mdx new file mode 100644 index 0000000000..1cb21ee9f6 --- /dev/null +++ b/docs/errors/chain/CommittingWeightsTooFast.mdx @@ -0,0 +1,16 @@ +--- +title: "CommittingWeightsTooFast" +description: "Wait for the rate-limit window to pass, then retry" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The neuron committed weights again before the per-UID rate limit elapsed since its last commit on that subnet. Compare blocks since the last commit against the `weights_rate_limit` hyperparameter (`btcli sudo get --netuid `) and wait. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`rate_limited`](/docs/errors/rate-limited). + +## Remediation + +Wait for the rate-limit window to pass, then retry + +The same explanation is available in the terminal: `btcli explain CommittingWeightsTooFast`. diff --git a/docs/errors/chain/ContractNotFound.mdx b/docs/errors/chain/ContractNotFound.mdx new file mode 100644 index 0000000000..eb4cdc871f --- /dev/null +++ b/docs/errors/chain/ContractNotFound.mdx @@ -0,0 +1,16 @@ +--- +title: "ContractNotFound" +description: "The referenced object is not on-chain; check the identifier" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +No contract instance exists at the destination address; the account has no `ContractInfoOf` entry. Verify the `dest` address and that the contract was instantiated and has not been terminated. + +Declared by the `Contracts` pallet; it classifies to the semantic code [`not_found`](/docs/errors/not-found). + +## Remediation + +The referenced object is not on-chain; check the identifier + +The same explanation is available in the terminal: `btcli explain ContractNotFound`. diff --git a/docs/errors/chain/ContractReverted.mdx b/docs/errors/chain/ContractReverted.mdx new file mode 100644 index 0000000000..28b38df8a7 --- /dev/null +++ b/docs/errors/chain/ContractReverted.mdx @@ -0,0 +1,16 @@ +--- +title: "ContractReverted" +description: "A chain-side invariant failed; nothing to fix client-side \u2014 report it if it persists" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The contract ran to completion but returned with the REVERT flag set, rolling back its state changes; only extrinsics surface this as an error. Dry-run the call via RPC and decode the returned output data for the contract's error value. + +Declared by the `Contracts` pallet; it classifies to the semantic code [`internal`](/docs/errors/internal). + +## Remediation + +A chain-side invariant failed; nothing to fix client-side — report it if it persists + +The same explanation is available in the terminal: `btcli explain ContractReverted`. diff --git a/docs/errors/chain/ContractTrapped.mdx b/docs/errors/chain/ContractTrapped.mdx new file mode 100644 index 0000000000..fc3d43b42f --- /dev/null +++ b/docs/errors/chain/ContractTrapped.mdx @@ -0,0 +1,16 @@ +--- +title: "ContractTrapped" +description: "A chain-side invariant failed; nothing to fix client-side \u2014 report it if it persists" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The contract aborted with a WASM trap, e.g. a panic, unreachable instruction, or memory violation, instead of returning normally. Dry-run the call with debug messages enabled and check the input data against the contract's expectations. + +Declared by the `Contracts` pallet; it classifies to the semantic code [`internal`](/docs/errors/internal). + +## Remediation + +A chain-side invariant failed; nothing to fix client-side — report it if it persists + +The same explanation is available in the terminal: `btcli explain ContractTrapped`. diff --git a/docs/errors/chain/ContributionPeriodEnded.mdx b/docs/errors/chain/ContributionPeriodEnded.mdx new file mode 100644 index 0000000000..cb35f6541d --- /dev/null +++ b/docs/errors/chain/ContributionPeriodEnded.mdx @@ -0,0 +1,16 @@ +--- +title: "ContributionPeriodEnded" +description: "The window has closed; restart the flow with fresh state" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +A contribution was made at or after the crowdloan's `end` block. Compare the `end` field of the `Crowdloans` entry with the current block number; the creator can extend it with `update_end` while the crowdloan is not finalized. + +Declared by the `Crowdloan` pallet; it classifies to the semantic code [`expired`](/docs/errors/expired). + +## Remediation + +The window has closed; restart the flow with fresh state + +The same explanation is available in the terminal: `btcli explain ContributionPeriodEnded`. diff --git a/docs/errors/chain/ContributionPeriodNotEnded.mdx b/docs/errors/chain/ContributionPeriodNotEnded.mdx new file mode 100644 index 0000000000..586770b91f --- /dev/null +++ b/docs/errors/chain/ContributionPeriodNotEnded.mdx @@ -0,0 +1,16 @@ +--- +title: "ContributionPeriodNotEnded" +description: "The required window has not opened yet; wait some blocks and retry" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The operation requires the crowdloan's contribution period to be over, but the current block is still before the crowdloan's `end` block. Compare the `end` field of the `Crowdloans` entry for the `crowdloan_id` with the current block number. + +Declared by the `Crowdloan` pallet; it classifies to the semantic code [`too_early`](/docs/errors/too-early). + +## Remediation + +The required window has not opened yet; wait some blocks and retry + +The same explanation is available in the terminal: `btcli explain ContributionPeriodNotEnded`. diff --git a/docs/errors/chain/ContributionTooLow.mdx b/docs/errors/chain/ContributionTooLow.mdx new file mode 100644 index 0000000000..4fd32764e7 --- /dev/null +++ b/docs/errors/chain/ContributionTooLow.mdx @@ -0,0 +1,16 @@ +--- +title: "ContributionTooLow" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The `amount` passed to `contribute` is below the crowdloan's configured minimum contribution. Check the `min_contribution` field of the `Crowdloans` entry for the `crowdloan_id` and contribute at least that amount. + +Declared by the `Crowdloan` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain ContributionTooLow`. diff --git a/docs/errors/chain/CreateOriginNotAllowed.mdx b/docs/errors/chain/CreateOriginNotAllowed.mdx new file mode 100644 index 0000000000..adf8af5863 --- /dev/null +++ b/docs/errors/chain/CreateOriginNotAllowed.mdx @@ -0,0 +1,16 @@ +--- +title: "CreateOriginNotAllowed" +description: "Sign with the key or origin that owns the target object, then retry" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +A CREATE, or a CALL that performs a nested CREATE, was attempted from an EVM address not permitted to deploy contracts. Check whether the deploying address is in the chain's allowed-deployers list. + +Declared by the `EVM` pallet; it classifies to the semantic code [`not_authorized`](/docs/errors/not-authorized). + +## Remediation + +Sign with the key or origin that owns the target object, then retry + +The same explanation is available in the terminal: `btcli explain CreateOriginNotAllowed`. diff --git a/docs/errors/chain/CurrencyError.mdx b/docs/errors/chain/CurrencyError.mdx new file mode 100644 index 0000000000..696b38ae8e --- /dev/null +++ b/docs/errors/chain/CurrencyError.mdx @@ -0,0 +1,16 @@ +--- +title: "CurrencyError" +description: "A chain-side invariant failed; nothing to fix client-side \u2014 report it if it persists" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +A balance hold, release, or burn inside pallet-safe-mode failed while managing an enter/extend deposit. Check the account's free balance and existing holds under the safe-mode `EnterOrExtend` hold reason. + +Declared by the `SafeMode` pallet; it classifies to the semantic code [`internal`](/docs/errors/internal). + +## Remediation + +A chain-side invariant failed; nothing to fix client-side — report it if it persists + +The same explanation is available in the terminal: `btcli explain CurrencyError`. diff --git a/docs/errors/chain/DeadAccount.mdx b/docs/errors/chain/DeadAccount.mdx new file mode 100644 index 0000000000..881159875f --- /dev/null +++ b/docs/errors/chain/DeadAccount.mdx @@ -0,0 +1,16 @@ +--- +title: "DeadAccount" +description: "The referenced object is not on-chain; check the identifier" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The beneficiary account does not exist and this operation is not allowed to create it. Check `System.Account` for the destination: it must already hold at least the existential deposit before the operation runs. + +Declared by the `Balances` pallet; it classifies to the semantic code [`not_found`](/docs/errors/not-found). + +## Remediation + +The referenced object is not on-chain; check the identifier + +The same explanation is available in the terminal: `btcli explain DeadAccount`. diff --git a/docs/errors/chain/DecodingFailed.mdx b/docs/errors/chain/DecodingFailed.mdx new file mode 100644 index 0000000000..6d5ca01254 --- /dev/null +++ b/docs/errors/chain/DecodingFailed.mdx @@ -0,0 +1,16 @@ +--- +title: "DecodingFailed" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +Input bytes passed to a contract API host function could not be SCALE-decoded into the expected type. Check the encoding of the call's input data or the argument bytes the contract passes to the runtime API. + +Declared by the `Contracts` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain DecodingFailed`. diff --git a/docs/errors/chain/DelegateDependencyAlreadyExists.mdx b/docs/errors/chain/DelegateDependencyAlreadyExists.mdx new file mode 100644 index 0000000000..a1b7251c81 --- /dev/null +++ b/docs/errors/chain/DelegateDependencyAlreadyExists.mdx @@ -0,0 +1,16 @@ +--- +title: "DelegateDependencyAlreadyExists" +description: "The object or state already exists; treat the goal as met or pick a different target" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The contract called `lock_delegate_dependency` for a code hash it has already locked. Check the contract's recorded delegate dependencies before adding, and unlock the old entry first if replacing it. + +Declared by the `Contracts` pallet; it classifies to the semantic code [`already_exists`](/docs/errors/already-exists). + +## Remediation + +The object or state already exists; treat the goal as met or pick a different target + +The same explanation is available in the terminal: `btcli explain DelegateDependencyAlreadyExists`. diff --git a/docs/errors/chain/DelegateDependencyNotFound.mdx b/docs/errors/chain/DelegateDependencyNotFound.mdx new file mode 100644 index 0000000000..30730734c3 --- /dev/null +++ b/docs/errors/chain/DelegateDependencyNotFound.mdx @@ -0,0 +1,16 @@ +--- +title: "DelegateDependencyNotFound" +description: "The referenced object is not on-chain; check the identifier" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +`unlock_delegate_dependency` was called for a code hash that is not among the contract's locked delegate dependencies. Check the code hash argument against the dependencies recorded in the contract's info. + +Declared by the `Contracts` pallet; it classifies to the semantic code [`not_found`](/docs/errors/not-found). + +## Remediation + +The referenced object is not on-chain; check the identifier + +The same explanation is available in the terminal: `btcli explain DelegateDependencyNotFound`. diff --git a/docs/errors/chain/DelegateTakeTooHigh.mdx b/docs/errors/chain/DelegateTakeTooHigh.mdx new file mode 100644 index 0000000000..f7ce0560c5 --- /dev/null +++ b/docs/errors/chain/DelegateTakeTooHigh.mdx @@ -0,0 +1,16 @@ +--- +title: "DelegateTakeTooHigh" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The `take` argument exceeds the maximum delegate take allowed by the chain (18% by default). Compare the requested value against the `MaxDelegateTake` storage item and lower it. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain DelegateTakeTooHigh`. diff --git a/docs/errors/chain/DelegateTakeTooLow.mdx b/docs/errors/chain/DelegateTakeTooLow.mdx new file mode 100644 index 0000000000..c06052e67b --- /dev/null +++ b/docs/errors/chain/DelegateTakeTooLow.mdx @@ -0,0 +1,16 @@ +--- +title: "DelegateTakeTooLow" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The `take` argument was below the `MinDelegateTake` minimum, or `increase_take`/`decrease_take` was not strictly increasing/decreasing relative to the current take. Check the hotkey's current take in the `Delegates` storage and the `MinDelegateTake` storage item. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain DelegateTakeTooLow`. diff --git a/docs/errors/chain/DelegateTxRateLimitExceeded.mdx b/docs/errors/chain/DelegateTxRateLimitExceeded.mdx new file mode 100644 index 0000000000..dec9c4a386 --- /dev/null +++ b/docs/errors/chain/DelegateTxRateLimitExceeded.mdx @@ -0,0 +1,16 @@ +--- +title: "DelegateTxRateLimitExceeded" +description: "Wait for the rate-limit window to pass, then retry" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The delegate changed its take again before the per-hotkey take-change rate limit elapsed. Compare blocks since the hotkey's last take transaction against the `TxDelegateTakeRateLimit` storage item and retry later. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`rate_limited`](/docs/errors/rate-limited). + +## Remediation + +Wait for the rate-limit window to pass, then retry + +The same explanation is available in the terminal: `btcli explain DelegateTxRateLimitExceeded`. diff --git a/docs/errors/chain/DeltaZero.mdx b/docs/errors/chain/DeltaZero.mdx new file mode 100644 index 0000000000..b0d89dc42e --- /dev/null +++ b/docs/errors/chain/DeltaZero.mdx @@ -0,0 +1,16 @@ +--- +title: "DeltaZero" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The issuance adjustment was called with a delta of zero, which is meaningless. Check the `delta` argument to `force_adjust_total_issuance` and pass a strictly positive amount. + +Declared by the `Balances` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain DeltaZero`. diff --git a/docs/errors/chain/DepositCannotBeWithdrawn.mdx b/docs/errors/chain/DepositCannotBeWithdrawn.mdx new file mode 100644 index 0000000000..c261a3b135 --- /dev/null +++ b/docs/errors/chain/DepositCannotBeWithdrawn.mdx @@ -0,0 +1,16 @@ +--- +title: "DepositCannotBeWithdrawn" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The creator called `withdraw` but holds nothing above the initial deposit, which stays locked until the crowdloan is dissolved. Compare the creator's `Contributions` entry with the `deposit` field of the `Crowdloans` entry. + +Declared by the `Crowdloan` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain DepositCannotBeWithdrawn`. diff --git a/docs/errors/chain/DepositTooLow.mdx b/docs/errors/chain/DepositTooLow.mdx new file mode 100644 index 0000000000..9e9704b8f4 --- /dev/null +++ b/docs/errors/chain/DepositTooLow.mdx @@ -0,0 +1,16 @@ +--- +title: "DepositTooLow" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The `deposit` argument to `create` is below the pallet's required minimum. Check the `MinimumDeposit` pallet constant and create the crowdloan with at least that initial deposit. + +Declared by the `Crowdloan` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain DepositTooLow`. diff --git a/docs/errors/chain/Deprecated.mdx b/docs/errors/chain/Deprecated.mdx new file mode 100644 index 0000000000..c4c996c552 --- /dev/null +++ b/docs/errors/chain/Deprecated.mdx @@ -0,0 +1,16 @@ +--- +title: "Deprecated" +description: "This call or feature is switched off on this network" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The extrinsic has been removed and always fails, e.g. `schedule_swap_coldkey`, the swap pallet's user-liquidity calls, or `sudo_set_total_issuance`. Migrate to the replacement call noted in the deprecation (for coldkey swaps, `announce_coldkey_swap` plus `coldkey_swap`). + +Declared by the `SubtensorModule`, `AdminUtils`, `Swap` pallets; it classifies to the semantic code [`disabled`](/docs/errors/disabled). + +## Remediation + +This call or feature is switched off on this network + +The same explanation is available in the terminal: `btcli explain Deprecated`. diff --git a/docs/errors/chain/DisabledTemporarily.mdx b/docs/errors/chain/DisabledTemporarily.mdx new file mode 100644 index 0000000000..20844764ae --- /dev/null +++ b/docs/errors/chain/DisabledTemporarily.mdx @@ -0,0 +1,16 @@ +--- +title: "DisabledTemporarily" +description: "This call or feature is switched off on this network" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The operation has been temporarily switched off in the runtime, usually as a hotfix measure. There is no active raise site in current code; if encountered, check the runtime version and release notes for when the feature is re-enabled. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`disabled`](/docs/errors/disabled). + +## Remediation + +This call or feature is switched off on this network + +The same explanation is available in the terminal: `btcli explain DisabledTemporarily`. diff --git a/docs/errors/chain/DrandConnectionFailure.mdx b/docs/errors/chain/DrandConnectionFailure.mdx new file mode 100644 index 0000000000..d019806c92 --- /dev/null +++ b/docs/errors/chain/DrandConnectionFailure.mdx @@ -0,0 +1,16 @@ +--- +title: "DrandConnectionFailure" +description: "A chain-side invariant failed; nothing to fix client-side \u2014 report it if it persists" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +Declared for failures reaching the drand HTTP API, but pulse fetching happens in the offchain worker, which logs errors instead of raising this. Check the node's offchain worker logs and outbound connectivity to the drand endpoints. + +Declared by the `Drand` pallet; it classifies to the semantic code [`internal`](/docs/errors/internal). + +## Remediation + +A chain-side invariant failed; nothing to fix client-side — report it if it persists + +The same explanation is available in the terminal: `btcli explain DrandConnectionFailure`. diff --git a/docs/errors/chain/Duplicate.mdx b/docs/errors/chain/Duplicate.mdx new file mode 100644 index 0000000000..2d5d848543 --- /dev/null +++ b/docs/errors/chain/Duplicate.mdx @@ -0,0 +1,16 @@ +--- +title: "Duplicate" +description: "The object or state already exists; treat the goal as met or pick a different target" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +This delegate is already registered as a proxy for the delegator with the same proxy type and delay. Check the delegator's `Proxies` entry before calling `add_proxy` with the same (delegate, proxy type, delay) tuple. + +Declared by the `Proxy` pallet; it classifies to the semantic code [`already_exists`](/docs/errors/already-exists). + +## Remediation + +The object or state already exists; treat the goal as met or pick a different target + +The same explanation is available in the terminal: `btcli explain Duplicate`. diff --git a/docs/errors/chain/DuplicateChild.mdx b/docs/errors/chain/DuplicateChild.mdx new file mode 100644 index 0000000000..e91949fd7e --- /dev/null +++ b/docs/errors/chain/DuplicateChild.mdx @@ -0,0 +1,16 @@ +--- +title: "DuplicateChild" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The children list passed to `set_children` contains the same child hotkey more than once. Deduplicate the `children` argument; current relations are visible in the `ChildKeys` storage for the parent hotkey and netuid. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain DuplicateChild`. diff --git a/docs/errors/chain/DuplicateContract.mdx b/docs/errors/chain/DuplicateContract.mdx new file mode 100644 index 0000000000..8668139ba9 --- /dev/null +++ b/docs/errors/chain/DuplicateContract.mdx @@ -0,0 +1,16 @@ +--- +title: "DuplicateContract" +description: "The object or state already exists; treat the goal as met or pick a different target" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +Instantiation would create a contract at an address already occupied by an existing contract. Check the derived contract address and vary the `salt` argument to obtain a fresh address. + +Declared by the `Contracts` pallet; it classifies to the semantic code [`already_exists`](/docs/errors/already-exists). + +## Remediation + +The object or state already exists; treat the goal as met or pick a different target + +The same explanation is available in the terminal: `btcli explain DuplicateContract`. diff --git a/docs/errors/chain/DuplicateOffenceReport.mdx b/docs/errors/chain/DuplicateOffenceReport.mdx new file mode 100644 index 0000000000..1c58051903 --- /dev/null +++ b/docs/errors/chain/DuplicateOffenceReport.mdx @@ -0,0 +1,16 @@ +--- +title: "DuplicateOffenceReport" +description: "The object or state already exists; treat the goal as met or pick a different target" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The equivocation proof is valid but this offence has already been reported and recorded. Check whether an equivocation report for the same offender, session, and round was previously submitted before reporting again. + +Declared by the `Grandpa` pallet; it classifies to the semantic code [`already_exists`](/docs/errors/already-exists). + +## Remediation + +The object or state already exists; treat the goal as met or pick a different target + +The same explanation is available in the terminal: `btcli explain DuplicateOffenceReport`. diff --git a/docs/errors/chain/DuplicateOrderInBatch.mdx b/docs/errors/chain/DuplicateOrderInBatch.mdx new file mode 100644 index 0000000000..23a428049a --- /dev/null +++ b/docs/errors/chain/DuplicateOrderInBatch.mdx @@ -0,0 +1,16 @@ +--- +title: "DuplicateOrderInBatch" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +Two entries in one `execute_batched_orders` call hash to the same order id, meaning the identical signed payload was included twice, which hard-fails the batch. Deduplicate by the blake2-256 hash of each SCALE-encoded `VersionedOrder`. + +Declared by the `LimitOrders` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain DuplicateOrderInBatch`. diff --git a/docs/errors/chain/DuplicateUids.mdx b/docs/errors/chain/DuplicateUids.mdx new file mode 100644 index 0000000000..40dd3e3551 --- /dev/null +++ b/docs/errors/chain/DuplicateUids.mdx @@ -0,0 +1,16 @@ +--- +title: "DuplicateUids" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The `uids` vector passed to `set_weights` (or a reveal) contains the same UID more than once. Deduplicate the uids/values pairs before submitting; each target neuron may appear only once per weight vector. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain DuplicateUids`. diff --git a/docs/errors/chain/DynamicTempoBlockedByCommitReveal.mdx b/docs/errors/chain/DynamicTempoBlockedByCommitReveal.mdx new file mode 100644 index 0000000000..e1b6b3d3ad --- /dev/null +++ b/docs/errors/chain/DynamicTempoBlockedByCommitReveal.mdx @@ -0,0 +1,16 @@ +--- +title: "DynamicTempoBlockedByCommitReveal" +description: "This call or feature is switched off on this network" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +`trigger_epoch` is refused while commit-reveal is enabled on the subnet, because an out-of-band epoch would desync the CRv3 reveal window from the Drand schedule and drop committed weights. Check the `commit_reveal_weights_enabled` hyperparameter; disable it before manually triggering epochs. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`disabled`](/docs/errors/disabled). + +## Remediation + +This call or feature is switched off on this network + +The same explanation is available in the terminal: `btcli explain DynamicTempoBlockedByCommitReveal`. diff --git a/docs/errors/chain/Entered.mdx b/docs/errors/chain/Entered.mdx new file mode 100644 index 0000000000..e0c43c38f3 --- /dev/null +++ b/docs/errors/chain/Entered.mdx @@ -0,0 +1,16 @@ +--- +title: "Entered" +description: "The object or state already exists; treat the goal as met or pick a different target" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +Safe-mode is currently active, so `enter` or `force_enter` cannot activate it again and `release_deposit` is blocked until it exits. Check `EnteredUntil` for the block at which safe-mode disengages. + +Declared by the `SafeMode` pallet; it classifies to the semantic code [`already_exists`](/docs/errors/already-exists). + +## Remediation + +The object or state already exists; treat the goal as met or pick a different target + +The same explanation is available in the terminal: `btcli explain Entered`. diff --git a/docs/errors/chain/EpochTriggerAlreadyPending.mdx b/docs/errors/chain/EpochTriggerAlreadyPending.mdx new file mode 100644 index 0000000000..dc263157ba --- /dev/null +++ b/docs/errors/chain/EpochTriggerAlreadyPending.mdx @@ -0,0 +1,16 @@ +--- +title: "EpochTriggerAlreadyPending" +description: "The object or state already exists; treat the goal as met or pick a different target" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +`trigger_epoch` was called while a previously triggered epoch is still queued for this subnet. Check the `PendingEpochAt` storage for the netuid and wait for the pending epoch to fire before triggering again. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`already_exists`](/docs/errors/already-exists). + +## Remediation + +The object or state already exists; treat the goal as met or pick a different target + +The same explanation is available in the terminal: `btcli explain EpochTriggerAlreadyPending`. diff --git a/docs/errors/chain/EvmKeyAssociateRateLimitExceeded.mdx b/docs/errors/chain/EvmKeyAssociateRateLimitExceeded.mdx new file mode 100644 index 0000000000..d0a6d56516 --- /dev/null +++ b/docs/errors/chain/EvmKeyAssociateRateLimitExceeded.mdx @@ -0,0 +1,16 @@ +--- +title: "EvmKeyAssociateRateLimitExceeded" +description: "Wait for the rate-limit window to pass, then retry" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +`associate_evm_key` was called again before the per-UID rate limit since the last association elapsed. Compare blocks since the association recorded in the `AssociatedEvmAddress` storage against the `EvmKeyAssociateRateLimit` runtime constant and retry later. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`rate_limited`](/docs/errors/rate-limited). + +## Remediation + +Wait for the rate-limit window to pass, then retry + +The same explanation is available in the terminal: `btcli explain EvmKeyAssociateRateLimitExceeded`. diff --git a/docs/errors/chain/EvmKeyAssociationLimitExceeded.mdx b/docs/errors/chain/EvmKeyAssociationLimitExceeded.mdx new file mode 100644 index 0000000000..90ea21aa1a --- /dev/null +++ b/docs/errors/chain/EvmKeyAssociationLimitExceeded.mdx @@ -0,0 +1,16 @@ +--- +title: "EvmKeyAssociationLimitExceeded" +description: "A chain-side capacity limit was hit; reduce the size or count of the request" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The EVM address is already associated with the maximum number of UIDs allowed on this subnet. Inspect the `AssociatedUidsByEvmAddress` storage for the (netuid, evm_key) pair and free a slot or use a different EVM address. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). + +## Remediation + +A chain-side capacity limit was hit; reduce the size or count of the request + +The same explanation is available in the terminal: `btcli explain EvmKeyAssociationLimitExceeded`. diff --git a/docs/errors/chain/ExistentialDeposit.mdx b/docs/errors/chain/ExistentialDeposit.mdx new file mode 100644 index 0000000000..ce2ff8b815 --- /dev/null +++ b/docs/errors/chain/ExistentialDeposit.mdx @@ -0,0 +1,16 @@ +--- +title: "ExistentialDeposit" +description: "Fund the signing account or reduce the amount; check with `btcli wallet balance`" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The amount is too small to create the destination account: its resulting balance would sit below the existential deposit. Compare the transfer value plus the destination's current free balance against the `ExistentialDeposit` constant. + +Declared by the `Balances` pallet; it classifies to the semantic code [`insufficient_balance`](/docs/errors/insufficient-balance). + +## Remediation + +Fund the signing account or reduce the amount; check with `btcli wallet balance` + +The same explanation is available in the terminal: `btcli explain ExistentialDeposit`. diff --git a/docs/errors/chain/ExistingVestingSchedule.mdx b/docs/errors/chain/ExistingVestingSchedule.mdx new file mode 100644 index 0000000000..716e4ee2bb --- /dev/null +++ b/docs/errors/chain/ExistingVestingSchedule.mdx @@ -0,0 +1,16 @@ +--- +title: "ExistingVestingSchedule" +description: "The object or state already exists; treat the goal as met or pick a different target" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +A vesting schedule already exists for the target account and this call cannot add another. Check the account's `Vesting` storage entry before attempting to set a new vested transfer or schedule. + +Declared by the `Balances` pallet; it classifies to the semantic code [`already_exists`](/docs/errors/already-exists). + +## Remediation + +The object or state already exists; treat the goal as met or pick a different target + +The same explanation is available in the terminal: `btcli explain ExistingVestingSchedule`. diff --git a/docs/errors/chain/Exited.mdx b/docs/errors/chain/Exited.mdx new file mode 100644 index 0000000000..f9fec25ddb --- /dev/null +++ b/docs/errors/chain/Exited.mdx @@ -0,0 +1,16 @@ +--- +title: "Exited" +description: "The object or state already exists; treat the goal as met or pick a different target" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +Safe-mode is not currently active, so `extend`, `force_extend`, or `force_exit` have nothing to act on. Check that `EnteredUntil` contains a value before extending or exiting. + +Declared by the `SafeMode` pallet; it classifies to the semantic code [`already_exists`](/docs/errors/already-exists). + +## Remediation + +The object or state already exists; treat the goal as met or pick a different target + +The same explanation is available in the terminal: `btcli explain Exited`. diff --git a/docs/errors/chain/ExpectedBeneficiaryOrigin.mdx b/docs/errors/chain/ExpectedBeneficiaryOrigin.mdx new file mode 100644 index 0000000000..19ba805ff8 --- /dev/null +++ b/docs/errors/chain/ExpectedBeneficiaryOrigin.mdx @@ -0,0 +1,16 @@ +--- +title: "ExpectedBeneficiaryOrigin" +description: "Sign with the key or origin that owns the target object, then retry" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +A lease operation such as terminating a subnet lease was signed by an account other than the lease's beneficiary coldkey. Check the beneficiary recorded in the `SubnetLeases` storage for the lease id and sign with that coldkey. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`not_authorized`](/docs/errors/not-authorized). + +## Remediation + +Sign with the key or origin that owns the target object, then retry + +The same explanation is available in the terminal: `btcli explain ExpectedBeneficiaryOrigin`. diff --git a/docs/errors/chain/Expendability.mdx b/docs/errors/chain/Expendability.mdx new file mode 100644 index 0000000000..f91df7da73 --- /dev/null +++ b/docs/errors/chain/Expendability.mdx @@ -0,0 +1,16 @@ +--- +title: "Expendability" +description: "Fund the signing account or reduce the amount; check with `btcli wallet balance`" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The transfer or payment would drop the sender below the existential deposit and kill the account while keep-alive semantics are required. Compare the sender's free balance minus the amount and fees against `ExistentialDeposit`, or use `transfer_allow_death` if reaping is acceptable. + +Declared by the `Balances` pallet; it classifies to the semantic code [`insufficient_balance`](/docs/errors/insufficient-balance). + +## Remediation + +Fund the signing account or reduce the amount; check with `btcli wallet balance` + +The same explanation is available in the terminal: `btcli explain Expendability`. diff --git a/docs/errors/chain/ExpiredWeightCommit.mdx b/docs/errors/chain/ExpiredWeightCommit.mdx new file mode 100644 index 0000000000..2c4a832286 --- /dev/null +++ b/docs/errors/chain/ExpiredWeightCommit.mdx @@ -0,0 +1,16 @@ +--- +title: "ExpiredWeightCommit" +description: "The window has closed; restart the flow with fresh state" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The hash supplied to `reveal_weights` matches a commit whose reveal window has already passed, so it can no longer be revealed. Check the `commit_reveal_period` hyperparameter and reveal within the allowed epochs after committing; re-commit and reveal on time. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`expired`](/docs/errors/expired). + +## Remediation + +The window has closed; restart the flow with fresh state + +The same explanation is available in the terminal: `btcli explain ExpiredWeightCommit`. diff --git a/docs/errors/chain/FailedToExtractRuntimeVersion.mdx b/docs/errors/chain/FailedToExtractRuntimeVersion.mdx new file mode 100644 index 0000000000..0c7880c1ac --- /dev/null +++ b/docs/errors/chain/FailedToExtractRuntimeVersion.mdx @@ -0,0 +1,16 @@ +--- +title: "FailedToExtractRuntimeVersion" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The new runtime code passed to `set_code` did not yield a readable version: calling `Core_version` or decoding `RuntimeVersion` failed. Check that the submitted blob is a valid, complete runtime wasm and not truncated or compressed incorrectly. + +Declared by the `System` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain FailedToExtractRuntimeVersion`. diff --git a/docs/errors/chain/FailedToSchedule.mdx b/docs/errors/chain/FailedToSchedule.mdx new file mode 100644 index 0000000000..ca43f6db51 --- /dev/null +++ b/docs/errors/chain/FailedToSchedule.mdx @@ -0,0 +1,16 @@ +--- +title: "FailedToSchedule" +description: "A chain-side invariant failed; nothing to fix client-side \u2014 report it if it persists" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The scheduler could not place the call into the agenda, typically because the target block's agenda is full or the schedule parameters are unusable. Check `Agenda` at the target block against `MaxScheduledPerBlock` and pick a different block if it is saturated. + +Declared by the `Scheduler` pallet; it classifies to the semantic code [`internal`](/docs/errors/internal). + +## Remediation + +A chain-side invariant failed; nothing to fix client-side — report it if it persists + +The same explanation is available in the terminal: `btcli explain FailedToSchedule`. diff --git a/docs/errors/chain/FaucetDisabled.mdx b/docs/errors/chain/FaucetDisabled.mdx new file mode 100644 index 0000000000..e57f1e4e62 --- /dev/null +++ b/docs/errors/chain/FaucetDisabled.mdx @@ -0,0 +1,16 @@ +--- +title: "FaucetDisabled" +description: "This call or feature is switched off on this network" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The `faucet` extrinsic was called on a runtime built without the pow-faucet feature, i.e. any real network. The faucet only works on local test chains compiled with that feature; use a funded wallet or testnet TAO instead. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`disabled`](/docs/errors/disabled). + +## Remediation + +This call or feature is switched off on this network + +The same explanation is available in the terminal: `btcli explain FaucetDisabled`. diff --git a/docs/errors/chain/FeeOverflow.mdx b/docs/errors/chain/FeeOverflow.mdx new file mode 100644 index 0000000000..e587723f04 --- /dev/null +++ b/docs/errors/chain/FeeOverflow.mdx @@ -0,0 +1,16 @@ +--- +title: "FeeOverflow" +description: "A chain-side invariant failed; nothing to fix client-side \u2014 report it if it persists" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +Fee arithmetic overflowed, either multiplying the fee per gas by `gas_limit` or converting the EVM fee into Substrate balance decimals. Check for absurdly large `gas_limit` or fee-per-gas values in the transaction. + +Declared by the `EVM` pallet; it classifies to the semantic code [`internal`](/docs/errors/internal). + +## Remediation + +A chain-side invariant failed; nothing to fix client-side — report it if it persists + +The same explanation is available in the terminal: `btcli explain FeeOverflow`. diff --git a/docs/errors/chain/FeeRateTooHigh.mdx b/docs/errors/chain/FeeRateTooHigh.mdx new file mode 100644 index 0000000000..413cbff6b5 --- /dev/null +++ b/docs/errors/chain/FeeRateTooHigh.mdx @@ -0,0 +1,16 @@ +--- +title: "FeeRateTooHigh" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +`set_fee_rate` was called with a rate above the swap pallet's `MaxFeeRate` config constant. Compare the `rate` argument (u16-scaled fraction) against `MaxFeeRate` before submitting. + +Declared by the `Swap` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain FeeRateTooHigh`. diff --git a/docs/errors/chain/FirstEmissionBlockNumberAlreadySet.mdx b/docs/errors/chain/FirstEmissionBlockNumberAlreadySet.mdx new file mode 100644 index 0000000000..ca6603ae37 --- /dev/null +++ b/docs/errors/chain/FirstEmissionBlockNumberAlreadySet.mdx @@ -0,0 +1,16 @@ +--- +title: "FirstEmissionBlockNumberAlreadySet" +description: "The object or state already exists; treat the goal as met or pick a different target" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +`start_call` was issued for a subnet whose emissions have already been started. Check the `FirstEmissionBlockNumber` storage for the netuid; a non-empty value means the subnet is already emitting and no action is needed. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`already_exists`](/docs/errors/already-exists). + +## Remediation + +The object or state already exists; treat the goal as met or pick a different target + +The same explanation is available in the terminal: `btcli explain FirstEmissionBlockNumberAlreadySet`. diff --git a/docs/errors/chain/GasLimitTooHigh.mdx b/docs/errors/chain/GasLimitTooHigh.mdx new file mode 100644 index 0000000000..0edeb2da09 --- /dev/null +++ b/docs/errors/chain/GasLimitTooHigh.mdx @@ -0,0 +1,16 @@ +--- +title: "GasLimitTooHigh" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The transaction's `gas_limit` exceeds the block gas limit configured for the EVM. Compare the `gas_limit` argument against the chain's block gas limit and lower it. + +Declared by the `EVM` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain GasLimitTooHigh`. diff --git a/docs/errors/chain/GasLimitTooLow.mdx b/docs/errors/chain/GasLimitTooLow.mdx new file mode 100644 index 0000000000..d7b78cdde8 --- /dev/null +++ b/docs/errors/chain/GasLimitTooLow.mdx @@ -0,0 +1,16 @@ +--- +title: "GasLimitTooLow" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The transaction's `gas_limit` is below the intrinsic gas required, or too small to cover the weight and proof-size base cost. Raise the `gas_limit` argument, comparing against an `eth_estimateGas` result. + +Declared by the `EVM` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain GasLimitTooLow`. diff --git a/docs/errors/chain/GasPriceTooLow.mdx b/docs/errors/chain/GasPriceTooLow.mdx new file mode 100644 index 0000000000..65f18faa16 --- /dev/null +++ b/docs/errors/chain/GasPriceTooLow.mdx @@ -0,0 +1,16 @@ +--- +title: "GasPriceTooLow" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The offered fee cannot satisfy the current base fee: `max_fee_per_gas` is below the block base fee, the priority fee exceeds the max fee, or the fee inputs are inconsistent. Check `max_fee_per_gas` and `max_priority_fee_per_gas` against the chain's base fee. + +Declared by the `EVM` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain GasPriceTooLow`. diff --git a/docs/errors/chain/HotKeyAccountNotExists.mdx b/docs/errors/chain/HotKeyAccountNotExists.mdx new file mode 100644 index 0000000000..7a90261894 --- /dev/null +++ b/docs/errors/chain/HotKeyAccountNotExists.mdx @@ -0,0 +1,16 @@ +--- +title: "HotKeyAccountNotExists" +description: "Register the hotkey on this subnet first with `btcli subnets register`" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The hotkey has no on-chain account, meaning it was never created through registration, so staking or delegation operations cannot reference it. Check the `Owner` storage for the hotkey or `btcli wallet overview`; register the hotkey on a subnet first. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`not_registered`](/docs/errors/not-registered). + +## Remediation + +Register the hotkey on this subnet first with `btcli subnets register` + +The same explanation is available in the terminal: `btcli explain HotKeyAccountNotExists`. diff --git a/docs/errors/chain/HotKeyAlreadyDelegate.mdx b/docs/errors/chain/HotKeyAlreadyDelegate.mdx new file mode 100644 index 0000000000..e17f1cc3d7 --- /dev/null +++ b/docs/errors/chain/HotKeyAlreadyDelegate.mdx @@ -0,0 +1,16 @@ +--- +title: "HotKeyAlreadyDelegate" +description: "The object or state already exists; treat the goal as met or pick a different target" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +`become_delegate` was called for a hotkey that is already a delegate. Check the `Delegates` storage for the hotkey; if it has a take entry it is already delegating and no action is needed. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`already_exists`](/docs/errors/already-exists). + +## Remediation + +The object or state already exists; treat the goal as met or pick a different target + +The same explanation is available in the terminal: `btcli explain HotKeyAlreadyDelegate`. diff --git a/docs/errors/chain/HotKeyAlreadyRegisteredInSubNet.mdx b/docs/errors/chain/HotKeyAlreadyRegisteredInSubNet.mdx new file mode 100644 index 0000000000..69feed2b61 --- /dev/null +++ b/docs/errors/chain/HotKeyAlreadyRegisteredInSubNet.mdx @@ -0,0 +1,16 @@ +--- +title: "HotKeyAlreadyRegisteredInSubNet" +description: "The object or state already exists; treat the goal as met or pick a different target" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +A registration or hotkey swap targeted a hotkey that already holds a UID on the subnet (or, for a swap without a netuid, on any subnet). Check the `Uids` storage for the (netuid, hotkey) pair or `btcli wallet overview`; use a different hotkey or netuid. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`already_exists`](/docs/errors/already-exists). + +## Remediation + +The object or state already exists; treat the goal as met or pick a different target + +The same explanation is available in the terminal: `btcli explain HotKeyAlreadyRegisteredInSubNet`. diff --git a/docs/errors/chain/HotKeyNotRegisteredInNetwork.mdx b/docs/errors/chain/HotKeyNotRegisteredInNetwork.mdx new file mode 100644 index 0000000000..5b42d36b01 --- /dev/null +++ b/docs/errors/chain/HotKeyNotRegisteredInNetwork.mdx @@ -0,0 +1,16 @@ +--- +title: "HotKeyNotRegisteredInNetwork" +description: "Register the hotkey on this subnet first with `btcli subnets register`" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The hotkey is not registered on the relevant subnet, raised by `serve_axon`/`serve_prometheus` for the serving netuid or by identity calls requiring registration on any subnet. Verify registration with `btcli query uid --netuid ` (or `btcli query netuids-for-hotkey`) and register via `btcli subnets register` first. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`not_registered`](/docs/errors/not-registered). + +## Remediation + +Register the hotkey on this subnet first with `btcli subnets register` + +The same explanation is available in the terminal: `btcli explain HotKeyNotRegisteredInNetwork`. diff --git a/docs/errors/chain/HotKeyNotRegisteredInSubNet.mdx b/docs/errors/chain/HotKeyNotRegisteredInSubNet.mdx new file mode 100644 index 0000000000..5b8a98f696 --- /dev/null +++ b/docs/errors/chain/HotKeyNotRegisteredInSubNet.mdx @@ -0,0 +1,16 @@ +--- +title: "HotKeyNotRegisteredInSubNet" +description: "Register the hotkey on this subnet first with `btcli subnets register`" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The hotkey holds no UID on the given netuid, so weight setting, commits, or UID lookups fail there. Check the `Uids` storage for the (netuid, hotkey) pair or `btcli query uid --netuid `; confirm the netuid argument and register the hotkey if needed. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`not_registered`](/docs/errors/not-registered). + +## Remediation + +Register the hotkey on this subnet first with `btcli subnets register` + +The same explanation is available in the terminal: `btcli explain HotKeyNotRegisteredInSubNet`. diff --git a/docs/errors/chain/HotKeySetTxRateLimitExceeded.mdx b/docs/errors/chain/HotKeySetTxRateLimitExceeded.mdx new file mode 100644 index 0000000000..c3e0445532 --- /dev/null +++ b/docs/errors/chain/HotKeySetTxRateLimitExceeded.mdx @@ -0,0 +1,16 @@ +--- +title: "HotKeySetTxRateLimitExceeded" +description: "Wait for the rate-limit window to pass, then retry" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The coldkey attempted a hotkey set/swap before `TxRateLimit` blocks had passed since its last such transaction. Check the coldkey's last transaction block against the `TxRateLimit` storage value and wait the remaining blocks. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`rate_limited`](/docs/errors/rate-limited). + +## Remediation + +Wait for the rate-limit window to pass, then retry + +The same explanation is available in the terminal: `btcli explain HotKeySetTxRateLimitExceeded`. diff --git a/docs/errors/chain/HotKeySwapOnSubnetIntervalNotPassed.mdx b/docs/errors/chain/HotKeySwapOnSubnetIntervalNotPassed.mdx new file mode 100644 index 0000000000..d20b570e19 --- /dev/null +++ b/docs/errors/chain/HotKeySwapOnSubnetIntervalNotPassed.mdx @@ -0,0 +1,16 @@ +--- +title: "HotKeySwapOnSubnetIntervalNotPassed" +description: "Wait for the rate-limit window to pass, then retry" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +A hotkey swap on a subnet was attempted before `HotkeySwapOnSubnetInterval` blocks passed since the coldkey's last swap on that netuid. Compare `LastHotkeySwapOnNetuid` for the coldkey with the current block and retry after the interval. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`rate_limited`](/docs/errors/rate-limited). + +## Remediation + +Wait for the rate-limit window to pass, then retry + +The same explanation is available in the terminal: `btcli explain HotKeySwapOnSubnetIntervalNotPassed`. diff --git a/docs/errors/chain/IncorrectCommitRevealVersion.mdx b/docs/errors/chain/IncorrectCommitRevealVersion.mdx new file mode 100644 index 0000000000..9b1775d86a --- /dev/null +++ b/docs/errors/chain/IncorrectCommitRevealVersion.mdx @@ -0,0 +1,16 @@ +--- +title: "IncorrectCommitRevealVersion" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The `commit_reveal_version` argument does not match the chain's current commit-reveal weights version. Query the `CommitRevealWeightsVersion` storage item and upgrade or configure the client to commit with that version. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain IncorrectCommitRevealVersion`. diff --git a/docs/errors/chain/IncorrectPartialFillAmount.mdx b/docs/errors/chain/IncorrectPartialFillAmount.mdx new file mode 100644 index 0000000000..761d71ce80 --- /dev/null +++ b/docs/errors/chain/IncorrectPartialFillAmount.mdx @@ -0,0 +1,16 @@ +--- +title: "IncorrectPartialFillAmount" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The `partial_fill` amount is zero or exceeds the order's remaining unfilled amount, or a full execution was submitted against an order already partially filled. Compare `partial_fill` with `order.amount` minus the filled amount recorded in `Orders`. + +Declared by the `LimitOrders` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain IncorrectPartialFillAmount`. diff --git a/docs/errors/chain/IncorrectWeightVersionKey.mdx b/docs/errors/chain/IncorrectWeightVersionKey.mdx new file mode 100644 index 0000000000..42eb5ba51c --- /dev/null +++ b/docs/errors/chain/IncorrectWeightVersionKey.mdx @@ -0,0 +1,16 @@ +--- +title: "IncorrectWeightVersionKey" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The `version_key` supplied with set_weights is older than the subnet's required weights version. Compare it against the `WeightsVersionKey` hyperparameter (`btcli sudo get --netuid `) and update the validator software or the key. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain IncorrectWeightVersionKey`. diff --git a/docs/errors/chain/Indeterministic.mdx b/docs/errors/chain/Indeterministic.mdx new file mode 100644 index 0000000000..0f9bebfd0b --- /dev/null +++ b/docs/errors/chain/Indeterministic.mdx @@ -0,0 +1,16 @@ +--- +title: "Indeterministic" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +Code flagged as non-deterministic (e.g. using floating point) was used where determinism is enforced, such as on-chain instantiation or calls. Check the determinism mode the code was uploaded with and rebuild the contract deterministically. + +Declared by the `Contracts` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain Indeterministic`. diff --git a/docs/errors/chain/InputForwarded.mdx b/docs/errors/chain/InputForwarded.mdx new file mode 100644 index 0000000000..f46852848e --- /dev/null +++ b/docs/errors/chain/InputForwarded.mdx @@ -0,0 +1,16 @@ +--- +title: "InputForwarded" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The contract forwarded its input to a callee via `seal_call` with the FORWARD_INPUT flag and then tried to read or forward it again. Check the call flags used; use CLONE_INPUT when the input is still needed afterwards. + +Declared by the `Contracts` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain InputForwarded`. diff --git a/docs/errors/chain/InputLengthsUnequal.mdx b/docs/errors/chain/InputLengthsUnequal.mdx new file mode 100644 index 0000000000..d3bf1fd8c0 --- /dev/null +++ b/docs/errors/chain/InputLengthsUnequal.mdx @@ -0,0 +1,16 @@ +--- +title: "InputLengthsUnequal" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +A batch weights call passed vectors of different lengths, e.g. netuids vs commit hashes, or uids vs values, salts and version_keys in batch reveal. Check that every parallel vector argument in the batch extrinsic has the same length. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain InputLengthsUnequal`. diff --git a/docs/errors/chain/InsufficientAlphaBalance.mdx b/docs/errors/chain/InsufficientAlphaBalance.mdx new file mode 100644 index 0000000000..28e27d9a82 --- /dev/null +++ b/docs/errors/chain/InsufficientAlphaBalance.mdx @@ -0,0 +1,16 @@ +--- +title: "InsufficientAlphaBalance" +description: "Fund the signing account or reduce the amount; check with `btcli wallet balance`" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +A stake decrease asked to debit more alpha than the hotkey-coldkey pair holds on that subnet. Compare the requested amount against the pair's current stake on the netuid (`btcli stake list` or the `Alpha` storage entry). + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`insufficient_balance`](/docs/errors/insufficient-balance). + +## Remediation + +Fund the signing account or reduce the amount; check with `btcli wallet balance` + +The same explanation is available in the terminal: `btcli explain InsufficientAlphaBalance`. diff --git a/docs/errors/chain/InsufficientBalance.mdx b/docs/errors/chain/InsufficientBalance.mdx new file mode 100644 index 0000000000..60896c1c48 --- /dev/null +++ b/docs/errors/chain/InsufficientBalance.mdx @@ -0,0 +1,16 @@ +--- +title: "InsufficientBalance" +description: "Fund the signing account or reduce the amount; check with `btcli wallet balance`" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The caller's spendable balance is below what the operation needs, whether a plain transfer, a crowdloan deposit or contribution, or a swap. Compare the account's free balance in `System.Account` (net of holds, freezes, and fees) against the amount being moved. + +Declared by the `Balances`, `Crowdloan`, `Swap` pallets; it classifies to the semantic code [`insufficient_balance`](/docs/errors/insufficient-balance). + +## Remediation + +Fund the signing account or reduce the amount; check with `btcli wallet balance` + +The same explanation is available in the terminal: `btcli explain InsufficientBalance`. diff --git a/docs/errors/chain/InsufficientInputAmount.mdx b/docs/errors/chain/InsufficientInputAmount.mdx new file mode 100644 index 0000000000..4cc52cde8d --- /dev/null +++ b/docs/errors/chain/InsufficientInputAmount.mdx @@ -0,0 +1,16 @@ +--- +title: "InsufficientInputAmount" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +Declared for swap inputs too small to execute, but no current code path raises it since the user-liquidity code was removed. If seen on an older runtime, check that the swap input amount is nonzero and large enough to produce output. + +Declared by the `Swap` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain InsufficientInputAmount`. diff --git a/docs/errors/chain/InsufficientLiquidity.mdx b/docs/errors/chain/InsufficientLiquidity.mdx new file mode 100644 index 0000000000..ac57b95931 --- /dev/null +++ b/docs/errors/chain/InsufficientLiquidity.mdx @@ -0,0 +1,16 @@ +--- +title: "InsufficientLiquidity" +description: "The pool cannot absorb this trade; reduce the amount or split it into smaller steps" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The pool cannot absorb the operation: the swap simulation failed, reserves are smaller than the payout, or the amount exceeds the pool's supported input. Check the subnet pool reserves `SubnetTAO`, `SubnetAlphaIn` and `SubnetAlphaOut` against the amount. + +Declared by the `SubtensorModule`, `Swap` pallets; it classifies to the semantic code [`insufficient_liquidity`](/docs/errors/insufficient-liquidity). + +## Remediation + +The pool cannot absorb this trade; reduce the amount or split it into smaller steps + +The same explanation is available in the terminal: `btcli explain InsufficientLiquidity`. diff --git a/docs/errors/chain/InsufficientStakeForLock.mdx b/docs/errors/chain/InsufficientStakeForLock.mdx new file mode 100644 index 0000000000..4b5b0f4a89 --- /dev/null +++ b/docs/errors/chain/InsufficientStakeForLock.mdx @@ -0,0 +1,16 @@ +--- +title: "InsufficientStakeForLock" +description: "Fund the signing account or reduce the amount; check with `btcli wallet balance`" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The requested lock amount exceeds the coldkey's total alpha stake on that subnet (existing locked mass included). Compare the amount against the coldkey's stake on the netuid, e.g. via `btcli stake list`, and lock less or add stake first. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`insufficient_balance`](/docs/errors/insufficient-balance). + +## Remediation + +Fund the signing account or reduce the amount; check with `btcli wallet balance` + +The same explanation is available in the terminal: `btcli explain InsufficientStakeForLock`. diff --git a/docs/errors/chain/InsufficientTaoBalance.mdx b/docs/errors/chain/InsufficientTaoBalance.mdx new file mode 100644 index 0000000000..50292229c4 --- /dev/null +++ b/docs/errors/chain/InsufficientTaoBalance.mdx @@ -0,0 +1,16 @@ +--- +title: "InsufficientTaoBalance" +description: "Fund the signing account or reduce the amount; check with `btcli wallet balance`" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The coldkey's free TAO balance is below the amount a TAO-side operation needs: a transfer between coldkeys, a burn or recycle, or a subnet-registration lock. Check the coldkey's balance with `btcli wallet balance` against the amount being moved. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`insufficient_balance`](/docs/errors/insufficient-balance). + +## Remediation + +Fund the signing account or reduce the amount; check with `btcli wallet balance` + +The same explanation is available in the terminal: `btcli explain InsufficientTaoBalance`. diff --git a/docs/errors/chain/InvalidCallFlags.mdx b/docs/errors/chain/InvalidCallFlags.mdx new file mode 100644 index 0000000000..a4cacd9bdb --- /dev/null +++ b/docs/errors/chain/InvalidCallFlags.mdx @@ -0,0 +1,16 @@ +--- +title: "InvalidCallFlags" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The flags bitmask given to `seal_call` or `seal_delegate_call` contains an unknown or disallowed combination; delegate calls accept only a restricted flag set. Check the flags argument against the supported `CallFlags` bit values. + +Declared by the `Contracts` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain InvalidCallFlags`. diff --git a/docs/errors/chain/InvalidChainId.mdx b/docs/errors/chain/InvalidChainId.mdx new file mode 100644 index 0000000000..be1e1d5b30 --- /dev/null +++ b/docs/errors/chain/InvalidChainId.mdx @@ -0,0 +1,16 @@ +--- +title: "InvalidChainId" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The EIP-155 chain id encoded in the signed transaction does not match this chain's configured chain id. Compare the transaction's chain id with the value returned by `eth_chainId` and re-sign the transaction. + +Declared by the `EVM` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain InvalidChainId`. diff --git a/docs/errors/chain/InvalidChild.mdx b/docs/errors/chain/InvalidChild.mdx new file mode 100644 index 0000000000..880bfe3715 --- /dev/null +++ b/docs/errors/chain/InvalidChild.mdx @@ -0,0 +1,16 @@ +--- +title: "InvalidChild" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The children or parents list includes the pivot hotkey itself (a self-loop), including during a hotkey swap when the new hotkey is already a child or parent of the old one. Check the `children` argument and `ChildKeys`/`ParentKeys` for the hotkeys involved. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain InvalidChild`. diff --git a/docs/errors/chain/InvalidChildkeyTake.mdx b/docs/errors/chain/InvalidChildkeyTake.mdx new file mode 100644 index 0000000000..e628fd3182 --- /dev/null +++ b/docs/errors/chain/InvalidChildkeyTake.mdx @@ -0,0 +1,16 @@ +--- +title: "InvalidChildkeyTake" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The childkey take is outside the allowed range for the subnet: below the effective minimum or above `MaxChildkeyTake`. Query `MinChildkeyTake` and `MaxChildkeyTake` and pick a `take` value within those bounds. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain InvalidChildkeyTake`. diff --git a/docs/errors/chain/InvalidCrowdloanId.mdx b/docs/errors/chain/InvalidCrowdloanId.mdx new file mode 100644 index 0000000000..e252afa8cc --- /dev/null +++ b/docs/errors/chain/InvalidCrowdloanId.mdx @@ -0,0 +1,16 @@ +--- +title: "InvalidCrowdloanId" +description: "The referenced object is not on-chain; check the identifier" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +No crowdloan exists in the `Crowdloans` storage map for the given `crowdloan_id`; it was never created or has been dissolved. Check `Crowdloans` for the id and `NextCrowdloanId` for the range of ids ever issued. + +Declared by the `Crowdloan` pallet; it classifies to the semantic code [`not_found`](/docs/errors/not-found). + +## Remediation + +The referenced object is not on-chain; check the identifier + +The same explanation is available in the terminal: `btcli explain InvalidCrowdloanId`. diff --git a/docs/errors/chain/InvalidDerivedAccount.mdx b/docs/errors/chain/InvalidDerivedAccount.mdx new file mode 100644 index 0000000000..bc002ff950 --- /dev/null +++ b/docs/errors/chain/InvalidDerivedAccount.mdx @@ -0,0 +1,16 @@ +--- +title: "InvalidDerivedAccount" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +Deriving the sub-account for `as_derivative` failed to decode into a valid account id from the (caller, index) entropy. Check the `index` argument and the caller account used for derivation. + +Declared by the `Utility` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain InvalidDerivedAccount`. diff --git a/docs/errors/chain/InvalidDerivedAccountId.mdx b/docs/errors/chain/InvalidDerivedAccountId.mdx new file mode 100644 index 0000000000..592c96950c --- /dev/null +++ b/docs/errors/chain/InvalidDerivedAccountId.mdx @@ -0,0 +1,16 @@ +--- +title: "InvalidDerivedAccountId" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +Deriving the pure proxy account id from the provided entropy failed to decode into a valid account. Check the spawner, `proxy_type`, and `index` arguments used with `create_pure` (or the equivalent lookup when destroying one). + +Declared by the `Proxy` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain InvalidDerivedAccountId`. diff --git a/docs/errors/chain/InvalidDifficulty.mdx b/docs/errors/chain/InvalidDifficulty.mdx new file mode 100644 index 0000000000..47c51a7e08 --- /dev/null +++ b/docs/errors/chain/InvalidDifficulty.mdx @@ -0,0 +1,16 @@ +--- +title: "InvalidDifficulty" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The submitted proof-of-work hash does not meet the required difficulty (the faucet uses a fixed 1,000,000; PoW registration uses the subnet's difficulty). Check the `Difficulty` storage for the netuid and regenerate work against the current block. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain InvalidDifficulty`. diff --git a/docs/errors/chain/InvalidEquivocationProof.mdx b/docs/errors/chain/InvalidEquivocationProof.mdx new file mode 100644 index 0000000000..adacb9f124 --- /dev/null +++ b/docs/errors/chain/InvalidEquivocationProof.mdx @@ -0,0 +1,16 @@ +--- +title: "InvalidEquivocationProof" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The submitted GRANDPA equivocation proof does not demonstrate a real double-vote: the two votes may be identical, from different rounds or set ids, or badly signed. Verify the proof contains two distinct votes by the same authority in the same round and authority set. + +Declared by the `Grandpa` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain InvalidEquivocationProof`. diff --git a/docs/errors/chain/InvalidFinalizationConfig.mdx b/docs/errors/chain/InvalidFinalizationConfig.mdx new file mode 100644 index 0000000000..0fcc94d9b7 --- /dev/null +++ b/docs/errors/chain/InvalidFinalizationConfig.mdx @@ -0,0 +1,16 @@ +--- +title: "InvalidFinalizationConfig" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +Exactly one of `call` or `target_address` must be set, but both or neither were provided to `create`, or the stored crowdloan holds an inconsistent pair at `finalize` time. Check those two fields on the `create` arguments or the `Crowdloans` entry. + +Declared by the `Crowdloan` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain InvalidFinalizationConfig`. diff --git a/docs/errors/chain/InvalidIdentity.mdx b/docs/errors/chain/InvalidIdentity.mdx new file mode 100644 index 0000000000..9d8695ba05 --- /dev/null +++ b/docs/errors/chain/InvalidIdentity.mdx @@ -0,0 +1,16 @@ +--- +title: "InvalidIdentity" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The submitted coldkey or subnet identity failed validation, typically a field exceeding its allowed byte length or malformed data. Check each identity field's length against the limits enforced by the chain before calling set_identity or set_subnet_identity. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain InvalidIdentity`. diff --git a/docs/errors/chain/InvalidIpAddress.mdx b/docs/errors/chain/InvalidIpAddress.mdx new file mode 100644 index 0000000000..152d03a958 --- /dev/null +++ b/docs/errors/chain/InvalidIpAddress.mdx @@ -0,0 +1,16 @@ +--- +title: "InvalidIpAddress" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The `ip` argument to serve_axon or serve_prometheus is not a valid address for the declared `ip_type` (prometheus additionally rejects the zero address). Verify the IP encodes correctly as IPv4 or IPv6 and matches the `ip_type` passed. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain InvalidIpAddress`. diff --git a/docs/errors/chain/InvalidIpType.mdx b/docs/errors/chain/InvalidIpType.mdx new file mode 100644 index 0000000000..7bd5c5f07f --- /dev/null +++ b/docs/errors/chain/InvalidIpType.mdx @@ -0,0 +1,16 @@ +--- +title: "InvalidIpType" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The `ip_type` argument to serve_axon or serve_prometheus is not 4 or 6. Check the value being sent by the miner or client; only IPv4 (4) and IPv6 (6) are accepted. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain InvalidIpType`. diff --git a/docs/errors/chain/InvalidKeyOwnershipProof.mdx b/docs/errors/chain/InvalidKeyOwnershipProof.mdx new file mode 100644 index 0000000000..531cca9466 --- /dev/null +++ b/docs/errors/chain/InvalidKeyOwnershipProof.mdx @@ -0,0 +1,16 @@ +--- +title: "InvalidKeyOwnershipProof" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The key ownership proof does not establish that the offending GRANDPA key belonged to the claimed validator at that session. Regenerate the proof via the `generate_key_ownership_proof` runtime API for the exact set id and authority id in the report. + +Declared by the `Grandpa` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain InvalidKeyOwnershipProof`. diff --git a/docs/errors/chain/InvalidLeaseBeneficiary.mdx b/docs/errors/chain/InvalidLeaseBeneficiary.mdx new file mode 100644 index 0000000000..9da16ffc75 --- /dev/null +++ b/docs/errors/chain/InvalidLeaseBeneficiary.mdx @@ -0,0 +1,16 @@ +--- +title: "InvalidLeaseBeneficiary" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The account registering a leased network is not the creator of the crowdloan currently being finalized. Check the crowdloan's `creator` field in the crowdloan pallet storage and submit the call from that coldkey. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain InvalidLeaseBeneficiary`. diff --git a/docs/errors/chain/InvalidLiquidityValue.mdx b/docs/errors/chain/InvalidLiquidityValue.mdx new file mode 100644 index 0000000000..522b60fbf9 --- /dev/null +++ b/docs/errors/chain/InvalidLiquidityValue.mdx @@ -0,0 +1,16 @@ +--- +title: "InvalidLiquidityValue" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +Legacy error from the removed V3 user-liquidity code, raised when an added or removed liquidity amount was below the pallet's `MinimumLiquidity`. Not raised on current runtimes; on older ones compare the `liquidity` argument to `MinimumLiquidity`. + +Declared by the `Swap` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain InvalidLiquidityValue`. diff --git a/docs/errors/chain/InvalidNonce.mdx b/docs/errors/chain/InvalidNonce.mdx new file mode 100644 index 0000000000..f14136488f --- /dev/null +++ b/docs/errors/chain/InvalidNonce.mdx @@ -0,0 +1,16 @@ +--- +title: "InvalidNonce" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The transaction nonce does not match the sender's current account nonce, being either too low (already used) or too high. Compare the transaction's `nonce` with the sender's on-chain nonce from `eth_getTransactionCount`. + +Declared by the `EVM` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain InvalidNonce`. diff --git a/docs/errors/chain/InvalidNumRootClaim.mdx b/docs/errors/chain/InvalidNumRootClaim.mdx new file mode 100644 index 0000000000..733f4d6bea --- /dev/null +++ b/docs/errors/chain/InvalidNumRootClaim.mdx @@ -0,0 +1,16 @@ +--- +title: "InvalidNumRootClaim" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The value passed to sudo_set_num_root_claims exceeds the compile-time maximum number of root claims. Check the `new_value` argument against the chain's `MAX_NUM_ROOT_CLAIMS` constant and pass a smaller number. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain InvalidNumRootClaim`. diff --git a/docs/errors/chain/InvalidOrigin.mdx b/docs/errors/chain/InvalidOrigin.mdx new file mode 100644 index 0000000000..27afca91e4 --- /dev/null +++ b/docs/errors/chain/InvalidOrigin.mdx @@ -0,0 +1,16 @@ +--- +title: "InvalidOrigin" +description: "Sign with the key or origin that owns the target object, then retry" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The caller is not the crowdloan's creator, which is required for `finalize`, `refund`, `dissolve`, and the update calls. Compare the signing account with the `creator` field of the `Crowdloans` entry for the `crowdloan_id`. + +Declared by the `Crowdloan` pallet; it classifies to the semantic code [`not_authorized`](/docs/errors/not-authorized). + +## Remediation + +Sign with the key or origin that owns the target object, then retry + +The same explanation is available in the terminal: `btcli explain InvalidOrigin`. diff --git a/docs/errors/chain/InvalidPort.mdx b/docs/errors/chain/InvalidPort.mdx new file mode 100644 index 0000000000..83d797c9d6 --- /dev/null +++ b/docs/errors/chain/InvalidPort.mdx @@ -0,0 +1,16 @@ +--- +title: "InvalidPort" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The `port` argument to serve_axon or serve_prometheus is 0, which is rejected. Check the miner or client axon configuration and serve on a non-zero port. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain InvalidPort`. diff --git a/docs/errors/chain/InvalidRecoveredPublicKey.mdx b/docs/errors/chain/InvalidRecoveredPublicKey.mdx new file mode 100644 index 0000000000..b4d38911bf --- /dev/null +++ b/docs/errors/chain/InvalidRecoveredPublicKey.mdx @@ -0,0 +1,16 @@ +--- +title: "InvalidRecoveredPublicKey" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The EVM key association signature recovered to a public key whose keccak hash does not equal the supplied `evm_key`. Verify the signature was produced by the claimed EVM key over the hotkey plus block hash message (EIP-191 format). + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain InvalidRecoveredPublicKey`. diff --git a/docs/errors/chain/InvalidRevealCommitHashNotMatch.mdx b/docs/errors/chain/InvalidRevealCommitHashNotMatch.mdx new file mode 100644 index 0000000000..6e8be71860 --- /dev/null +++ b/docs/errors/chain/InvalidRevealCommitHashNotMatch.mdx @@ -0,0 +1,16 @@ +--- +title: "InvalidRevealCommitHashNotMatch" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The revealed uids, values, salt and version_key hash to a value that matches none of the hotkey's pending non-expired commits. Check that the reveal parameters and salt exactly match what was committed, and inspect `WeightCommits` for the hotkey and netuid. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain InvalidRevealCommitHashNotMatch`. diff --git a/docs/errors/chain/InvalidRevealRound.mdx b/docs/errors/chain/InvalidRevealRound.mdx new file mode 100644 index 0000000000..aaa06d1f51 --- /dev/null +++ b/docs/errors/chain/InvalidRevealRound.mdx @@ -0,0 +1,16 @@ +--- +title: "InvalidRevealRound" +description: "The window has closed; restart the flow with fresh state" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +A timelocked weights commit specified a `reveal_round` older than the latest stored DRAND round, so it could be decrypted immediately. Query the drand pallet's `LastStoredRound` and commit with a future round number. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`expired`](/docs/errors/expired). + +## Remediation + +The window has closed; restart the flow with fresh state + +The same explanation is available in the terminal: `btcli explain InvalidRevealRound`. diff --git a/docs/errors/chain/InvalidRootClaimThreshold.mdx b/docs/errors/chain/InvalidRootClaimThreshold.mdx new file mode 100644 index 0000000000..cadd782755 --- /dev/null +++ b/docs/errors/chain/InvalidRootClaimThreshold.mdx @@ -0,0 +1,16 @@ +--- +title: "InvalidRootClaimThreshold" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The value passed to set the root claim threshold exceeds the chain's maximum allowed threshold. Check the `new_value` argument against the `MAX_ROOT_CLAIM_THRESHOLD` constant and the current `RootClaimableThreshold` for the netuid. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain InvalidRootClaimThreshold`. diff --git a/docs/errors/chain/InvalidRoundNumber.mdx b/docs/errors/chain/InvalidRoundNumber.mdx new file mode 100644 index 0000000000..b1d3ad195a --- /dev/null +++ b/docs/errors/chain/InvalidRoundNumber.mdx @@ -0,0 +1,16 @@ +--- +title: "InvalidRoundNumber" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +A submitted drand pulse has an unacceptable round: the first pulse ever stored must have a round greater than zero, and each later pulse must be exactly `LastStoredRound` plus one. Compare the pulse round against `LastStoredRound`. + +Declared by the `Drand` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain InvalidRoundNumber`. diff --git a/docs/errors/chain/InvalidSchedule.mdx b/docs/errors/chain/InvalidSchedule.mdx new file mode 100644 index 0000000000..2752dc9710 --- /dev/null +++ b/docs/errors/chain/InvalidSchedule.mdx @@ -0,0 +1,16 @@ +--- +title: "InvalidSchedule" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The pallet's schedule is misconfigured, e.g. a zero weight or zero `ref_time_by_fuel` for a basic operation, making gas conversion impossible. This is a runtime configuration issue; inspect the `Schedule` constant rather than call arguments. + +Declared by the `Contracts` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain InvalidSchedule`. diff --git a/docs/errors/chain/InvalidSeal.mdx b/docs/errors/chain/InvalidSeal.mdx new file mode 100644 index 0000000000..beded35ebf --- /dev/null +++ b/docs/errors/chain/InvalidSeal.mdx @@ -0,0 +1,16 @@ +--- +title: "InvalidSeal" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The seal hash recomputed from the supplied `block_number`, `nonce` and key does not equal the submitted `work`. Verify the PoW solver built the seal for the same key and block it submits, and that the work bytes were not corrupted in transit. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain InvalidSeal`. diff --git a/docs/errors/chain/InvalidSignature.mdx b/docs/errors/chain/InvalidSignature.mdx new file mode 100644 index 0000000000..fc140c6410 --- /dev/null +++ b/docs/errors/chain/InvalidSignature.mdx @@ -0,0 +1,16 @@ +--- +title: "InvalidSignature" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +Signature verification failed: the sender of an Ethereum or EVM transaction could not be recovered from its signature, or a limit order's Sr25519 signature does not match the order payload and signer. Check the signing key, chain id, and the exact payload bytes that were signed. + +Declared by the `Ethereum`, `EVM`, `LimitOrders` pallets; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain InvalidSignature`. diff --git a/docs/errors/chain/InvalidSpecName.mdx b/docs/errors/chain/InvalidSpecName.mdx new file mode 100644 index 0000000000..6ac1b678de --- /dev/null +++ b/docs/errors/chain/InvalidSpecName.mdx @@ -0,0 +1,16 @@ +--- +title: "InvalidSpecName" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The new runtime's `spec_name` does not match the current runtime, so `set_code` refuses the upgrade. Check the `RuntimeVersion` embedded in the new wasm and ensure the spec name is identical to the chain's current one. + +Declared by the `System` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain InvalidSpecName`. diff --git a/docs/errors/chain/InvalidSubnetNumber.mdx b/docs/errors/chain/InvalidSubnetNumber.mdx new file mode 100644 index 0000000000..82d543f566 --- /dev/null +++ b/docs/errors/chain/InvalidSubnetNumber.mdx @@ -0,0 +1,16 @@ +--- +title: "InvalidSubnetNumber" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +A root-claim call passed an empty subnet set or more subnets than the per-call maximum (claim_root, or KeepSubnets in set_root_claim_type). Check the `subnets` argument is non-empty and within the `MAX_SUBNET_CLAIMS` limit. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain InvalidSubnetNumber`. diff --git a/docs/errors/chain/InvalidTickRange.mdx b/docs/errors/chain/InvalidTickRange.mdx new file mode 100644 index 0000000000..54aca7d231 --- /dev/null +++ b/docs/errors/chain/InvalidTickRange.mdx @@ -0,0 +1,16 @@ +--- +title: "InvalidTickRange" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +Legacy error from the removed V3 user-liquidity code, raised when `tick_low` was not below `tick_high` or a tick failed to convert to a sqrt price. Not raised on current runtimes; check the tick range arguments on older ones. + +Declared by the `Swap` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain InvalidTickRange`. diff --git a/docs/errors/chain/InvalidValue.mdx b/docs/errors/chain/InvalidValue.mdx new file mode 100644 index 0000000000..ff4c125fe7 --- /dev/null +++ b/docs/errors/chain/InvalidValue.mdx @@ -0,0 +1,16 @@ +--- +title: "InvalidValue" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +A generic out-of-range parameter on an admin or sudo call, e.g. mechanism counts, emission splits summing away from 65535, max UIDs or take bounds. Check the specific argument against the min/max storage items the extrinsic validates (e.g. `MinAllowedUids`, `MaxMechanismCount`). + +Declared by the `SubtensorModule`, `AdminUtils` pallets; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain InvalidValue`. diff --git a/docs/errors/chain/InvalidVotingPowerEmaAlpha.mdx b/docs/errors/chain/InvalidVotingPowerEmaAlpha.mdx new file mode 100644 index 0000000000..4b83453e4b --- /dev/null +++ b/docs/errors/chain/InvalidVotingPowerEmaAlpha.mdx @@ -0,0 +1,16 @@ +--- +title: "InvalidVotingPowerEmaAlpha" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The alpha passed to set the voting power EMA exceeds 10^18, which represents 1.0. Check the `alpha` argument to sudo_set_voting_power_ema_alpha; it must be at most 10^18, and the current value is in `VotingPowerEmaAlpha` per netuid. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain InvalidVotingPowerEmaAlpha`. diff --git a/docs/errors/chain/InvalidWorkBlock.mdx b/docs/errors/chain/InvalidWorkBlock.mdx new file mode 100644 index 0000000000..46b146722d --- /dev/null +++ b/docs/errors/chain/InvalidWorkBlock.mdx @@ -0,0 +1,16 @@ +--- +title: "InvalidWorkBlock" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The `block_number` in the proof-of-work submission is in the future or more than 3 blocks old, so the work is stale. Compare the submitted block number with the current chain height and regenerate the PoW against a fresh block. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain InvalidWorkBlock`. diff --git a/docs/errors/chain/IssuanceDeactivated.mdx b/docs/errors/chain/IssuanceDeactivated.mdx new file mode 100644 index 0000000000..baebf10240 --- /dev/null +++ b/docs/errors/chain/IssuanceDeactivated.mdx @@ -0,0 +1,16 @@ +--- +title: "IssuanceDeactivated" +description: "This call or feature is switched off on this network" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +Total issuance cannot be adjusted because issuance has already been deactivated. Check the Balances `InactiveIssuance` state before calling `force_adjust_total_issuance` again. + +Declared by the `Balances` pallet; it classifies to the semantic code [`disabled`](/docs/errors/disabled). + +## Remediation + +This call or feature is switched off on this network + +The same explanation is available in the terminal: `btcli explain IssuanceDeactivated`. diff --git a/docs/errors/chain/LeaseCannotEndInThePast.mdx b/docs/errors/chain/LeaseCannotEndInThePast.mdx new file mode 100644 index 0000000000..6d24f23977 --- /dev/null +++ b/docs/errors/chain/LeaseCannotEndInThePast.mdx @@ -0,0 +1,16 @@ +--- +title: "LeaseCannotEndInThePast" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The `end_block` supplied when registering a leased network is not after the current block. Check the current chain height and pass an `end_block` in the future, or omit it for a perpetual lease. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain LeaseCannotEndInThePast`. diff --git a/docs/errors/chain/LeaseDoesNotExist.mdx b/docs/errors/chain/LeaseDoesNotExist.mdx new file mode 100644 index 0000000000..0235974c43 --- /dev/null +++ b/docs/errors/chain/LeaseDoesNotExist.mdx @@ -0,0 +1,16 @@ +--- +title: "LeaseDoesNotExist" +description: "The referenced object is not on-chain; check the identifier" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The `lease_id` argument does not correspond to any stored lease. Query the `SubnetLeases` storage map to confirm the lease id and whether it was already terminated. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`not_found`](/docs/errors/not-found). + +## Remediation + +The referenced object is not on-chain; check the identifier + +The same explanation is available in the terminal: `btcli explain LeaseDoesNotExist`. diff --git a/docs/errors/chain/LeaseHasNoEndBlock.mdx b/docs/errors/chain/LeaseHasNoEndBlock.mdx new file mode 100644 index 0000000000..1c6704d0ba --- /dev/null +++ b/docs/errors/chain/LeaseHasNoEndBlock.mdx @@ -0,0 +1,16 @@ +--- +title: "LeaseHasNoEndBlock" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The lease being terminated is perpetual (its `end_block` is None), so it can never be ended this way. Check the lease's `end_block` field in `SubnetLeases` for the given lease id. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain LeaseHasNoEndBlock`. diff --git a/docs/errors/chain/LeaseHasNotEnded.mdx b/docs/errors/chain/LeaseHasNotEnded.mdx new file mode 100644 index 0000000000..6021e59bf9 --- /dev/null +++ b/docs/errors/chain/LeaseHasNotEnded.mdx @@ -0,0 +1,16 @@ +--- +title: "LeaseHasNotEnded" +description: "The required window has not opened yet; wait some blocks and retry" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The lease termination was attempted before the lease's `end_block` was reached. Compare the current block height with the `end_block` stored in `SubnetLeases` for the lease id and retry after it passes. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`too_early`](/docs/errors/too-early). + +## Remediation + +The required window has not opened yet; wait some blocks and retry + +The same explanation is available in the terminal: `btcli explain LeaseHasNotEnded`. diff --git a/docs/errors/chain/LeaseNetuidNotFound.mdx b/docs/errors/chain/LeaseNetuidNotFound.mdx new file mode 100644 index 0000000000..4a2ce6263f --- /dev/null +++ b/docs/errors/chain/LeaseNetuidNotFound.mdx @@ -0,0 +1,16 @@ +--- +title: "LeaseNetuidNotFound" +description: "The referenced object is not on-chain; check the identifier" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +After registering the leased network, no subnet owned by the lease's derived coldkey could be found, so the netuid lookup failed. Inspect `SubnetOwner` entries for the lease coldkey; this usually indicates the registration did not complete. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`not_found`](/docs/errors/not-found). + +## Remediation + +The referenced object is not on-chain; check the identifier + +The same explanation is available in the terminal: `btcli explain LeaseNetuidNotFound`. diff --git a/docs/errors/chain/LimitOrdersDisabled.mdx b/docs/errors/chain/LimitOrdersDisabled.mdx new file mode 100644 index 0000000000..a677ec9431 --- /dev/null +++ b/docs/errors/chain/LimitOrdersDisabled.mdx @@ -0,0 +1,16 @@ +--- +title: "LimitOrdersDisabled" +description: "This call or feature is switched off on this network" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +Order execution was attempted while the pallet's global switch is off. Check the `LimitOrdersEnabled` storage value; root must call `set_pallet_status` with true to enable the pallet. + +Declared by the `LimitOrders` pallet; it classifies to the semantic code [`disabled`](/docs/errors/disabled). + +## Remediation + +This call or feature is switched off on this network + +The same explanation is available in the terminal: `btcli explain LimitOrdersDisabled`. diff --git a/docs/errors/chain/LiquidAlphaDisabled.mdx b/docs/errors/chain/LiquidAlphaDisabled.mdx new file mode 100644 index 0000000000..02cc3f8c87 --- /dev/null +++ b/docs/errors/chain/LiquidAlphaDisabled.mdx @@ -0,0 +1,16 @@ +--- +title: "LiquidAlphaDisabled" +description: "This call or feature is switched off on this network" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +Setting `alpha_low`/`alpha_high` was attempted while liquid alpha is disabled on the subnet. Check the `LiquidAlphaOn` hyperparameter (`btcli sudo get --netuid `) and have the subnet owner enable liquid alpha first. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`disabled`](/docs/errors/disabled). + +## Remediation + +This call or feature is switched off on this network + +The same explanation is available in the terminal: `btcli explain LiquidAlphaDisabled`. diff --git a/docs/errors/chain/LiquidityRestrictions.mdx b/docs/errors/chain/LiquidityRestrictions.mdx new file mode 100644 index 0000000000..05375872bb --- /dev/null +++ b/docs/errors/chain/LiquidityRestrictions.mdx @@ -0,0 +1,16 @@ +--- +title: "LiquidityRestrictions" +description: "Fund the signing account or reduce the amount; check with `btcli wallet balance`" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The withdrawal is blocked by locks or freezes on the account even though the raw balance looks sufficient. Check the account's `Locks` and `Freezes` entries and compare the frozen amount against what would remain after the withdrawal. + +Declared by the `Balances` pallet; it classifies to the semantic code [`insufficient_balance`](/docs/errors/insufficient-balance). + +## Remediation + +Fund the signing account or reduce the amount; check with `btcli wallet balance` + +The same explanation is available in the terminal: `btcli explain LiquidityRestrictions`. diff --git a/docs/errors/chain/LockHotkeyMismatch.mdx b/docs/errors/chain/LockHotkeyMismatch.mdx new file mode 100644 index 0000000000..bff2493ad8 --- /dev/null +++ b/docs/errors/chain/LockHotkeyMismatch.mdx @@ -0,0 +1,16 @@ +--- +title: "LockHotkeyMismatch" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The coldkey already has a conviction lock on this subnet bound to a different hotkey, and locks for one coldkey per subnet must target a single hotkey. Check the existing lock's hotkey in the lock storage for the coldkey and netuid, or move the lock first. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain LockHotkeyMismatch`. diff --git a/docs/errors/chain/LockIdOverFlow.mdx b/docs/errors/chain/LockIdOverFlow.mdx new file mode 100644 index 0000000000..c18128b3f8 --- /dev/null +++ b/docs/errors/chain/LockIdOverFlow.mdx @@ -0,0 +1,16 @@ +--- +title: "LockIdOverFlow" +description: "A chain-side capacity limit was hit; reduce the size or count of the request" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The global network-registration lock id counter reached its u32 maximum while queueing a subnet registration, so no new lock could be created. Check the `NetworkRegistrationLockId` storage value; this indicates lock id exhaustion, not a balance problem. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). + +## Remediation + +A chain-side capacity limit was hit; reduce the size or count of the request + +The same explanation is available in the terminal: `btcli explain LockIdOverFlow`. diff --git a/docs/errors/chain/MaxAllowedUIdsLessThanCurrentUIds.mdx b/docs/errors/chain/MaxAllowedUIdsLessThanCurrentUIds.mdx new file mode 100644 index 0000000000..d3d2c0ad30 --- /dev/null +++ b/docs/errors/chain/MaxAllowedUIdsLessThanCurrentUIds.mdx @@ -0,0 +1,16 @@ +--- +title: "MaxAllowedUIdsLessThanCurrentUIds" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +`sudo_set_max_allowed_uids` was given a value below the number of neurons already registered on the subnet. Compare the `max_allowed_uids` argument against `SubnetworkN` for that netuid. + +Declared by the `AdminUtils` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain MaxAllowedUIdsLessThanCurrentUIds`. diff --git a/docs/errors/chain/MaxAllowedUidsGreaterThanDefaultMaxAllowedUids.mdx b/docs/errors/chain/MaxAllowedUidsGreaterThanDefaultMaxAllowedUids.mdx new file mode 100644 index 0000000000..7c3d6182e6 --- /dev/null +++ b/docs/errors/chain/MaxAllowedUidsGreaterThanDefaultMaxAllowedUids.mdx @@ -0,0 +1,16 @@ +--- +title: "MaxAllowedUidsGreaterThanDefaultMaxAllowedUids" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +`sudo_set_max_allowed_uids` was given a value above the chain-wide ceiling. Compare the `max_allowed_uids` argument against the `DefaultMaxAllowedUids` storage value. + +Declared by the `AdminUtils` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain MaxAllowedUidsGreaterThanDefaultMaxAllowedUids`. diff --git a/docs/errors/chain/MaxAllowedUidsLessThanMinAllowedUids.mdx b/docs/errors/chain/MaxAllowedUidsLessThanMinAllowedUids.mdx new file mode 100644 index 0000000000..81aa372953 --- /dev/null +++ b/docs/errors/chain/MaxAllowedUidsLessThanMinAllowedUids.mdx @@ -0,0 +1,16 @@ +--- +title: "MaxAllowedUidsLessThanMinAllowedUids" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +`sudo_set_max_allowed_uids` was given a value below the subnet's configured minimum. Compare the `max_allowed_uids` argument against `MinAllowedUids` for that netuid. + +Declared by the `AdminUtils` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain MaxAllowedUidsLessThanMinAllowedUids`. diff --git a/docs/errors/chain/MaxCallDepthReached.mdx b/docs/errors/chain/MaxCallDepthReached.mdx new file mode 100644 index 0000000000..112ae50e61 --- /dev/null +++ b/docs/errors/chain/MaxCallDepthReached.mdx @@ -0,0 +1,16 @@ +--- +title: "MaxCallDepthReached" +description: "A chain-side capacity limit was hit; reduce the size or count of the request" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +A nested contract call would exceed the maximum call stack depth defined in the pallet schedule. Inspect the cross-contract call chain for deep or unbounded recursion and flatten it or raise the configured depth. + +Declared by the `Contracts` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). + +## Remediation + +A chain-side capacity limit was hit; reduce the size or count of the request + +The same explanation is available in the terminal: `btcli explain MaxCallDepthReached`. diff --git a/docs/errors/chain/MaxContributionReached.mdx b/docs/errors/chain/MaxContributionReached.mdx new file mode 100644 index 0000000000..72184bed9f --- /dev/null +++ b/docs/errors/chain/MaxContributionReached.mdx @@ -0,0 +1,16 @@ +--- +title: "MaxContributionReached" +description: "A chain-side capacity limit was hit; reduce the size or count of the request" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The contributor's cumulative contribution already equals the crowdloan's per-contributor cap, so further contributions are rejected. Compare their `Contributions` entry with the `MaxContributions` value for the `crowdloan_id`. + +Declared by the `Crowdloan` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). + +## Remediation + +A chain-side capacity limit was hit; reduce the size or count of the request + +The same explanation is available in the terminal: `btcli explain MaxContributionReached`. diff --git a/docs/errors/chain/MaxContributorsReached.mdx b/docs/errors/chain/MaxContributorsReached.mdx new file mode 100644 index 0000000000..7714a6c325 --- /dev/null +++ b/docs/errors/chain/MaxContributorsReached.mdx @@ -0,0 +1,16 @@ +--- +title: "MaxContributorsReached" +description: "A chain-side capacity limit was hit; reduce the size or count of the request" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The crowdloan already has the maximum number of distinct contributors, so accounts without an existing contribution are rejected. Compare the `contributors_count` field of the `Crowdloans` entry with the `MaxContributors` pallet constant. + +Declared by the `Crowdloan` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). + +## Remediation + +A chain-side capacity limit was hit; reduce the size or count of the request + +The same explanation is available in the terminal: `btcli explain MaxContributorsReached`. diff --git a/docs/errors/chain/MaxDelegateDependenciesReached.mdx b/docs/errors/chain/MaxDelegateDependenciesReached.mdx new file mode 100644 index 0000000000..2349819bd8 --- /dev/null +++ b/docs/errors/chain/MaxDelegateDependenciesReached.mdx @@ -0,0 +1,16 @@ +--- +title: "MaxDelegateDependenciesReached" +description: "A chain-side capacity limit was hit; reduce the size or count of the request" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +`lock_delegate_dependency` failed because the contract already holds the maximum number of delegate dependencies allowed by the runtime. Check the `MaxDelegateDependencies` config value and unlock unused dependencies first. + +Declared by the `Contracts` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). + +## Remediation + +A chain-side capacity limit was hit; reduce the size or count of the request + +The same explanation is available in the terminal: `btcli explain MaxDelegateDependenciesReached`. diff --git a/docs/errors/chain/MaxValidatorsLargerThanMaxUIds.mdx b/docs/errors/chain/MaxValidatorsLargerThanMaxUIds.mdx new file mode 100644 index 0000000000..b83009ea12 --- /dev/null +++ b/docs/errors/chain/MaxValidatorsLargerThanMaxUIds.mdx @@ -0,0 +1,16 @@ +--- +title: "MaxValidatorsLargerThanMaxUIds" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +`sudo_set_max_allowed_validators` was given a value exceeding the subnet's UID capacity. Compare the `max_allowed_validators` argument against `MaxAllowedUids` for that netuid. + +Declared by the `AdminUtils` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain MaxValidatorsLargerThanMaxUIds`. diff --git a/docs/errors/chain/MaxWeightExceeded.mdx b/docs/errors/chain/MaxWeightExceeded.mdx new file mode 100644 index 0000000000..e061d7dc43 --- /dev/null +++ b/docs/errors/chain/MaxWeightExceeded.mdx @@ -0,0 +1,16 @@ +--- +title: "MaxWeightExceeded" +description: "A chain-side capacity limit was hit; reduce the size or count of the request" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +After normalization, one of the submitted weights exceeds the subnet's maximum weight limit (self-weight is exempt). Check the `MaxWeightsLimit` hyperparameter (`btcli sudo get --netuid `) and flatten the weight vector before setting. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). + +## Remediation + +A chain-side capacity limit was hit; reduce the size or count of the request + +The same explanation is available in the terminal: `btcli explain MaxWeightExceeded`. diff --git a/docs/errors/chain/MaxWeightTooLow.mdx b/docs/errors/chain/MaxWeightTooLow.mdx new file mode 100644 index 0000000000..c5d4b90f1c --- /dev/null +++ b/docs/errors/chain/MaxWeightTooLow.mdx @@ -0,0 +1,16 @@ +--- +title: "MaxWeightTooLow" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The `max_weight` argument supplied with the final multisig approval is lower than the actual weight of the call being dispatched. Compute the call's real dispatch weight and pass a `max_weight` at least that large to `as_multi`. + +Declared by the `Multisig` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain MaxWeightTooLow`. diff --git a/docs/errors/chain/MaximumContributionTooLow.mdx b/docs/errors/chain/MaximumContributionTooLow.mdx new file mode 100644 index 0000000000..2a5f8cd6ca --- /dev/null +++ b/docs/errors/chain/MaximumContributionTooLow.mdx @@ -0,0 +1,16 @@ +--- +title: "MaximumContributionTooLow" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The `new_max_contribution` passed to `set_max_contribution` is below the crowdloan's `min_contribution` or below the creator's existing contribution. Check both against the `Crowdloans` entry and the creator's `Contributions` entry. + +Declared by the `Crowdloan` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain MaximumContributionTooLow`. diff --git a/docs/errors/chain/MechanismDoesNotExist.mdx b/docs/errors/chain/MechanismDoesNotExist.mdx new file mode 100644 index 0000000000..53565cc8f7 --- /dev/null +++ b/docs/errors/chain/MechanismDoesNotExist.mdx @@ -0,0 +1,16 @@ +--- +title: "MechanismDoesNotExist" +description: "Use an existing netuid; `btcli subnets list` shows valid ones" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The target subnet or its sub-mechanism does not exist: the netuid is unknown, the mechanism index is at or above `MechanismCountCurrent`, or a non-dynamic `mechid` was requested. Check the netuid with `btcli subnets list` and the mechanism count for that subnet. + +Declared by the `SubtensorModule`, `Swap` pallets; it classifies to the semantic code [`subnet_not_exists`](/docs/errors/subnet-not-exists). + +## Remediation + +Use an existing netuid; `btcli subnets list` shows valid ones + +The same explanation is available in the terminal: `btcli explain MechanismDoesNotExist`. diff --git a/docs/errors/chain/MigrationInProgress.mdx b/docs/errors/chain/MigrationInProgress.mdx new file mode 100644 index 0000000000..3b706fb332 --- /dev/null +++ b/docs/errors/chain/MigrationInProgress.mdx @@ -0,0 +1,16 @@ +--- +title: "MigrationInProgress" +description: "The required window has not opened yet; wait some blocks and retry" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +A multi-block storage migration of the contracts pallet is still running, so other extrinsics of the pallet are rejected until it completes. Check the migration status in storage and retry once done, or submit `migrate` calls to advance it. + +Declared by the `Contracts` pallet; it classifies to the semantic code [`too_early`](/docs/errors/too-early). + +## Remediation + +The required window has not opened yet; wait some blocks and retry + +The same explanation is available in the terminal: `btcli explain MigrationInProgress`. diff --git a/docs/errors/chain/MinAllowedUidsGreaterThanCurrentUids.mdx b/docs/errors/chain/MinAllowedUidsGreaterThanCurrentUids.mdx new file mode 100644 index 0000000000..78569183d9 --- /dev/null +++ b/docs/errors/chain/MinAllowedUidsGreaterThanCurrentUids.mdx @@ -0,0 +1,16 @@ +--- +title: "MinAllowedUidsGreaterThanCurrentUids" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +`sudo_set_min_allowed_uids` was given a value not strictly below the number of neurons currently registered on the subnet. Compare the `min_allowed_uids` argument against `SubnetworkN` for that netuid. + +Declared by the `AdminUtils` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain MinAllowedUidsGreaterThanCurrentUids`. diff --git a/docs/errors/chain/MinAllowedUidsGreaterThanMaxAllowedUids.mdx b/docs/errors/chain/MinAllowedUidsGreaterThanMaxAllowedUids.mdx new file mode 100644 index 0000000000..62b3b2c95d --- /dev/null +++ b/docs/errors/chain/MinAllowedUidsGreaterThanMaxAllowedUids.mdx @@ -0,0 +1,16 @@ +--- +title: "MinAllowedUidsGreaterThanMaxAllowedUids" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +`sudo_set_min_allowed_uids` was given a value not strictly below the subnet's maximum UID capacity. Compare the `min_allowed_uids` argument against `MaxAllowedUids` for that netuid. + +Declared by the `AdminUtils` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain MinAllowedUidsGreaterThanMaxAllowedUids`. diff --git a/docs/errors/chain/MinimumContributionTooHigh.mdx b/docs/errors/chain/MinimumContributionTooHigh.mdx new file mode 100644 index 0000000000..4061bc16ba --- /dev/null +++ b/docs/errors/chain/MinimumContributionTooHigh.mdx @@ -0,0 +1,16 @@ +--- +title: "MinimumContributionTooHigh" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The `new_min_contribution` passed to `update_min_contribution` exceeds the crowdloan's configured per-contributor maximum. Compare it with the `MaxContributions` entry for the `crowdloan_id`. + +Declared by the `Crowdloan` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain MinimumContributionTooHigh`. diff --git a/docs/errors/chain/MinimumContributionTooLow.mdx b/docs/errors/chain/MinimumContributionTooLow.mdx new file mode 100644 index 0000000000..399e35f8e8 --- /dev/null +++ b/docs/errors/chain/MinimumContributionTooLow.mdx @@ -0,0 +1,16 @@ +--- +title: "MinimumContributionTooLow" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The minimum contribution given to `create` or `update_min_contribution` is below the chain-wide floor. Check the `AbsoluteMinimumContribution` pallet constant and raise the `min_contribution` argument to at least that value. + +Declared by the `Crowdloan` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain MinimumContributionTooLow`. diff --git a/docs/errors/chain/MinimumThreshold.mdx b/docs/errors/chain/MinimumThreshold.mdx new file mode 100644 index 0000000000..48fda10bd1 --- /dev/null +++ b/docs/errors/chain/MinimumThreshold.mdx @@ -0,0 +1,16 @@ +--- +title: "MinimumThreshold" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The multisig `threshold` argument was below 2, which these calls do not accept. Pass a threshold of 2 or more, or use `as_multi_threshold_1` for the single-approval case. + +Declared by the `Multisig` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain MinimumThreshold`. diff --git a/docs/errors/chain/MultiBlockMigrationsOngoing.mdx b/docs/errors/chain/MultiBlockMigrationsOngoing.mdx new file mode 100644 index 0000000000..c5e0886874 --- /dev/null +++ b/docs/errors/chain/MultiBlockMigrationsOngoing.mdx @@ -0,0 +1,16 @@ +--- +title: "MultiBlockMigrationsOngoing" +description: "The required window has not opened yet; wait some blocks and retry" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +Runtime code replacement is blocked while a multi-block migration is still executing. Wait for the ongoing migrations to complete (check the multi-block migrations cursor) before retrying `set_code` or the authorized upgrade. + +Declared by the `System` pallet; it classifies to the semantic code [`too_early`](/docs/errors/too-early). + +## Remediation + +The required window has not opened yet; wait some blocks and retry + +The same explanation is available in the terminal: `btcli explain MultiBlockMigrationsOngoing`. diff --git a/docs/errors/chain/Named.mdx b/docs/errors/chain/Named.mdx new file mode 100644 index 0000000000..64cb2b0705 --- /dev/null +++ b/docs/errors/chain/Named.mdx @@ -0,0 +1,16 @@ +--- +title: "Named" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +An unnamed scheduler function was used on a task that was scheduled with a name. Use the named variants (`cancel_named`, `schedule_named`) with the task's id, which you can find via the `Lookup` storage map. + +Declared by the `Scheduler` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain Named`. diff --git a/docs/errors/chain/NeedWaitingMoreBlocksToStarCall.mdx b/docs/errors/chain/NeedWaitingMoreBlocksToStarCall.mdx new file mode 100644 index 0000000000..811da4d0a9 --- /dev/null +++ b/docs/errors/chain/NeedWaitingMoreBlocksToStarCall.mdx @@ -0,0 +1,16 @@ +--- +title: "NeedWaitingMoreBlocksToStarCall" +description: "The required window has not opened yet; wait some blocks and retry" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The subnet owner called start_call before enough blocks had passed since the subnet was registered. Compare the current block with `NetworkRegisteredAt` for the netuid plus the start-call delay and retry once the window opens. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`too_early`](/docs/errors/too-early). + +## Remediation + +The required window has not opened yet; wait some blocks and retry + +The same explanation is available in the terminal: `btcli explain NeedWaitingMoreBlocksToStarCall`. diff --git a/docs/errors/chain/NegativeSigmoidSteepness.mdx b/docs/errors/chain/NegativeSigmoidSteepness.mdx new file mode 100644 index 0000000000..cc1073b2d0 --- /dev/null +++ b/docs/errors/chain/NegativeSigmoidSteepness.mdx @@ -0,0 +1,16 @@ +--- +title: "NegativeSigmoidSteepness" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +A non-root caller (subnet owner) passed a negative value to `sudo_set_alpha_sigmoid_steepness`; negative steepness values are reserved for the root origin. Use a non-negative `steepness` or submit the call as root. + +Declared by the `AdminUtils` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain NegativeSigmoidSteepness`. diff --git a/docs/errors/chain/NetworkDissolveAlreadyQueued.mdx b/docs/errors/chain/NetworkDissolveAlreadyQueued.mdx new file mode 100644 index 0000000000..e2e8ec31b9 --- /dev/null +++ b/docs/errors/chain/NetworkDissolveAlreadyQueued.mdx @@ -0,0 +1,16 @@ +--- +title: "NetworkDissolveAlreadyQueued" +description: "The object or state already exists; treat the goal as met or pick a different target" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The subnet is already in the dissolve cleanup queue, so it cannot be queued for dissolution again. Check the `DissolveCleanupQueue` storage value for the netuid before submitting another dissolve request. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`already_exists`](/docs/errors/already-exists). + +## Remediation + +The object or state already exists; treat the goal as met or pick a different target + +The same explanation is available in the terminal: `btcli explain NetworkDissolveAlreadyQueued`. diff --git a/docs/errors/chain/NetworkTxRateLimitExceeded.mdx b/docs/errors/chain/NetworkTxRateLimitExceeded.mdx new file mode 100644 index 0000000000..5b955bab66 --- /dev/null +++ b/docs/errors/chain/NetworkTxRateLimitExceeded.mdx @@ -0,0 +1,16 @@ +--- +title: "NetworkTxRateLimitExceeded" +description: "Wait for the rate-limit window to pass, then retry" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The coldkey attempted register_network again before the network registration rate limit elapsed since its previous registration. Check the coldkey's last register-network block against the `NetworkRateLimit` storage value and wait the remaining blocks. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`rate_limited`](/docs/errors/rate-limited). + +## Remediation + +Wait for the rate-limit window to pass, then retry + +The same explanation is available in the terminal: `btcli explain NetworkTxRateLimitExceeded`. diff --git a/docs/errors/chain/NeuronNoValidatorPermit.mdx b/docs/errors/chain/NeuronNoValidatorPermit.mdx new file mode 100644 index 0000000000..80d4d53d9a --- /dev/null +++ b/docs/errors/chain/NeuronNoValidatorPermit.mdx @@ -0,0 +1,16 @@ +--- +title: "NeuronNoValidatorPermit" +description: "Sign with the key or origin that owns the target object, then retry" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The hotkey tried to set weights on other neurons without holding a validator permit on that subnet. Check `ValidatorPermit` for the neuron's uid (e.g. via the metagraph or `btcli subnets metagraph`) and whether its stake ranks it as a validator. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`not_authorized`](/docs/errors/not-authorized). + +## Remediation + +Sign with the key or origin that owns the target object, then retry + +The same explanation is available in the terminal: `btcli explain NeuronNoValidatorPermit`. diff --git a/docs/errors/chain/NewColdKeyIsHotkey.mdx b/docs/errors/chain/NewColdKeyIsHotkey.mdx new file mode 100644 index 0000000000..d5df62f7a5 --- /dev/null +++ b/docs/errors/chain/NewColdKeyIsHotkey.mdx @@ -0,0 +1,16 @@ +--- +title: "NewColdKeyIsHotkey" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The proposed new coldkey in a coldkey swap is already an existing hotkey account, which is not allowed. Check the `Owner` storage for the candidate key to confirm it is not registered as a hotkey, and pick a fresh coldkey. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain NewColdKeyIsHotkey`. diff --git a/docs/errors/chain/NewHotKeyIsSameWithOld.mdx b/docs/errors/chain/NewHotKeyIsSameWithOld.mdx new file mode 100644 index 0000000000..43f0846e4d --- /dev/null +++ b/docs/errors/chain/NewHotKeyIsSameWithOld.mdx @@ -0,0 +1,16 @@ +--- +title: "NewHotKeyIsSameWithOld" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +Swap_hotkey was called with `new_hotkey` equal to the current hotkey, so there is nothing to swap. Check the extrinsic arguments and supply a different destination hotkey. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain NewHotKeyIsSameWithOld`. diff --git a/docs/errors/chain/NewHotKeyNotCleanForRootSwap.mdx b/docs/errors/chain/NewHotKeyNotCleanForRootSwap.mdx new file mode 100644 index 0000000000..50112aa888 --- /dev/null +++ b/docs/errors/chain/NewHotKeyNotCleanForRootSwap.mdx @@ -0,0 +1,16 @@ +--- +title: "NewHotKeyNotCleanForRootSwap" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The destination hotkey has pending root claimable dividends, non-zero root stake, or root-claimed history, so root accounting cannot merge safely. Check `RootClaimable` and root-subnet stake for the new hotkey; claim or clear them, or use a fresh hotkey. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain NewHotKeyNotCleanForRootSwap`. diff --git a/docs/errors/chain/NoApprovalsNeeded.mdx b/docs/errors/chain/NoApprovalsNeeded.mdx new file mode 100644 index 0000000000..ca07f59780 --- /dev/null +++ b/docs/errors/chain/NoApprovalsNeeded.mdx @@ -0,0 +1,16 @@ +--- +title: "NoApprovalsNeeded" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The multisig call does not need any more approvals; it is only waiting for final execution with the full call data. Instead of another `approve_as_multi`, submit `as_multi` with the complete call to dispatch it. + +Declared by the `Multisig` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain NoApprovalsNeeded`. diff --git a/docs/errors/chain/NoChainExtension.mdx b/docs/errors/chain/NoChainExtension.mdx new file mode 100644 index 0000000000..e69efba9a6 --- /dev/null +++ b/docs/errors/chain/NoChainExtension.mdx @@ -0,0 +1,16 @@ +--- +title: "NoChainExtension" +description: "This call or feature is switched off on this network" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The contract invoked `call_chain_extension` but this runtime registers no chain extension; such code is normally rejected at upload. Check that the target chain provides the chain extension the contract was built against. + +Declared by the `Contracts` pallet; it classifies to the semantic code [`disabled`](/docs/errors/disabled). + +## Remediation + +This call or feature is switched off on this network + +The same explanation is available in the terminal: `btcli explain NoChainExtension`. diff --git a/docs/errors/chain/NoContribution.mdx b/docs/errors/chain/NoContribution.mdx new file mode 100644 index 0000000000..ad2f46d691 --- /dev/null +++ b/docs/errors/chain/NoContribution.mdx @@ -0,0 +1,16 @@ +--- +title: "NoContribution" +description: "The referenced object is not on-chain; check the identifier" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The account has no contribution recorded for this crowdloan, so `withdraw` has nothing to pay out (or `dissolve` finds no creator contribution). Check the `Contributions` double map under the `crowdloan_id` for the account in question. + +Declared by the `Crowdloan` pallet; it classifies to the semantic code [`not_found`](/docs/errors/not-found). + +## Remediation + +The referenced object is not on-chain; check the identifier + +The same explanation is available in the terminal: `btcli explain NoContribution`. diff --git a/docs/errors/chain/NoDeposit.mdx b/docs/errors/chain/NoDeposit.mdx new file mode 100644 index 0000000000..336ccb3de2 --- /dev/null +++ b/docs/errors/chain/NoDeposit.mdx @@ -0,0 +1,16 @@ +--- +title: "NoDeposit" +description: "The referenced object is not on-chain; check the identifier" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +No safe-mode deposit exists for the given account and block combination, so nothing can be released or slashed. Check the `Deposits` storage map for an entry under that account and deposit block. + +Declared by the `SafeMode` pallet; it classifies to the semantic code [`not_found`](/docs/errors/not-found). + +## Remediation + +The referenced object is not on-chain; check the identifier + +The same explanation is available in the terminal: `btcli explain NoDeposit`. diff --git a/docs/errors/chain/NoExistingLock.mdx b/docs/errors/chain/NoExistingLock.mdx new file mode 100644 index 0000000000..c983b30806 --- /dev/null +++ b/docs/errors/chain/NoExistingLock.mdx @@ -0,0 +1,16 @@ +--- +title: "NoExistingLock" +description: "The referenced object is not on-chain; check the identifier" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +Move_lock was called but no conviction lock exists for the signing coldkey on that subnet. Check the lock storage for the coldkey and netuid, and create a lock before attempting to move it to another hotkey. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`not_found`](/docs/errors/not-found). + +## Remediation + +The referenced object is not on-chain; check the identifier + +The same explanation is available in the terminal: `btcli explain NoExistingLock`. diff --git a/docs/errors/chain/NoMigrationPerformed.mdx b/docs/errors/chain/NoMigrationPerformed.mdx new file mode 100644 index 0000000000..397377b6f7 --- /dev/null +++ b/docs/errors/chain/NoMigrationPerformed.mdx @@ -0,0 +1,16 @@ +--- +title: "NoMigrationPerformed" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +A `migrate` call ran but no migration step executed, either because no migration is pending or the supplied `weight_limit` is too small for one step. Check the in-progress migration status and increase the weight limit argument. + +Declared by the `Contracts` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain NoMigrationPerformed`. diff --git a/docs/errors/chain/NoNeuronIdAvailable.mdx b/docs/errors/chain/NoNeuronIdAvailable.mdx new file mode 100644 index 0000000000..99f645af62 --- /dev/null +++ b/docs/errors/chain/NoNeuronIdAvailable.mdx @@ -0,0 +1,16 @@ +--- +title: "NoNeuronIdAvailable" +description: "A chain-side capacity limit was hit; reduce the size or count of the request" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +Registration could not obtain a uid: the subnet's `MaxAllowedUids` is 0, or the subnet is full and every neuron is immune from pruning. Check `SubnetworkN` versus `MaxAllowedUids` for the netuid and retry after immunity periods expire. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). + +## Remediation + +A chain-side capacity limit was hit; reduce the size or count of the request + +The same explanation is available in the terminal: `btcli explain NoNeuronIdAvailable`. diff --git a/docs/errors/chain/NoPermission.mdx b/docs/errors/chain/NoPermission.mdx new file mode 100644 index 0000000000..764b348ee5 --- /dev/null +++ b/docs/errors/chain/NoPermission.mdx @@ -0,0 +1,16 @@ +--- +title: "NoPermission" +description: "Sign with the key or origin that owns the target object, then retry" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The proxy pallet refused the action: the proxied call could escalate privileges, or the caller lacks authority over the pure proxy (e.g. `kill_pure` by a non-spawner). Check the proxy type's call filter and the original `create_pure` arguments. + +Declared by the `Proxy` pallet; it classifies to the semantic code [`not_authorized`](/docs/errors/not-authorized). + +## Remediation + +Sign with the key or origin that owns the target object, then retry + +The same explanation is available in the terminal: `btcli explain NoPermission`. diff --git a/docs/errors/chain/NoSelfProxy.mdx b/docs/errors/chain/NoSelfProxy.mdx new file mode 100644 index 0000000000..fdf7db45e3 --- /dev/null +++ b/docs/errors/chain/NoSelfProxy.mdx @@ -0,0 +1,16 @@ +--- +title: "NoSelfProxy" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +An account attempted to register itself as its own proxy, which is not allowed. Check the `delegate` argument to `add_proxy` and ensure it differs from the calling (delegator) account. + +Declared by the `Proxy` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain NoSelfProxy`. diff --git a/docs/errors/chain/NoTimepoint.mdx b/docs/errors/chain/NoTimepoint.mdx new file mode 100644 index 0000000000..fba597bc00 --- /dev/null +++ b/docs/errors/chain/NoTimepoint.mdx @@ -0,0 +1,16 @@ +--- +title: "NoTimepoint" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +No timepoint was supplied but this multisig operation is already underway, so the approval cannot be matched to it. Read the operation's `when` field from the `Multisigs` entry for the call hash and pass that height and index as `maybe_timepoint`. + +Declared by the `Multisig` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain NoTimepoint`. diff --git a/docs/errors/chain/NoWeightsCommitFound.mdx b/docs/errors/chain/NoWeightsCommitFound.mdx new file mode 100644 index 0000000000..d3c7b673f7 --- /dev/null +++ b/docs/errors/chain/NoWeightsCommitFound.mdx @@ -0,0 +1,16 @@ +--- +title: "NoWeightsCommitFound" +description: "The referenced object is not on-chain; check the identifier" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +A weights reveal was submitted but no pending (non-expired) commit exists for the hotkey and netuid, possibly because it already expired. Query the `WeightCommits` storage map for the hotkey and check the commit hasn't passed the reveal window. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`not_found`](/docs/errors/not-found). + +## Remediation + +The referenced object is not on-chain; check the identifier + +The same explanation is available in the terminal: `btcli explain NoWeightsCommitFound`. diff --git a/docs/errors/chain/NonAssociatedColdKey.mdx b/docs/errors/chain/NonAssociatedColdKey.mdx new file mode 100644 index 0000000000..4f7879d640 --- /dev/null +++ b/docs/errors/chain/NonAssociatedColdKey.mdx @@ -0,0 +1,16 @@ +--- +title: "NonAssociatedColdKey" +description: "Sign with the key or origin that owns the target object, then retry" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The signing coldkey does not own the hotkey it is trying to operate on (stake, swap, serve, children or take changes). Check the `Owner` storage entry for the hotkey, e.g. via `btcli wallet overview`, and sign with the coldkey that registered it. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`not_authorized`](/docs/errors/not-authorized). + +## Remediation + +Sign with the key or origin that owns the target object, then retry + +The same explanation is available in the terminal: `btcli explain NonAssociatedColdKey`. diff --git a/docs/errors/chain/NonDefaultComposite.mdx b/docs/errors/chain/NonDefaultComposite.mdx new file mode 100644 index 0000000000..b966fc6694 --- /dev/null +++ b/docs/errors/chain/NonDefaultComposite.mdx @@ -0,0 +1,16 @@ +--- +title: "NonDefaultComposite" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The account cannot be killed because its composite account data is not in the default state. Check the account's `System.Account` entry; all balance and data fields must be default before the account can be removed this way. + +Declared by the `System` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain NonDefaultComposite`. diff --git a/docs/errors/chain/NonZeroRefCount.mdx b/docs/errors/chain/NonZeroRefCount.mdx new file mode 100644 index 0000000000..a7feeb3b4e --- /dev/null +++ b/docs/errors/chain/NonZeroRefCount.mdx @@ -0,0 +1,16 @@ +--- +title: "NonZeroRefCount" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The account cannot be purged because other pallets still reference it. Check the `consumers`, `providers`, and `sufficients` counters in the account's `System.Account` record; all references must be released first. + +Declared by the `System` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain NonZeroRefCount`. diff --git a/docs/errors/chain/NoneValue.mdx b/docs/errors/chain/NoneValue.mdx new file mode 100644 index 0000000000..cfd89f2997 --- /dev/null +++ b/docs/errors/chain/NoneValue.mdx @@ -0,0 +1,16 @@ +--- +title: "NoneValue" +description: "The referenced object is not on-chain; check the identifier" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +Template leftover in the drand pallet meaning a storage value was read before ever being set; no current code path raises it. If seen, inspect the drand pallet's storage items for missing initialization. + +Declared by the `Drand` pallet; it classifies to the semantic code [`not_found`](/docs/errors/not-found). + +## Remediation + +The referenced object is not on-chain; check the identifier + +The same explanation is available in the terminal: `btcli explain NoneValue`. diff --git a/docs/errors/chain/NotAllowed.mdx b/docs/errors/chain/NotAllowed.mdx new file mode 100644 index 0000000000..b751f85f5f --- /dev/null +++ b/docs/errors/chain/NotAllowed.mdx @@ -0,0 +1,16 @@ +--- +title: "NotAllowed" +description: "Sign with the key or origin that owns the target object, then retry" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +Contract deployment was blocked because the source address is not in the `WhitelistedCreators` list while the whitelist check is enabled. Check the deployer address against `WhitelistedCreators` and the `DisableWhitelistCheck` storage value. + +Declared by the `EVM` pallet; it classifies to the semantic code [`not_authorized`](/docs/errors/not-authorized). + +## Remediation + +Sign with the key or origin that owns the target object, then retry + +The same explanation is available in the terminal: `btcli explain NotAllowed`. diff --git a/docs/errors/chain/NotAuthorized.mdx b/docs/errors/chain/NotAuthorized.mdx new file mode 100644 index 0000000000..4211102069 --- /dev/null +++ b/docs/errors/chain/NotAuthorized.mdx @@ -0,0 +1,16 @@ +--- +title: "NotAuthorized" +description: "Sign with the key or origin that owns the target object, then retry" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The caller is not permitted to manage this preimage; unnoting or unrequesting requires the pallet's manager origin or the account that originally deposited it. Check which origin noted or requested the preimage and use that origin or the configured `ManagerOrigin`. + +Declared by the `Preimage` pallet; it classifies to the semantic code [`not_authorized`](/docs/errors/not-authorized). + +## Remediation + +Sign with the key or origin that owns the target object, then retry + +The same explanation is available in the terminal: `btcli explain NotAuthorized`. diff --git a/docs/errors/chain/NotConfigured.mdx b/docs/errors/chain/NotConfigured.mdx new file mode 100644 index 0000000000..0499181f50 --- /dev/null +++ b/docs/errors/chain/NotConfigured.mdx @@ -0,0 +1,16 @@ +--- +title: "NotConfigured" +description: "This call or feature is switched off on this network" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The permissionless safe-mode operation is disabled because its config option is `None`: `EnterDepositAmount` for `enter`, `ExtendDepositAmount` for `extend`, or `ReleaseDelay` for `release_deposit`. Check the runtime config or use the root-only force variants. + +Declared by the `SafeMode` pallet; it classifies to the semantic code [`disabled`](/docs/errors/disabled). + +## Remediation + +This call or feature is switched off on this network + +The same explanation is available in the terminal: `btcli explain NotConfigured`. diff --git a/docs/errors/chain/NotEnoughAlphaOutToRecycle.mdx b/docs/errors/chain/NotEnoughAlphaOutToRecycle.mdx new file mode 100644 index 0000000000..a3a2d29208 --- /dev/null +++ b/docs/errors/chain/NotEnoughAlphaOutToRecycle.mdx @@ -0,0 +1,16 @@ +--- +title: "NotEnoughAlphaOutToRecycle" +description: "The pool cannot absorb this trade; reduce the amount or split it into smaller steps" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +A recycle or burn of alpha requested more than the subnet's outstanding alpha supply. Compare the amount against the `SubnetAlphaOut` storage value for the netuid and reduce the recycle amount. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`insufficient_liquidity`](/docs/errors/insufficient-liquidity). + +## Remediation + +The pool cannot absorb this trade; reduce the amount or split it into smaller steps + +The same explanation is available in the terminal: `btcli explain NotEnoughAlphaOutToRecycle`. diff --git a/docs/errors/chain/NotEnoughBalanceToPaySwapColdKey.mdx b/docs/errors/chain/NotEnoughBalanceToPaySwapColdKey.mdx new file mode 100644 index 0000000000..b34d2ce3b7 --- /dev/null +++ b/docs/errors/chain/NotEnoughBalanceToPaySwapColdKey.mdx @@ -0,0 +1,16 @@ +--- +title: "NotEnoughBalanceToPaySwapColdKey" +description: "Fund the signing account or reduce the amount; check with `btcli wallet balance`" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The coldkey's free TAO balance cannot cover the coldkey swap cost, which is recycled when the swap executes. Check the balance with `btcli wallet balance` against the swap cost and top up before scheduling the swap. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`insufficient_balance`](/docs/errors/insufficient-balance). + +## Remediation + +Fund the signing account or reduce the amount; check with `btcli wallet balance` + +The same explanation is available in the terminal: `btcli explain NotEnoughBalanceToPaySwapColdKey`. diff --git a/docs/errors/chain/NotEnoughBalanceToPaySwapHotKey.mdx b/docs/errors/chain/NotEnoughBalanceToPaySwapHotKey.mdx new file mode 100644 index 0000000000..f42ae346b1 --- /dev/null +++ b/docs/errors/chain/NotEnoughBalanceToPaySwapHotKey.mdx @@ -0,0 +1,16 @@ +--- +title: "NotEnoughBalanceToPaySwapHotKey" +description: "Fund the signing account or reduce the amount; check with `btcli wallet balance`" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The coldkey's free TAO balance is below the hotkey swap cost (a per-subnet cost applies when swapping on a single netuid). Check `btcli wallet balance` against the key swap cost and fund the coldkey before retrying. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`insufficient_balance`](/docs/errors/insufficient-balance). + +## Remediation + +Fund the signing account or reduce the amount; check with `btcli wallet balance` + +The same explanation is available in the terminal: `btcli explain NotEnoughBalanceToPaySwapHotKey`. diff --git a/docs/errors/chain/NotEnoughBalanceToStake.mdx b/docs/errors/chain/NotEnoughBalanceToStake.mdx new file mode 100644 index 0000000000..0c1addbb78 --- /dev/null +++ b/docs/errors/chain/NotEnoughBalanceToStake.mdx @@ -0,0 +1,16 @@ +--- +title: "NotEnoughBalanceToStake" +description: "Fund the signing account or reduce the amount; check with `btcli wallet balance`" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The coldkey's free balance is less than the TAO required, either the stake amount in add_stake or the burn cost of a registration. Check `btcli wallet balance` against the amount or the current registration burn (`Burn` storage for the netuid). + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`insufficient_balance`](/docs/errors/insufficient-balance). + +## Remediation + +Fund the signing account or reduce the amount; check with `btcli wallet balance` + +The same explanation is available in the terminal: `btcli explain NotEnoughBalanceToStake`. diff --git a/docs/errors/chain/NotEnoughStake.mdx b/docs/errors/chain/NotEnoughStake.mdx new file mode 100644 index 0000000000..b713f7d2a3 --- /dev/null +++ b/docs/errors/chain/NotEnoughStake.mdx @@ -0,0 +1,16 @@ +--- +title: "NotEnoughStake" +description: "Fund the signing account or reduce the amount; check with `btcli wallet balance`" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The caller's hotkey holds less stake than the action requires; a generic insufficient-stake failure on staking-related calls. Check the hotkey's stake on the relevant subnet, e.g. `btcli stake list`, against the amount the extrinsic needs. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`insufficient_balance`](/docs/errors/insufficient-balance). + +## Remediation + +Fund the signing account or reduce the amount; check with `btcli wallet balance` + +The same explanation is available in the terminal: `btcli explain NotEnoughStake`. diff --git a/docs/errors/chain/NotEnoughStakeToSetChildkeys.mdx b/docs/errors/chain/NotEnoughStakeToSetChildkeys.mdx new file mode 100644 index 0000000000..0def334719 --- /dev/null +++ b/docs/errors/chain/NotEnoughStakeToSetChildkeys.mdx @@ -0,0 +1,16 @@ +--- +title: "NotEnoughStakeToSetChildkeys" +description: "Fund the signing account or reduce the amount; check with `btcli wallet balance`" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +Raised by `set_children` when the parent hotkey's total stake is below `StakeThreshold` and it is not the subnet owner hotkey. Compare the hotkey's total stake (`btcli stake list`) against the `StakeThreshold` storage value. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`insufficient_balance`](/docs/errors/insufficient-balance). + +## Remediation + +Fund the signing account or reduce the amount; check with `btcli wallet balance` + +The same explanation is available in the terminal: `btcli explain NotEnoughStakeToSetChildkeys`. diff --git a/docs/errors/chain/NotEnoughStakeToSetWeights.mdx b/docs/errors/chain/NotEnoughStakeToSetWeights.mdx new file mode 100644 index 0000000000..28f9524b44 --- /dev/null +++ b/docs/errors/chain/NotEnoughStakeToSetWeights.mdx @@ -0,0 +1,16 @@ +--- +title: "NotEnoughStakeToSetWeights" +description: "Fund the signing account or reduce the amount; check with `btcli wallet balance`" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +Setting or committing weights failed because the hotkey's stake weight on the subnet is below `StakeThreshold` (the weights-min-stake floor); the subnet owner hotkey is exempt. Check the hotkey's stake on that netuid against `StakeThreshold`. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`insufficient_balance`](/docs/errors/insufficient-balance). + +## Remediation + +Fund the signing account or reduce the amount; check with `btcli wallet balance` + +The same explanation is available in the terminal: `btcli explain NotEnoughStakeToSetWeights`. diff --git a/docs/errors/chain/NotEnoughStakeToWithdraw.mdx b/docs/errors/chain/NotEnoughStakeToWithdraw.mdx new file mode 100644 index 0000000000..3aa6f65433 --- /dev/null +++ b/docs/errors/chain/NotEnoughStakeToWithdraw.mdx @@ -0,0 +1,16 @@ +--- +title: "NotEnoughStakeToWithdraw" +description: "Fund the signing account or reduce the amount; check with `btcli wallet balance`" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +An unstake, stake move, swap, or transfer requested more alpha than the hotkey-coldkey pair holds on that subnet. Compare the requested amount against the pair's current stake on the netuid (`btcli stake list` or the `Alpha` storage). + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`insufficient_balance`](/docs/errors/insufficient-balance). + +## Remediation + +Fund the signing account or reduce the amount; check with `btcli wallet balance` + +The same explanation is available in the terminal: `btcli explain NotEnoughStakeToWithdraw`. diff --git a/docs/errors/chain/NotFound.mdx b/docs/errors/chain/NotFound.mdx new file mode 100644 index 0000000000..ac7e3b04ed --- /dev/null +++ b/docs/errors/chain/NotFound.mdx @@ -0,0 +1,16 @@ +--- +title: "NotFound" +description: "The referenced object is not on-chain; check the identifier" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The referenced item does not exist in storage: no multisig operation for that call hash in `Multisigs`, no scheduled task at that slot or name in `Agenda`/`Lookup`, or no matching proxy registration in `Proxies`. Verify the identifier against current chain state. + +Declared by the `Multisig`, `Scheduler`, `Proxy` pallets; it classifies to the semantic code [`not_found`](/docs/errors/not-found). + +## Remediation + +The referenced object is not on-chain; check the identifier + +The same explanation is available in the terminal: `btcli explain NotFound`. diff --git a/docs/errors/chain/NotNoted.mdx b/docs/errors/chain/NotNoted.mdx new file mode 100644 index 0000000000..f4976e0034 --- /dev/null +++ b/docs/errors/chain/NotNoted.mdx @@ -0,0 +1,16 @@ +--- +title: "NotNoted" +description: "The referenced object is not on-chain; check the identifier" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The preimage cannot be unnoted because no preimage was ever noted for this hash. Check `RequestStatusFor` (or legacy `StatusFor`) for the hash to confirm what, if anything, is stored. + +Declared by the `Preimage` pallet; it classifies to the semantic code [`not_found`](/docs/errors/not-found). + +## Remediation + +The referenced object is not on-chain; check the identifier + +The same explanation is available in the terminal: `btcli explain NotNoted`. diff --git a/docs/errors/chain/NotOwner.mdx b/docs/errors/chain/NotOwner.mdx new file mode 100644 index 0000000000..854299cf46 --- /dev/null +++ b/docs/errors/chain/NotOwner.mdx @@ -0,0 +1,16 @@ +--- +title: "NotOwner" +description: "Sign with the key or origin that owns the target object, then retry" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +Only the account that opened the multisig operation (its depositor) may cancel it or adjust its deposit. Compare the sender against the `depositor` field stored in the `Multisigs` entry for this call hash. + +Declared by the `Multisig` pallet; it classifies to the semantic code [`not_authorized`](/docs/errors/not-authorized). + +## Remediation + +Sign with the key or origin that owns the target object, then retry + +The same explanation is available in the terminal: `btcli explain NotOwner`. diff --git a/docs/errors/chain/NotPermittedOnRootSubnet.mdx b/docs/errors/chain/NotPermittedOnRootSubnet.mdx new file mode 100644 index 0000000000..deff9e6315 --- /dev/null +++ b/docs/errors/chain/NotPermittedOnRootSubnet.mdx @@ -0,0 +1,16 @@ +--- +title: "NotPermittedOnRootSubnet" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +An admin-utils call that only applies to regular subnets (burn half-life, burn increase multiplier, owner-cut flags, or the subnet emission toggle) was targeted at the root network. Check that the `netuid` argument is not the root netuid 0. + +Declared by the `AdminUtils` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain NotPermittedOnRootSubnet`. diff --git a/docs/errors/chain/NotProxy.mdx b/docs/errors/chain/NotProxy.mdx new file mode 100644 index 0000000000..b4623d1558 --- /dev/null +++ b/docs/errors/chain/NotProxy.mdx @@ -0,0 +1,16 @@ +--- +title: "NotProxy" +description: "Sign with the key or origin that owns the target object, then retry" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The sender is not registered as a proxy for the account it tried to act for. Check the `Proxies` entry of the `real` account and confirm the sender appears there with a proxy type and delay compatible with the call. + +Declared by the `Proxy` pallet; it classifies to the semantic code [`not_authorized`](/docs/errors/not-authorized). + +## Remediation + +Sign with the key or origin that owns the target object, then retry + +The same explanation is available in the terminal: `btcli explain NotProxy`. diff --git a/docs/errors/chain/NotReadyToDissolve.mdx b/docs/errors/chain/NotReadyToDissolve.mdx new file mode 100644 index 0000000000..f4812783f4 --- /dev/null +++ b/docs/errors/chain/NotReadyToDissolve.mdx @@ -0,0 +1,16 @@ +--- +title: "NotReadyToDissolve" +description: "The required window has not opened yet; wait some blocks and retry" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +`dissolve` was called while outside contributions remain: the crowdloan's `raised` amount still exceeds the creator's own contribution. Call `refund` until only the creator's `Contributions` entry remains and equals `raised` in the `Crowdloans` record. + +Declared by the `Crowdloan` pallet; it classifies to the semantic code [`too_early`](/docs/errors/too-early). + +## Remediation + +The required window has not opened yet; wait some blocks and retry + +The same explanation is available in the terminal: `btcli explain NotReadyToDissolve`. diff --git a/docs/errors/chain/NotRequested.mdx b/docs/errors/chain/NotRequested.mdx new file mode 100644 index 0000000000..63ca765640 --- /dev/null +++ b/docs/errors/chain/NotRequested.mdx @@ -0,0 +1,16 @@ +--- +title: "NotRequested" +description: "The referenced object is not on-chain; check the identifier" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The preimage request cannot be removed because there are no outstanding requests for this hash. Check the request status for the hash in `RequestStatusFor` before calling `unrequest_preimage`. + +Declared by the `Preimage` pallet; it classifies to the semantic code [`not_found`](/docs/errors/not-found). + +## Remediation + +The referenced object is not on-chain; check the identifier + +The same explanation is available in the terminal: `btcli explain NotRequested`. diff --git a/docs/errors/chain/NotRootSubnet.mdx b/docs/errors/chain/NotRootSubnet.mdx new file mode 100644 index 0000000000..c2b1d09953 --- /dev/null +++ b/docs/errors/chain/NotRootSubnet.mdx @@ -0,0 +1,16 @@ +--- +title: "NotRootSubnet" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +A call that only operates on the root network, such as setting root network weights, was given a non-root netuid. Check the netuid argument; root operations must target netuid 0. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain NotRootSubnet`. diff --git a/docs/errors/chain/NotSubnetOwner.mdx b/docs/errors/chain/NotSubnetOwner.mdx new file mode 100644 index 0000000000..81b63c39a9 --- /dev/null +++ b/docs/errors/chain/NotSubnetOwner.mdx @@ -0,0 +1,16 @@ +--- +title: "NotSubnetOwner" +description: "Sign with the key or origin that owns the target object, then retry" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The signing coldkey is not the recorded owner of the subnet it tried to administer (e.g. setting subnet identity or owner-only hyperparameters). Compare the caller against the `SubnetOwner` storage entry for that netuid. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`not_authorized`](/docs/errors/not-authorized). + +## Remediation + +Sign with the key or origin that owns the target object, then retry + +The same explanation is available in the terminal: `btcli explain NotSubnetOwner`. diff --git a/docs/errors/chain/NothingAuthorized.mdx b/docs/errors/chain/NothingAuthorized.mdx new file mode 100644 index 0000000000..6723c9efcf --- /dev/null +++ b/docs/errors/chain/NothingAuthorized.mdx @@ -0,0 +1,16 @@ +--- +title: "NothingAuthorized" +description: "The referenced object is not on-chain; check the identifier" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +No code upgrade has been authorized, so `apply_authorized_upgrade` has nothing to apply. Check the `AuthorizedUpgrade` storage item in System; an authorization must be recorded before applying the new code. + +Declared by the `System` pallet; it classifies to the semantic code [`not_found`](/docs/errors/not-found). + +## Remediation + +The referenced object is not on-chain; check the identifier + +The same explanation is available in the terminal: `btcli explain NothingAuthorized`. diff --git a/docs/errors/chain/OrderAlreadyProcessed.mdx b/docs/errors/chain/OrderAlreadyProcessed.mdx new file mode 100644 index 0000000000..e99f3e9227 --- /dev/null +++ b/docs/errors/chain/OrderAlreadyProcessed.mdx @@ -0,0 +1,16 @@ +--- +title: "OrderAlreadyProcessed" +description: "The object or state already exists; treat the goal as met or pick a different target" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The order id already has a terminal status: execution found it fulfilled, or `cancel_order` found any existing status for it. Check the `Orders` storage map under the blake2-256 hash of the SCALE-encoded `VersionedOrder`. + +Declared by the `LimitOrders` pallet; it classifies to the semantic code [`already_exists`](/docs/errors/already-exists). + +## Remediation + +The object or state already exists; treat the goal as met or pick a different target + +The same explanation is available in the terminal: `btcli explain OrderAlreadyProcessed`. diff --git a/docs/errors/chain/OrderCancelled.mdx b/docs/errors/chain/OrderCancelled.mdx new file mode 100644 index 0000000000..462e0835ce --- /dev/null +++ b/docs/errors/chain/OrderCancelled.mdx @@ -0,0 +1,16 @@ +--- +title: "OrderCancelled" +description: "The window has closed; restart the flow with fresh state" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The order was previously cancelled via `cancel_order` and can never be executed. Check the `Orders` storage entry for the order id; a `Cancelled` status is terminal, so the signer must sign and submit a fresh order. + +Declared by the `LimitOrders` pallet; it classifies to the semantic code [`expired`](/docs/errors/expired). + +## Remediation + +The window has closed; restart the flow with fresh state + +The same explanation is available in the terminal: `btcli explain OrderCancelled`. diff --git a/docs/errors/chain/OrderExpired.mdx b/docs/errors/chain/OrderExpired.mdx new file mode 100644 index 0000000000..4cb8446d6d --- /dev/null +++ b/docs/errors/chain/OrderExpired.mdx @@ -0,0 +1,16 @@ +--- +title: "OrderExpired" +description: "The window has closed; restart the flow with fresh state" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The current chain time is past the order's `expiry` field, which is a unix timestamp in milliseconds, so the order can no longer execute. Compare the `expiry` in the signed order payload with the chain's current `Timestamp` value. + +Declared by the `LimitOrders` pallet; it classifies to the semantic code [`expired`](/docs/errors/expired). + +## Remediation + +The window has closed; restart the flow with fresh state + +The same explanation is available in the terminal: `btcli explain OrderExpired`. diff --git a/docs/errors/chain/OrderNetUidMismatch.mdx b/docs/errors/chain/OrderNetUidMismatch.mdx new file mode 100644 index 0000000000..e7c1de1135 --- /dev/null +++ b/docs/errors/chain/OrderNetUidMismatch.mdx @@ -0,0 +1,16 @@ +--- +title: "OrderNetUidMismatch" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +An order inside an `execute_batched_orders` call has a `netuid` field different from the batch's `netuid` parameter, which hard-fails the entire batch. Check each order payload's `netuid` against the batch argument and split mismatched orders out. + +Declared by the `LimitOrders` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain OrderNetUidMismatch`. diff --git a/docs/errors/chain/OutOfBounds.mdx b/docs/errors/chain/OutOfBounds.mdx new file mode 100644 index 0000000000..27fa332952 --- /dev/null +++ b/docs/errors/chain/OutOfBounds.mdx @@ -0,0 +1,16 @@ +--- +title: "OutOfBounds" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +A pointer and length pair passed to a contract API host function references memory outside the contract's sandbox. Check the buffer pointers and lengths the contract passes to seal functions; this usually indicates a low-level contract bug. + +Declared by the `Contracts` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain OutOfBounds`. diff --git a/docs/errors/chain/OutOfGas.mdx b/docs/errors/chain/OutOfGas.mdx new file mode 100644 index 0000000000..b24f7f2a10 --- /dev/null +++ b/docs/errors/chain/OutOfGas.mdx @@ -0,0 +1,16 @@ +--- +title: "OutOfGas" +description: "A chain-side capacity limit was hit; reduce the size or count of the request" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The contract exhausted the gas limit supplied for this execution before completing. Increase the `gas_limit` argument on `call` or `instantiate`; dry-run the call via RPC to estimate the required weight. + +Declared by the `Contracts` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). + +## Remediation + +A chain-side capacity limit was hit; reduce the size or count of the request + +The same explanation is available in the terminal: `btcli explain OutOfGas`. diff --git a/docs/errors/chain/OutOfTransientStorage.mdx b/docs/errors/chain/OutOfTransientStorage.mdx new file mode 100644 index 0000000000..02b5715c74 --- /dev/null +++ b/docs/errors/chain/OutOfTransientStorage.mdx @@ -0,0 +1,16 @@ +--- +title: "OutOfTransientStorage" +description: "A chain-side capacity limit was hit; reduce the size or count of the request" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +A write would exceed the per-execution byte limit for transient storage. Check how much data the contract places in transient storage during the call against the runtime's transient storage limit. + +Declared by the `Contracts` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). + +## Remediation + +A chain-side capacity limit was hit; reduce the size or count of the request + +The same explanation is available in the terminal: `btcli explain OutOfTransientStorage`. diff --git a/docs/errors/chain/OutputBufferTooSmall.mdx b/docs/errors/chain/OutputBufferTooSmall.mdx new file mode 100644 index 0000000000..dc07f37446 --- /dev/null +++ b/docs/errors/chain/OutputBufferTooSmall.mdx @@ -0,0 +1,16 @@ +--- +title: "OutputBufferTooSmall" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The output buffer the contract supplied to an API call is smaller than the data the runtime needs to write back. Check the output length pointer the contract passes and enlarge the buffer; usually a contract-side bug. + +Declared by the `Contracts` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain OutputBufferTooSmall`. diff --git a/docs/errors/chain/Overflow.mdx b/docs/errors/chain/Overflow.mdx new file mode 100644 index 0000000000..b114fb529f --- /dev/null +++ b/docs/errors/chain/Overflow.mdx @@ -0,0 +1,16 @@ +--- +title: "Overflow" +description: "A chain-side invariant failed; nothing to fix client-side \u2014 report it if it persists" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +A checked arithmetic operation overflowed, e.g. incrementing `NextSubnetLeaseId` when registering a leased network, or adding to a crowdloan's `raised` amount or contributor count. Internal guard; inspect the amounts involved as this should not occur with realistic values. + +Declared by the `SubtensorModule`, `Crowdloan` pallets; it classifies to the semantic code [`internal`](/docs/errors/internal). + +## Remediation + +A chain-side invariant failed; nothing to fix client-side — report it if it persists + +The same explanation is available in the terminal: `btcli explain Overflow`. diff --git a/docs/errors/chain/POWRegistrationDisabled.mdx b/docs/errors/chain/POWRegistrationDisabled.mdx new file mode 100644 index 0000000000..ab3faf1cf7 --- /dev/null +++ b/docs/errors/chain/POWRegistrationDisabled.mdx @@ -0,0 +1,16 @@ +--- +title: "POWRegistrationDisabled" +description: "This call or feature is switched off on this network" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +`sudo_set_network_pow_registration_allowed` unconditionally fails because proof-of-work registration is deprecated and its toggle can no longer be changed. Nothing to check; the call is permanently disabled. + +Declared by the `AdminUtils` pallet; it classifies to the semantic code [`disabled`](/docs/errors/disabled). + +## Remediation + +This call or feature is switched off on this network + +The same explanation is available in the terminal: `btcli explain POWRegistrationDisabled`. diff --git a/docs/errors/chain/PalletHotkeyNotRegistered.mdx b/docs/errors/chain/PalletHotkeyNotRegistered.mdx new file mode 100644 index 0000000000..8d018762c2 --- /dev/null +++ b/docs/errors/chain/PalletHotkeyNotRegistered.mdx @@ -0,0 +1,16 @@ +--- +title: "PalletHotkeyNotRegistered" +description: "Register the hotkey on this subnet first with `btcli subnets register`" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +Root tried to enable the pallet via `set_pallet_status` before its hotkey was registered to the pallet's intermediary account. Check that the `PalletHotkey` constant is registered for the pallet account, which genesis or the `on_runtime_upgrade` migration performs. + +Declared by the `LimitOrders` pallet; it classifies to the semantic code [`not_registered`](/docs/errors/not-registered). + +## Remediation + +Register the hotkey on this subnet first with `btcli subnets register` + +The same explanation is available in the terminal: `btcli explain PalletHotkeyNotRegistered`. diff --git a/docs/errors/chain/PartialFillsNotEnabled.mdx b/docs/errors/chain/PartialFillsNotEnabled.mdx new file mode 100644 index 0000000000..ebb4d97b54 --- /dev/null +++ b/docs/errors/chain/PartialFillsNotEnabled.mdx @@ -0,0 +1,16 @@ +--- +title: "PartialFillsNotEnabled" +description: "This call or feature is switched off on this network" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +A `partial_fill` amount was supplied for an order whose signed payload has `partial_fills_enabled` set to false. Check that field in the order payload; partial execution requires the signer to have opted in when signing. + +Declared by the `LimitOrders` pallet; it classifies to the semantic code [`disabled`](/docs/errors/disabled). + +## Remediation + +This call or feature is switched off on this network + +The same explanation is available in the terminal: `btcli explain PartialFillsNotEnabled`. diff --git a/docs/errors/chain/PauseFailed.mdx b/docs/errors/chain/PauseFailed.mdx new file mode 100644 index 0000000000..33fc2c60df --- /dev/null +++ b/docs/errors/chain/PauseFailed.mdx @@ -0,0 +1,16 @@ +--- +title: "PauseFailed" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +A GRANDPA pause was signalled while the authority set is not live, i.e. it is already paused or a pause is already pending. Check the Grandpa `State` storage before signalling a pause. + +Declared by the `Grandpa` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain PauseFailed`. diff --git a/docs/errors/chain/PaymentOverflow.mdx b/docs/errors/chain/PaymentOverflow.mdx new file mode 100644 index 0000000000..3e62ba1829 --- /dev/null +++ b/docs/errors/chain/PaymentOverflow.mdx @@ -0,0 +1,16 @@ +--- +title: "PaymentOverflow" +description: "A chain-side invariant failed; nothing to fix client-side \u2014 report it if it persists" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +Arithmetic overflowed while computing the total payment or refund for an EVM transaction, such as refunding remaining gas at the effective gas price. Check for extreme gas price or gas limit values in the transaction. + +Declared by the `EVM` pallet; it classifies to the semantic code [`internal`](/docs/errors/internal). + +## Remediation + +A chain-side invariant failed; nothing to fix client-side — report it if it persists + +The same explanation is available in the terminal: `btcli explain PaymentOverflow`. diff --git a/docs/errors/chain/PreLogExists.mdx b/docs/errors/chain/PreLogExists.mdx new file mode 100644 index 0000000000..ec6b6fc4b6 --- /dev/null +++ b/docs/errors/chain/PreLogExists.mdx @@ -0,0 +1,16 @@ +--- +title: "PreLogExists" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +An `ethereum.transact` extrinsic was submitted in a block that already carries a pre-log digest, meaning an Ethereum transaction is being injected by other means. Check the block's digest for a pre-runtime Ethereum log; transact is not allowed alongside it. + +Declared by the `Ethereum` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain PreLogExists`. diff --git a/docs/errors/chain/PriceConditionNotMet.mdx b/docs/errors/chain/PriceConditionNotMet.mdx new file mode 100644 index 0000000000..2f99ab42ae --- /dev/null +++ b/docs/errors/chain/PriceConditionNotMet.mdx @@ -0,0 +1,16 @@ +--- +title: "PriceConditionNotMet" +description: "The required window has not opened yet; wait some blocks and retry" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The subnet's current alpha price does not satisfy the order's trigger: buys and stop-losses require price at or below `limit_price`, take-profits at or above it. Compare `current_alpha_price` for the order's `netuid`, scaled by 1e9, with the `limit_price` field. + +Declared by the `LimitOrders` pallet; it classifies to the semantic code [`too_early`](/docs/errors/too-early). + +## Remediation + +The required window has not opened yet; wait some blocks and retry + +The same explanation is available in the terminal: `btcli explain PriceConditionNotMet`. diff --git a/docs/errors/chain/PriceLimitExceeded.mdx b/docs/errors/chain/PriceLimitExceeded.mdx new file mode 100644 index 0000000000..c356a4ed26 --- /dev/null +++ b/docs/errors/chain/PriceLimitExceeded.mdx @@ -0,0 +1,16 @@ +--- +title: "PriceLimitExceeded" +description: "The pool cannot absorb this trade; reduce the amount or split it into smaller steps" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The `limit_price` given to a swap is not beyond the current pool price in the trade's direction, so the swap would immediately breach it. Compare the limit price argument against the subnet's current alpha price before submitting. + +Declared by the `Swap` pallet; it classifies to the semantic code [`insufficient_liquidity`](/docs/errors/insufficient-liquidity). + +## Remediation + +The pool cannot absorb this trade; reduce the amount or split it into smaller steps + +The same explanation is available in the terminal: `btcli explain PriceLimitExceeded`. diff --git a/docs/errors/chain/ProportionOverflow.mdx b/docs/errors/chain/ProportionOverflow.mdx new file mode 100644 index 0000000000..87d45cda5b --- /dev/null +++ b/docs/errors/chain/ProportionOverflow.mdx @@ -0,0 +1,16 @@ +--- +title: "ProportionOverflow" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The child proportions passed to `set_children` sum to more than u64::MAX. Reduce the per-child proportion values so their total fits in a u64; each proportion is a fraction of u64::MAX. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain ProportionOverflow`. diff --git a/docs/errors/chain/PulseVerificationError.mdx b/docs/errors/chain/PulseVerificationError.mdx new file mode 100644 index 0000000000..b623130533 --- /dev/null +++ b/docs/errors/chain/PulseVerificationError.mdx @@ -0,0 +1,16 @@ +--- +title: "PulseVerificationError" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +BLS signature verification of a submitted drand pulse against the stored beacon configuration failed. Check the pulse's signature and round against the `BeaconConfig` storage (drand quicknet public key). + +Declared by the `Drand` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain PulseVerificationError`. diff --git a/docs/errors/chain/RandomSubjectTooLong.mdx b/docs/errors/chain/RandomSubjectTooLong.mdx new file mode 100644 index 0000000000..6f5b59c71a --- /dev/null +++ b/docs/errors/chain/RandomSubjectTooLong.mdx @@ -0,0 +1,16 @@ +--- +title: "RandomSubjectTooLong" +description: "A chain-side capacity limit was hit; reduce the size or count of the request" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The subject buffer given to the deprecated `seal_random` API exceeds the schedule's `subject_len` limit. Shorten the randomness subject the contract passes or check the schedule's limits section. + +Declared by the `Contracts` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). + +## Remediation + +A chain-side capacity limit was hit; reduce the size or count of the request + +The same explanation is available in the terminal: `btcli explain RandomSubjectTooLong`. diff --git a/docs/errors/chain/ReentranceDenied.mdx b/docs/errors/chain/ReentranceDenied.mdx new file mode 100644 index 0000000000..01defe0646 --- /dev/null +++ b/docs/errors/chain/ReentranceDenied.mdx @@ -0,0 +1,16 @@ +--- +title: "ReentranceDenied" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +A call tried to re-enter a contract already on the call stack without reentrancy being allowed, or contract code called back into the contracts pallet through the runtime. Check the callee address against the current call stack and the ALLOW_REENTRY call flag. + +Declared by the `Contracts` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain ReentranceDenied`. diff --git a/docs/errors/chain/Reentrancy.mdx b/docs/errors/chain/Reentrancy.mdx new file mode 100644 index 0000000000..e9d66d3e39 --- /dev/null +++ b/docs/errors/chain/Reentrancy.mdx @@ -0,0 +1,16 @@ +--- +title: "Reentrancy" +description: "A chain-side invariant failed; nothing to fix client-side \u2014 report it if it persists" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +EVM execution re-entered the pallet while another EVM execution was already in progress on the same thread, e.g. a precompile or runtime call dispatching back into the EVM. Inspect precompiles and runtime code that invoke the EVM from within an EVM call. + +Declared by the `EVM` pallet; it classifies to the semantic code [`internal`](/docs/errors/internal). + +## Remediation + +A chain-side invariant failed; nothing to fix client-side — report it if it persists + +The same explanation is available in the terminal: `btcli explain Reentrancy`. diff --git a/docs/errors/chain/RegistrationNotPermittedOnRootSubnet.mdx b/docs/errors/chain/RegistrationNotPermittedOnRootSubnet.mdx new file mode 100644 index 0000000000..b1bda60fad --- /dev/null +++ b/docs/errors/chain/RegistrationNotPermittedOnRootSubnet.mdx @@ -0,0 +1,16 @@ +--- +title: "RegistrationNotPermittedOnRootSubnet" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +A neuron registration or child-hotkey operation (`register`, `burned_register`, `set_children`) was called with the root netuid, where these calls are invalid. Check the netuid argument; use a regular subnet, or `root_register` for root membership. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain RegistrationNotPermittedOnRootSubnet`. diff --git a/docs/errors/chain/RegistrationPriceLimitExceeded.mdx b/docs/errors/chain/RegistrationPriceLimitExceeded.mdx new file mode 100644 index 0000000000..49374625fd --- /dev/null +++ b/docs/errors/chain/RegistrationPriceLimitExceeded.mdx @@ -0,0 +1,16 @@ +--- +title: "RegistrationPriceLimitExceeded" +description: "A chain-side capacity limit was hit; reduce the size or count of the request" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +`burned_register` with a price limit failed because the subnet's current registration burn cost exceeds the supplied `limit_price`. Check the current burn via `Burn` storage or `btcli subnets list` and raise the limit or wait for the cost to decay. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). + +## Remediation + +A chain-side capacity limit was hit; reduce the size or count of the request + +The same explanation is available in the terminal: `btcli explain RegistrationPriceLimitExceeded`. diff --git a/docs/errors/chain/RelayerMissMatch.mdx b/docs/errors/chain/RelayerMissMatch.mdx new file mode 100644 index 0000000000..2d00cf347a --- /dev/null +++ b/docs/errors/chain/RelayerMissMatch.mdx @@ -0,0 +1,16 @@ +--- +title: "RelayerMissMatch" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The order's `relayer` allowlist is set but the account that submitted the execution transaction is not in it. Compare the extrinsic's signing account against the `relayer` list in the signed order payload. + +Declared by the `LimitOrders` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain RelayerMissMatch`. diff --git a/docs/errors/chain/RelayerRequiredForPartialFill.mdx b/docs/errors/chain/RelayerRequiredForPartialFill.mdx new file mode 100644 index 0000000000..0ef8bf0cb4 --- /dev/null +++ b/docs/errors/chain/RelayerRequiredForPartialFill.mdx @@ -0,0 +1,16 @@ +--- +title: "RelayerRequiredForPartialFill" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +A `partial_fill` was requested for an order whose `relayer` field is empty; partial fills are only permitted on orders that restrict who may execute them. Check the order payload and either set a relayer list or execute the full amount. + +Declared by the `LimitOrders` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain RelayerRequiredForPartialFill`. diff --git a/docs/errors/chain/Requested.mdx b/docs/errors/chain/Requested.mdx new file mode 100644 index 0000000000..4414ac60cd --- /dev/null +++ b/docs/errors/chain/Requested.mdx @@ -0,0 +1,16 @@ +--- +title: "Requested" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The preimage cannot be unnoted while there are still outstanding requests for it. Check the request count in the hash's request status; all requests must be cleared before the preimage can be removed. + +Declared by the `Preimage` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain Requested`. diff --git a/docs/errors/chain/RequireSudo.mdx b/docs/errors/chain/RequireSudo.mdx new file mode 100644 index 0000000000..43acb87c4a --- /dev/null +++ b/docs/errors/chain/RequireSudo.mdx @@ -0,0 +1,16 @@ +--- +title: "RequireSudo" +description: "Sign with the key or origin that owns the target object, then retry" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The call requires the sudo key but was signed by a different account. Compare the sender against the account stored in the Sudo pallet's `Key` storage item. + +Declared by the `Sudo` pallet; it classifies to the semantic code [`not_authorized`](/docs/errors/not-authorized). + +## Remediation + +Sign with the key or origin that owns the target object, then retry + +The same explanation is available in the terminal: `btcli explain RequireSudo`. diff --git a/docs/errors/chain/RescheduleNoChange.mdx b/docs/errors/chain/RescheduleNoChange.mdx new file mode 100644 index 0000000000..93f305ffd1 --- /dev/null +++ b/docs/errors/chain/RescheduleNoChange.mdx @@ -0,0 +1,16 @@ +--- +title: "RescheduleNoChange" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The reschedule was rejected because the new dispatch time equals the task's currently scheduled time. Check the task's existing slot in `Agenda` and pass a genuinely different `when` block. + +Declared by the `Scheduler` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain RescheduleNoChange`. diff --git a/docs/errors/chain/ReservesOutOfBalance.mdx b/docs/errors/chain/ReservesOutOfBalance.mdx new file mode 100644 index 0000000000..5640230b56 --- /dev/null +++ b/docs/errors/chain/ReservesOutOfBalance.mdx @@ -0,0 +1,16 @@ +--- +title: "ReservesOutOfBalance" +description: "The pool cannot absorb this trade; reduce the amount or split it into smaller steps" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +Swap balancer initialization failed because the subnet's TAO and alpha reserves produce an invalid ratio, for example both reserves are zero when an initial price is supplied. Inspect the subnet's TAO and alpha reserves and the `SwapBalancer` entry. + +Declared by the `Swap` pallet; it classifies to the semantic code [`insufficient_liquidity`](/docs/errors/insufficient-liquidity). + +## Remediation + +The pool cannot absorb this trade; reduce the amount or split it into smaller steps + +The same explanation is available in the terminal: `btcli explain ReservesOutOfBalance`. diff --git a/docs/errors/chain/ReservesTooLow.mdx b/docs/errors/chain/ReservesTooLow.mdx new file mode 100644 index 0000000000..4cde918479 --- /dev/null +++ b/docs/errors/chain/ReservesTooLow.mdx @@ -0,0 +1,16 @@ +--- +title: "ReservesTooLow" +description: "The pool cannot absorb this trade; reduce the amount or split it into smaller steps" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The output-side reserve is below the swap pallet's `MinimumReserve`, or a swap step produced zero output for a nonzero input. Check the subnet's TAO and alpha reserves against `MinimumReserve` and reduce the trade size. + +Declared by the `Swap` pallet; it classifies to the semantic code [`insufficient_liquidity`](/docs/errors/insufficient-liquidity). + +## Remediation + +The pool cannot absorb this trade; reduce the amount or split it into smaller steps + +The same explanation is available in the terminal: `btcli explain ReservesTooLow`. diff --git a/docs/errors/chain/ResumeFailed.mdx b/docs/errors/chain/ResumeFailed.mdx new file mode 100644 index 0000000000..6678d19b62 --- /dev/null +++ b/docs/errors/chain/ResumeFailed.mdx @@ -0,0 +1,16 @@ +--- +title: "ResumeFailed" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +A GRANDPA resume was signalled while the authority set is not paused, i.e. it is live or already pending a resume. Check the Grandpa `State` storage before signalling a resume. + +Declared by the `Grandpa` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain ResumeFailed`. diff --git a/docs/errors/chain/RevealPeriodTooLarge.mdx b/docs/errors/chain/RevealPeriodTooLarge.mdx new file mode 100644 index 0000000000..be328d3145 --- /dev/null +++ b/docs/errors/chain/RevealPeriodTooLarge.mdx @@ -0,0 +1,16 @@ +--- +title: "RevealPeriodTooLarge" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +`set_reveal_period` was given a commit-reveal period above the compiled-in maximum number of epochs. Lower the `reveal_period` argument; the current setting is readable from `RevealPeriodEpochs` for the netuid. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain RevealPeriodTooLarge`. diff --git a/docs/errors/chain/RevealPeriodTooSmall.mdx b/docs/errors/chain/RevealPeriodTooSmall.mdx new file mode 100644 index 0000000000..265252ef26 --- /dev/null +++ b/docs/errors/chain/RevealPeriodTooSmall.mdx @@ -0,0 +1,16 @@ +--- +title: "RevealPeriodTooSmall" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +`set_reveal_period` was given a commit-reveal period below the compiled-in minimum number of epochs. Raise the `reveal_period` argument; the current setting is readable from `RevealPeriodEpochs` for the netuid. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain RevealPeriodTooSmall`. diff --git a/docs/errors/chain/RevealTooEarly.mdx b/docs/errors/chain/RevealTooEarly.mdx new file mode 100644 index 0000000000..a2916054cc --- /dev/null +++ b/docs/errors/chain/RevealTooEarly.mdx @@ -0,0 +1,16 @@ +--- +title: "RevealTooEarly" +description: "The required window has not opened yet; wait some blocks and retry" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +A weight reveal was submitted before the commit's reveal window: the current epoch must equal the commit epoch plus the reveal period. Check the commit in `WeightCommits` and the subnet's `RevealPeriodEpochs`, then wait for the reveal epoch. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`too_early`](/docs/errors/too-early). + +## Remediation + +The required window has not opened yet; wait some blocks and retry + +The same explanation is available in the terminal: `btcli explain RevealTooEarly`. diff --git a/docs/errors/chain/RootNetUidNotAllowed.mdx b/docs/errors/chain/RootNetUidNotAllowed.mdx new file mode 100644 index 0000000000..928e3f07af --- /dev/null +++ b/docs/errors/chain/RootNetUidNotAllowed.mdx @@ -0,0 +1,16 @@ +--- +title: "RootNetUidNotAllowed" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The order or batch targets the root subnet, netuid 0, which the limit orders pallet does not serve. Check the `netuid` field of the order payload or the `netuid` parameter of the batch call and target a non-root subnet. + +Declared by the `LimitOrders` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain RootNetUidNotAllowed`. diff --git a/docs/errors/chain/RootNetworkDoesNotExist.mdx b/docs/errors/chain/RootNetworkDoesNotExist.mdx new file mode 100644 index 0000000000..96764bca41 --- /dev/null +++ b/docs/errors/chain/RootNetworkDoesNotExist.mdx @@ -0,0 +1,16 @@ +--- +title: "RootNetworkDoesNotExist" +description: "Use an existing netuid; `btcli subnets list` shows valid ones" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +Root registration or root stake claiming found no root network in chain state, which only happens on misconfigured or freshly bootstrapped chains. Verify netuid 0 exists in `NetworksAdded`. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`subnet_not_exists`](/docs/errors/subnet-not-exists). + +## Remediation + +Use an existing netuid; `btcli subnets list` shows valid ones + +The same explanation is available in the terminal: `btcli explain RootNetworkDoesNotExist`. diff --git a/docs/errors/chain/SameAutoStakeHotkeyAlreadySet.mdx b/docs/errors/chain/SameAutoStakeHotkeyAlreadySet.mdx new file mode 100644 index 0000000000..9163843436 --- /dev/null +++ b/docs/errors/chain/SameAutoStakeHotkeyAlreadySet.mdx @@ -0,0 +1,16 @@ +--- +title: "SameAutoStakeHotkeyAlreadySet" +description: "The object or state already exists; treat the goal as met or pick a different target" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The coldkey tried to set its auto-stake destination on a subnet to the hotkey that is already configured. Read `AutoStakeDestination` for the coldkey and netuid before calling; only a different hotkey is accepted. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`already_exists`](/docs/errors/already-exists). + +## Remediation + +The object or state already exists; treat the goal as met or pick a different target + +The same explanation is available in the terminal: `btcli explain SameAutoStakeHotkeyAlreadySet`. diff --git a/docs/errors/chain/SameNetuid.mdx b/docs/errors/chain/SameNetuid.mdx new file mode 100644 index 0000000000..bb7306a3cd --- /dev/null +++ b/docs/errors/chain/SameNetuid.mdx @@ -0,0 +1,16 @@ +--- +title: "SameNetuid" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +A stake swap or move where coldkey, hotkey, and subnet are all unchanged, so `origin_netuid` equals `destination_netuid` with nothing to transition. Check the call arguments; at least the subnet or one of the keys must differ. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain SameNetuid`. diff --git a/docs/errors/chain/SenderInSignatories.mdx b/docs/errors/chain/SenderInSignatories.mdx new file mode 100644 index 0000000000..ef13949901 --- /dev/null +++ b/docs/errors/chain/SenderInSignatories.mdx @@ -0,0 +1,16 @@ +--- +title: "SenderInSignatories" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The multisig sender was included in the `other_signatories` list, but that list must contain only the remaining signatories. Remove the sender's own account from `other_signatories` before resubmitting. + +Declared by the `Multisig` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain SenderInSignatories`. diff --git a/docs/errors/chain/ServingRateLimitExceeded.mdx b/docs/errors/chain/ServingRateLimitExceeded.mdx new file mode 100644 index 0000000000..c20b83fc73 --- /dev/null +++ b/docs/errors/chain/ServingRateLimitExceeded.mdx @@ -0,0 +1,16 @@ +--- +title: "ServingRateLimitExceeded" +description: "Wait for the rate-limit window to pass, then retry" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +`serve_axon` or `serve_prometheus` was called again before enough blocks passed since the neuron's last serving update. Check the axon's last update block in `Axons` against the `ServingRateLimit` (serving_rate_limit hyperparameter) and wait. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`rate_limited`](/docs/errors/rate-limited). + +## Remediation + +Wait for the rate-limit window to pass, then retry + +The same explanation is available in the terminal: `btcli explain ServingRateLimitExceeded`. diff --git a/docs/errors/chain/SettingWeightsTooFast.mdx b/docs/errors/chain/SettingWeightsTooFast.mdx new file mode 100644 index 0000000000..c5512229a3 --- /dev/null +++ b/docs/errors/chain/SettingWeightsTooFast.mdx @@ -0,0 +1,16 @@ +--- +title: "SettingWeightsTooFast" +description: "Wait for the rate-limit window to pass, then retry" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The neuron set weights again before `WeightsSetRateLimit` blocks elapsed since its last weight update on that subnet. Check the weights_rate_limit hyperparameter and the neuron's `LastUpdate` entry, then wait the remaining blocks. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`rate_limited`](/docs/errors/rate-limited). + +## Remediation + +Wait for the rate-limit window to pass, then retry + +The same explanation is available in the terminal: `btcli explain SettingWeightsTooFast`. diff --git a/docs/errors/chain/SignatoriesOutOfOrder.mdx b/docs/errors/chain/SignatoriesOutOfOrder.mdx new file mode 100644 index 0000000000..1d8b3a3bb3 --- /dev/null +++ b/docs/errors/chain/SignatoriesOutOfOrder.mdx @@ -0,0 +1,16 @@ +--- +title: "SignatoriesOutOfOrder" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The `other_signatories` list is not sorted in strictly ascending account order, which the multisig pallet requires for a canonical account derivation. Sort the list and remove duplicates before resubmitting. + +Declared by the `Multisig` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain SignatoriesOutOfOrder`. diff --git a/docs/errors/chain/SlippageTooHigh.mdx b/docs/errors/chain/SlippageTooHigh.mdx new file mode 100644 index 0000000000..15e5008da2 --- /dev/null +++ b/docs/errors/chain/SlippageTooHigh.mdx @@ -0,0 +1,16 @@ +--- +title: "SlippageTooHigh" +description: "The price moved past the slippage-protection limit (stake trades are protected by default with a 5% tolerance); retry, raise the tolerance (`--rate-tolerance 0.1` / rate_tolerance=0.1), or disable protection entirely (`--no-slippage-protection` / slippage_protection=False)" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +A stake, unstake, or move with a price limit would execute at a worse rate than the limit allows and `allow_partial` was false. Compare the `limit_price` argument with the subnet's current alpha price (`btcli subnets price`) or permit partial execution. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`insufficient_liquidity`](/docs/errors/insufficient-liquidity). + +## Remediation + +The price moved past the slippage-protection limit (stake trades are protected by default with a 5% tolerance); retry, raise the tolerance (`--rate-tolerance 0.1` / rate_tolerance=0.1), or disable protection entirely (`--no-slippage-protection` / slippage_protection=False) + +The same explanation is available in the terminal: `btcli explain SlippageTooHigh`. diff --git a/docs/errors/chain/SpaceLimitExceeded.mdx b/docs/errors/chain/SpaceLimitExceeded.mdx new file mode 100644 index 0000000000..08165d5ac9 --- /dev/null +++ b/docs/errors/chain/SpaceLimitExceeded.mdx @@ -0,0 +1,16 @@ +--- +title: "SpaceLimitExceeded" +description: "Wait for the rate-limit window to pass, then retry" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The commitment would push the account's byte quota for the current epoch over the cap; each `set_commitment` consumes at least 100 bytes. Check `UsedSpaceOf` for the netuid and account against `MaxSpace`, or wait for the next epoch to reset usage. + +Declared by the `Commitments` pallet; it classifies to the semantic code [`rate_limited`](/docs/errors/rate-limited). + +## Remediation + +Wait for the rate-limit window to pass, then retry + +The same explanation is available in the terminal: `btcli explain SpaceLimitExceeded`. diff --git a/docs/errors/chain/SpecVersionNeedsToIncrease.mdx b/docs/errors/chain/SpecVersionNeedsToIncrease.mdx new file mode 100644 index 0000000000..6f5996b1b0 --- /dev/null +++ b/docs/errors/chain/SpecVersionNeedsToIncrease.mdx @@ -0,0 +1,16 @@ +--- +title: "SpecVersionNeedsToIncrease" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The new runtime's `spec_version` is not greater than the current one, so the upgrade is rejected. Check the `RuntimeVersion` in the new wasm and bump `spec_version` above the version currently on chain. + +Declared by the `System` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain SpecVersionNeedsToIncrease`. diff --git a/docs/errors/chain/StakeTooLowForRoot.mdx b/docs/errors/chain/StakeTooLowForRoot.mdx new file mode 100644 index 0000000000..23c9bcc637 --- /dev/null +++ b/docs/errors/chain/StakeTooLowForRoot.mdx @@ -0,0 +1,16 @@ +--- +title: "StakeTooLowForRoot" +description: "Fund the signing account or reduce the amount; check with `btcli wallet balance`" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +`root_register` when the root network is full and the hotkey's stake on netuid 0 does not exceed the lowest-staked current root member. Compare your hotkey's root stake against existing root validators (`btcli subnets metagraph 0` or `btcli query neurons --netuid 0`). + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`insufficient_balance`](/docs/errors/insufficient-balance). + +## Remediation + +Fund the signing account or reduce the amount; check with `btcli wallet balance` + +The same explanation is available in the terminal: `btcli explain StakeTooLowForRoot`. diff --git a/docs/errors/chain/StakeUnavailable.mdx b/docs/errors/chain/StakeUnavailable.mdx new file mode 100644 index 0000000000..5c50959d17 --- /dev/null +++ b/docs/errors/chain/StakeUnavailable.mdx @@ -0,0 +1,16 @@ +--- +title: "StakeUnavailable" +description: "Fund the signing account or reduce the amount; check with `btcli wallet balance`" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +An unstake would dip into stake that is still locked: the requested alpha exceeds the coldkey's unlocked balance on that subnet. Check the `Lock` entry for the coldkey and netuid; only total stake minus the decaying locked mass can be unstaked. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`insufficient_balance`](/docs/errors/insufficient-balance). + +## Remediation + +Fund the signing account or reduce the amount; check with `btcli wallet balance` + +The same explanation is available in the terminal: `btcli explain StakeUnavailable`. diff --git a/docs/errors/chain/StakingRateLimitExceeded.mdx b/docs/errors/chain/StakingRateLimitExceeded.mdx new file mode 100644 index 0000000000..eb2ee7ae7c --- /dev/null +++ b/docs/errors/chain/StakingRateLimitExceeded.mdx @@ -0,0 +1,16 @@ +--- +title: "StakingRateLimitExceeded" +description: "Wait for the rate-limit window to pass, then retry" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +Staking operations (add_stake, remove_stake, and similar) were submitted faster than the per-block staking rate limit allows for the hotkey-coldkey pair. Space the transactions out and retry in a later block. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`rate_limited`](/docs/errors/rate-limited). + +## Remediation + +Wait for the rate-limit window to pass, then retry + +The same explanation is available in the terminal: `btcli explain StakingRateLimitExceeded`. diff --git a/docs/errors/chain/StartCallNotReady.mdx b/docs/errors/chain/StartCallNotReady.mdx new file mode 100644 index 0000000000..04554e901a --- /dev/null +++ b/docs/errors/chain/StartCallNotReady.mdx @@ -0,0 +1,16 @@ +--- +title: "StartCallNotReady" +description: "The required window has not opened yet; wait some blocks and retry" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +`start_call` was made before `StartCallDelay` blocks elapsed since the subnet was registered. Compare the current block against `NetworkRegisteredAt` for the netuid plus `StartCallDelay`, and wait for the remainder. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`too_early`](/docs/errors/too-early). + +## Remediation + +The required window has not opened yet; wait some blocks and retry + +The same explanation is available in the terminal: `btcli explain StartCallNotReady`. diff --git a/docs/errors/chain/StateChangeDenied.mdx b/docs/errors/chain/StateChangeDenied.mdx new file mode 100644 index 0000000000..288dbfc42e --- /dev/null +++ b/docs/errors/chain/StateChangeDenied.mdx @@ -0,0 +1,16 @@ +--- +title: "StateChangeDenied" +description: "Sign with the key or origin that owns the target object, then retry" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The contract invoked a state-modifying host function, such as a storage write, transfer, or value-bearing call, while executing in read-only mode. Check whether the enclosing call was made with the read-only flag or from a static context. + +Declared by the `Contracts` pallet; it classifies to the semantic code [`not_authorized`](/docs/errors/not-authorized). + +## Remediation + +Sign with the key or origin that owns the target object, then retry + +The same explanation is available in the terminal: `btcli explain StateChangeDenied`. diff --git a/docs/errors/chain/StorageDepositLimitExhausted.mdx b/docs/errors/chain/StorageDepositLimitExhausted.mdx new file mode 100644 index 0000000000..9211f438d4 --- /dev/null +++ b/docs/errors/chain/StorageDepositLimitExhausted.mdx @@ -0,0 +1,16 @@ +--- +title: "StorageDepositLimitExhausted" +description: "A chain-side capacity limit was hit; reduce the size or count of the request" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The execution created more storage than the caller's `storage_deposit_limit` allows to be charged. Raise the `storage_deposit_limit` argument or reduce storage usage; a dry-run reports the required `storage_deposit`. + +Declared by the `Contracts` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). + +## Remediation + +A chain-side capacity limit was hit; reduce the size or count of the request + +The same explanation is available in the terminal: `btcli explain StorageDepositLimitExhausted`. diff --git a/docs/errors/chain/StorageDepositNotEnoughFunds.mdx b/docs/errors/chain/StorageDepositNotEnoughFunds.mdx new file mode 100644 index 0000000000..182ae7d55b --- /dev/null +++ b/docs/errors/chain/StorageDepositNotEnoughFunds.mdx @@ -0,0 +1,16 @@ +--- +title: "StorageDepositNotEnoughFunds" +description: "Fund the signing account or reduce the amount; check with `btcli wallet balance`" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The origin's free balance cannot cover the storage deposit limit specified or required for this call. Check the caller's withdrawable balance against the `storage_deposit_limit` argument and the dry-run's reported deposit. + +Declared by the `Contracts` pallet; it classifies to the semantic code [`insufficient_balance`](/docs/errors/insufficient-balance). + +## Remediation + +Fund the signing account or reduce the amount; check with `btcli wallet balance` + +The same explanation is available in the terminal: `btcli explain StorageDepositNotEnoughFunds`. diff --git a/docs/errors/chain/StorageOverflow.mdx b/docs/errors/chain/StorageOverflow.mdx new file mode 100644 index 0000000000..6e14d26ecc --- /dev/null +++ b/docs/errors/chain/StorageOverflow.mdx @@ -0,0 +1,16 @@ +--- +title: "StorageOverflow" +description: "A chain-side invariant failed; nothing to fix client-side \u2014 report it if it persists" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +Template leftover in the drand pallet for a counter increment overflowing `u32::MAX`; no current code path raises it. If seen, inspect the drand pallet's stored counters for values near the u32 limit. + +Declared by the `Drand` pallet; it classifies to the semantic code [`internal`](/docs/errors/internal). + +## Remediation + +A chain-side invariant failed; nothing to fix client-side — report it if it persists + +The same explanation is available in the terminal: `btcli explain StorageOverflow`. diff --git a/docs/errors/chain/SubNetRegistrationDisabled.mdx b/docs/errors/chain/SubNetRegistrationDisabled.mdx new file mode 100644 index 0000000000..ab1ff922e0 --- /dev/null +++ b/docs/errors/chain/SubNetRegistrationDisabled.mdx @@ -0,0 +1,16 @@ +--- +title: "SubNetRegistrationDisabled" +description: "This call or feature is switched off on this network" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +Neuron registration is switched off: either the subnet's `NetworkRegistrationAllowed` flag is false, or network creation has not opened yet (`NetworkRegistrationStartBlock` is in the future). Check the network_registration_allowed hyperparameter for the netuid. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`disabled`](/docs/errors/disabled). + +## Remediation + +This call or feature is switched off on this network + +The same explanation is available in the terminal: `btcli explain SubNetRegistrationDisabled`. diff --git a/docs/errors/chain/SubnetBuybackRateLimitExceeded.mdx b/docs/errors/chain/SubnetBuybackRateLimitExceeded.mdx new file mode 100644 index 0000000000..bf466553b1 --- /dev/null +++ b/docs/errors/chain/SubnetBuybackRateLimitExceeded.mdx @@ -0,0 +1,16 @@ +--- +title: "SubnetBuybackRateLimitExceeded" +description: "Wait for the rate-limit window to pass, then retry" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +A subnet buyback operation (staking TAO and immediately burning the acquired alpha, e.g. via `add_stake_burn`) was repeated within its rate-limit window. Wait for the window to pass before retrying the buyback. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`rate_limited`](/docs/errors/rate-limited). + +## Remediation + +Wait for the rate-limit window to pass, then retry + +The same explanation is available in the terminal: `btcli explain SubnetBuybackRateLimitExceeded`. diff --git a/docs/errors/chain/SubnetDoesNotExist.mdx b/docs/errors/chain/SubnetDoesNotExist.mdx new file mode 100644 index 0000000000..d3239e1da8 --- /dev/null +++ b/docs/errors/chain/SubnetDoesNotExist.mdx @@ -0,0 +1,16 @@ +--- +title: "SubnetDoesNotExist" +description: "Use an existing netuid; `btcli subnets list` shows valid ones" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The admin-utils call targets a netuid with no registered subnet. Verify the `netuid` argument against `NetworksAdded` (the set of existing subnets) before setting hyperparameters. + +Declared by the `AdminUtils` pallet; it classifies to the semantic code [`subnet_not_exists`](/docs/errors/subnet-not-exists). + +## Remediation + +Use an existing netuid; `btcli subnets list` shows valid ones + +The same explanation is available in the terminal: `btcli explain SubnetDoesNotExist`. diff --git a/docs/errors/chain/SubnetLimitReached.mdx b/docs/errors/chain/SubnetLimitReached.mdx new file mode 100644 index 0000000000..173aeafdd0 --- /dev/null +++ b/docs/errors/chain/SubnetLimitReached.mdx @@ -0,0 +1,16 @@ +--- +title: "SubnetLimitReached" +description: "A chain-side capacity limit was hit; reduce the size or count of the request" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +`register_network` failed because the subnet count is at the network limit and no existing subnet is eligible to be pruned. Check the number of registered subnets (`btcli subnets list`) against the subnet limit and retry once a subnet becomes prunable. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). + +## Remediation + +A chain-side capacity limit was hit; reduce the size or count of the request + +The same explanation is available in the terminal: `btcli explain SubnetLimitReached`. diff --git a/docs/errors/chain/SubnetNotExists.mdx b/docs/errors/chain/SubnetNotExists.mdx new file mode 100644 index 0000000000..2b2dcf65c9 --- /dev/null +++ b/docs/errors/chain/SubnetNotExists.mdx @@ -0,0 +1,16 @@ +--- +title: "SubnetNotExists" +description: "Use an existing netuid; `btcli subnets list` shows valid ones" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The netuid passed to the call does not correspond to a registered subnet. Verify the netuid argument against `NetworksAdded` or `btcli subnets list`; the subnet may also have been dissolved. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`subnet_not_exists`](/docs/errors/subnet-not-exists). + +## Remediation + +Use an existing netuid; `btcli subnets list` shows valid ones + +The same explanation is available in the terminal: `btcli explain SubnetNotExists`. diff --git a/docs/errors/chain/SubtokenDisabled.mdx b/docs/errors/chain/SubtokenDisabled.mdx new file mode 100644 index 0000000000..c429fd65d3 --- /dev/null +++ b/docs/errors/chain/SubtokenDisabled.mdx @@ -0,0 +1,16 @@ +--- +title: "SubtokenDisabled" +description: "The subnet is not active yet; wait for start_call" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The subnet's alpha token is not yet enabled, so staking, swapping, and trading on it are blocked; `SubtokenEnabled` is false until the owner makes the `start_call` after registration. Check `SubtokenEnabled` for the netuid involved. + +Declared by the `SubtensorModule`, `Swap` pallets; it classifies to the semantic code [`subtoken_disabled`](/docs/errors/subtoken-disabled). + +## Remediation + +The subnet is not active yet; wait for start_call + +The same explanation is available in the terminal: `btcli explain SubtokenDisabled`. diff --git a/docs/errors/chain/SwapInputTooLarge.mdx b/docs/errors/chain/SwapInputTooLarge.mdx new file mode 100644 index 0000000000..15ae3ad440 --- /dev/null +++ b/docs/errors/chain/SwapInputTooLarge.mdx @@ -0,0 +1,16 @@ +--- +title: "SwapInputTooLarge" +description: "The pool cannot absorb this trade; reduce the amount or split it into smaller steps" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The swap's net input after fees exceeds 1000 times the input-side reserve, the pallet's hard per-trade cap. Compare the input amount against the subnet's input-side reserve (TAO or alpha) and split the trade if needed. + +Declared by the `Swap` pallet; it classifies to the semantic code [`insufficient_liquidity`](/docs/errors/insufficient-liquidity). + +## Remediation + +The pool cannot absorb this trade; reduce the amount or split it into smaller steps + +The same explanation is available in the terminal: `btcli explain SwapInputTooLarge`. diff --git a/docs/errors/chain/SwapReturnedZero.mdx b/docs/errors/chain/SwapReturnedZero.mdx new file mode 100644 index 0000000000..6b12b80bc2 --- /dev/null +++ b/docs/errors/chain/SwapReturnedZero.mdx @@ -0,0 +1,16 @@ +--- +title: "SwapReturnedZero" +description: "The pool cannot absorb this trade; reduce the amount or split it into smaller steps" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The netted pool swap in `execute_batched_orders` produced zero output for a non-zero input, meaning the pool lacks liquidity or the derived price limit clamped the swap entirely. Check the subnet pool's reserves and the batch's tightest slippage-derived price limit. + +Declared by the `LimitOrders` pallet; it classifies to the semantic code [`insufficient_liquidity`](/docs/errors/insufficient-liquidity). + +## Remediation + +The pool cannot absorb this trade; reduce the amount or split it into smaller steps + +The same explanation is available in the terminal: `btcli explain SwapReturnedZero`. diff --git a/docs/errors/chain/SymbolAlreadyInUse.mdx b/docs/errors/chain/SymbolAlreadyInUse.mdx new file mode 100644 index 0000000000..bf8378cc86 --- /dev/null +++ b/docs/errors/chain/SymbolAlreadyInUse.mdx @@ -0,0 +1,16 @@ +--- +title: "SymbolAlreadyInUse" +description: "The object or state already exists; treat the goal as met or pick a different target" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The token symbol requested for the subnet is already assigned to another subnet. Scan `TokenSymbol` across netuids and pick a symbol that is not taken. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`already_exists`](/docs/errors/already-exists). + +## Remediation + +The object or state already exists; treat the goal as met or pick a different target + +The same explanation is available in the terminal: `btcli explain SymbolAlreadyInUse`. diff --git a/docs/errors/chain/SymbolDoesNotExist.mdx b/docs/errors/chain/SymbolDoesNotExist.mdx new file mode 100644 index 0000000000..ad891e3da7 --- /dev/null +++ b/docs/errors/chain/SymbolDoesNotExist.mdx @@ -0,0 +1,16 @@ +--- +title: "SymbolDoesNotExist" +description: "The referenced object is not on-chain; check the identifier" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The requested token symbol is not in the chain's predefined symbol table, so it cannot be assigned to a subnet. Check the symbol argument against the chain's built-in `SYMBOLS` list and choose a valid entry. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`not_found`](/docs/errors/not-found). + +## Remediation + +The referenced object is not on-chain; check the identifier + +The same explanation is available in the terminal: `btcli explain SymbolDoesNotExist`. diff --git a/docs/errors/chain/TargetBlockNumberInPast.mdx b/docs/errors/chain/TargetBlockNumberInPast.mdx new file mode 100644 index 0000000000..ee0e0e4db7 --- /dev/null +++ b/docs/errors/chain/TargetBlockNumberInPast.mdx @@ -0,0 +1,16 @@ +--- +title: "TargetBlockNumberInPast" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The scheduler was given a dispatch block that is not in the future. Compare the `when` argument against the current block number and choose a strictly later block. + +Declared by the `Scheduler` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain TargetBlockNumberInPast`. diff --git a/docs/errors/chain/TempoOutOfBounds.mdx b/docs/errors/chain/TempoOutOfBounds.mdx new file mode 100644 index 0000000000..eeb9eab56b --- /dev/null +++ b/docs/errors/chain/TempoOutOfBounds.mdx @@ -0,0 +1,16 @@ +--- +title: "TempoOutOfBounds" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The subnet owner gave `sudo_set_tempo` a tempo outside the allowed MIN_TEMPO to MAX_TEMPO range (360-50,400 blocks). Check the tempo argument against those chain constants and pick a value inside the bounds; only root may set a tempo outside them. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain TempoOutOfBounds`. diff --git a/docs/errors/chain/TerminatedInConstructor.mdx b/docs/errors/chain/TerminatedInConstructor.mdx new file mode 100644 index 0000000000..28b44c8007 --- /dev/null +++ b/docs/errors/chain/TerminatedInConstructor.mdx @@ -0,0 +1,16 @@ +--- +title: "TerminatedInConstructor" +description: "A chain-side invariant failed; nothing to fix client-side \u2014 report it if it persists" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The contract called `seal_terminate` inside its constructor, self-destructing during instantiation, which is forbidden. Inspect the constructor logic; termination is only allowed in regular message calls. + +Declared by the `Contracts` pallet; it classifies to the semantic code [`internal`](/docs/errors/internal). + +## Remediation + +A chain-side invariant failed; nothing to fix client-side — report it if it persists + +The same explanation is available in the terminal: `btcli explain TerminatedInConstructor`. diff --git a/docs/errors/chain/TerminatedWhileReentrant.mdx b/docs/errors/chain/TerminatedWhileReentrant.mdx new file mode 100644 index 0000000000..8854bc47c3 --- /dev/null +++ b/docs/errors/chain/TerminatedWhileReentrant.mdx @@ -0,0 +1,16 @@ +--- +title: "TerminatedWhileReentrant" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +`seal_terminate` was called on a contract that appears more than once on the call stack, so termination was refused. Check the call chain for reentrant calls into the contract being terminated. + +Declared by the `Contracts` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain TerminatedWhileReentrant`. diff --git a/docs/errors/chain/TooBig.mdx b/docs/errors/chain/TooBig.mdx new file mode 100644 index 0000000000..625c6a961b --- /dev/null +++ b/docs/errors/chain/TooBig.mdx @@ -0,0 +1,16 @@ +--- +title: "TooBig" +description: "A chain-side capacity limit was hit; reduce the size or count of the request" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The preimage exceeds the maximum size the pallet will store on-chain (4 MiB). Check the byte length of the preimage against the pallet's `MAX_SIZE` limit before noting it. + +Declared by the `Preimage` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). + +## Remediation + +A chain-side capacity limit was hit; reduce the size or count of the request + +The same explanation is available in the terminal: `btcli explain TooBig`. diff --git a/docs/errors/chain/TooFew.mdx b/docs/errors/chain/TooFew.mdx new file mode 100644 index 0000000000..b9b0a4dec4 --- /dev/null +++ b/docs/errors/chain/TooFew.mdx @@ -0,0 +1,16 @@ +--- +title: "TooFew" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The bulk preimage upgrade was requested with zero hashes, so there is nothing to do. Pass at least one hash to `ensure_updated`. + +Declared by the `Preimage` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain TooFew`. diff --git a/docs/errors/chain/TooFewSignatories.mdx b/docs/errors/chain/TooFewSignatories.mdx new file mode 100644 index 0000000000..c01a3c6106 --- /dev/null +++ b/docs/errors/chain/TooFewSignatories.mdx @@ -0,0 +1,16 @@ +--- +title: "TooFewSignatories" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The multisig signatory list is too short: `other_signatories` must contain at least one account besides the sender. Check the length of `other_signatories` against the threshold you are using. + +Declared by the `Multisig` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain TooFewSignatories`. diff --git a/docs/errors/chain/TooMany.mdx b/docs/errors/chain/TooMany.mdx new file mode 100644 index 0000000000..5fddb44c41 --- /dev/null +++ b/docs/errors/chain/TooMany.mdx @@ -0,0 +1,16 @@ +--- +title: "TooMany" +description: "A chain-side capacity limit was hit; reduce the size or count of the request" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +A limit was exceeded: more preimage hashes than `MAX_HASH_UPGRADE_BULK_COUNT` were passed to `ensure_updated`, or the account has hit its proxy or announcement cap. Check the account's `Proxies` and `Announcements` entries against `MaxProxies` and `MaxPending`. + +Declared by the `Preimage`, `Proxy` pallets; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). + +## Remediation + +A chain-side capacity limit was hit; reduce the size or count of the request + +The same explanation is available in the terminal: `btcli explain TooMany`. diff --git a/docs/errors/chain/TooManyCalls.mdx b/docs/errors/chain/TooManyCalls.mdx new file mode 100644 index 0000000000..80c45f503b --- /dev/null +++ b/docs/errors/chain/TooManyCalls.mdx @@ -0,0 +1,16 @@ +--- +title: "TooManyCalls" +description: "A chain-side capacity limit was hit; reduce the size or count of the request" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The batch submitted to the utility pallet contains more calls than the batched-calls limit allows. Check the length of the `calls` vector and split the work across multiple smaller `batch`, `batch_all`, or `force_batch` submissions. + +Declared by the `Utility` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). + +## Remediation + +A chain-side capacity limit was hit; reduce the size or count of the request + +The same explanation is available in the terminal: `btcli explain TooManyCalls`. diff --git a/docs/errors/chain/TooManyChildren.mdx b/docs/errors/chain/TooManyChildren.mdx new file mode 100644 index 0000000000..2ad0fce861 --- /dev/null +++ b/docs/errors/chain/TooManyChildren.mdx @@ -0,0 +1,16 @@ +--- +title: "TooManyChildren" +description: "A chain-side capacity limit was hit; reduce the size or count of the request" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +`set_children` was called with more than 5 child hotkeys for a parent on the subnet. Trim the children list to at most 5 entries. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). + +## Remediation + +A chain-side capacity limit was hit; reduce the size or count of the request + +The same explanation is available in the terminal: `btcli explain TooManyChildren`. diff --git a/docs/errors/chain/TooManyFieldsInCommitmentInfo.mdx b/docs/errors/chain/TooManyFieldsInCommitmentInfo.mdx new file mode 100644 index 0000000000..e6424076da --- /dev/null +++ b/docs/errors/chain/TooManyFieldsInCommitmentInfo.mdx @@ -0,0 +1,16 @@ +--- +title: "TooManyFieldsInCommitmentInfo" +description: "A chain-side capacity limit was hit; reduce the size or count of the request" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The `CommitmentInfo` passed to `set_commitment` contains more entries in `fields` than the pallet's `MaxFields` config allows. Count the fields in the `info` argument and trim to the `MaxFields` limit. + +Declared by the `Commitments` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). + +## Remediation + +A chain-side capacity limit was hit; reduce the size or count of the request + +The same explanation is available in the terminal: `btcli explain TooManyFieldsInCommitmentInfo`. diff --git a/docs/errors/chain/TooManyFreezes.mdx b/docs/errors/chain/TooManyFreezes.mdx new file mode 100644 index 0000000000..674f79e05e --- /dev/null +++ b/docs/errors/chain/TooManyFreezes.mdx @@ -0,0 +1,16 @@ +--- +title: "TooManyFreezes" +description: "A chain-side capacity limit was hit; reduce the size or count of the request" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The account already has the maximum number of balance freezes, so a new freeze cannot be added. Check the account's `Freezes` entry against the `MaxFreezes` constant; an existing freeze must be thawed first. + +Declared by the `Balances` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). + +## Remediation + +A chain-side capacity limit was hit; reduce the size or count of the request + +The same explanation is available in the terminal: `btcli explain TooManyFreezes`. diff --git a/docs/errors/chain/TooManyHolds.mdx b/docs/errors/chain/TooManyHolds.mdx new file mode 100644 index 0000000000..a54f309996 --- /dev/null +++ b/docs/errors/chain/TooManyHolds.mdx @@ -0,0 +1,16 @@ +--- +title: "TooManyHolds" +description: "A chain-side capacity limit was hit; reduce the size or count of the request" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The account already carries the maximum number of balance holds, one per hold reason variant. Check the account's `Holds` entry; an existing hold must be released before a new reason can place another. + +Declared by the `Balances` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). + +## Remediation + +A chain-side capacity limit was hit; reduce the size or count of the request + +The same explanation is available in the terminal: `btcli explain TooManyHolds`. diff --git a/docs/errors/chain/TooManyPendingExtrinsics.mdx b/docs/errors/chain/TooManyPendingExtrinsics.mdx new file mode 100644 index 0000000000..451ed311d0 --- /dev/null +++ b/docs/errors/chain/TooManyPendingExtrinsics.mdx @@ -0,0 +1,16 @@ +--- +title: "TooManyPendingExtrinsics" +description: "A chain-side capacity limit was hit; reduce the size or count of the request" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +`store_encrypted` was rejected because the shield pallet's queue of encrypted extrinsics is already at capacity. Compare the `PendingExtrinsics` count against `MaxPendingExtrinsicsLimit` and wait for queued items to be processed or expire. + +Declared by the `MevShield` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). + +## Remediation + +A chain-side capacity limit was hit; reduce the size or count of the request + +The same explanation is available in the terminal: `btcli explain TooManyPendingExtrinsics`. diff --git a/docs/errors/chain/TooManyRegistrationsThisBlock.mdx b/docs/errors/chain/TooManyRegistrationsThisBlock.mdx new file mode 100644 index 0000000000..640f53ac93 --- /dev/null +++ b/docs/errors/chain/TooManyRegistrationsThisBlock.mdx @@ -0,0 +1,16 @@ +--- +title: "TooManyRegistrationsThisBlock" +description: "Wait for the rate-limit window to pass, then retry" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +Registrations in the current block already reached the subnet's per-block cap (`MaxRegistrationsPerBlock`, the max_regs_per_block hyperparameter); root registration enforces the same cap on netuid 0. Retry in the next block. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`rate_limited`](/docs/errors/rate-limited). + +## Remediation + +Wait for the rate-limit window to pass, then retry + +The same explanation is available in the terminal: `btcli explain TooManyRegistrationsThisBlock`. diff --git a/docs/errors/chain/TooManyRegistrationsThisInterval.mdx b/docs/errors/chain/TooManyRegistrationsThisInterval.mdx new file mode 100644 index 0000000000..66c9874ef0 --- /dev/null +++ b/docs/errors/chain/TooManyRegistrationsThisInterval.mdx @@ -0,0 +1,16 @@ +--- +title: "TooManyRegistrationsThisInterval" +description: "Wait for the rate-limit window to pass, then retry" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +Registrations in the current interval reached the cap of three times `TargetRegistrationsPerInterval` for the subnet. Compare `RegistrationsThisInterval` against that hyperparameter and wait for the next interval to start. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`rate_limited`](/docs/errors/rate-limited). + +## Remediation + +Wait for the rate-limit window to pass, then retry + +The same explanation is available in the terminal: `btcli explain TooManyRegistrationsThisInterval`. diff --git a/docs/errors/chain/TooManyReserves.mdx b/docs/errors/chain/TooManyReserves.mdx new file mode 100644 index 0000000000..80c2640620 --- /dev/null +++ b/docs/errors/chain/TooManyReserves.mdx @@ -0,0 +1,16 @@ +--- +title: "TooManyReserves" +description: "A chain-side capacity limit was hit; reduce the size or count of the request" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The account already has the maximum number of named reserves. Check the account's `Reserves` entry against the `MaxReserves` constant; a named reserve must be unreserved before adding another. + +Declared by the `Balances` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). + +## Remediation + +A chain-side capacity limit was hit; reduce the size or count of the request + +The same explanation is available in the terminal: `btcli explain TooManyReserves`. diff --git a/docs/errors/chain/TooManySignatories.mdx b/docs/errors/chain/TooManySignatories.mdx new file mode 100644 index 0000000000..08b8a3f8e0 --- /dev/null +++ b/docs/errors/chain/TooManySignatories.mdx @@ -0,0 +1,16 @@ +--- +title: "TooManySignatories" +description: "A chain-side capacity limit was hit; reduce the size or count of the request" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The multisig signatory list exceeds the maximum allowed. Compare the length of `other_signatories` plus the sender against the pallet's `MaxSignatories` constant. + +Declared by the `Multisig` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). + +## Remediation + +A chain-side capacity limit was hit; reduce the size or count of the request + +The same explanation is available in the terminal: `btcli explain TooManySignatories`. diff --git a/docs/errors/chain/TooManyTopics.mdx b/docs/errors/chain/TooManyTopics.mdx new file mode 100644 index 0000000000..97c76a677b --- /dev/null +++ b/docs/errors/chain/TooManyTopics.mdx @@ -0,0 +1,16 @@ +--- +title: "TooManyTopics" +description: "A chain-side capacity limit was hit; reduce the size or count of the request" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The number of topics passed to `seal_deposit_event` exceeds the schedule's `event_topics` limit. Reduce the number of indexed topics in the contract's event definition or compare against the schedule limit. + +Declared by the `Contracts` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). + +## Remediation + +A chain-side capacity limit was hit; reduce the size or count of the request + +The same explanation is available in the terminal: `btcli explain TooManyTopics`. diff --git a/docs/errors/chain/TooManyUIDsPerMechanism.mdx b/docs/errors/chain/TooManyUIDsPerMechanism.mdx new file mode 100644 index 0000000000..749b3a62d5 --- /dev/null +++ b/docs/errors/chain/TooManyUIDsPerMechanism.mdx @@ -0,0 +1,16 @@ +--- +title: "TooManyUIDsPerMechanism" +description: "A chain-side capacity limit was hit; reduce the size or count of the request" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +Setting max UIDs or mechanism count would make max_uids times mechanism_count exceed the chain default of 256 UIDs per subnet. Check `MaxAllowedUids` and the subnet's mechanism count so their product stays within the limit. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). + +## Remediation + +A chain-side capacity limit was hit; reduce the size or count of the request + +The same explanation is available in the terminal: `btcli explain TooManyUIDsPerMechanism`. diff --git a/docs/errors/chain/TooManyUnrevealedCommits.mdx b/docs/errors/chain/TooManyUnrevealedCommits.mdx new file mode 100644 index 0000000000..a3027a8ae5 --- /dev/null +++ b/docs/errors/chain/TooManyUnrevealedCommits.mdx @@ -0,0 +1,16 @@ +--- +title: "TooManyUnrevealedCommits" +description: "A chain-side capacity limit was hit; reduce the size or count of the request" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +`commit_weights` (or a CRv3 commit) failed because the hotkey already has 10 unrevealed commits queued on the subnet. Inspect `WeightCommits` for the hotkey and reveal or let old commits expire before committing again. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). + +## Remediation + +A chain-side capacity limit was hit; reduce the size or count of the request + +The same explanation is available in the terminal: `btcli explain TooManyUnrevealedCommits`. diff --git a/docs/errors/chain/TooSoon.mdx b/docs/errors/chain/TooSoon.mdx new file mode 100644 index 0000000000..6a2002946e --- /dev/null +++ b/docs/errors/chain/TooSoon.mdx @@ -0,0 +1,16 @@ +--- +title: "TooSoon" +description: "Wait for the rate-limit window to pass, then retry" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +A forced GRANDPA authority change was signalled too soon after the previous one. Check the Grandpa `NextForced` storage and wait until the current block passes it before signalling another forced change. + +Declared by the `Grandpa` pallet; it classifies to the semantic code [`rate_limited`](/docs/errors/rate-limited). + +## Remediation + +Wait for the rate-limit window to pass, then retry + +The same explanation is available in the terminal: `btcli explain TooSoon`. diff --git a/docs/errors/chain/TransactionMustComeFromEOA.mdx b/docs/errors/chain/TransactionMustComeFromEOA.mdx new file mode 100644 index 0000000000..693c68c459 --- /dev/null +++ b/docs/errors/chain/TransactionMustComeFromEOA.mdx @@ -0,0 +1,16 @@ +--- +title: "TransactionMustComeFromEOA" +description: "Sign with the key or origin that owns the target object, then retry" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +Rejected per EIP-3607: the sender address has contract code deployed, and transactions must originate from externally owned accounts. Check `eth_getCode` for the `source` address and sign with a plain EOA key instead. + +Declared by the `EVM` pallet; it classifies to the semantic code [`not_authorized`](/docs/errors/not-authorized). + +## Remediation + +Sign with the key or origin that owns the target object, then retry + +The same explanation is available in the terminal: `btcli explain TransactionMustComeFromEOA`. diff --git a/docs/errors/chain/TransactorAccountShouldBeHotKey.mdx b/docs/errors/chain/TransactorAccountShouldBeHotKey.mdx new file mode 100644 index 0000000000..3f0f575f6d --- /dev/null +++ b/docs/errors/chain/TransactorAccountShouldBeHotKey.mdx @@ -0,0 +1,16 @@ +--- +title: "TransactorAccountShouldBeHotKey" +description: "Sign with the key or origin that owns the target object, then retry" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The extrinsic must be signed by the hotkey itself, but a different account (typically the coldkey) was the origin. Check which key signs the transaction; calls like axon serving expect the hotkey as origin. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`not_authorized`](/docs/errors/not-authorized). + +## Remediation + +Sign with the key or origin that owns the target object, then retry + +The same explanation is available in the terminal: `btcli explain TransactorAccountShouldBeHotKey`. diff --git a/docs/errors/chain/TransferDisallowed.mdx b/docs/errors/chain/TransferDisallowed.mdx new file mode 100644 index 0000000000..7dc3b1bc15 --- /dev/null +++ b/docs/errors/chain/TransferDisallowed.mdx @@ -0,0 +1,16 @@ +--- +title: "TransferDisallowed" +description: "This call or feature is switched off on this network" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +A stake transfer or cross-subnet move was attempted while the origin or destination subnet has stake transfers switched off. Check the `TransferToggle` storage for both netuids involved; the subnet owner must enable transfers first. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`disabled`](/docs/errors/disabled). + +## Remediation + +This call or feature is switched off on this network + +The same explanation is available in the terminal: `btcli explain TransferDisallowed`. diff --git a/docs/errors/chain/TransferFailed.mdx b/docs/errors/chain/TransferFailed.mdx new file mode 100644 index 0000000000..e44a351eed --- /dev/null +++ b/docs/errors/chain/TransferFailed.mdx @@ -0,0 +1,16 @@ +--- +title: "TransferFailed" +description: "Fund the signing account or reduce the amount; check with `btcli wallet balance`" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +A balance transfer performed during the contract call failed, most likely because the sender lacks enough free balance. Check the transferring account's free balance against the `value` being sent, accounting for the existential deposit. + +Declared by the `Contracts` pallet; it classifies to the semantic code [`insufficient_balance`](/docs/errors/insufficient-balance). + +## Remediation + +Fund the signing account or reduce the amount; check with `btcli wallet balance` + +The same explanation is available in the terminal: `btcli explain TransferFailed`. diff --git a/docs/errors/chain/TrimmingWouldExceedMaxImmunePercentage.mdx b/docs/errors/chain/TrimmingWouldExceedMaxImmunePercentage.mdx new file mode 100644 index 0000000000..b85c073e98 --- /dev/null +++ b/docs/errors/chain/TrimmingWouldExceedMaxImmunePercentage.mdx @@ -0,0 +1,16 @@ +--- +title: "TrimmingWouldExceedMaxImmunePercentage" +description: "A chain-side capacity limit was hit; reduce the size or count of the request" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +Trimming the subnet's UIDs cannot proceed because immune neurons would make up at least the maximum immune share (80%) of the reduced slot count. Check neurons still in `ImmunityPeriod` and retry after their immunity lapses or with a higher max UID target. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). + +## Remediation + +A chain-side capacity limit was hit; reduce the size or count of the request + +The same explanation is available in the terminal: `btcli explain TrimmingWouldExceedMaxImmunePercentage`. diff --git a/docs/errors/chain/TxChildkeyTakeRateLimitExceeded.mdx b/docs/errors/chain/TxChildkeyTakeRateLimitExceeded.mdx new file mode 100644 index 0000000000..de9d9551f5 --- /dev/null +++ b/docs/errors/chain/TxChildkeyTakeRateLimitExceeded.mdx @@ -0,0 +1,16 @@ +--- +title: "TxChildkeyTakeRateLimitExceeded" +description: "Wait for the rate-limit window to pass, then retry" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +`set_childkey_take` was called again for the hotkey on this subnet before its rate-limit window elapsed. Check the block of the last childkey-take change against `TxChildkeyTakeRateLimit` and wait out the remainder. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`rate_limited`](/docs/errors/rate-limited). + +## Remediation + +Wait for the rate-limit window to pass, then retry + +The same explanation is available in the terminal: `btcli explain TxChildkeyTakeRateLimitExceeded`. diff --git a/docs/errors/chain/TxRateLimitExceeded.mdx b/docs/errors/chain/TxRateLimitExceeded.mdx new file mode 100644 index 0000000000..74d91bbe94 --- /dev/null +++ b/docs/errors/chain/TxRateLimitExceeded.mdx @@ -0,0 +1,16 @@ +--- +title: "TxRateLimitExceeded" +description: "Wait for the rate-limit window to pass, then retry" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +An owner or admin transaction (e.g. `set_children`, tempo or hyperparameter updates, setting the owner hotkey) was repeated within its rate-limit window for that key and subnet. Check when the same transaction type last succeeded and wait for the limit to pass. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`rate_limited`](/docs/errors/rate-limited). + +## Remediation + +Wait for the rate-limit window to pass, then retry + +The same explanation is available in the terminal: `btcli explain TxRateLimitExceeded`. diff --git a/docs/errors/chain/UidMapCouldNotBeCleared.mdx b/docs/errors/chain/UidMapCouldNotBeCleared.mdx new file mode 100644 index 0000000000..87d4169ada --- /dev/null +++ b/docs/errors/chain/UidMapCouldNotBeCleared.mdx @@ -0,0 +1,16 @@ +--- +title: "UidMapCouldNotBeCleared" +description: "A chain-side invariant failed; nothing to fix client-side \u2014 report it if it persists" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +During a UID reshuffle or trim, clearing the subnet's `Uids` map left residual entries (the storage clear returned a cursor). Internal state inconsistency rather than a caller error; inspect the `Uids` storage for the netuid and report it. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`internal`](/docs/errors/internal). + +## Remediation + +A chain-side invariant failed; nothing to fix client-side — report it if it persists + +The same explanation is available in the terminal: `btcli explain UidMapCouldNotBeCleared`. diff --git a/docs/errors/chain/UidVecContainInvalidOne.mdx b/docs/errors/chain/UidVecContainInvalidOne.mdx new file mode 100644 index 0000000000..37492d7fe9 --- /dev/null +++ b/docs/errors/chain/UidVecContainInvalidOne.mdx @@ -0,0 +1,16 @@ +--- +title: "UidVecContainInvalidOne" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The weight submission includes a UID that is not registered on the subnet, i.e. at least one entry is not below `SubnetworkN`. Check the `uids` argument against the subnet's neuron count in the metagraph (`btcli subnets metagraph`). + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain UidVecContainInvalidOne`. diff --git a/docs/errors/chain/UidsLengthExceedUidsInSubNet.mdx b/docs/errors/chain/UidsLengthExceedUidsInSubNet.mdx new file mode 100644 index 0000000000..50dfbb6414 --- /dev/null +++ b/docs/errors/chain/UidsLengthExceedUidsInSubNet.mdx @@ -0,0 +1,16 @@ +--- +title: "UidsLengthExceedUidsInSubNet" +description: "A chain-side capacity limit was hit; reduce the size or count of the request" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The weight submission contains more UID entries than there are neurons registered on the subnet. Compare the length of the `uids` argument against `SubnetworkN` for the netuid and trim the vector. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). + +## Remediation + +A chain-side capacity limit was hit; reduce the size or count of the request + +The same explanation is available in the terminal: `btcli explain UidsLengthExceedUidsInSubNet`. diff --git a/docs/errors/chain/UnableToRecoverPublicKey.mdx b/docs/errors/chain/UnableToRecoverPublicKey.mdx new file mode 100644 index 0000000000..be53050c28 --- /dev/null +++ b/docs/errors/chain/UnableToRecoverPublicKey.mdx @@ -0,0 +1,16 @@ +--- +title: "UnableToRecoverPublicKey" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +While associating an EVM key, the secp256k1 public key recovered from the supplied signature could not be parsed. Check that the signature was produced by signing the expected EIP-191 message (hotkey plus block hash) with the EVM private key. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain UnableToRecoverPublicKey`. diff --git a/docs/errors/chain/Unannounced.mdx b/docs/errors/chain/Unannounced.mdx new file mode 100644 index 0000000000..dde1ee9689 --- /dev/null +++ b/docs/errors/chain/Unannounced.mdx @@ -0,0 +1,16 @@ +--- +title: "Unannounced" +description: "The required window has not opened yet; wait some blocks and retry" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The proxied call was executed before its announcement matured, or no matching announcement exists at all. Check the proxy's `Announcements` entry for the call hash and the `delay` on the proxy registration; enough blocks must elapse between `announce` and `proxy_announced`. + +Declared by the `Proxy` pallet; it classifies to the semantic code [`too_early`](/docs/errors/too-early). + +## Remediation + +The required window has not opened yet; wait some blocks and retry + +The same explanation is available in the terminal: `btcli explain Unannounced`. diff --git a/docs/errors/chain/Unauthorized.mdx b/docs/errors/chain/Unauthorized.mdx new file mode 100644 index 0000000000..bea4a8a5d0 --- /dev/null +++ b/docs/errors/chain/Unauthorized.mdx @@ -0,0 +1,16 @@ +--- +title: "Unauthorized" +description: "Sign with the key or origin that owns the target object, then retry" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +In System, the code passed to `apply_authorized_upgrade` does not hash to the value in `AuthorizedUpgrade`. In LimitOrders, the caller is not the order's signer, who alone may cancel it. Check the code hash against the authorization, or the sender against the order's `signer`. + +Declared by the `System`, `LimitOrders` pallets; it classifies to the semantic code [`not_authorized`](/docs/errors/not-authorized). + +## Remediation + +Sign with the key or origin that owns the target object, then retry + +The same explanation is available in the terminal: `btcli explain Unauthorized`. diff --git a/docs/errors/chain/Undefined.mdx b/docs/errors/chain/Undefined.mdx new file mode 100644 index 0000000000..4d413d9728 --- /dev/null +++ b/docs/errors/chain/Undefined.mdx @@ -0,0 +1,16 @@ +--- +title: "Undefined" +description: "A chain-side invariant failed; nothing to fix client-side \u2014 report it if it persists" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +Catch-all EVM validation error for cases without a dedicated variant, such as a malformed EIP-7702 authorization list or an unknown validation failure. Inspect the raw transaction for unsupported fields and check the node logs. + +Declared by the `EVM` pallet; it classifies to the semantic code [`internal`](/docs/errors/internal). + +## Remediation + +A chain-side invariant failed; nothing to fix client-side — report it if it persists + +The same explanation is available in the terminal: `btcli explain Undefined`. diff --git a/docs/errors/chain/Underflow.mdx b/docs/errors/chain/Underflow.mdx new file mode 100644 index 0000000000..f0ce65753d --- /dev/null +++ b/docs/errors/chain/Underflow.mdx @@ -0,0 +1,16 @@ +--- +title: "Underflow" +description: "A chain-side invariant failed; nothing to fix client-side \u2014 report it if it persists" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +A checked subtraction underflowed in crowdloan accounting, e.g. `raised` exceeding `cap` when computing remaining room, or a contributor count decrement, indicating inconsistent state. Inspect the `Crowdloans` and `Contributions` entries for the `crowdloan_id`. + +Declared by the `Crowdloan` pallet; it classifies to the semantic code [`internal`](/docs/errors/internal). + +## Remediation + +A chain-side invariant failed; nothing to fix client-side — report it if it persists + +The same explanation is available in the terminal: `btcli explain Underflow`. diff --git a/docs/errors/chain/UnexpectedTimepoint.mdx b/docs/errors/chain/UnexpectedTimepoint.mdx new file mode 100644 index 0000000000..3fc5f58053 --- /dev/null +++ b/docs/errors/chain/UnexpectedTimepoint.mdx @@ -0,0 +1,16 @@ +--- +title: "UnexpectedTimepoint" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +A timepoint was supplied but no multisig operation is underway for this call hash; the first approval must open the operation with no timepoint. Pass `maybe_timepoint: None` on the opening call, or verify the call hash matches an existing `Multisigs` entry. + +Declared by the `Multisig` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain UnexpectedTimepoint`. diff --git a/docs/errors/chain/UnexpectedUnreserveLeftover.mdx b/docs/errors/chain/UnexpectedUnreserveLeftover.mdx new file mode 100644 index 0000000000..153d775e3c --- /dev/null +++ b/docs/errors/chain/UnexpectedUnreserveLeftover.mdx @@ -0,0 +1,16 @@ +--- +title: "UnexpectedUnreserveLeftover" +description: "A chain-side invariant failed; nothing to fix client-side \u2014 report it if it persists" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +While lowering a commitment deposit, `Currency::unreserve` failed to return the full difference, leaving a leftover, which signals an internal inconsistency. Check the account's reserved balance against the deposit recorded in `CommitmentOf`. + +Declared by the `Commitments` pallet; it classifies to the semantic code [`internal`](/docs/errors/internal). + +## Remediation + +A chain-side invariant failed; nothing to fix client-side — report it if it persists + +The same explanation is available in the terminal: `btcli explain UnexpectedUnreserveLeftover`. diff --git a/docs/errors/chain/UnlockAmountTooHigh.mdx b/docs/errors/chain/UnlockAmountTooHigh.mdx new file mode 100644 index 0000000000..dae264d542 --- /dev/null +++ b/docs/errors/chain/UnlockAmountTooHigh.mdx @@ -0,0 +1,16 @@ +--- +title: "UnlockAmountTooHigh" +description: "Fund the signing account or reduce the amount; check with `btcli wallet balance`" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +An unlock requested more alpha than remains locked for the coldkey on that subnet. Check the lock's remaining decaying locked mass in the `Lock` storage entry and request at most that amount. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`insufficient_balance`](/docs/errors/insufficient-balance). + +## Remediation + +Fund the signing account or reduce the amount; check with `btcli wallet balance` + +The same explanation is available in the terminal: `btcli explain UnlockAmountTooHigh`. diff --git a/docs/errors/chain/Unproxyable.mdx b/docs/errors/chain/Unproxyable.mdx new file mode 100644 index 0000000000..ef2ce4e305 --- /dev/null +++ b/docs/errors/chain/Unproxyable.mdx @@ -0,0 +1,16 @@ +--- +title: "Unproxyable" +description: "Sign with the key or origin that owns the target object, then retry" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The attempted call is not permitted by the registered proxy type's call filter. Check which `proxy_type` the sender holds in the real account's `Proxies` entry and whether that type's `InstanceFilter` allows this specific call. + +Declared by the `Proxy` pallet; it classifies to the semantic code [`not_authorized`](/docs/errors/not-authorized). + +## Remediation + +Sign with the key or origin that owns the target object, then retry + +The same explanation is available in the terminal: `btcli explain Unproxyable`. diff --git a/docs/errors/chain/Unreachable.mdx b/docs/errors/chain/Unreachable.mdx new file mode 100644 index 0000000000..2166b9885b --- /dev/null +++ b/docs/errors/chain/Unreachable.mdx @@ -0,0 +1,16 @@ +--- +title: "Unreachable" +description: "A chain-side invariant failed; nothing to fix client-side \u2014 report it if it persists" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +`announce_next_key` could not identify the current block author via the `FindAuthors` lookup, which should be impossible in a normally authored block. Check the block's author digest and the shield pallet's authorship wiring. + +Declared by the `MevShield` pallet; it classifies to the semantic code [`internal`](/docs/errors/internal). + +## Remediation + +A chain-side invariant failed; nothing to fix client-side — report it if it persists + +The same explanation is available in the terminal: `btcli explain Unreachable`. diff --git a/docs/errors/chain/UnverifiedPulse.mdx b/docs/errors/chain/UnverifiedPulse.mdx new file mode 100644 index 0000000000..5d909b13c6 --- /dev/null +++ b/docs/errors/chain/UnverifiedPulse.mdx @@ -0,0 +1,16 @@ +--- +title: "UnverifiedPulse" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +Declared for drand pulses that fail validity checks, but current code raises `PulseVerificationError` for verification failures and silently skips unverified pulses. If seen on an older runtime, check the pulse signature against `BeaconConfig`. + +Declared by the `Drand` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain UnverifiedPulse`. diff --git a/docs/errors/chain/ValueNotInBounds.mdx b/docs/errors/chain/ValueNotInBounds.mdx new file mode 100644 index 0000000000..0113d968c1 --- /dev/null +++ b/docs/errors/chain/ValueNotInBounds.mdx @@ -0,0 +1,16 @@ +--- +title: "ValueNotInBounds" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +An admin-utils argument fell outside its allowed range: `min_burn` must be below `MinBurnUpperBound` and the subnet's max burn, `max_burn` above `MaxBurnLowerBound` and the min burn, and `max_epochs_per_block` at least 1. Check the argument against those bounds. + +Declared by the `AdminUtils` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain ValueNotInBounds`. diff --git a/docs/errors/chain/ValueTooLarge.mdx b/docs/errors/chain/ValueTooLarge.mdx new file mode 100644 index 0000000000..aeb885de88 --- /dev/null +++ b/docs/errors/chain/ValueTooLarge.mdx @@ -0,0 +1,16 @@ +--- +title: "ValueTooLarge" +description: "A chain-side capacity limit was hit; reduce the size or count of the request" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +A value written to contract storage or emitted as event data exceeds the `MaxValueSize` limit. Compare the size of the stored value or event payload against the runtime's maximum value size constant. + +Declared by the `Contracts` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). + +## Remediation + +A chain-side capacity limit was hit; reduce the size or count of the request + +The same explanation is available in the terminal: `btcli explain ValueTooLarge`. diff --git a/docs/errors/chain/VestingBalance.mdx b/docs/errors/chain/VestingBalance.mdx new file mode 100644 index 0000000000..c86d19d585 --- /dev/null +++ b/docs/errors/chain/VestingBalance.mdx @@ -0,0 +1,16 @@ +--- +title: "VestingBalance" +description: "Fund the signing account or reduce the amount; check with `btcli wallet balance`" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The account's balance is locked under a vesting schedule, leaving too little usable balance to send the requested value. Check the account's `Vesting` schedules and its lock in `Locks`, and compare the unvested (still locked) amount against what the transfer needs. + +Declared by the `Balances` pallet; it classifies to the semantic code [`insufficient_balance`](/docs/errors/insufficient-balance). + +## Remediation + +Fund the signing account or reduce the amount; check with `btcli wallet balance` + +The same explanation is available in the terminal: `btcli explain VestingBalance`. diff --git a/docs/errors/chain/VotingPowerTrackingNotEnabled.mdx b/docs/errors/chain/VotingPowerTrackingNotEnabled.mdx new file mode 100644 index 0000000000..af3881418f --- /dev/null +++ b/docs/errors/chain/VotingPowerTrackingNotEnabled.mdx @@ -0,0 +1,16 @@ +--- +title: "VotingPowerTrackingNotEnabled" +description: "This call or feature is switched off on this network" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +Disabling voting power tracking was requested on a subnet where tracking is not currently active. Check the `VotingPowerTrackingEnabled` storage flag for the netuid before calling the disable extrinsic. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`disabled`](/docs/errors/disabled). + +## Remediation + +This call or feature is switched off on this network + +The same explanation is available in the terminal: `btcli explain VotingPowerTrackingNotEnabled`. diff --git a/docs/errors/chain/WaitingForDissolvedSubnetCleanup.mdx b/docs/errors/chain/WaitingForDissolvedSubnetCleanup.mdx new file mode 100644 index 0000000000..cd468dedb1 --- /dev/null +++ b/docs/errors/chain/WaitingForDissolvedSubnetCleanup.mdx @@ -0,0 +1,16 @@ +--- +title: "WaitingForDissolvedSubnetCleanup" +description: "The required window has not opened yet; wait some blocks and retry" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The operation is blocked while a dissolved subnet's storage is still being torn down in the background. Check the `DissolveCleanupQueue` and retry after the on-idle cleanup for that netuid completes. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`too_early`](/docs/errors/too-early). + +## Remediation + +The required window has not opened yet; wait some blocks and retry + +The same explanation is available in the terminal: `btcli explain WaitingForDissolvedSubnetCleanup`. diff --git a/docs/errors/chain/WeightExceedsAbsoluteMax.mdx b/docs/errors/chain/WeightExceedsAbsoluteMax.mdx new file mode 100644 index 0000000000..16c15d0608 --- /dev/null +++ b/docs/errors/chain/WeightExceedsAbsoluteMax.mdx @@ -0,0 +1,16 @@ +--- +title: "WeightExceedsAbsoluteMax" +description: "A chain-side capacity limit was hit; reduce the size or count of the request" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +`set_on_initialize_weight` or `set_max_extrinsic_weight` was given a value above the shield pallet's hard cap of half the total block weight. Compare the `value` argument against the `MAX_ON_INITIALIZE_WEIGHT` constant. + +Declared by the `MevShield` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). + +## Remediation + +A chain-side capacity limit was hit; reduce the size or count of the request + +The same explanation is available in the terminal: `btcli explain WeightExceedsAbsoluteMax`. diff --git a/docs/errors/chain/WeightVecLengthIsLow.mdx b/docs/errors/chain/WeightVecLengthIsLow.mdx new file mode 100644 index 0000000000..ddea68e598 --- /dev/null +++ b/docs/errors/chain/WeightVecLengthIsLow.mdx @@ -0,0 +1,16 @@ +--- +title: "WeightVecLengthIsLow" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The weight submission has fewer entries than the subnet's minimum (setting only a self-weight is the one exception). Compare the vector length against the `MinAllowedWeights` (min_allowed_weights hyperparameter) for the netuid. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain WeightVecLengthIsLow`. diff --git a/docs/errors/chain/WeightVecNotEqualSize.mdx b/docs/errors/chain/WeightVecNotEqualSize.mdx new file mode 100644 index 0000000000..91db639d38 --- /dev/null +++ b/docs/errors/chain/WeightVecNotEqualSize.mdx @@ -0,0 +1,16 @@ +--- +title: "WeightVecNotEqualSize" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The `uids` and `values` vectors passed to a weight-setting call have different lengths, so they cannot be paired. Check the call arguments; both vectors must have exactly one value per UID. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain WeightVecNotEqualSize`. diff --git a/docs/errors/chain/WithdrawFailed.mdx b/docs/errors/chain/WithdrawFailed.mdx new file mode 100644 index 0000000000..64e71f9b81 --- /dev/null +++ b/docs/errors/chain/WithdrawFailed.mdx @@ -0,0 +1,16 @@ +--- +title: "WithdrawFailed" +description: "Fund the signing account or reduce the amount; check with `btcli wallet balance`" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +Withdrawing the transaction fee from the sender's mapped account failed even though the balance check passed, e.g. due to locks, holds, or existential deposit constraints. Check the account's locks and its free versus withdrawable balance. + +Declared by the `EVM` pallet; it classifies to the semantic code [`insufficient_balance`](/docs/errors/insufficient-balance). + +## Remediation + +Fund the signing account or reduce the amount; check with `btcli wallet balance` + +The same explanation is available in the terminal: `btcli explain WithdrawFailed`. diff --git a/docs/errors/chain/WrongTimepoint.mdx b/docs/errors/chain/WrongTimepoint.mdx new file mode 100644 index 0000000000..bdaf3acf82 --- /dev/null +++ b/docs/errors/chain/WrongTimepoint.mdx @@ -0,0 +1,16 @@ +--- +title: "WrongTimepoint" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The timepoint supplied does not match the one recorded when this multisig operation was opened. Read the correct `when` height and index from the `Multisigs` entry for the call hash and resubmit with that exact timepoint. + +Declared by the `Multisig` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain WrongTimepoint`. diff --git a/docs/errors/chain/XCMDecodeFailed.mdx b/docs/errors/chain/XCMDecodeFailed.mdx new file mode 100644 index 0000000000..333b39909b --- /dev/null +++ b/docs/errors/chain/XCMDecodeFailed.mdx @@ -0,0 +1,16 @@ +--- +title: "XCMDecodeFailed" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The bytes the contract passed to `xcm_execute` or `xcm_send` could not be decoded as a versioned XCM message. Check the XCM encoding and version the contract produces against what the runtime supports. + +Declared by the `Contracts` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain XCMDecodeFailed`. diff --git a/docs/errors/chain/ZeroBalanceAfterWithdrawn.mdx b/docs/errors/chain/ZeroBalanceAfterWithdrawn.mdx new file mode 100644 index 0000000000..96f576c82c --- /dev/null +++ b/docs/errors/chain/ZeroBalanceAfterWithdrawn.mdx @@ -0,0 +1,16 @@ +--- +title: "ZeroBalanceAfterWithdrawn" +description: "Fund the signing account or reduce the amount; check with `btcli wallet balance`" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +Withdrawing TAO from the coldkey (e.g. paying a registration burn or adding stake) would leave the account at zero, below what keeps it alive. Check the coldkey's free balance and leave at least the existential deposit after the amount withdrawn. + +Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`insufficient_balance`](/docs/errors/insufficient-balance). + +## Remediation + +Fund the signing account or reduce the amount; check with `btcli wallet balance` + +The same explanation is available in the terminal: `btcli explain ZeroBalanceAfterWithdrawn`. diff --git a/docs/errors/chain/ZeroShareInBatch.mdx b/docs/errors/chain/ZeroShareInBatch.mdx new file mode 100644 index 0000000000..63a488b474 --- /dev/null +++ b/docs/errors/chain/ZeroShareInBatch.mdx @@ -0,0 +1,16 @@ +--- +title: "ZeroShareInBatch" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +An order's pro-rata share of the batch output floored to zero, so the whole batch was rejected rather than consuming that order's input for no payout. Check the order's `amount` relative to the batch totals and retry it in a differently composed batch. + +Declared by the `LimitOrders` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). + +## Remediation + +Check the argument values against the operation schema + +The same explanation is available in the terminal: `btcli explain ZeroShareInBatch`. diff --git a/docs/errors/chain/index.mdx b/docs/errors/chain/index.mdx new file mode 100644 index 0000000000..fabac96594 --- /dev/null +++ b/docs/errors/chain/index.mdx @@ -0,0 +1,366 @@ +--- +title: "Chain errors" +description: "Every exact chain error name, classified to a semantic code." +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The exact chain error name (from the extrinsic receipt) maps to a semantic [code](/docs/errors); the description says what triggered the failure and where to check. Each name has its own page: + +| Chain error | Code | Description | +| --- | --- | --- | +| [`AccountNotAllowedCommit`](/docs/errors/chain/AccountNotAllowedCommit) | [`not_authorized`](/docs/errors/not-authorized) | Raised by `set_commitment` when the runtime commit check fails: the subnet must exist and the signing hotkey must be registered on it. Verify the `netuid` and that the hotkey has a UID on that subnet. | +| [`AccountRejectsLockedAlpha`](/docs/errors/chain/AccountRejectsLockedAlpha) | [`invalid_argument`](/docs/errors/invalid-argument) | Locked alpha was being transferred to a coldkey whose `AccountFlags` do not have the accept-locked-alpha bit set, e.g. during a lock transfer or coldkey swap of locks. Check the destination coldkey's `AccountFlags` storage and have the recipient opt in to receiving locked alpha before retrying. | +| [`ActiveLockExists`](/docs/errors/chain/ActiveLockExists) | [`already_exists`](/docs/errors/already-exists) | The destination coldkey already holds a lock with nonzero locked mass on that subnet, so a new or transferred lock cannot be created there. Inspect the `Lock` storage for the (coldkey, netuid, hotkey) triple and wait for the existing lock to unlock or remove it first. | +| [`ActivityCutoffFactorMilliOutOfBounds`](/docs/errors/chain/ActivityCutoffFactorMilliOutOfBounds) | [`invalid_argument`](/docs/errors/invalid-argument) | The `factor_milli` argument to set the activity-cutoff factor was outside the allowed 1000-50000 per-mille range (1 to 50 tempos). Adjust the argument to fall within those bounds before resubmitting. | +| [`ActivityCutoffTooLow`](/docs/errors/chain/ActivityCutoffTooLow) | [`invalid_argument`](/docs/errors/invalid-argument) | An admin tried to set the subnet's activity cutoff below the chain-wide minimum. Compare the requested value against the `MinActivityCutoff` storage item and current `activity_cutoff` in `btcli sudo get --netuid `. | +| [`AddStakeBurnRateLimitExceeded`](/docs/errors/chain/AddStakeBurnRateLimitExceeded) | [`rate_limited`](/docs/errors/rate-limited) | The add-stake-and-burn operation was submitted again before its per-key rate-limit window elapsed. Wait some blocks and retry; no active raise site exists in current code, so this mainly appears on older runtimes. | +| [`AdminActionProhibitedDuringWeightsWindow`](/docs/errors/chain/AdminActionProhibitedDuringWeightsWindow) | [`too_early`](/docs/errors/too-early) | An owner or admin hyperparameter change was attempted inside the protected freeze window just before the subnet's epoch runs. Check `AdminFreezeWindow` and the blocks remaining until the next epoch (subnet tempo), then retry after the epoch fires. | +| [`AllNetworksInImmunity`](/docs/errors/chain/AllNetworksInImmunity) | [`too_early`](/docs/errors/too-early) | Creating a new subnet required pruning an existing one, but every candidate subnet is still inside its network immunity period so none can be dissolved. Check `NetworkImmunityPeriod` and each subnet's `NetworkRegisteredAt`, and retry once a subnet leaves immunity. | +| [`AlphaHighTooLow`](/docs/errors/chain/AlphaHighTooLow) | [`invalid_argument`](/docs/errors/invalid-argument) | The `alpha_high` argument to set liquid-alpha values was below the minimum of roughly 0.025 (1638/65535 in u16 units). Raise `alpha_high` in the `sudo_set_alpha_values` call; current values are in the `AlphaValues` storage per netuid. | +| [`AlphaLowOutOfRange`](/docs/errors/chain/AlphaLowOutOfRange) | [`invalid_argument`](/docs/errors/invalid-argument) | The `alpha_low` argument to set liquid-alpha values was below the ~0.025 minimum (1638/65535) or greater than `alpha_high`. Choose alpha_low within that range and not exceeding alpha_high; current settings are in the `AlphaValues` storage for the netuid. | +| [`AlreadyApproved`](/docs/errors/chain/AlreadyApproved) | [`already_exists`](/docs/errors/already-exists) | The sender has already approved this multisig call, so a repeat approval is redundant. Check the `Multisigs` entry for the call hash: the sender's account already appears in its `approvals` list. | +| [`AlreadyDeposited`](/docs/errors/chain/AlreadyDeposited) | [`already_exists`](/docs/errors/already-exists) | The account calling safe-mode `enter` or `extend` already has a safe-mode deposit on hold, so another cannot be placed. Check the account's `Deposits` entries and its balance held under the `EnterOrExtend` hold reason. | +| [`AlreadyFinalized`](/docs/errors/chain/AlreadyFinalized) | [`already_exists`](/docs/errors/already-exists) | The crowdloan's `finalized` flag is already true, so withdraw, finalize, refund, dissolve, and the update calls are all rejected. Check the `finalized` field of the `Crowdloans` entry for the given `crowdloan_id`. | +| [`AlreadyFinalizing`](/docs/errors/chain/AlreadyFinalizing) | [`already_exists`](/docs/errors/already-exists) | A `finalize` call was made while another finalization is still in progress, i.e. the dispatched call from a previous finalize has not cleared. Check that the `CurrentCrowdloanId` storage value is empty before retrying. | +| [`AlreadyNoted`](/docs/errors/chain/AlreadyNoted) | [`already_exists`](/docs/errors/already-exists) | The preimage for this hash has already been noted on-chain, so `note_preimage` has nothing to add. Check `RequestStatusFor` (or legacy `StatusFor`) for the hash before submitting the bytes again. | +| [`AlreadyStored`](/docs/errors/chain/AlreadyStored) | [`already_exists`](/docs/errors/already-exists) | The call data supplied for storage is already stored on-chain for this multisig operation. Check whether the call bytes were previously stored for this call hash before submitting them again. | +| [`AmountTooLow`](/docs/errors/chain/AmountTooLow) | [`invalid_argument`](/docs/errors/invalid-argument) | A stake, unstake, move or swap amount was zero or its TAO equivalent fell below the minimum stake threshold after fees and slippage. Compare the amount against the `DefaultMinStake` storage item and the subnet's alpha price before retrying with a larger amount. | +| [`AnnouncedColdkeyHashDoesNotMatch`](/docs/errors/chain/AnnouncedColdkeyHashDoesNotMatch) | [`invalid_argument`](/docs/errors/invalid-argument) | The `new_coldkey` passed to `coldkey_swap` hashes to a different value than the hash committed in the earlier `announce_coldkey_swap`. Verify the announced hash in the `ColdkeySwapAnnouncements` storage matches the BlakeTwo256 hash of the coldkey you are swapping to. | +| [`AnnouncementDepositInvariantViolated`](/docs/errors/chain/AnnouncementDepositInvariantViolated) | [`internal`](/docs/errors/internal) | Internal invariant failure in `announce`: recomputing the announcement deposit returned nothing after the pending announcements were updated. Inspect the caller's `Announcements` entry and the announcement deposit constants; this indicates a pallet bug rather than bad input. | +| [`ArithmeticOverflow`](/docs/errors/chain/ArithmeticOverflow) | [`internal`](/docs/errors/internal) | Converting a TAO amount to alpha during batched order execution overflowed the fixed-point range, typically when the pool price is tiny relative to the batch's total buy TAO. Check the subnet's current alpha price against the batch's aggregate buy amounts. | +| [`AutoEpochAlreadyImminent`](/docs/errors/chain/AutoEpochAlreadyImminent) | [`already_exists`](/docs/errors/already-exists) | `trigger_epoch` was called when the next automatic epoch is closer than the `AdminFreezeWindow`, so a manual trigger would have no effect. Check the subnet's tempo and blocks until the next epoch, and simply wait for it to fire. | +| [`BadEncKeyLen`](/docs/errors/chain/BadEncKeyLen) | [`invalid_argument`](/docs/errors/invalid-argument) | The `enc_key` passed to `announce_next_key` is not the exact ML-KEM-768 encapsulation key length (1184 bytes). Check the byte length of the `enc_key` argument before announcing. | +| [`BalanceLow`](/docs/errors/chain/BalanceLow) | [`insufficient_balance`](/docs/errors/insufficient-balance) | The sender's mapped account cannot cover the transaction's value plus maximum gas fee, detected during validation or when withdrawing the fee. Check the account balance against `value` plus `gas_limit` times the effective gas price. | +| [`BalanceWithdrawalError`](/docs/errors/chain/BalanceWithdrawalError) | [`insufficient_balance`](/docs/errors/insufficient-balance) | The requested TAO could not be withdrawn from the coldkey's free balance, typically due to insufficient funds, the existential deposit, or frozen/reserved balance. Check the coldkey's balance with `btcli wallet balance` and reduce the amount or top up. | +| [`BeneficiaryDoesNotOwnHotkey`](/docs/errors/chain/BeneficiaryDoesNotOwnHotkey) | [`not_authorized`](/docs/errors/not-authorized) | When ending a subnet lease, the hotkey passed for the ownership handover is not owned by the lease's beneficiary coldkey. Check the `Owner` storage for that hotkey and pass a hotkey the beneficiary coldkey actually owns. | +| [`BlockDurationTooLong`](/docs/errors/chain/BlockDurationTooLong) | [`invalid_argument`](/docs/errors/invalid-argument) | The requested `end` block is more than `MaximumBlockDuration` blocks after the current block. Compare `end` minus the current block number against the `MaximumBlockDuration` pallet constant when calling `create` or `update_end`. | +| [`BlockDurationTooShort`](/docs/errors/chain/BlockDurationTooShort) | [`invalid_argument`](/docs/errors/invalid-argument) | The requested `end` block is fewer than `MinimumBlockDuration` blocks after the current block. Compare `end` minus the current block number against the `MinimumBlockDuration` pallet constant when calling `create` or `update_end`. | +| [`BondsMovingAverageMaxReached`](/docs/errors/chain/BondsMovingAverageMaxReached) | [`limit_exceeded`](/docs/errors/limit-exceeded) | A subnet owner called `sudo_set_bonds_moving_average` with a value above 975000, the cap for owner-set values; root is exempt. Lower the `bonds_moving_average` argument or submit the call as root. | +| [`CallDisabled`](/docs/errors/chain/CallDisabled) | [`disabled`](/docs/errors/disabled) | The extrinsic has been switched off in the current runtime and cannot be dispatched. There is no active raise site in current code; if seen, check release notes for whether the call was re-enabled in a newer runtime version. | +| [`CallFiltered`](/docs/errors/chain/CallFiltered) | [`not_authorized`](/docs/errors/not-authorized) | The runtime's origin call filter (e.g. `BaseCallFilter` or a restricted origin) rejected this call before dispatch. Check whether the specific call is permitted for the origin you used, including any proxy or safe-mode filtering in effect. | +| [`CallUnavailable`](/docs/errors/chain/CallUnavailable) | [`not_found`](/docs/errors/not-found) | During `finalize` the crowdloan's stored call could not be fetched from preimage storage, so nothing was dispatched. Check that the preimage referenced by the `call` field of the `Crowdloans` entry still exists in the preimage pallet. | +| [`CanNotSetRootNetworkWeights`](/docs/errors/chain/CanNotSetRootNetworkWeights) | [`invalid_argument`](/docs/errors/invalid-argument) | `set_weights` was called with netuid 0, the root network, where normal weight setting is not allowed. Use a non-root `netuid` argument; root weights are handled by a separate mechanism. | +| [`CannotAddSelfAsDelegateDependency`](/docs/errors/chain/CannotAddSelfAsDelegateDependency) | [`invalid_argument`](/docs/errors/invalid-argument) | A contract called `lock_delegate_dependency` with its own code hash, which is not permitted. Check the code hash argument passed to the delegate dependency API against the contract's own code hash. | +| [`CannotAffordLockCost`](/docs/errors/chain/CannotAffordLockCost) | [`insufficient_balance`](/docs/errors/insufficient-balance) | The coldkey's free balance cannot cover the current dynamic subnet-creation lock cost. Compare `btcli subnets create-cost` (or `btcli query subnet-registration-cost`) against the coldkey balance from `btcli wallet balance` before registering a subnet. | +| [`CannotBurnOrRecycleOnRootSubnet`](/docs/errors/chain/CannotBurnOrRecycleOnRootSubnet) | [`invalid_argument`](/docs/errors/invalid-argument) | `recycle_alpha` or `burn_alpha` was called with netuid 0, and TAO on the root subnet cannot be burned or recycled. Pass a non-root `netuid` argument for the subnet whose alpha you want to recycle or burn. | +| [`CannotEndInPast`](/docs/errors/chain/CannotEndInPast) | [`invalid_argument`](/docs/errors/invalid-argument) | The `end` block passed to `create` or `update_end` is not after the current block. Compare the `end` argument against the current block number; it must be strictly greater. | +| [`CannotReleaseYet`](/docs/errors/chain/CannotReleaseYet) | [`too_early`](/docs/errors/too-early) | `release_deposit` was called too early: the current block must exceed the deposit's block plus `ReleaseDelay`, and safe-mode must be exited. Check the block key of the entry in `Deposits` against the `ReleaseDelay` config. | +| [`CannotUseSystemAccount`](/docs/errors/chain/CannotUseSystemAccount) | [`not_authorized`](/docs/errors/not-authorized) | The hotkey supplied for registration, hotkey swap, or subnet-owner-hotkey assignment is a reserved subnet system account. Use a regular user-generated hotkey instead; system accounts are derived per-subnet and rejected by `is_subnet_account_id`. | +| [`CapNotRaised`](/docs/errors/chain/CapNotRaised) | [`too_early`](/docs/errors/too-early) | `finalize` was called before the crowdloan's `raised` amount equals its `cap`. Compare the `raised` and `cap` fields of the `Crowdloans` entry; contribute the remainder or lower the cap with `update_cap` before finalizing. | +| [`CapRaised`](/docs/errors/chain/CapRaised) | [`limit_exceeded`](/docs/errors/limit-exceeded) | A contribution was attempted on a crowdloan whose `raised` amount has already reached its `cap`, so no further contributions are accepted. Compare the `raised` and `cap` fields of the `Crowdloans` entry for the `crowdloan_id`. | +| [`CapTooLow`](/docs/errors/chain/CapTooLow) | [`invalid_argument`](/docs/errors/invalid-argument) | On `create` the `cap` is not strictly greater than the initial `deposit`, or on `update_cap` the new cap is below the amount already raised. Compare the cap argument against the `deposit` or the `raised` field of the `Crowdloans` entry. | +| [`ChainIdMismatch`](/docs/errors/chain/ChainIdMismatch) | [`invalid_argument`](/docs/errors/invalid-argument) | The order payload's `chain_id` field differs from this chain's configured EVM chain id, e.g. an order signed for testnet was submitted to mainnet. Compare the order's `chain_id` with the runtime's `pallet_evm_chain_id` value and re-sign if needed. | +| [`ChangePending`](/docs/errors/chain/ChangePending) | [`already_exists`](/docs/errors/already-exists) | A GRANDPA authority-set change has already been signalled and is still pending, so a new change cannot be scheduled. Check the Grandpa `PendingChange` and `State` storage and wait for the pending change to be applied first. | +| [`ChildParentInconsistency`](/docs/errors/chain/ChildParentInconsistency) | [`invalid_argument`](/docs/errors/invalid-argument) | A `set_children` or parent-delegation call would make the same hotkey appear as both a child and a parent, or referenced a child missing from the proposed mapping. Inspect the `ChildKeys` and `ParentKeys` storage for the hotkeys involved and remove the overlap. | +| [`CodeInUse`](/docs/errors/chain/CodeInUse) | [`invalid_argument`](/docs/errors/invalid-argument) | `remove_code` was refused because at least one contract instance still references the code hash. Check the code's reference count and terminate or `set_code` the contracts using it before removal. | +| [`CodeInfoNotFound`](/docs/errors/chain/CodeInfoNotFound) | [`not_found`](/docs/errors/not-found) | No `CodeInfoOf` entry exists for the supplied code hash, so its owner and deposit metadata cannot be read. Verify the code hash argument and that the code was uploaded and not since removed. | +| [`CodeNotFound`](/docs/errors/chain/CodeNotFound) | [`not_found`](/docs/errors/not-found) | No uploaded WASM binary exists under the supplied code hash. Verify the `code_hash` argument used in instantiation, `set_code`, or a delegate call, and that the code was uploaded via `upload_code` and not removed. | +| [`CodeRejected`](/docs/errors/chain/CodeRejected) | [`invalid_argument`](/docs/errors/invalid-argument) | The uploaded WASM failed validation, most often because it imports a host API the node does not support, e.g. newer ink! against an older node. Rerun the node with `-lruntime::contracts=debug` to see the detailed rejection reason. | +| [`CodeTooLarge`](/docs/errors/chain/CodeTooLarge) | [`limit_exceeded`](/docs/errors/limit-exceeded) | The code blob passed to `instantiate_with_code` or `upload_code` exceeds the maximum code length in the pallet's schedule. Compare the WASM binary size against the schedule's code length limit and shrink the contract. | +| [`ColdKeyAlreadyAssociated`](/docs/errors/chain/ColdKeyAlreadyAssociated) | [`already_exists`](/docs/errors/already-exists) | The destination coldkey of a coldkey swap already has staking hotkeys associated with it, so it cannot receive the swapped identity. Check the `StakingHotkeys` storage for the new coldkey and swap to a fresh, unused coldkey instead. | +| [`ColdkeySwapAlreadyDisputed`](/docs/errors/chain/ColdkeySwapAlreadyDisputed) | [`already_exists`](/docs/errors/already-exists) | `dispute_coldkey_swap` was called for a coldkey whose pending swap announcement is already under dispute. Check the `ColdkeySwapDisputes` storage for the coldkey; no further dispute action is needed. | +| [`ColdkeySwapAnnounced`](/docs/errors/chain/ColdkeySwapAnnounced) | [`already_exists`](/docs/errors/already-exists) | The coldkey has a pending swap announcement, so all but a small allow-list of extrinsics are blocked until the swap completes or is cleared. Check the `ColdkeySwapAnnouncements` storage for the coldkey and either finish the swap with `coldkey_swap` or clear the announcement. | +| [`ColdkeySwapAnnouncementNotFound`](/docs/errors/chain/ColdkeySwapAnnouncementNotFound) | [`not_found`](/docs/errors/not-found) | `coldkey_swap`, `dispute_coldkey_swap`, or `clear_coldkey_swap_announcement` was called for a coldkey with no pending announcement. Check the `ColdkeySwapAnnouncements` storage; you must call `announce_coldkey_swap` first. | +| [`ColdkeySwapClearTooEarly`](/docs/errors/chain/ColdkeySwapClearTooEarly) | [`too_early`](/docs/errors/too-early) | The swap announcement cannot be cleared until the reannouncement delay after the announcement's execution block has passed. Compare the current block with the `when` stored in `ColdkeySwapAnnouncements` plus `ColdkeySwapReannouncementDelay` and retry later. | +| [`ColdkeySwapDisputed`](/docs/errors/chain/ColdkeySwapDisputed) | [`not_authorized`](/docs/errors/not-authorized) | All extrinsics from this coldkey are blocked because its pending coldkey swap is under dispute. Check the `ColdkeySwapDisputes` storage for the coldkey; the dispute must be resolved by root before the account can transact. | +| [`ColdkeySwapReannouncedTooEarly`](/docs/errors/chain/ColdkeySwapReannouncedTooEarly) | [`too_early`](/docs/errors/too-early) | `announce_coldkey_swap` was called again before the reannouncement delay after the previous announcement's execution block elapsed. Compare the current block with the stored announcement time plus `ColdkeySwapReannouncementDelay` and retry later. | +| [`ColdkeySwapTooEarly`](/docs/errors/chain/ColdkeySwapTooEarly) | [`too_early`](/docs/errors/too-early) | `coldkey_swap` was executed before the announcement delay had elapsed since `announce_coldkey_swap`. Check the execution block stored in `ColdkeySwapAnnouncements` (announcement time plus `ColdkeySwapAnnouncementDelay`) and wait until then. | +| [`CommitRevealDisabled`](/docs/errors/chain/CommitRevealDisabled) | [`disabled`](/docs/errors/disabled) | A weight commit or reveal was submitted on a subnet where commit-reveal is turned off. Check the `commit_reveal_weights_enabled` hyperparameter for the netuid (`btcli sudo get --netuid `); use plain `set_weights` instead when it is disabled. | +| [`CommitRevealEnabled`](/docs/errors/chain/CommitRevealEnabled) | [`disabled`](/docs/errors/disabled) | Plain `set_weights` was called on a subnet where commit-reveal is enabled, which requires the commit/reveal flow instead. Check the `commit_reveal_weights_enabled` hyperparameter for the netuid and switch to `commit_weights`/`reveal_weights`. | +| [`CommittingWeightsTooFast`](/docs/errors/chain/CommittingWeightsTooFast) | [`rate_limited`](/docs/errors/rate-limited) | The neuron committed weights again before the per-UID rate limit elapsed since its last commit on that subnet. Compare blocks since the last commit against the `weights_rate_limit` hyperparameter (`btcli sudo get --netuid `) and wait. | +| [`ContractNotFound`](/docs/errors/chain/ContractNotFound) | [`not_found`](/docs/errors/not-found) | No contract instance exists at the destination address; the account has no `ContractInfoOf` entry. Verify the `dest` address and that the contract was instantiated and has not been terminated. | +| [`ContractReverted`](/docs/errors/chain/ContractReverted) | [`internal`](/docs/errors/internal) | The contract ran to completion but returned with the REVERT flag set, rolling back its state changes; only extrinsics surface this as an error. Dry-run the call via RPC and decode the returned output data for the contract's error value. | +| [`ContractTrapped`](/docs/errors/chain/ContractTrapped) | [`internal`](/docs/errors/internal) | The contract aborted with a WASM trap, e.g. a panic, unreachable instruction, or memory violation, instead of returning normally. Dry-run the call with debug messages enabled and check the input data against the contract's expectations. | +| [`ContributionPeriodEnded`](/docs/errors/chain/ContributionPeriodEnded) | [`expired`](/docs/errors/expired) | A contribution was made at or after the crowdloan's `end` block. Compare the `end` field of the `Crowdloans` entry with the current block number; the creator can extend it with `update_end` while the crowdloan is not finalized. | +| [`ContributionPeriodNotEnded`](/docs/errors/chain/ContributionPeriodNotEnded) | [`too_early`](/docs/errors/too-early) | The operation requires the crowdloan's contribution period to be over, but the current block is still before the crowdloan's `end` block. Compare the `end` field of the `Crowdloans` entry for the `crowdloan_id` with the current block number. | +| [`ContributionTooLow`](/docs/errors/chain/ContributionTooLow) | [`invalid_argument`](/docs/errors/invalid-argument) | The `amount` passed to `contribute` is below the crowdloan's configured minimum contribution. Check the `min_contribution` field of the `Crowdloans` entry for the `crowdloan_id` and contribute at least that amount. | +| [`CreateOriginNotAllowed`](/docs/errors/chain/CreateOriginNotAllowed) | [`not_authorized`](/docs/errors/not-authorized) | A CREATE, or a CALL that performs a nested CREATE, was attempted from an EVM address not permitted to deploy contracts. Check whether the deploying address is in the chain's allowed-deployers list. | +| [`CurrencyError`](/docs/errors/chain/CurrencyError) | [`internal`](/docs/errors/internal) | A balance hold, release, or burn inside pallet-safe-mode failed while managing an enter/extend deposit. Check the account's free balance and existing holds under the safe-mode `EnterOrExtend` hold reason. | +| [`DeadAccount`](/docs/errors/chain/DeadAccount) | [`not_found`](/docs/errors/not-found) | The beneficiary account does not exist and this operation is not allowed to create it. Check `System.Account` for the destination: it must already hold at least the existential deposit before the operation runs. | +| [`DecodingFailed`](/docs/errors/chain/DecodingFailed) | [`invalid_argument`](/docs/errors/invalid-argument) | Input bytes passed to a contract API host function could not be SCALE-decoded into the expected type. Check the encoding of the call's input data or the argument bytes the contract passes to the runtime API. | +| [`DelegateDependencyAlreadyExists`](/docs/errors/chain/DelegateDependencyAlreadyExists) | [`already_exists`](/docs/errors/already-exists) | The contract called `lock_delegate_dependency` for a code hash it has already locked. Check the contract's recorded delegate dependencies before adding, and unlock the old entry first if replacing it. | +| [`DelegateDependencyNotFound`](/docs/errors/chain/DelegateDependencyNotFound) | [`not_found`](/docs/errors/not-found) | `unlock_delegate_dependency` was called for a code hash that is not among the contract's locked delegate dependencies. Check the code hash argument against the dependencies recorded in the contract's info. | +| [`DelegateTakeTooHigh`](/docs/errors/chain/DelegateTakeTooHigh) | [`invalid_argument`](/docs/errors/invalid-argument) | The `take` argument exceeds the maximum delegate take allowed by the chain (18% by default). Compare the requested value against the `MaxDelegateTake` storage item and lower it. | +| [`DelegateTakeTooLow`](/docs/errors/chain/DelegateTakeTooLow) | [`invalid_argument`](/docs/errors/invalid-argument) | The `take` argument was below the `MinDelegateTake` minimum, or `increase_take`/`decrease_take` was not strictly increasing/decreasing relative to the current take. Check the hotkey's current take in the `Delegates` storage and the `MinDelegateTake` storage item. | +| [`DelegateTxRateLimitExceeded`](/docs/errors/chain/DelegateTxRateLimitExceeded) | [`rate_limited`](/docs/errors/rate-limited) | The delegate changed its take again before the per-hotkey take-change rate limit elapsed. Compare blocks since the hotkey's last take transaction against the `TxDelegateTakeRateLimit` storage item and retry later. | +| [`DeltaZero`](/docs/errors/chain/DeltaZero) | [`invalid_argument`](/docs/errors/invalid-argument) | The issuance adjustment was called with a delta of zero, which is meaningless. Check the `delta` argument to `force_adjust_total_issuance` and pass a strictly positive amount. | +| [`DepositCannotBeWithdrawn`](/docs/errors/chain/DepositCannotBeWithdrawn) | [`invalid_argument`](/docs/errors/invalid-argument) | The creator called `withdraw` but holds nothing above the initial deposit, which stays locked until the crowdloan is dissolved. Compare the creator's `Contributions` entry with the `deposit` field of the `Crowdloans` entry. | +| [`DepositTooLow`](/docs/errors/chain/DepositTooLow) | [`invalid_argument`](/docs/errors/invalid-argument) | The `deposit` argument to `create` is below the pallet's required minimum. Check the `MinimumDeposit` pallet constant and create the crowdloan with at least that initial deposit. | +| [`Deprecated`](/docs/errors/chain/Deprecated) | [`disabled`](/docs/errors/disabled) | The extrinsic has been removed and always fails, e.g. `schedule_swap_coldkey`, the swap pallet's user-liquidity calls, or `sudo_set_total_issuance`. Migrate to the replacement call noted in the deprecation (for coldkey swaps, `announce_coldkey_swap` plus `coldkey_swap`). | +| [`DisabledTemporarily`](/docs/errors/chain/DisabledTemporarily) | [`disabled`](/docs/errors/disabled) | The operation has been temporarily switched off in the runtime, usually as a hotfix measure. There is no active raise site in current code; if encountered, check the runtime version and release notes for when the feature is re-enabled. | +| [`DrandConnectionFailure`](/docs/errors/chain/DrandConnectionFailure) | [`internal`](/docs/errors/internal) | Declared for failures reaching the drand HTTP API, but pulse fetching happens in the offchain worker, which logs errors instead of raising this. Check the node's offchain worker logs and outbound connectivity to the drand endpoints. | +| [`Duplicate`](/docs/errors/chain/Duplicate) | [`already_exists`](/docs/errors/already-exists) | This delegate is already registered as a proxy for the delegator with the same proxy type and delay. Check the delegator's `Proxies` entry before calling `add_proxy` with the same (delegate, proxy type, delay) tuple. | +| [`DuplicateChild`](/docs/errors/chain/DuplicateChild) | [`invalid_argument`](/docs/errors/invalid-argument) | The children list passed to `set_children` contains the same child hotkey more than once. Deduplicate the `children` argument; current relations are visible in the `ChildKeys` storage for the parent hotkey and netuid. | +| [`DuplicateContract`](/docs/errors/chain/DuplicateContract) | [`already_exists`](/docs/errors/already-exists) | Instantiation would create a contract at an address already occupied by an existing contract. Check the derived contract address and vary the `salt` argument to obtain a fresh address. | +| [`DuplicateOffenceReport`](/docs/errors/chain/DuplicateOffenceReport) | [`already_exists`](/docs/errors/already-exists) | The equivocation proof is valid but this offence has already been reported and recorded. Check whether an equivocation report for the same offender, session, and round was previously submitted before reporting again. | +| [`DuplicateOrderInBatch`](/docs/errors/chain/DuplicateOrderInBatch) | [`invalid_argument`](/docs/errors/invalid-argument) | Two entries in one `execute_batched_orders` call hash to the same order id, meaning the identical signed payload was included twice, which hard-fails the batch. Deduplicate by the blake2-256 hash of each SCALE-encoded `VersionedOrder`. | +| [`DuplicateUids`](/docs/errors/chain/DuplicateUids) | [`invalid_argument`](/docs/errors/invalid-argument) | The `uids` vector passed to `set_weights` (or a reveal) contains the same UID more than once. Deduplicate the uids/values pairs before submitting; each target neuron may appear only once per weight vector. | +| [`DynamicTempoBlockedByCommitReveal`](/docs/errors/chain/DynamicTempoBlockedByCommitReveal) | [`disabled`](/docs/errors/disabled) | `trigger_epoch` is refused while commit-reveal is enabled on the subnet, because an out-of-band epoch would desync the CRv3 reveal window from the Drand schedule and drop committed weights. Check the `commit_reveal_weights_enabled` hyperparameter; disable it before manually triggering epochs. | +| [`Entered`](/docs/errors/chain/Entered) | [`already_exists`](/docs/errors/already-exists) | Safe-mode is currently active, so `enter` or `force_enter` cannot activate it again and `release_deposit` is blocked until it exits. Check `EnteredUntil` for the block at which safe-mode disengages. | +| [`EpochTriggerAlreadyPending`](/docs/errors/chain/EpochTriggerAlreadyPending) | [`already_exists`](/docs/errors/already-exists) | `trigger_epoch` was called while a previously triggered epoch is still queued for this subnet. Check the `PendingEpochAt` storage for the netuid and wait for the pending epoch to fire before triggering again. | +| [`EvmKeyAssociateRateLimitExceeded`](/docs/errors/chain/EvmKeyAssociateRateLimitExceeded) | [`rate_limited`](/docs/errors/rate-limited) | `associate_evm_key` was called again before the per-UID rate limit since the last association elapsed. Compare blocks since the association recorded in the `AssociatedEvmAddress` storage against the `EvmKeyAssociateRateLimit` runtime constant and retry later. | +| [`EvmKeyAssociationLimitExceeded`](/docs/errors/chain/EvmKeyAssociationLimitExceeded) | [`limit_exceeded`](/docs/errors/limit-exceeded) | The EVM address is already associated with the maximum number of UIDs allowed on this subnet. Inspect the `AssociatedUidsByEvmAddress` storage for the (netuid, evm_key) pair and free a slot or use a different EVM address. | +| [`ExistentialDeposit`](/docs/errors/chain/ExistentialDeposit) | [`insufficient_balance`](/docs/errors/insufficient-balance) | The amount is too small to create the destination account: its resulting balance would sit below the existential deposit. Compare the transfer value plus the destination's current free balance against the `ExistentialDeposit` constant. | +| [`ExistingVestingSchedule`](/docs/errors/chain/ExistingVestingSchedule) | [`already_exists`](/docs/errors/already-exists) | A vesting schedule already exists for the target account and this call cannot add another. Check the account's `Vesting` storage entry before attempting to set a new vested transfer or schedule. | +| [`Exited`](/docs/errors/chain/Exited) | [`already_exists`](/docs/errors/already-exists) | Safe-mode is not currently active, so `extend`, `force_extend`, or `force_exit` have nothing to act on. Check that `EnteredUntil` contains a value before extending or exiting. | +| [`ExpectedBeneficiaryOrigin`](/docs/errors/chain/ExpectedBeneficiaryOrigin) | [`not_authorized`](/docs/errors/not-authorized) | A lease operation such as terminating a subnet lease was signed by an account other than the lease's beneficiary coldkey. Check the beneficiary recorded in the `SubnetLeases` storage for the lease id and sign with that coldkey. | +| [`Expendability`](/docs/errors/chain/Expendability) | [`insufficient_balance`](/docs/errors/insufficient-balance) | The transfer or payment would drop the sender below the existential deposit and kill the account while keep-alive semantics are required. Compare the sender's free balance minus the amount and fees against `ExistentialDeposit`, or use `transfer_allow_death` if reaping is acceptable. | +| [`ExpiredWeightCommit`](/docs/errors/chain/ExpiredWeightCommit) | [`expired`](/docs/errors/expired) | The hash supplied to `reveal_weights` matches a commit whose reveal window has already passed, so it can no longer be revealed. Check the `commit_reveal_period` hyperparameter and reveal within the allowed epochs after committing; re-commit and reveal on time. | +| [`FailedToExtractRuntimeVersion`](/docs/errors/chain/FailedToExtractRuntimeVersion) | [`invalid_argument`](/docs/errors/invalid-argument) | The new runtime code passed to `set_code` did not yield a readable version: calling `Core_version` or decoding `RuntimeVersion` failed. Check that the submitted blob is a valid, complete runtime wasm and not truncated or compressed incorrectly. | +| [`FailedToSchedule`](/docs/errors/chain/FailedToSchedule) | [`internal`](/docs/errors/internal) | The scheduler could not place the call into the agenda, typically because the target block's agenda is full or the schedule parameters are unusable. Check `Agenda` at the target block against `MaxScheduledPerBlock` and pick a different block if it is saturated. | +| [`FaucetDisabled`](/docs/errors/chain/FaucetDisabled) | [`disabled`](/docs/errors/disabled) | The `faucet` extrinsic was called on a runtime built without the pow-faucet feature, i.e. any real network. The faucet only works on local test chains compiled with that feature; use a funded wallet or testnet TAO instead. | +| [`FeeOverflow`](/docs/errors/chain/FeeOverflow) | [`internal`](/docs/errors/internal) | Fee arithmetic overflowed, either multiplying the fee per gas by `gas_limit` or converting the EVM fee into Substrate balance decimals. Check for absurdly large `gas_limit` or fee-per-gas values in the transaction. | +| [`FeeRateTooHigh`](/docs/errors/chain/FeeRateTooHigh) | [`invalid_argument`](/docs/errors/invalid-argument) | `set_fee_rate` was called with a rate above the swap pallet's `MaxFeeRate` config constant. Compare the `rate` argument (u16-scaled fraction) against `MaxFeeRate` before submitting. | +| [`FirstEmissionBlockNumberAlreadySet`](/docs/errors/chain/FirstEmissionBlockNumberAlreadySet) | [`already_exists`](/docs/errors/already-exists) | `start_call` was issued for a subnet whose emissions have already been started. Check the `FirstEmissionBlockNumber` storage for the netuid; a non-empty value means the subnet is already emitting and no action is needed. | +| [`GasLimitTooHigh`](/docs/errors/chain/GasLimitTooHigh) | [`invalid_argument`](/docs/errors/invalid-argument) | The transaction's `gas_limit` exceeds the block gas limit configured for the EVM. Compare the `gas_limit` argument against the chain's block gas limit and lower it. | +| [`GasLimitTooLow`](/docs/errors/chain/GasLimitTooLow) | [`invalid_argument`](/docs/errors/invalid-argument) | The transaction's `gas_limit` is below the intrinsic gas required, or too small to cover the weight and proof-size base cost. Raise the `gas_limit` argument, comparing against an `eth_estimateGas` result. | +| [`GasPriceTooLow`](/docs/errors/chain/GasPriceTooLow) | [`invalid_argument`](/docs/errors/invalid-argument) | The offered fee cannot satisfy the current base fee: `max_fee_per_gas` is below the block base fee, the priority fee exceeds the max fee, or the fee inputs are inconsistent. Check `max_fee_per_gas` and `max_priority_fee_per_gas` against the chain's base fee. | +| [`HotKeyAccountNotExists`](/docs/errors/chain/HotKeyAccountNotExists) | [`not_registered`](/docs/errors/not-registered) | The hotkey has no on-chain account, meaning it was never created through registration, so staking or delegation operations cannot reference it. Check the `Owner` storage for the hotkey or `btcli wallet overview`; register the hotkey on a subnet first. | +| [`HotKeyAlreadyDelegate`](/docs/errors/chain/HotKeyAlreadyDelegate) | [`already_exists`](/docs/errors/already-exists) | `become_delegate` was called for a hotkey that is already a delegate. Check the `Delegates` storage for the hotkey; if it has a take entry it is already delegating and no action is needed. | +| [`HotKeyAlreadyRegisteredInSubNet`](/docs/errors/chain/HotKeyAlreadyRegisteredInSubNet) | [`already_exists`](/docs/errors/already-exists) | A registration or hotkey swap targeted a hotkey that already holds a UID on the subnet (or, for a swap without a netuid, on any subnet). Check the `Uids` storage for the (netuid, hotkey) pair or `btcli wallet overview`; use a different hotkey or netuid. | +| [`HotKeyNotRegisteredInNetwork`](/docs/errors/chain/HotKeyNotRegisteredInNetwork) | [`not_registered`](/docs/errors/not-registered) | The hotkey is not registered on the relevant subnet, raised by `serve_axon`/`serve_prometheus` for the serving netuid or by identity calls requiring registration on any subnet. Verify registration with `btcli query uid --netuid ` (or `btcli query netuids-for-hotkey`) and register via `btcli subnets register` first. | +| [`HotKeyNotRegisteredInSubNet`](/docs/errors/chain/HotKeyNotRegisteredInSubNet) | [`not_registered`](/docs/errors/not-registered) | The hotkey holds no UID on the given netuid, so weight setting, commits, or UID lookups fail there. Check the `Uids` storage for the (netuid, hotkey) pair or `btcli query uid --netuid `; confirm the netuid argument and register the hotkey if needed. | +| [`HotKeySetTxRateLimitExceeded`](/docs/errors/chain/HotKeySetTxRateLimitExceeded) | [`rate_limited`](/docs/errors/rate-limited) | The coldkey attempted a hotkey set/swap before `TxRateLimit` blocks had passed since its last such transaction. Check the coldkey's last transaction block against the `TxRateLimit` storage value and wait the remaining blocks. | +| [`HotKeySwapOnSubnetIntervalNotPassed`](/docs/errors/chain/HotKeySwapOnSubnetIntervalNotPassed) | [`rate_limited`](/docs/errors/rate-limited) | A hotkey swap on a subnet was attempted before `HotkeySwapOnSubnetInterval` blocks passed since the coldkey's last swap on that netuid. Compare `LastHotkeySwapOnNetuid` for the coldkey with the current block and retry after the interval. | +| [`IncorrectCommitRevealVersion`](/docs/errors/chain/IncorrectCommitRevealVersion) | [`invalid_argument`](/docs/errors/invalid-argument) | The `commit_reveal_version` argument does not match the chain's current commit-reveal weights version. Query the `CommitRevealWeightsVersion` storage item and upgrade or configure the client to commit with that version. | +| [`IncorrectPartialFillAmount`](/docs/errors/chain/IncorrectPartialFillAmount) | [`invalid_argument`](/docs/errors/invalid-argument) | The `partial_fill` amount is zero or exceeds the order's remaining unfilled amount, or a full execution was submitted against an order already partially filled. Compare `partial_fill` with `order.amount` minus the filled amount recorded in `Orders`. | +| [`IncorrectWeightVersionKey`](/docs/errors/chain/IncorrectWeightVersionKey) | [`invalid_argument`](/docs/errors/invalid-argument) | The `version_key` supplied with set_weights is older than the subnet's required weights version. Compare it against the `WeightsVersionKey` hyperparameter (`btcli sudo get --netuid `) and update the validator software or the key. | +| [`Indeterministic`](/docs/errors/chain/Indeterministic) | [`invalid_argument`](/docs/errors/invalid-argument) | Code flagged as non-deterministic (e.g. using floating point) was used where determinism is enforced, such as on-chain instantiation or calls. Check the determinism mode the code was uploaded with and rebuild the contract deterministically. | +| [`InputForwarded`](/docs/errors/chain/InputForwarded) | [`invalid_argument`](/docs/errors/invalid-argument) | The contract forwarded its input to a callee via `seal_call` with the FORWARD_INPUT flag and then tried to read or forward it again. Check the call flags used; use CLONE_INPUT when the input is still needed afterwards. | +| [`InputLengthsUnequal`](/docs/errors/chain/InputLengthsUnequal) | [`invalid_argument`](/docs/errors/invalid-argument) | A batch weights call passed vectors of different lengths, e.g. netuids vs commit hashes, or uids vs values, salts and version_keys in batch reveal. Check that every parallel vector argument in the batch extrinsic has the same length. | +| [`InsufficientAlphaBalance`](/docs/errors/chain/InsufficientAlphaBalance) | [`insufficient_balance`](/docs/errors/insufficient-balance) | A stake decrease asked to debit more alpha than the hotkey-coldkey pair holds on that subnet. Compare the requested amount against the pair's current stake on the netuid (`btcli stake list` or the `Alpha` storage entry). | +| [`InsufficientBalance`](/docs/errors/chain/InsufficientBalance) | [`insufficient_balance`](/docs/errors/insufficient-balance) | The caller's spendable balance is below what the operation needs, whether a plain transfer, a crowdloan deposit or contribution, or a swap. Compare the account's free balance in `System.Account` (net of holds, freezes, and fees) against the amount being moved. | +| [`InsufficientInputAmount`](/docs/errors/chain/InsufficientInputAmount) | [`invalid_argument`](/docs/errors/invalid-argument) | Declared for swap inputs too small to execute, but no current code path raises it since the user-liquidity code was removed. If seen on an older runtime, check that the swap input amount is nonzero and large enough to produce output. | +| [`InsufficientLiquidity`](/docs/errors/chain/InsufficientLiquidity) | [`insufficient_liquidity`](/docs/errors/insufficient-liquidity) | The pool cannot absorb the operation: the swap simulation failed, reserves are smaller than the payout, or the amount exceeds the pool's supported input. Check the subnet pool reserves `SubnetTAO`, `SubnetAlphaIn` and `SubnetAlphaOut` against the amount. | +| [`InsufficientStakeForLock`](/docs/errors/chain/InsufficientStakeForLock) | [`insufficient_balance`](/docs/errors/insufficient-balance) | The requested lock amount exceeds the coldkey's total alpha stake on that subnet (existing locked mass included). Compare the amount against the coldkey's stake on the netuid, e.g. via `btcli stake list`, and lock less or add stake first. | +| [`InsufficientTaoBalance`](/docs/errors/chain/InsufficientTaoBalance) | [`insufficient_balance`](/docs/errors/insufficient-balance) | The coldkey's free TAO balance is below the amount a TAO-side operation needs: a transfer between coldkeys, a burn or recycle, or a subnet-registration lock. Check the coldkey's balance with `btcli wallet balance` against the amount being moved. | +| [`InvalidCallFlags`](/docs/errors/chain/InvalidCallFlags) | [`invalid_argument`](/docs/errors/invalid-argument) | The flags bitmask given to `seal_call` or `seal_delegate_call` contains an unknown or disallowed combination; delegate calls accept only a restricted flag set. Check the flags argument against the supported `CallFlags` bit values. | +| [`InvalidChainId`](/docs/errors/chain/InvalidChainId) | [`invalid_argument`](/docs/errors/invalid-argument) | The EIP-155 chain id encoded in the signed transaction does not match this chain's configured chain id. Compare the transaction's chain id with the value returned by `eth_chainId` and re-sign the transaction. | +| [`InvalidChild`](/docs/errors/chain/InvalidChild) | [`invalid_argument`](/docs/errors/invalid-argument) | The children or parents list includes the pivot hotkey itself (a self-loop), including during a hotkey swap when the new hotkey is already a child or parent of the old one. Check the `children` argument and `ChildKeys`/`ParentKeys` for the hotkeys involved. | +| [`InvalidChildkeyTake`](/docs/errors/chain/InvalidChildkeyTake) | [`invalid_argument`](/docs/errors/invalid-argument) | The childkey take is outside the allowed range for the subnet: below the effective minimum or above `MaxChildkeyTake`. Query `MinChildkeyTake` and `MaxChildkeyTake` and pick a `take` value within those bounds. | +| [`InvalidCrowdloanId`](/docs/errors/chain/InvalidCrowdloanId) | [`not_found`](/docs/errors/not-found) | No crowdloan exists in the `Crowdloans` storage map for the given `crowdloan_id`; it was never created or has been dissolved. Check `Crowdloans` for the id and `NextCrowdloanId` for the range of ids ever issued. | +| [`InvalidDerivedAccount`](/docs/errors/chain/InvalidDerivedAccount) | [`invalid_argument`](/docs/errors/invalid-argument) | Deriving the sub-account for `as_derivative` failed to decode into a valid account id from the (caller, index) entropy. Check the `index` argument and the caller account used for derivation. | +| [`InvalidDerivedAccountId`](/docs/errors/chain/InvalidDerivedAccountId) | [`invalid_argument`](/docs/errors/invalid-argument) | Deriving the pure proxy account id from the provided entropy failed to decode into a valid account. Check the spawner, `proxy_type`, and `index` arguments used with `create_pure` (or the equivalent lookup when destroying one). | +| [`InvalidDifficulty`](/docs/errors/chain/InvalidDifficulty) | [`invalid_argument`](/docs/errors/invalid-argument) | The submitted proof-of-work hash does not meet the required difficulty (the faucet uses a fixed 1,000,000; PoW registration uses the subnet's difficulty). Check the `Difficulty` storage for the netuid and regenerate work against the current block. | +| [`InvalidEquivocationProof`](/docs/errors/chain/InvalidEquivocationProof) | [`invalid_argument`](/docs/errors/invalid-argument) | The submitted GRANDPA equivocation proof does not demonstrate a real double-vote: the two votes may be identical, from different rounds or set ids, or badly signed. Verify the proof contains two distinct votes by the same authority in the same round and authority set. | +| [`InvalidFinalizationConfig`](/docs/errors/chain/InvalidFinalizationConfig) | [`invalid_argument`](/docs/errors/invalid-argument) | Exactly one of `call` or `target_address` must be set, but both or neither were provided to `create`, or the stored crowdloan holds an inconsistent pair at `finalize` time. Check those two fields on the `create` arguments or the `Crowdloans` entry. | +| [`InvalidIdentity`](/docs/errors/chain/InvalidIdentity) | [`invalid_argument`](/docs/errors/invalid-argument) | The submitted coldkey or subnet identity failed validation, typically a field exceeding its allowed byte length or malformed data. Check each identity field's length against the limits enforced by the chain before calling set_identity or set_subnet_identity. | +| [`InvalidIpAddress`](/docs/errors/chain/InvalidIpAddress) | [`invalid_argument`](/docs/errors/invalid-argument) | The `ip` argument to serve_axon or serve_prometheus is not a valid address for the declared `ip_type` (prometheus additionally rejects the zero address). Verify the IP encodes correctly as IPv4 or IPv6 and matches the `ip_type` passed. | +| [`InvalidIpType`](/docs/errors/chain/InvalidIpType) | [`invalid_argument`](/docs/errors/invalid-argument) | The `ip_type` argument to serve_axon or serve_prometheus is not 4 or 6. Check the value being sent by the miner or client; only IPv4 (4) and IPv6 (6) are accepted. | +| [`InvalidKeyOwnershipProof`](/docs/errors/chain/InvalidKeyOwnershipProof) | [`invalid_argument`](/docs/errors/invalid-argument) | The key ownership proof does not establish that the offending GRANDPA key belonged to the claimed validator at that session. Regenerate the proof via the `generate_key_ownership_proof` runtime API for the exact set id and authority id in the report. | +| [`InvalidLeaseBeneficiary`](/docs/errors/chain/InvalidLeaseBeneficiary) | [`invalid_argument`](/docs/errors/invalid-argument) | The account registering a leased network is not the creator of the crowdloan currently being finalized. Check the crowdloan's `creator` field in the crowdloan pallet storage and submit the call from that coldkey. | +| [`InvalidLiquidityValue`](/docs/errors/chain/InvalidLiquidityValue) | [`invalid_argument`](/docs/errors/invalid-argument) | Legacy error from the removed V3 user-liquidity code, raised when an added or removed liquidity amount was below the pallet's `MinimumLiquidity`. Not raised on current runtimes; on older ones compare the `liquidity` argument to `MinimumLiquidity`. | +| [`InvalidNonce`](/docs/errors/chain/InvalidNonce) | [`invalid_argument`](/docs/errors/invalid-argument) | The transaction nonce does not match the sender's current account nonce, being either too low (already used) or too high. Compare the transaction's `nonce` with the sender's on-chain nonce from `eth_getTransactionCount`. | +| [`InvalidNumRootClaim`](/docs/errors/chain/InvalidNumRootClaim) | [`invalid_argument`](/docs/errors/invalid-argument) | The value passed to sudo_set_num_root_claims exceeds the compile-time maximum number of root claims. Check the `new_value` argument against the chain's `MAX_NUM_ROOT_CLAIMS` constant and pass a smaller number. | +| [`InvalidOrigin`](/docs/errors/chain/InvalidOrigin) | [`not_authorized`](/docs/errors/not-authorized) | The caller is not the crowdloan's creator, which is required for `finalize`, `refund`, `dissolve`, and the update calls. Compare the signing account with the `creator` field of the `Crowdloans` entry for the `crowdloan_id`. | +| [`InvalidPort`](/docs/errors/chain/InvalidPort) | [`invalid_argument`](/docs/errors/invalid-argument) | The `port` argument to serve_axon or serve_prometheus is 0, which is rejected. Check the miner or client axon configuration and serve on a non-zero port. | +| [`InvalidRecoveredPublicKey`](/docs/errors/chain/InvalidRecoveredPublicKey) | [`invalid_argument`](/docs/errors/invalid-argument) | The EVM key association signature recovered to a public key whose keccak hash does not equal the supplied `evm_key`. Verify the signature was produced by the claimed EVM key over the hotkey plus block hash message (EIP-191 format). | +| [`InvalidRevealCommitHashNotMatch`](/docs/errors/chain/InvalidRevealCommitHashNotMatch) | [`invalid_argument`](/docs/errors/invalid-argument) | The revealed uids, values, salt and version_key hash to a value that matches none of the hotkey's pending non-expired commits. Check that the reveal parameters and salt exactly match what was committed, and inspect `WeightCommits` for the hotkey and netuid. | +| [`InvalidRevealRound`](/docs/errors/chain/InvalidRevealRound) | [`expired`](/docs/errors/expired) | A timelocked weights commit specified a `reveal_round` older than the latest stored DRAND round, so it could be decrypted immediately. Query the drand pallet's `LastStoredRound` and commit with a future round number. | +| [`InvalidRootClaimThreshold`](/docs/errors/chain/InvalidRootClaimThreshold) | [`invalid_argument`](/docs/errors/invalid-argument) | The value passed to set the root claim threshold exceeds the chain's maximum allowed threshold. Check the `new_value` argument against the `MAX_ROOT_CLAIM_THRESHOLD` constant and the current `RootClaimableThreshold` for the netuid. | +| [`InvalidRoundNumber`](/docs/errors/chain/InvalidRoundNumber) | [`invalid_argument`](/docs/errors/invalid-argument) | A submitted drand pulse has an unacceptable round: the first pulse ever stored must have a round greater than zero, and each later pulse must be exactly `LastStoredRound` plus one. Compare the pulse round against `LastStoredRound`. | +| [`InvalidSchedule`](/docs/errors/chain/InvalidSchedule) | [`invalid_argument`](/docs/errors/invalid-argument) | The pallet's schedule is misconfigured, e.g. a zero weight or zero `ref_time_by_fuel` for a basic operation, making gas conversion impossible. This is a runtime configuration issue; inspect the `Schedule` constant rather than call arguments. | +| [`InvalidSeal`](/docs/errors/chain/InvalidSeal) | [`invalid_argument`](/docs/errors/invalid-argument) | The seal hash recomputed from the supplied `block_number`, `nonce` and key does not equal the submitted `work`. Verify the PoW solver built the seal for the same key and block it submits, and that the work bytes were not corrupted in transit. | +| [`InvalidSignature`](/docs/errors/chain/InvalidSignature) | [`invalid_argument`](/docs/errors/invalid-argument) | Signature verification failed: the sender of an Ethereum or EVM transaction could not be recovered from its signature, or a limit order's Sr25519 signature does not match the order payload and signer. Check the signing key, chain id, and the exact payload bytes that were signed. | +| [`InvalidSpecName`](/docs/errors/chain/InvalidSpecName) | [`invalid_argument`](/docs/errors/invalid-argument) | The new runtime's `spec_name` does not match the current runtime, so `set_code` refuses the upgrade. Check the `RuntimeVersion` embedded in the new wasm and ensure the spec name is identical to the chain's current one. | +| [`InvalidSubnetNumber`](/docs/errors/chain/InvalidSubnetNumber) | [`invalid_argument`](/docs/errors/invalid-argument) | A root-claim call passed an empty subnet set or more subnets than the per-call maximum (claim_root, or KeepSubnets in set_root_claim_type). Check the `subnets` argument is non-empty and within the `MAX_SUBNET_CLAIMS` limit. | +| [`InvalidTickRange`](/docs/errors/chain/InvalidTickRange) | [`invalid_argument`](/docs/errors/invalid-argument) | Legacy error from the removed V3 user-liquidity code, raised when `tick_low` was not below `tick_high` or a tick failed to convert to a sqrt price. Not raised on current runtimes; check the tick range arguments on older ones. | +| [`InvalidValue`](/docs/errors/chain/InvalidValue) | [`invalid_argument`](/docs/errors/invalid-argument) | A generic out-of-range parameter on an admin or sudo call, e.g. mechanism counts, emission splits summing away from 65535, max UIDs or take bounds. Check the specific argument against the min/max storage items the extrinsic validates (e.g. `MinAllowedUids`, `MaxMechanismCount`). | +| [`InvalidVotingPowerEmaAlpha`](/docs/errors/chain/InvalidVotingPowerEmaAlpha) | [`invalid_argument`](/docs/errors/invalid-argument) | The alpha passed to set the voting power EMA exceeds 10^18, which represents 1.0. Check the `alpha` argument to sudo_set_voting_power_ema_alpha; it must be at most 10^18, and the current value is in `VotingPowerEmaAlpha` per netuid. | +| [`InvalidWorkBlock`](/docs/errors/chain/InvalidWorkBlock) | [`invalid_argument`](/docs/errors/invalid-argument) | The `block_number` in the proof-of-work submission is in the future or more than 3 blocks old, so the work is stale. Compare the submitted block number with the current chain height and regenerate the PoW against a fresh block. | +| [`IssuanceDeactivated`](/docs/errors/chain/IssuanceDeactivated) | [`disabled`](/docs/errors/disabled) | Total issuance cannot be adjusted because issuance has already been deactivated. Check the Balances `InactiveIssuance` state before calling `force_adjust_total_issuance` again. | +| [`LeaseCannotEndInThePast`](/docs/errors/chain/LeaseCannotEndInThePast) | [`invalid_argument`](/docs/errors/invalid-argument) | The `end_block` supplied when registering a leased network is not after the current block. Check the current chain height and pass an `end_block` in the future, or omit it for a perpetual lease. | +| [`LeaseDoesNotExist`](/docs/errors/chain/LeaseDoesNotExist) | [`not_found`](/docs/errors/not-found) | The `lease_id` argument does not correspond to any stored lease. Query the `SubnetLeases` storage map to confirm the lease id and whether it was already terminated. | +| [`LeaseHasNoEndBlock`](/docs/errors/chain/LeaseHasNoEndBlock) | [`invalid_argument`](/docs/errors/invalid-argument) | The lease being terminated is perpetual (its `end_block` is None), so it can never be ended this way. Check the lease's `end_block` field in `SubnetLeases` for the given lease id. | +| [`LeaseHasNotEnded`](/docs/errors/chain/LeaseHasNotEnded) | [`too_early`](/docs/errors/too-early) | The lease termination was attempted before the lease's `end_block` was reached. Compare the current block height with the `end_block` stored in `SubnetLeases` for the lease id and retry after it passes. | +| [`LeaseNetuidNotFound`](/docs/errors/chain/LeaseNetuidNotFound) | [`not_found`](/docs/errors/not-found) | After registering the leased network, no subnet owned by the lease's derived coldkey could be found, so the netuid lookup failed. Inspect `SubnetOwner` entries for the lease coldkey; this usually indicates the registration did not complete. | +| [`LimitOrdersDisabled`](/docs/errors/chain/LimitOrdersDisabled) | [`disabled`](/docs/errors/disabled) | Order execution was attempted while the pallet's global switch is off. Check the `LimitOrdersEnabled` storage value; root must call `set_pallet_status` with true to enable the pallet. | +| [`LiquidAlphaDisabled`](/docs/errors/chain/LiquidAlphaDisabled) | [`disabled`](/docs/errors/disabled) | Setting `alpha_low`/`alpha_high` was attempted while liquid alpha is disabled on the subnet. Check the `LiquidAlphaOn` hyperparameter (`btcli sudo get --netuid `) and have the subnet owner enable liquid alpha first. | +| [`LiquidityRestrictions`](/docs/errors/chain/LiquidityRestrictions) | [`insufficient_balance`](/docs/errors/insufficient-balance) | The withdrawal is blocked by locks or freezes on the account even though the raw balance looks sufficient. Check the account's `Locks` and `Freezes` entries and compare the frozen amount against what would remain after the withdrawal. | +| [`LockHotkeyMismatch`](/docs/errors/chain/LockHotkeyMismatch) | [`invalid_argument`](/docs/errors/invalid-argument) | The coldkey already has a conviction lock on this subnet bound to a different hotkey, and locks for one coldkey per subnet must target a single hotkey. Check the existing lock's hotkey in the lock storage for the coldkey and netuid, or move the lock first. | +| [`LockIdOverFlow`](/docs/errors/chain/LockIdOverFlow) | [`limit_exceeded`](/docs/errors/limit-exceeded) | The global network-registration lock id counter reached its u32 maximum while queueing a subnet registration, so no new lock could be created. Check the `NetworkRegistrationLockId` storage value; this indicates lock id exhaustion, not a balance problem. | +| [`MaxAllowedUIdsLessThanCurrentUIds`](/docs/errors/chain/MaxAllowedUIdsLessThanCurrentUIds) | [`invalid_argument`](/docs/errors/invalid-argument) | `sudo_set_max_allowed_uids` was given a value below the number of neurons already registered on the subnet. Compare the `max_allowed_uids` argument against `SubnetworkN` for that netuid. | +| [`MaxAllowedUidsGreaterThanDefaultMaxAllowedUids`](/docs/errors/chain/MaxAllowedUidsGreaterThanDefaultMaxAllowedUids) | [`invalid_argument`](/docs/errors/invalid-argument) | `sudo_set_max_allowed_uids` was given a value above the chain-wide ceiling. Compare the `max_allowed_uids` argument against the `DefaultMaxAllowedUids` storage value. | +| [`MaxAllowedUidsLessThanMinAllowedUids`](/docs/errors/chain/MaxAllowedUidsLessThanMinAllowedUids) | [`invalid_argument`](/docs/errors/invalid-argument) | `sudo_set_max_allowed_uids` was given a value below the subnet's configured minimum. Compare the `max_allowed_uids` argument against `MinAllowedUids` for that netuid. | +| [`MaxCallDepthReached`](/docs/errors/chain/MaxCallDepthReached) | [`limit_exceeded`](/docs/errors/limit-exceeded) | A nested contract call would exceed the maximum call stack depth defined in the pallet schedule. Inspect the cross-contract call chain for deep or unbounded recursion and flatten it or raise the configured depth. | +| [`MaxContributionReached`](/docs/errors/chain/MaxContributionReached) | [`limit_exceeded`](/docs/errors/limit-exceeded) | The contributor's cumulative contribution already equals the crowdloan's per-contributor cap, so further contributions are rejected. Compare their `Contributions` entry with the `MaxContributions` value for the `crowdloan_id`. | +| [`MaxContributorsReached`](/docs/errors/chain/MaxContributorsReached) | [`limit_exceeded`](/docs/errors/limit-exceeded) | The crowdloan already has the maximum number of distinct contributors, so accounts without an existing contribution are rejected. Compare the `contributors_count` field of the `Crowdloans` entry with the `MaxContributors` pallet constant. | +| [`MaxDelegateDependenciesReached`](/docs/errors/chain/MaxDelegateDependenciesReached) | [`limit_exceeded`](/docs/errors/limit-exceeded) | `lock_delegate_dependency` failed because the contract already holds the maximum number of delegate dependencies allowed by the runtime. Check the `MaxDelegateDependencies` config value and unlock unused dependencies first. | +| [`MaxValidatorsLargerThanMaxUIds`](/docs/errors/chain/MaxValidatorsLargerThanMaxUIds) | [`invalid_argument`](/docs/errors/invalid-argument) | `sudo_set_max_allowed_validators` was given a value exceeding the subnet's UID capacity. Compare the `max_allowed_validators` argument against `MaxAllowedUids` for that netuid. | +| [`MaxWeightExceeded`](/docs/errors/chain/MaxWeightExceeded) | [`limit_exceeded`](/docs/errors/limit-exceeded) | After normalization, one of the submitted weights exceeds the subnet's maximum weight limit (self-weight is exempt). Check the `MaxWeightsLimit` hyperparameter (`btcli sudo get --netuid `) and flatten the weight vector before setting. | +| [`MaxWeightTooLow`](/docs/errors/chain/MaxWeightTooLow) | [`invalid_argument`](/docs/errors/invalid-argument) | The `max_weight` argument supplied with the final multisig approval is lower than the actual weight of the call being dispatched. Compute the call's real dispatch weight and pass a `max_weight` at least that large to `as_multi`. | +| [`MaximumContributionTooLow`](/docs/errors/chain/MaximumContributionTooLow) | [`invalid_argument`](/docs/errors/invalid-argument) | The `new_max_contribution` passed to `set_max_contribution` is below the crowdloan's `min_contribution` or below the creator's existing contribution. Check both against the `Crowdloans` entry and the creator's `Contributions` entry. | +| [`MechanismDoesNotExist`](/docs/errors/chain/MechanismDoesNotExist) | [`subnet_not_exists`](/docs/errors/subnet-not-exists) | The target subnet or its sub-mechanism does not exist: the netuid is unknown, the mechanism index is at or above `MechanismCountCurrent`, or a non-dynamic `mechid` was requested. Check the netuid with `btcli subnets list` and the mechanism count for that subnet. | +| [`MigrationInProgress`](/docs/errors/chain/MigrationInProgress) | [`too_early`](/docs/errors/too-early) | A multi-block storage migration of the contracts pallet is still running, so other extrinsics of the pallet are rejected until it completes. Check the migration status in storage and retry once done, or submit `migrate` calls to advance it. | +| [`MinAllowedUidsGreaterThanCurrentUids`](/docs/errors/chain/MinAllowedUidsGreaterThanCurrentUids) | [`invalid_argument`](/docs/errors/invalid-argument) | `sudo_set_min_allowed_uids` was given a value not strictly below the number of neurons currently registered on the subnet. Compare the `min_allowed_uids` argument against `SubnetworkN` for that netuid. | +| [`MinAllowedUidsGreaterThanMaxAllowedUids`](/docs/errors/chain/MinAllowedUidsGreaterThanMaxAllowedUids) | [`invalid_argument`](/docs/errors/invalid-argument) | `sudo_set_min_allowed_uids` was given a value not strictly below the subnet's maximum UID capacity. Compare the `min_allowed_uids` argument against `MaxAllowedUids` for that netuid. | +| [`MinimumContributionTooHigh`](/docs/errors/chain/MinimumContributionTooHigh) | [`invalid_argument`](/docs/errors/invalid-argument) | The `new_min_contribution` passed to `update_min_contribution` exceeds the crowdloan's configured per-contributor maximum. Compare it with the `MaxContributions` entry for the `crowdloan_id`. | +| [`MinimumContributionTooLow`](/docs/errors/chain/MinimumContributionTooLow) | [`invalid_argument`](/docs/errors/invalid-argument) | The minimum contribution given to `create` or `update_min_contribution` is below the chain-wide floor. Check the `AbsoluteMinimumContribution` pallet constant and raise the `min_contribution` argument to at least that value. | +| [`MinimumThreshold`](/docs/errors/chain/MinimumThreshold) | [`invalid_argument`](/docs/errors/invalid-argument) | The multisig `threshold` argument was below 2, which these calls do not accept. Pass a threshold of 2 or more, or use `as_multi_threshold_1` for the single-approval case. | +| [`MultiBlockMigrationsOngoing`](/docs/errors/chain/MultiBlockMigrationsOngoing) | [`too_early`](/docs/errors/too-early) | Runtime code replacement is blocked while a multi-block migration is still executing. Wait for the ongoing migrations to complete (check the multi-block migrations cursor) before retrying `set_code` or the authorized upgrade. | +| [`Named`](/docs/errors/chain/Named) | [`invalid_argument`](/docs/errors/invalid-argument) | An unnamed scheduler function was used on a task that was scheduled with a name. Use the named variants (`cancel_named`, `schedule_named`) with the task's id, which you can find via the `Lookup` storage map. | +| [`NeedWaitingMoreBlocksToStarCall`](/docs/errors/chain/NeedWaitingMoreBlocksToStarCall) | [`too_early`](/docs/errors/too-early) | The subnet owner called start_call before enough blocks had passed since the subnet was registered. Compare the current block with `NetworkRegisteredAt` for the netuid plus the start-call delay and retry once the window opens. | +| [`NegativeSigmoidSteepness`](/docs/errors/chain/NegativeSigmoidSteepness) | [`invalid_argument`](/docs/errors/invalid-argument) | A non-root caller (subnet owner) passed a negative value to `sudo_set_alpha_sigmoid_steepness`; negative steepness values are reserved for the root origin. Use a non-negative `steepness` or submit the call as root. | +| [`NetworkDissolveAlreadyQueued`](/docs/errors/chain/NetworkDissolveAlreadyQueued) | [`already_exists`](/docs/errors/already-exists) | The subnet is already in the dissolve cleanup queue, so it cannot be queued for dissolution again. Check the `DissolveCleanupQueue` storage value for the netuid before submitting another dissolve request. | +| [`NetworkTxRateLimitExceeded`](/docs/errors/chain/NetworkTxRateLimitExceeded) | [`rate_limited`](/docs/errors/rate-limited) | The coldkey attempted register_network again before the network registration rate limit elapsed since its previous registration. Check the coldkey's last register-network block against the `NetworkRateLimit` storage value and wait the remaining blocks. | +| [`NeuronNoValidatorPermit`](/docs/errors/chain/NeuronNoValidatorPermit) | [`not_authorized`](/docs/errors/not-authorized) | The hotkey tried to set weights on other neurons without holding a validator permit on that subnet. Check `ValidatorPermit` for the neuron's uid (e.g. via the metagraph or `btcli subnets metagraph`) and whether its stake ranks it as a validator. | +| [`NewColdKeyIsHotkey`](/docs/errors/chain/NewColdKeyIsHotkey) | [`invalid_argument`](/docs/errors/invalid-argument) | The proposed new coldkey in a coldkey swap is already an existing hotkey account, which is not allowed. Check the `Owner` storage for the candidate key to confirm it is not registered as a hotkey, and pick a fresh coldkey. | +| [`NewHotKeyIsSameWithOld`](/docs/errors/chain/NewHotKeyIsSameWithOld) | [`invalid_argument`](/docs/errors/invalid-argument) | swap_hotkey was called with `new_hotkey` equal to the current hotkey, so there is nothing to swap. Check the extrinsic arguments and supply a different destination hotkey. | +| [`NewHotKeyNotCleanForRootSwap`](/docs/errors/chain/NewHotKeyNotCleanForRootSwap) | [`invalid_argument`](/docs/errors/invalid-argument) | The destination hotkey has pending root claimable dividends, non-zero root stake, or root-claimed history, so root accounting cannot merge safely. Check `RootClaimable` and root-subnet stake for the new hotkey; claim or clear them, or use a fresh hotkey. | +| [`NoApprovalsNeeded`](/docs/errors/chain/NoApprovalsNeeded) | [`invalid_argument`](/docs/errors/invalid-argument) | The multisig call does not need any more approvals; it is only waiting for final execution with the full call data. Instead of another `approve_as_multi`, submit `as_multi` with the complete call to dispatch it. | +| [`NoChainExtension`](/docs/errors/chain/NoChainExtension) | [`disabled`](/docs/errors/disabled) | The contract invoked `call_chain_extension` but this runtime registers no chain extension; such code is normally rejected at upload. Check that the target chain provides the chain extension the contract was built against. | +| [`NoContribution`](/docs/errors/chain/NoContribution) | [`not_found`](/docs/errors/not-found) | The account has no contribution recorded for this crowdloan, so `withdraw` has nothing to pay out (or `dissolve` finds no creator contribution). Check the `Contributions` double map under the `crowdloan_id` for the account in question. | +| [`NoDeposit`](/docs/errors/chain/NoDeposit) | [`not_found`](/docs/errors/not-found) | No safe-mode deposit exists for the given account and block combination, so nothing can be released or slashed. Check the `Deposits` storage map for an entry under that account and deposit block. | +| [`NoExistingLock`](/docs/errors/chain/NoExistingLock) | [`not_found`](/docs/errors/not-found) | move_lock was called but no conviction lock exists for the signing coldkey on that subnet. Check the lock storage for the coldkey and netuid, and create a lock before attempting to move it to another hotkey. | +| [`NoMigrationPerformed`](/docs/errors/chain/NoMigrationPerformed) | [`invalid_argument`](/docs/errors/invalid-argument) | A `migrate` call ran but no migration step executed, either because no migration is pending or the supplied `weight_limit` is too small for one step. Check the in-progress migration status and increase the weight limit argument. | +| [`NoNeuronIdAvailable`](/docs/errors/chain/NoNeuronIdAvailable) | [`limit_exceeded`](/docs/errors/limit-exceeded) | Registration could not obtain a uid: the subnet's `MaxAllowedUids` is 0, or the subnet is full and every neuron is immune from pruning. Check `SubnetworkN` versus `MaxAllowedUids` for the netuid and retry after immunity periods expire. | +| [`NoPermission`](/docs/errors/chain/NoPermission) | [`not_authorized`](/docs/errors/not-authorized) | The proxy pallet refused the action: the proxied call could escalate privileges, or the caller lacks authority over the pure proxy (e.g. `kill_pure` by a non-spawner). Check the proxy type's call filter and the original `create_pure` arguments. | +| [`NoSelfProxy`](/docs/errors/chain/NoSelfProxy) | [`invalid_argument`](/docs/errors/invalid-argument) | An account attempted to register itself as its own proxy, which is not allowed. Check the `delegate` argument to `add_proxy` and ensure it differs from the calling (delegator) account. | +| [`NoTimepoint`](/docs/errors/chain/NoTimepoint) | [`invalid_argument`](/docs/errors/invalid-argument) | No timepoint was supplied but this multisig operation is already underway, so the approval cannot be matched to it. Read the operation's `when` field from the `Multisigs` entry for the call hash and pass that height and index as `maybe_timepoint`. | +| [`NoWeightsCommitFound`](/docs/errors/chain/NoWeightsCommitFound) | [`not_found`](/docs/errors/not-found) | A weights reveal was submitted but no pending (non-expired) commit exists for the hotkey and netuid, possibly because it already expired. Query the `WeightCommits` storage map for the hotkey and check the commit hasn't passed the reveal window. | +| [`NonAssociatedColdKey`](/docs/errors/chain/NonAssociatedColdKey) | [`not_authorized`](/docs/errors/not-authorized) | The signing coldkey does not own the hotkey it is trying to operate on (stake, swap, serve, children or take changes). Check the `Owner` storage entry for the hotkey, e.g. via `btcli wallet overview`, and sign with the coldkey that registered it. | +| [`NonDefaultComposite`](/docs/errors/chain/NonDefaultComposite) | [`invalid_argument`](/docs/errors/invalid-argument) | The account cannot be killed because its composite account data is not in the default state. Check the account's `System.Account` entry; all balance and data fields must be default before the account can be removed this way. | +| [`NonZeroRefCount`](/docs/errors/chain/NonZeroRefCount) | [`invalid_argument`](/docs/errors/invalid-argument) | The account cannot be purged because other pallets still reference it. Check the `consumers`, `providers`, and `sufficients` counters in the account's `System.Account` record; all references must be released first. | +| [`NoneValue`](/docs/errors/chain/NoneValue) | [`not_found`](/docs/errors/not-found) | Template leftover in the drand pallet meaning a storage value was read before ever being set; no current code path raises it. If seen, inspect the drand pallet's storage items for missing initialization. | +| [`NotAllowed`](/docs/errors/chain/NotAllowed) | [`not_authorized`](/docs/errors/not-authorized) | Contract deployment was blocked because the source address is not in the `WhitelistedCreators` list while the whitelist check is enabled. Check the deployer address against `WhitelistedCreators` and the `DisableWhitelistCheck` storage value. | +| [`NotAuthorized`](/docs/errors/chain/NotAuthorized) | [`not_authorized`](/docs/errors/not-authorized) | The caller is not permitted to manage this preimage; unnoting or unrequesting requires the pallet's manager origin or the account that originally deposited it. Check which origin noted or requested the preimage and use that origin or the configured `ManagerOrigin`. | +| [`NotConfigured`](/docs/errors/chain/NotConfigured) | [`disabled`](/docs/errors/disabled) | The permissionless safe-mode operation is disabled because its config option is `None`: `EnterDepositAmount` for `enter`, `ExtendDepositAmount` for `extend`, or `ReleaseDelay` for `release_deposit`. Check the runtime config or use the root-only force variants. | +| [`NotEnoughAlphaOutToRecycle`](/docs/errors/chain/NotEnoughAlphaOutToRecycle) | [`insufficient_liquidity`](/docs/errors/insufficient-liquidity) | A recycle or burn of alpha requested more than the subnet's outstanding alpha supply. Compare the amount against the `SubnetAlphaOut` storage value for the netuid and reduce the recycle amount. | +| [`NotEnoughBalanceToPaySwapColdKey`](/docs/errors/chain/NotEnoughBalanceToPaySwapColdKey) | [`insufficient_balance`](/docs/errors/insufficient-balance) | The coldkey's free TAO balance cannot cover the coldkey swap cost, which is recycled when the swap executes. Check the balance with `btcli wallet balance` against the swap cost and top up before scheduling the swap. | +| [`NotEnoughBalanceToPaySwapHotKey`](/docs/errors/chain/NotEnoughBalanceToPaySwapHotKey) | [`insufficient_balance`](/docs/errors/insufficient-balance) | The coldkey's free TAO balance is below the hotkey swap cost (a per-subnet cost applies when swapping on a single netuid). Check `btcli wallet balance` against the key swap cost and fund the coldkey before retrying. | +| [`NotEnoughBalanceToStake`](/docs/errors/chain/NotEnoughBalanceToStake) | [`insufficient_balance`](/docs/errors/insufficient-balance) | The coldkey's free balance is less than the TAO required, either the stake amount in add_stake or the burn cost of a registration. Check `btcli wallet balance` against the amount or the current registration burn (`Burn` storage for the netuid). | +| [`NotEnoughStake`](/docs/errors/chain/NotEnoughStake) | [`insufficient_balance`](/docs/errors/insufficient-balance) | The caller's hotkey holds less stake than the action requires; a generic insufficient-stake failure on staking-related calls. Check the hotkey's stake on the relevant subnet, e.g. `btcli stake list`, against the amount the extrinsic needs. | +| [`NotEnoughStakeToSetChildkeys`](/docs/errors/chain/NotEnoughStakeToSetChildkeys) | [`insufficient_balance`](/docs/errors/insufficient-balance) | Raised by `set_children` when the parent hotkey's total stake is below `StakeThreshold` and it is not the subnet owner hotkey. Compare the hotkey's total stake (`btcli stake list`) against the `StakeThreshold` storage value. | +| [`NotEnoughStakeToSetWeights`](/docs/errors/chain/NotEnoughStakeToSetWeights) | [`insufficient_balance`](/docs/errors/insufficient-balance) | Setting or committing weights failed because the hotkey's stake weight on the subnet is below `StakeThreshold` (the weights-min-stake floor); the subnet owner hotkey is exempt. Check the hotkey's stake on that netuid against `StakeThreshold`. | +| [`NotEnoughStakeToWithdraw`](/docs/errors/chain/NotEnoughStakeToWithdraw) | [`insufficient_balance`](/docs/errors/insufficient-balance) | An unstake, stake move, swap, or transfer requested more alpha than the hotkey-coldkey pair holds on that subnet. Compare the requested amount against the pair's current stake on the netuid (`btcli stake list` or the `Alpha` storage). | +| [`NotFound`](/docs/errors/chain/NotFound) | [`not_found`](/docs/errors/not-found) | The referenced item does not exist in storage: no multisig operation for that call hash in `Multisigs`, no scheduled task at that slot or name in `Agenda`/`Lookup`, or no matching proxy registration in `Proxies`. Verify the identifier against current chain state. | +| [`NotNoted`](/docs/errors/chain/NotNoted) | [`not_found`](/docs/errors/not-found) | The preimage cannot be unnoted because no preimage was ever noted for this hash. Check `RequestStatusFor` (or legacy `StatusFor`) for the hash to confirm what, if anything, is stored. | +| [`NotOwner`](/docs/errors/chain/NotOwner) | [`not_authorized`](/docs/errors/not-authorized) | Only the account that opened the multisig operation (its depositor) may cancel it or adjust its deposit. Compare the sender against the `depositor` field stored in the `Multisigs` entry for this call hash. | +| [`NotPermittedOnRootSubnet`](/docs/errors/chain/NotPermittedOnRootSubnet) | [`invalid_argument`](/docs/errors/invalid-argument) | An admin-utils call that only applies to regular subnets (burn half-life, burn increase multiplier, owner-cut flags, or the subnet emission toggle) was targeted at the root network. Check that the `netuid` argument is not the root netuid 0. | +| [`NotProxy`](/docs/errors/chain/NotProxy) | [`not_authorized`](/docs/errors/not-authorized) | The sender is not registered as a proxy for the account it tried to act for. Check the `Proxies` entry of the `real` account and confirm the sender appears there with a proxy type and delay compatible with the call. | +| [`NotReadyToDissolve`](/docs/errors/chain/NotReadyToDissolve) | [`too_early`](/docs/errors/too-early) | `dissolve` was called while outside contributions remain: the crowdloan's `raised` amount still exceeds the creator's own contribution. Call `refund` until only the creator's `Contributions` entry remains and equals `raised` in the `Crowdloans` record. | +| [`NotRequested`](/docs/errors/chain/NotRequested) | [`not_found`](/docs/errors/not-found) | The preimage request cannot be removed because there are no outstanding requests for this hash. Check the request status for the hash in `RequestStatusFor` before calling `unrequest_preimage`. | +| [`NotRootSubnet`](/docs/errors/chain/NotRootSubnet) | [`invalid_argument`](/docs/errors/invalid-argument) | A call that only operates on the root network, such as setting root network weights, was given a non-root netuid. Check the netuid argument; root operations must target netuid 0. | +| [`NotSubnetOwner`](/docs/errors/chain/NotSubnetOwner) | [`not_authorized`](/docs/errors/not-authorized) | The signing coldkey is not the recorded owner of the subnet it tried to administer (e.g. setting subnet identity or owner-only hyperparameters). Compare the caller against the `SubnetOwner` storage entry for that netuid. | +| [`NothingAuthorized`](/docs/errors/chain/NothingAuthorized) | [`not_found`](/docs/errors/not-found) | No code upgrade has been authorized, so `apply_authorized_upgrade` has nothing to apply. Check the `AuthorizedUpgrade` storage item in System; an authorization must be recorded before applying the new code. | +| [`OrderAlreadyProcessed`](/docs/errors/chain/OrderAlreadyProcessed) | [`already_exists`](/docs/errors/already-exists) | The order id already has a terminal status: execution found it fulfilled, or `cancel_order` found any existing status for it. Check the `Orders` storage map under the blake2-256 hash of the SCALE-encoded `VersionedOrder`. | +| [`OrderCancelled`](/docs/errors/chain/OrderCancelled) | [`expired`](/docs/errors/expired) | The order was previously cancelled via `cancel_order` and can never be executed. Check the `Orders` storage entry for the order id; a `Cancelled` status is terminal, so the signer must sign and submit a fresh order. | +| [`OrderExpired`](/docs/errors/chain/OrderExpired) | [`expired`](/docs/errors/expired) | The current chain time is past the order's `expiry` field, which is a unix timestamp in milliseconds, so the order can no longer execute. Compare the `expiry` in the signed order payload with the chain's current `Timestamp` value. | +| [`OrderNetUidMismatch`](/docs/errors/chain/OrderNetUidMismatch) | [`invalid_argument`](/docs/errors/invalid-argument) | An order inside an `execute_batched_orders` call has a `netuid` field different from the batch's `netuid` parameter, which hard-fails the entire batch. Check each order payload's `netuid` against the batch argument and split mismatched orders out. | +| [`OutOfBounds`](/docs/errors/chain/OutOfBounds) | [`invalid_argument`](/docs/errors/invalid-argument) | A pointer and length pair passed to a contract API host function references memory outside the contract's sandbox. Check the buffer pointers and lengths the contract passes to seal functions; this usually indicates a low-level contract bug. | +| [`OutOfGas`](/docs/errors/chain/OutOfGas) | [`limit_exceeded`](/docs/errors/limit-exceeded) | The contract exhausted the gas limit supplied for this execution before completing. Increase the `gas_limit` argument on `call` or `instantiate`; dry-run the call via RPC to estimate the required weight. | +| [`OutOfTransientStorage`](/docs/errors/chain/OutOfTransientStorage) | [`limit_exceeded`](/docs/errors/limit-exceeded) | A write would exceed the per-execution byte limit for transient storage. Check how much data the contract places in transient storage during the call against the runtime's transient storage limit. | +| [`OutputBufferTooSmall`](/docs/errors/chain/OutputBufferTooSmall) | [`invalid_argument`](/docs/errors/invalid-argument) | The output buffer the contract supplied to an API call is smaller than the data the runtime needs to write back. Check the output length pointer the contract passes and enlarge the buffer; usually a contract-side bug. | +| [`Overflow`](/docs/errors/chain/Overflow) | [`internal`](/docs/errors/internal) | A checked arithmetic operation overflowed, e.g. incrementing `NextSubnetLeaseId` when registering a leased network, or adding to a crowdloan's `raised` amount or contributor count. Internal guard; inspect the amounts involved as this should not occur with realistic values. | +| [`POWRegistrationDisabled`](/docs/errors/chain/POWRegistrationDisabled) | [`disabled`](/docs/errors/disabled) | `sudo_set_network_pow_registration_allowed` unconditionally fails because proof-of-work registration is deprecated and its toggle can no longer be changed. Nothing to check; the call is permanently disabled. | +| [`PalletHotkeyNotRegistered`](/docs/errors/chain/PalletHotkeyNotRegistered) | [`not_registered`](/docs/errors/not-registered) | Root tried to enable the pallet via `set_pallet_status` before its hotkey was registered to the pallet's intermediary account. Check that the `PalletHotkey` constant is registered for the pallet account, which genesis or the `on_runtime_upgrade` migration performs. | +| [`PartialFillsNotEnabled`](/docs/errors/chain/PartialFillsNotEnabled) | [`disabled`](/docs/errors/disabled) | A `partial_fill` amount was supplied for an order whose signed payload has `partial_fills_enabled` set to false. Check that field in the order payload; partial execution requires the signer to have opted in when signing. | +| [`PauseFailed`](/docs/errors/chain/PauseFailed) | [`invalid_argument`](/docs/errors/invalid-argument) | A GRANDPA pause was signalled while the authority set is not live, i.e. it is already paused or a pause is already pending. Check the Grandpa `State` storage before signalling a pause. | +| [`PaymentOverflow`](/docs/errors/chain/PaymentOverflow) | [`internal`](/docs/errors/internal) | Arithmetic overflowed while computing the total payment or refund for an EVM transaction, such as refunding remaining gas at the effective gas price. Check for extreme gas price or gas limit values in the transaction. | +| [`PreLogExists`](/docs/errors/chain/PreLogExists) | [`invalid_argument`](/docs/errors/invalid-argument) | An `ethereum.transact` extrinsic was submitted in a block that already carries a pre-log digest, meaning an Ethereum transaction is being injected by other means. Check the block's digest for a pre-runtime Ethereum log; transact is not allowed alongside it. | +| [`PriceConditionNotMet`](/docs/errors/chain/PriceConditionNotMet) | [`too_early`](/docs/errors/too-early) | The subnet's current alpha price does not satisfy the order's trigger: buys and stop-losses require price at or below `limit_price`, take-profits at or above it. Compare `current_alpha_price` for the order's `netuid`, scaled by 1e9, with the `limit_price` field. | +| [`PriceLimitExceeded`](/docs/errors/chain/PriceLimitExceeded) | [`insufficient_liquidity`](/docs/errors/insufficient-liquidity) | The `limit_price` given to a swap is not beyond the current pool price in the trade's direction, so the swap would immediately breach it. Compare the limit price argument against the subnet's current alpha price before submitting. | +| [`ProportionOverflow`](/docs/errors/chain/ProportionOverflow) | [`invalid_argument`](/docs/errors/invalid-argument) | The child proportions passed to `set_children` sum to more than u64::MAX. Reduce the per-child proportion values so their total fits in a u64; each proportion is a fraction of u64::MAX. | +| [`PulseVerificationError`](/docs/errors/chain/PulseVerificationError) | [`invalid_argument`](/docs/errors/invalid-argument) | BLS signature verification of a submitted drand pulse against the stored beacon configuration failed. Check the pulse's signature and round against the `BeaconConfig` storage (drand quicknet public key). | +| [`RandomSubjectTooLong`](/docs/errors/chain/RandomSubjectTooLong) | [`limit_exceeded`](/docs/errors/limit-exceeded) | The subject buffer given to the deprecated `seal_random` API exceeds the schedule's `subject_len` limit. Shorten the randomness subject the contract passes or check the schedule's limits section. | +| [`ReentranceDenied`](/docs/errors/chain/ReentranceDenied) | [`invalid_argument`](/docs/errors/invalid-argument) | A call tried to re-enter a contract already on the call stack without reentrancy being allowed, or contract code called back into the contracts pallet through the runtime. Check the callee address against the current call stack and the ALLOW_REENTRY call flag. | +| [`Reentrancy`](/docs/errors/chain/Reentrancy) | [`internal`](/docs/errors/internal) | EVM execution re-entered the pallet while another EVM execution was already in progress on the same thread, e.g. a precompile or runtime call dispatching back into the EVM. Inspect precompiles and runtime code that invoke the EVM from within an EVM call. | +| [`RegistrationNotPermittedOnRootSubnet`](/docs/errors/chain/RegistrationNotPermittedOnRootSubnet) | [`invalid_argument`](/docs/errors/invalid-argument) | A neuron registration or child-hotkey operation (`register`, `burned_register`, `set_children`) was called with the root netuid, where these calls are invalid. Check the netuid argument; use a regular subnet, or `root_register` for root membership. | +| [`RegistrationPriceLimitExceeded`](/docs/errors/chain/RegistrationPriceLimitExceeded) | [`limit_exceeded`](/docs/errors/limit-exceeded) | `burned_register` with a price limit failed because the subnet's current registration burn cost exceeds the supplied `limit_price`. Check the current burn via `Burn` storage or `btcli subnets list` and raise the limit or wait for the cost to decay. | +| [`RelayerMissMatch`](/docs/errors/chain/RelayerMissMatch) | [`invalid_argument`](/docs/errors/invalid-argument) | The order's `relayer` allowlist is set but the account that submitted the execution transaction is not in it. Compare the extrinsic's signing account against the `relayer` list in the signed order payload. | +| [`RelayerRequiredForPartialFill`](/docs/errors/chain/RelayerRequiredForPartialFill) | [`invalid_argument`](/docs/errors/invalid-argument) | A `partial_fill` was requested for an order whose `relayer` field is empty; partial fills are only permitted on orders that restrict who may execute them. Check the order payload and either set a relayer list or execute the full amount. | +| [`Requested`](/docs/errors/chain/Requested) | [`invalid_argument`](/docs/errors/invalid-argument) | The preimage cannot be unnoted while there are still outstanding requests for it. Check the request count in the hash's request status; all requests must be cleared before the preimage can be removed. | +| [`RequireSudo`](/docs/errors/chain/RequireSudo) | [`not_authorized`](/docs/errors/not-authorized) | The call requires the sudo key but was signed by a different account. Compare the sender against the account stored in the Sudo pallet's `Key` storage item. | +| [`RescheduleNoChange`](/docs/errors/chain/RescheduleNoChange) | [`invalid_argument`](/docs/errors/invalid-argument) | The reschedule was rejected because the new dispatch time equals the task's currently scheduled time. Check the task's existing slot in `Agenda` and pass a genuinely different `when` block. | +| [`ReservesOutOfBalance`](/docs/errors/chain/ReservesOutOfBalance) | [`insufficient_liquidity`](/docs/errors/insufficient-liquidity) | Swap balancer initialization failed because the subnet's TAO and alpha reserves produce an invalid ratio, for example both reserves are zero when an initial price is supplied. Inspect the subnet's TAO and alpha reserves and the `SwapBalancer` entry. | +| [`ReservesTooLow`](/docs/errors/chain/ReservesTooLow) | [`insufficient_liquidity`](/docs/errors/insufficient-liquidity) | The output-side reserve is below the swap pallet's `MinimumReserve`, or a swap step produced zero output for a nonzero input. Check the subnet's TAO and alpha reserves against `MinimumReserve` and reduce the trade size. | +| [`ResumeFailed`](/docs/errors/chain/ResumeFailed) | [`invalid_argument`](/docs/errors/invalid-argument) | A GRANDPA resume was signalled while the authority set is not paused, i.e. it is live or already pending a resume. Check the Grandpa `State` storage before signalling a resume. | +| [`RevealPeriodTooLarge`](/docs/errors/chain/RevealPeriodTooLarge) | [`invalid_argument`](/docs/errors/invalid-argument) | `set_reveal_period` was given a commit-reveal period above the compiled-in maximum number of epochs. Lower the `reveal_period` argument; the current setting is readable from `RevealPeriodEpochs` for the netuid. | +| [`RevealPeriodTooSmall`](/docs/errors/chain/RevealPeriodTooSmall) | [`invalid_argument`](/docs/errors/invalid-argument) | `set_reveal_period` was given a commit-reveal period below the compiled-in minimum number of epochs. Raise the `reveal_period` argument; the current setting is readable from `RevealPeriodEpochs` for the netuid. | +| [`RevealTooEarly`](/docs/errors/chain/RevealTooEarly) | [`too_early`](/docs/errors/too-early) | A weight reveal was submitted before the commit's reveal window: the current epoch must equal the commit epoch plus the reveal period. Check the commit in `WeightCommits` and the subnet's `RevealPeriodEpochs`, then wait for the reveal epoch. | +| [`RootNetUidNotAllowed`](/docs/errors/chain/RootNetUidNotAllowed) | [`invalid_argument`](/docs/errors/invalid-argument) | The order or batch targets the root subnet, netuid 0, which the limit orders pallet does not serve. Check the `netuid` field of the order payload or the `netuid` parameter of the batch call and target a non-root subnet. | +| [`RootNetworkDoesNotExist`](/docs/errors/chain/RootNetworkDoesNotExist) | [`subnet_not_exists`](/docs/errors/subnet-not-exists) | Root registration or root stake claiming found no root network in chain state, which only happens on misconfigured or freshly bootstrapped chains. Verify netuid 0 exists in `NetworksAdded`. | +| [`SameAutoStakeHotkeyAlreadySet`](/docs/errors/chain/SameAutoStakeHotkeyAlreadySet) | [`already_exists`](/docs/errors/already-exists) | The coldkey tried to set its auto-stake destination on a subnet to the hotkey that is already configured. Read `AutoStakeDestination` for the coldkey and netuid before calling; only a different hotkey is accepted. | +| [`SameNetuid`](/docs/errors/chain/SameNetuid) | [`invalid_argument`](/docs/errors/invalid-argument) | A stake swap or move where coldkey, hotkey, and subnet are all unchanged, so `origin_netuid` equals `destination_netuid` with nothing to transition. Check the call arguments; at least the subnet or one of the keys must differ. | +| [`SenderInSignatories`](/docs/errors/chain/SenderInSignatories) | [`invalid_argument`](/docs/errors/invalid-argument) | The multisig sender was included in the `other_signatories` list, but that list must contain only the remaining signatories. Remove the sender's own account from `other_signatories` before resubmitting. | +| [`ServingRateLimitExceeded`](/docs/errors/chain/ServingRateLimitExceeded) | [`rate_limited`](/docs/errors/rate-limited) | `serve_axon` or `serve_prometheus` was called again before enough blocks passed since the neuron's last serving update. Check the axon's last update block in `Axons` against the `ServingRateLimit` (serving_rate_limit hyperparameter) and wait. | +| [`SettingWeightsTooFast`](/docs/errors/chain/SettingWeightsTooFast) | [`rate_limited`](/docs/errors/rate-limited) | The neuron set weights again before `WeightsSetRateLimit` blocks elapsed since its last weight update on that subnet. Check the weights_rate_limit hyperparameter and the neuron's `LastUpdate` entry, then wait the remaining blocks. | +| [`SignatoriesOutOfOrder`](/docs/errors/chain/SignatoriesOutOfOrder) | [`invalid_argument`](/docs/errors/invalid-argument) | The `other_signatories` list is not sorted in strictly ascending account order, which the multisig pallet requires for a canonical account derivation. Sort the list and remove duplicates before resubmitting. | +| [`SlippageTooHigh`](/docs/errors/chain/SlippageTooHigh) | [`insufficient_liquidity`](/docs/errors/insufficient-liquidity) | A stake, unstake, or move with a price limit would execute at a worse rate than the limit allows and `allow_partial` was false. Compare the `limit_price` argument with the subnet's current alpha price (`btcli subnets price`) or permit partial execution. | +| [`SpaceLimitExceeded`](/docs/errors/chain/SpaceLimitExceeded) | [`rate_limited`](/docs/errors/rate-limited) | The commitment would push the account's byte quota for the current epoch over the cap; each `set_commitment` consumes at least 100 bytes. Check `UsedSpaceOf` for the netuid and account against `MaxSpace`, or wait for the next epoch to reset usage. | +| [`SpecVersionNeedsToIncrease`](/docs/errors/chain/SpecVersionNeedsToIncrease) | [`invalid_argument`](/docs/errors/invalid-argument) | The new runtime's `spec_version` is not greater than the current one, so the upgrade is rejected. Check the `RuntimeVersion` in the new wasm and bump `spec_version` above the version currently on chain. | +| [`StakeTooLowForRoot`](/docs/errors/chain/StakeTooLowForRoot) | [`insufficient_balance`](/docs/errors/insufficient-balance) | `root_register` when the root network is full and the hotkey's stake on netuid 0 does not exceed the lowest-staked current root member. Compare your hotkey's root stake against existing root validators (`btcli subnets metagraph 0` or `btcli query neurons --netuid 0`). | +| [`StakeUnavailable`](/docs/errors/chain/StakeUnavailable) | [`insufficient_balance`](/docs/errors/insufficient-balance) | An unstake would dip into stake that is still locked: the requested alpha exceeds the coldkey's unlocked balance on that subnet. Check the `Lock` entry for the coldkey and netuid; only total stake minus the decaying locked mass can be unstaked. | +| [`StakingRateLimitExceeded`](/docs/errors/chain/StakingRateLimitExceeded) | [`rate_limited`](/docs/errors/rate-limited) | Staking operations (add_stake, remove_stake, and similar) were submitted faster than the per-block staking rate limit allows for the hotkey-coldkey pair. Space the transactions out and retry in a later block. | +| [`StartCallNotReady`](/docs/errors/chain/StartCallNotReady) | [`too_early`](/docs/errors/too-early) | `start_call` was made before `StartCallDelay` blocks elapsed since the subnet was registered. Compare the current block against `NetworkRegisteredAt` for the netuid plus `StartCallDelay`, and wait for the remainder. | +| [`StateChangeDenied`](/docs/errors/chain/StateChangeDenied) | [`not_authorized`](/docs/errors/not-authorized) | The contract invoked a state-modifying host function, such as a storage write, transfer, or value-bearing call, while executing in read-only mode. Check whether the enclosing call was made with the read-only flag or from a static context. | +| [`StorageDepositLimitExhausted`](/docs/errors/chain/StorageDepositLimitExhausted) | [`limit_exceeded`](/docs/errors/limit-exceeded) | The execution created more storage than the caller's `storage_deposit_limit` allows to be charged. Raise the `storage_deposit_limit` argument or reduce storage usage; a dry-run reports the required `storage_deposit`. | +| [`StorageDepositNotEnoughFunds`](/docs/errors/chain/StorageDepositNotEnoughFunds) | [`insufficient_balance`](/docs/errors/insufficient-balance) | The origin's free balance cannot cover the storage deposit limit specified or required for this call. Check the caller's withdrawable balance against the `storage_deposit_limit` argument and the dry-run's reported deposit. | +| [`StorageOverflow`](/docs/errors/chain/StorageOverflow) | [`internal`](/docs/errors/internal) | Template leftover in the drand pallet for a counter increment overflowing `u32::MAX`; no current code path raises it. If seen, inspect the drand pallet's stored counters for values near the u32 limit. | +| [`SubNetRegistrationDisabled`](/docs/errors/chain/SubNetRegistrationDisabled) | [`disabled`](/docs/errors/disabled) | Neuron registration is switched off: either the subnet's `NetworkRegistrationAllowed` flag is false, or network creation has not opened yet (`NetworkRegistrationStartBlock` is in the future). Check the network_registration_allowed hyperparameter for the netuid. | +| [`SubnetBuybackRateLimitExceeded`](/docs/errors/chain/SubnetBuybackRateLimitExceeded) | [`rate_limited`](/docs/errors/rate-limited) | A subnet buyback operation (staking TAO and immediately burning the acquired alpha, e.g. via `add_stake_burn`) was repeated within its rate-limit window. Wait for the window to pass before retrying the buyback. | +| [`SubnetDoesNotExist`](/docs/errors/chain/SubnetDoesNotExist) | [`subnet_not_exists`](/docs/errors/subnet-not-exists) | The admin-utils call targets a netuid with no registered subnet. Verify the `netuid` argument against `NetworksAdded` (the set of existing subnets) before setting hyperparameters. | +| [`SubnetLimitReached`](/docs/errors/chain/SubnetLimitReached) | [`limit_exceeded`](/docs/errors/limit-exceeded) | `register_network` failed because the subnet count is at the network limit and no existing subnet is eligible to be pruned. Check the number of registered subnets (`btcli subnets list`) against the subnet limit and retry once a subnet becomes prunable. | +| [`SubnetNotExists`](/docs/errors/chain/SubnetNotExists) | [`subnet_not_exists`](/docs/errors/subnet-not-exists) | The netuid passed to the call does not correspond to a registered subnet. Verify the netuid argument against `NetworksAdded` or `btcli subnets list`; the subnet may also have been dissolved. | +| [`SubtokenDisabled`](/docs/errors/chain/SubtokenDisabled) | [`subtoken_disabled`](/docs/errors/subtoken-disabled) | The subnet's alpha token is not yet enabled, so staking, swapping, and trading on it are blocked; `SubtokenEnabled` is false until the owner makes the `start_call` after registration. Check `SubtokenEnabled` for the netuid involved. | +| [`SwapInputTooLarge`](/docs/errors/chain/SwapInputTooLarge) | [`insufficient_liquidity`](/docs/errors/insufficient-liquidity) | The swap's net input after fees exceeds 1000 times the input-side reserve, the pallet's hard per-trade cap. Compare the input amount against the subnet's input-side reserve (TAO or alpha) and split the trade if needed. | +| [`SwapReturnedZero`](/docs/errors/chain/SwapReturnedZero) | [`insufficient_liquidity`](/docs/errors/insufficient-liquidity) | The netted pool swap in `execute_batched_orders` produced zero output for a non-zero input, meaning the pool lacks liquidity or the derived price limit clamped the swap entirely. Check the subnet pool's reserves and the batch's tightest slippage-derived price limit. | +| [`SymbolAlreadyInUse`](/docs/errors/chain/SymbolAlreadyInUse) | [`already_exists`](/docs/errors/already-exists) | The token symbol requested for the subnet is already assigned to another subnet. Scan `TokenSymbol` across netuids and pick a symbol that is not taken. | +| [`SymbolDoesNotExist`](/docs/errors/chain/SymbolDoesNotExist) | [`not_found`](/docs/errors/not-found) | The requested token symbol is not in the chain's predefined symbol table, so it cannot be assigned to a subnet. Check the symbol argument against the chain's built-in `SYMBOLS` list and choose a valid entry. | +| [`TargetBlockNumberInPast`](/docs/errors/chain/TargetBlockNumberInPast) | [`invalid_argument`](/docs/errors/invalid-argument) | The scheduler was given a dispatch block that is not in the future. Compare the `when` argument against the current block number and choose a strictly later block. | +| [`TempoOutOfBounds`](/docs/errors/chain/TempoOutOfBounds) | [`invalid_argument`](/docs/errors/invalid-argument) | The subnet owner gave `sudo_set_tempo` a tempo outside the allowed MIN_TEMPO to MAX_TEMPO range (360-50,400 blocks). Check the tempo argument against those chain constants and pick a value inside the bounds; only root may set a tempo outside them. | +| [`TerminatedInConstructor`](/docs/errors/chain/TerminatedInConstructor) | [`internal`](/docs/errors/internal) | The contract called `seal_terminate` inside its constructor, self-destructing during instantiation, which is forbidden. Inspect the constructor logic; termination is only allowed in regular message calls. | +| [`TerminatedWhileReentrant`](/docs/errors/chain/TerminatedWhileReentrant) | [`invalid_argument`](/docs/errors/invalid-argument) | `seal_terminate` was called on a contract that appears more than once on the call stack, so termination was refused. Check the call chain for reentrant calls into the contract being terminated. | +| [`TooBig`](/docs/errors/chain/TooBig) | [`limit_exceeded`](/docs/errors/limit-exceeded) | The preimage exceeds the maximum size the pallet will store on-chain (4 MiB). Check the byte length of the preimage against the pallet's `MAX_SIZE` limit before noting it. | +| [`TooFew`](/docs/errors/chain/TooFew) | [`invalid_argument`](/docs/errors/invalid-argument) | The bulk preimage upgrade was requested with zero hashes, so there is nothing to do. Pass at least one hash to `ensure_updated`. | +| [`TooFewSignatories`](/docs/errors/chain/TooFewSignatories) | [`invalid_argument`](/docs/errors/invalid-argument) | The multisig signatory list is too short: `other_signatories` must contain at least one account besides the sender. Check the length of `other_signatories` against the threshold you are using. | +| [`TooMany`](/docs/errors/chain/TooMany) | [`limit_exceeded`](/docs/errors/limit-exceeded) | A limit was exceeded: more preimage hashes than `MAX_HASH_UPGRADE_BULK_COUNT` were passed to `ensure_updated`, or the account has hit its proxy or announcement cap. Check the account's `Proxies` and `Announcements` entries against `MaxProxies` and `MaxPending`. | +| [`TooManyCalls`](/docs/errors/chain/TooManyCalls) | [`limit_exceeded`](/docs/errors/limit-exceeded) | The batch submitted to the utility pallet contains more calls than the batched-calls limit allows. Check the length of the `calls` vector and split the work across multiple smaller `batch`, `batch_all`, or `force_batch` submissions. | +| [`TooManyChildren`](/docs/errors/chain/TooManyChildren) | [`limit_exceeded`](/docs/errors/limit-exceeded) | `set_children` was called with more than 5 child hotkeys for a parent on the subnet. Trim the children list to at most 5 entries. | +| [`TooManyFieldsInCommitmentInfo`](/docs/errors/chain/TooManyFieldsInCommitmentInfo) | [`limit_exceeded`](/docs/errors/limit-exceeded) | The `CommitmentInfo` passed to `set_commitment` contains more entries in `fields` than the pallet's `MaxFields` config allows. Count the fields in the `info` argument and trim to the `MaxFields` limit. | +| [`TooManyFreezes`](/docs/errors/chain/TooManyFreezes) | [`limit_exceeded`](/docs/errors/limit-exceeded) | The account already has the maximum number of balance freezes, so a new freeze cannot be added. Check the account's `Freezes` entry against the `MaxFreezes` constant; an existing freeze must be thawed first. | +| [`TooManyHolds`](/docs/errors/chain/TooManyHolds) | [`limit_exceeded`](/docs/errors/limit-exceeded) | The account already carries the maximum number of balance holds, one per hold reason variant. Check the account's `Holds` entry; an existing hold must be released before a new reason can place another. | +| [`TooManyPendingExtrinsics`](/docs/errors/chain/TooManyPendingExtrinsics) | [`limit_exceeded`](/docs/errors/limit-exceeded) | `store_encrypted` was rejected because the shield pallet's queue of encrypted extrinsics is already at capacity. Compare the `PendingExtrinsics` count against `MaxPendingExtrinsicsLimit` and wait for queued items to be processed or expire. | +| [`TooManyRegistrationsThisBlock`](/docs/errors/chain/TooManyRegistrationsThisBlock) | [`rate_limited`](/docs/errors/rate-limited) | Registrations in the current block already reached the subnet's per-block cap (`MaxRegistrationsPerBlock`, the max_regs_per_block hyperparameter); root registration enforces the same cap on netuid 0. Retry in the next block. | +| [`TooManyRegistrationsThisInterval`](/docs/errors/chain/TooManyRegistrationsThisInterval) | [`rate_limited`](/docs/errors/rate-limited) | Registrations in the current interval reached the cap of three times `TargetRegistrationsPerInterval` for the subnet. Compare `RegistrationsThisInterval` against that hyperparameter and wait for the next interval to start. | +| [`TooManyReserves`](/docs/errors/chain/TooManyReserves) | [`limit_exceeded`](/docs/errors/limit-exceeded) | The account already has the maximum number of named reserves. Check the account's `Reserves` entry against the `MaxReserves` constant; a named reserve must be unreserved before adding another. | +| [`TooManySignatories`](/docs/errors/chain/TooManySignatories) | [`limit_exceeded`](/docs/errors/limit-exceeded) | The multisig signatory list exceeds the maximum allowed. Compare the length of `other_signatories` plus the sender against the pallet's `MaxSignatories` constant. | +| [`TooManyTopics`](/docs/errors/chain/TooManyTopics) | [`limit_exceeded`](/docs/errors/limit-exceeded) | The number of topics passed to `seal_deposit_event` exceeds the schedule's `event_topics` limit. Reduce the number of indexed topics in the contract's event definition or compare against the schedule limit. | +| [`TooManyUIDsPerMechanism`](/docs/errors/chain/TooManyUIDsPerMechanism) | [`limit_exceeded`](/docs/errors/limit-exceeded) | Setting max UIDs or mechanism count would make max_uids times mechanism_count exceed the chain default of 256 UIDs per subnet. Check `MaxAllowedUids` and the subnet's mechanism count so their product stays within the limit. | +| [`TooManyUnrevealedCommits`](/docs/errors/chain/TooManyUnrevealedCommits) | [`limit_exceeded`](/docs/errors/limit-exceeded) | `commit_weights` (or a CRv3 commit) failed because the hotkey already has 10 unrevealed commits queued on the subnet. Inspect `WeightCommits` for the hotkey and reveal or let old commits expire before committing again. | +| [`TooSoon`](/docs/errors/chain/TooSoon) | [`rate_limited`](/docs/errors/rate-limited) | A forced GRANDPA authority change was signalled too soon after the previous one. Check the Grandpa `NextForced` storage and wait until the current block passes it before signalling another forced change. | +| [`TransactionMustComeFromEOA`](/docs/errors/chain/TransactionMustComeFromEOA) | [`not_authorized`](/docs/errors/not-authorized) | Rejected per EIP-3607: the sender address has contract code deployed, and transactions must originate from externally owned accounts. Check `eth_getCode` for the `source` address and sign with a plain EOA key instead. | +| [`TransactorAccountShouldBeHotKey`](/docs/errors/chain/TransactorAccountShouldBeHotKey) | [`not_authorized`](/docs/errors/not-authorized) | The extrinsic must be signed by the hotkey itself, but a different account (typically the coldkey) was the origin. Check which key signs the transaction; calls like axon serving expect the hotkey as origin. | +| [`TransferDisallowed`](/docs/errors/chain/TransferDisallowed) | [`disabled`](/docs/errors/disabled) | A stake transfer or cross-subnet move was attempted while the origin or destination subnet has stake transfers switched off. Check the `TransferToggle` storage for both netuids involved; the subnet owner must enable transfers first. | +| [`TransferFailed`](/docs/errors/chain/TransferFailed) | [`insufficient_balance`](/docs/errors/insufficient-balance) | A balance transfer performed during the contract call failed, most likely because the sender lacks enough free balance. Check the transferring account's free balance against the `value` being sent, accounting for the existential deposit. | +| [`TrimmingWouldExceedMaxImmunePercentage`](/docs/errors/chain/TrimmingWouldExceedMaxImmunePercentage) | [`limit_exceeded`](/docs/errors/limit-exceeded) | Trimming the subnet's UIDs cannot proceed because immune neurons would make up at least the maximum immune share (80%) of the reduced slot count. Check neurons still in `ImmunityPeriod` and retry after their immunity lapses or with a higher max UID target. | +| [`TxChildkeyTakeRateLimitExceeded`](/docs/errors/chain/TxChildkeyTakeRateLimitExceeded) | [`rate_limited`](/docs/errors/rate-limited) | `set_childkey_take` was called again for the hotkey on this subnet before its rate-limit window elapsed. Check the block of the last childkey-take change against `TxChildkeyTakeRateLimit` and wait out the remainder. | +| [`TxRateLimitExceeded`](/docs/errors/chain/TxRateLimitExceeded) | [`rate_limited`](/docs/errors/rate-limited) | An owner or admin transaction (e.g. `set_children`, tempo or hyperparameter updates, setting the owner hotkey) was repeated within its rate-limit window for that key and subnet. Check when the same transaction type last succeeded and wait for the limit to pass. | +| [`UidMapCouldNotBeCleared`](/docs/errors/chain/UidMapCouldNotBeCleared) | [`internal`](/docs/errors/internal) | During a UID reshuffle or trim, clearing the subnet's `Uids` map left residual entries (the storage clear returned a cursor). Internal state inconsistency rather than a caller error; inspect the `Uids` storage for the netuid and report it. | +| [`UidVecContainInvalidOne`](/docs/errors/chain/UidVecContainInvalidOne) | [`invalid_argument`](/docs/errors/invalid-argument) | The weight submission includes a UID that is not registered on the subnet, i.e. at least one entry is not below `SubnetworkN`. Check the `uids` argument against the subnet's neuron count in the metagraph (`btcli subnets metagraph`). | +| [`UidsLengthExceedUidsInSubNet`](/docs/errors/chain/UidsLengthExceedUidsInSubNet) | [`limit_exceeded`](/docs/errors/limit-exceeded) | The weight submission contains more UID entries than there are neurons registered on the subnet. Compare the length of the `uids` argument against `SubnetworkN` for the netuid and trim the vector. | +| [`UnableToRecoverPublicKey`](/docs/errors/chain/UnableToRecoverPublicKey) | [`invalid_argument`](/docs/errors/invalid-argument) | While associating an EVM key, the secp256k1 public key recovered from the supplied signature could not be parsed. Check that the signature was produced by signing the expected EIP-191 message (hotkey plus block hash) with the EVM private key. | +| [`Unannounced`](/docs/errors/chain/Unannounced) | [`too_early`](/docs/errors/too-early) | The proxied call was executed before its announcement matured, or no matching announcement exists at all. Check the proxy's `Announcements` entry for the call hash and the `delay` on the proxy registration; enough blocks must elapse between `announce` and `proxy_announced`. | +| [`Unauthorized`](/docs/errors/chain/Unauthorized) | [`not_authorized`](/docs/errors/not-authorized) | In System, the code passed to `apply_authorized_upgrade` does not hash to the value in `AuthorizedUpgrade`. In LimitOrders, the caller is not the order's signer, who alone may cancel it. Check the code hash against the authorization, or the sender against the order's `signer`. | +| [`Undefined`](/docs/errors/chain/Undefined) | [`internal`](/docs/errors/internal) | Catch-all EVM validation error for cases without a dedicated variant, such as a malformed EIP-7702 authorization list or an unknown validation failure. Inspect the raw transaction for unsupported fields and check the node logs. | +| [`Underflow`](/docs/errors/chain/Underflow) | [`internal`](/docs/errors/internal) | A checked subtraction underflowed in crowdloan accounting, e.g. `raised` exceeding `cap` when computing remaining room, or a contributor count decrement, indicating inconsistent state. Inspect the `Crowdloans` and `Contributions` entries for the `crowdloan_id`. | +| [`UnexpectedTimepoint`](/docs/errors/chain/UnexpectedTimepoint) | [`invalid_argument`](/docs/errors/invalid-argument) | A timepoint was supplied but no multisig operation is underway for this call hash; the first approval must open the operation with no timepoint. Pass `maybe_timepoint: None` on the opening call, or verify the call hash matches an existing `Multisigs` entry. | +| [`UnexpectedUnreserveLeftover`](/docs/errors/chain/UnexpectedUnreserveLeftover) | [`internal`](/docs/errors/internal) | While lowering a commitment deposit, `Currency::unreserve` failed to return the full difference, leaving a leftover, which signals an internal inconsistency. Check the account's reserved balance against the deposit recorded in `CommitmentOf`. | +| [`UnlockAmountTooHigh`](/docs/errors/chain/UnlockAmountTooHigh) | [`insufficient_balance`](/docs/errors/insufficient-balance) | An unlock requested more alpha than remains locked for the coldkey on that subnet. Check the lock's remaining decaying locked mass in the `Lock` storage entry and request at most that amount. | +| [`Unproxyable`](/docs/errors/chain/Unproxyable) | [`not_authorized`](/docs/errors/not-authorized) | The attempted call is not permitted by the registered proxy type's call filter. Check which `proxy_type` the sender holds in the real account's `Proxies` entry and whether that type's `InstanceFilter` allows this specific call. | +| [`Unreachable`](/docs/errors/chain/Unreachable) | [`internal`](/docs/errors/internal) | `announce_next_key` could not identify the current block author via the `FindAuthors` lookup, which should be impossible in a normally authored block. Check the block's author digest and the shield pallet's authorship wiring. | +| [`UnverifiedPulse`](/docs/errors/chain/UnverifiedPulse) | [`invalid_argument`](/docs/errors/invalid-argument) | Declared for drand pulses that fail validity checks, but current code raises `PulseVerificationError` for verification failures and silently skips unverified pulses. If seen on an older runtime, check the pulse signature against `BeaconConfig`. | +| [`ValueNotInBounds`](/docs/errors/chain/ValueNotInBounds) | [`invalid_argument`](/docs/errors/invalid-argument) | An admin-utils argument fell outside its allowed range: `min_burn` must be below `MinBurnUpperBound` and the subnet's max burn, `max_burn` above `MaxBurnLowerBound` and the min burn, and `max_epochs_per_block` at least 1. Check the argument against those bounds. | +| [`ValueTooLarge`](/docs/errors/chain/ValueTooLarge) | [`limit_exceeded`](/docs/errors/limit-exceeded) | A value written to contract storage or emitted as event data exceeds the `MaxValueSize` limit. Compare the size of the stored value or event payload against the runtime's maximum value size constant. | +| [`VestingBalance`](/docs/errors/chain/VestingBalance) | [`insufficient_balance`](/docs/errors/insufficient-balance) | The account's balance is locked under a vesting schedule, leaving too little usable balance to send the requested value. Check the account's `Vesting` schedules and its lock in `Locks`, and compare the unvested (still locked) amount against what the transfer needs. | +| [`VotingPowerTrackingNotEnabled`](/docs/errors/chain/VotingPowerTrackingNotEnabled) | [`disabled`](/docs/errors/disabled) | Disabling voting power tracking was requested on a subnet where tracking is not currently active. Check the `VotingPowerTrackingEnabled` storage flag for the netuid before calling the disable extrinsic. | +| [`WaitingForDissolvedSubnetCleanup`](/docs/errors/chain/WaitingForDissolvedSubnetCleanup) | [`too_early`](/docs/errors/too-early) | The operation is blocked while a dissolved subnet's storage is still being torn down in the background. Check the `DissolveCleanupQueue` and retry after the on-idle cleanup for that netuid completes. | +| [`WeightExceedsAbsoluteMax`](/docs/errors/chain/WeightExceedsAbsoluteMax) | [`limit_exceeded`](/docs/errors/limit-exceeded) | `set_on_initialize_weight` or `set_max_extrinsic_weight` was given a value above the shield pallet's hard cap of half the total block weight. Compare the `value` argument against the `MAX_ON_INITIALIZE_WEIGHT` constant. | +| [`WeightVecLengthIsLow`](/docs/errors/chain/WeightVecLengthIsLow) | [`invalid_argument`](/docs/errors/invalid-argument) | The weight submission has fewer entries than the subnet's minimum (setting only a self-weight is the one exception). Compare the vector length against the `MinAllowedWeights` (min_allowed_weights hyperparameter) for the netuid. | +| [`WeightVecNotEqualSize`](/docs/errors/chain/WeightVecNotEqualSize) | [`invalid_argument`](/docs/errors/invalid-argument) | The `uids` and `values` vectors passed to a weight-setting call have different lengths, so they cannot be paired. Check the call arguments; both vectors must have exactly one value per UID. | +| [`WithdrawFailed`](/docs/errors/chain/WithdrawFailed) | [`insufficient_balance`](/docs/errors/insufficient-balance) | Withdrawing the transaction fee from the sender's mapped account failed even though the balance check passed, e.g. due to locks, holds, or existential deposit constraints. Check the account's locks and its free versus withdrawable balance. | +| [`WrongTimepoint`](/docs/errors/chain/WrongTimepoint) | [`invalid_argument`](/docs/errors/invalid-argument) | The timepoint supplied does not match the one recorded when this multisig operation was opened. Read the correct `when` height and index from the `Multisigs` entry for the call hash and resubmit with that exact timepoint. | +| [`XCMDecodeFailed`](/docs/errors/chain/XCMDecodeFailed) | [`invalid_argument`](/docs/errors/invalid-argument) | The bytes the contract passed to `xcm_execute` or `xcm_send` could not be decoded as a versioned XCM message. Check the XCM encoding and version the contract produces against what the runtime supports. | +| [`ZeroBalanceAfterWithdrawn`](/docs/errors/chain/ZeroBalanceAfterWithdrawn) | [`insufficient_balance`](/docs/errors/insufficient-balance) | Withdrawing TAO from the coldkey (e.g. paying a registration burn or adding stake) would leave the account at zero, below what keeps it alive. Check the coldkey's free balance and leave at least the existential deposit after the amount withdrawn. | +| [`ZeroShareInBatch`](/docs/errors/chain/ZeroShareInBatch) | [`invalid_argument`](/docs/errors/invalid-argument) | An order's pro-rata share of the batch output floored to zero, so the whole batch was rejected rather than consuming that order's input for no payout. Check the order's `amount` relative to the batch totals and retry it in a differently composed batch. | diff --git a/docs/errors/chain/meta.json b/docs/errors/chain/meta.json new file mode 100644 index 0000000000..f2cbb0d6e7 --- /dev/null +++ b/docs/errors/chain/meta.json @@ -0,0 +1,361 @@ +{ + "title": "Chain errors", + "pages": [ + "index", + "AccountNotAllowedCommit", + "AccountRejectsLockedAlpha", + "ActiveLockExists", + "ActivityCutoffFactorMilliOutOfBounds", + "ActivityCutoffTooLow", + "AddStakeBurnRateLimitExceeded", + "AdminActionProhibitedDuringWeightsWindow", + "AllNetworksInImmunity", + "AlphaHighTooLow", + "AlphaLowOutOfRange", + "AlreadyApproved", + "AlreadyDeposited", + "AlreadyFinalized", + "AlreadyFinalizing", + "AlreadyNoted", + "AlreadyStored", + "AmountTooLow", + "AnnouncedColdkeyHashDoesNotMatch", + "AnnouncementDepositInvariantViolated", + "ArithmeticOverflow", + "AutoEpochAlreadyImminent", + "BadEncKeyLen", + "BalanceLow", + "BalanceWithdrawalError", + "BeneficiaryDoesNotOwnHotkey", + "BlockDurationTooLong", + "BlockDurationTooShort", + "BondsMovingAverageMaxReached", + "CallDisabled", + "CallFiltered", + "CallUnavailable", + "CanNotSetRootNetworkWeights", + "CannotAddSelfAsDelegateDependency", + "CannotAffordLockCost", + "CannotBurnOrRecycleOnRootSubnet", + "CannotEndInPast", + "CannotReleaseYet", + "CannotUseSystemAccount", + "CapNotRaised", + "CapRaised", + "CapTooLow", + "ChainIdMismatch", + "ChangePending", + "ChildParentInconsistency", + "CodeInUse", + "CodeInfoNotFound", + "CodeNotFound", + "CodeRejected", + "CodeTooLarge", + "ColdKeyAlreadyAssociated", + "ColdkeySwapAlreadyDisputed", + "ColdkeySwapAnnounced", + "ColdkeySwapAnnouncementNotFound", + "ColdkeySwapClearTooEarly", + "ColdkeySwapDisputed", + "ColdkeySwapReannouncedTooEarly", + "ColdkeySwapTooEarly", + "CommitRevealDisabled", + "CommitRevealEnabled", + "CommittingWeightsTooFast", + "ContractNotFound", + "ContractReverted", + "ContractTrapped", + "ContributionPeriodEnded", + "ContributionPeriodNotEnded", + "ContributionTooLow", + "CreateOriginNotAllowed", + "CurrencyError", + "DeadAccount", + "DecodingFailed", + "DelegateDependencyAlreadyExists", + "DelegateDependencyNotFound", + "DelegateTakeTooHigh", + "DelegateTakeTooLow", + "DelegateTxRateLimitExceeded", + "DeltaZero", + "DepositCannotBeWithdrawn", + "DepositTooLow", + "Deprecated", + "DisabledTemporarily", + "DrandConnectionFailure", + "Duplicate", + "DuplicateChild", + "DuplicateContract", + "DuplicateOffenceReport", + "DuplicateOrderInBatch", + "DuplicateUids", + "DynamicTempoBlockedByCommitReveal", + "Entered", + "EpochTriggerAlreadyPending", + "EvmKeyAssociateRateLimitExceeded", + "EvmKeyAssociationLimitExceeded", + "ExistentialDeposit", + "ExistingVestingSchedule", + "Exited", + "ExpectedBeneficiaryOrigin", + "Expendability", + "ExpiredWeightCommit", + "FailedToExtractRuntimeVersion", + "FailedToSchedule", + "FaucetDisabled", + "FeeOverflow", + "FeeRateTooHigh", + "FirstEmissionBlockNumberAlreadySet", + "GasLimitTooHigh", + "GasLimitTooLow", + "GasPriceTooLow", + "HotKeyAccountNotExists", + "HotKeyAlreadyDelegate", + "HotKeyAlreadyRegisteredInSubNet", + "HotKeyNotRegisteredInNetwork", + "HotKeyNotRegisteredInSubNet", + "HotKeySetTxRateLimitExceeded", + "HotKeySwapOnSubnetIntervalNotPassed", + "IncorrectCommitRevealVersion", + "IncorrectPartialFillAmount", + "IncorrectWeightVersionKey", + "Indeterministic", + "InputForwarded", + "InputLengthsUnequal", + "InsufficientAlphaBalance", + "InsufficientBalance", + "InsufficientInputAmount", + "InsufficientLiquidity", + "InsufficientStakeForLock", + "InsufficientTaoBalance", + "InvalidCallFlags", + "InvalidChainId", + "InvalidChild", + "InvalidChildkeyTake", + "InvalidCrowdloanId", + "InvalidDerivedAccount", + "InvalidDerivedAccountId", + "InvalidDifficulty", + "InvalidEquivocationProof", + "InvalidFinalizationConfig", + "InvalidIdentity", + "InvalidIpAddress", + "InvalidIpType", + "InvalidKeyOwnershipProof", + "InvalidLeaseBeneficiary", + "InvalidLiquidityValue", + "InvalidNonce", + "InvalidNumRootClaim", + "InvalidOrigin", + "InvalidPort", + "InvalidRecoveredPublicKey", + "InvalidRevealCommitHashNotMatch", + "InvalidRevealRound", + "InvalidRootClaimThreshold", + "InvalidRoundNumber", + "InvalidSchedule", + "InvalidSeal", + "InvalidSignature", + "InvalidSpecName", + "InvalidSubnetNumber", + "InvalidTickRange", + "InvalidValue", + "InvalidVotingPowerEmaAlpha", + "InvalidWorkBlock", + "IssuanceDeactivated", + "LeaseCannotEndInThePast", + "LeaseDoesNotExist", + "LeaseHasNoEndBlock", + "LeaseHasNotEnded", + "LeaseNetuidNotFound", + "LimitOrdersDisabled", + "LiquidAlphaDisabled", + "LiquidityRestrictions", + "LockHotkeyMismatch", + "LockIdOverFlow", + "MaxAllowedUIdsLessThanCurrentUIds", + "MaxAllowedUidsGreaterThanDefaultMaxAllowedUids", + "MaxAllowedUidsLessThanMinAllowedUids", + "MaxCallDepthReached", + "MaxContributionReached", + "MaxContributorsReached", + "MaxDelegateDependenciesReached", + "MaxValidatorsLargerThanMaxUIds", + "MaxWeightExceeded", + "MaxWeightTooLow", + "MaximumContributionTooLow", + "MechanismDoesNotExist", + "MigrationInProgress", + "MinAllowedUidsGreaterThanCurrentUids", + "MinAllowedUidsGreaterThanMaxAllowedUids", + "MinimumContributionTooHigh", + "MinimumContributionTooLow", + "MinimumThreshold", + "MultiBlockMigrationsOngoing", + "Named", + "NeedWaitingMoreBlocksToStarCall", + "NegativeSigmoidSteepness", + "NetworkDissolveAlreadyQueued", + "NetworkTxRateLimitExceeded", + "NeuronNoValidatorPermit", + "NewColdKeyIsHotkey", + "NewHotKeyIsSameWithOld", + "NewHotKeyNotCleanForRootSwap", + "NoApprovalsNeeded", + "NoChainExtension", + "NoContribution", + "NoDeposit", + "NoExistingLock", + "NoMigrationPerformed", + "NoNeuronIdAvailable", + "NoPermission", + "NoSelfProxy", + "NoTimepoint", + "NoWeightsCommitFound", + "NonAssociatedColdKey", + "NonDefaultComposite", + "NonZeroRefCount", + "NoneValue", + "NotAllowed", + "NotAuthorized", + "NotConfigured", + "NotEnoughAlphaOutToRecycle", + "NotEnoughBalanceToPaySwapColdKey", + "NotEnoughBalanceToPaySwapHotKey", + "NotEnoughBalanceToStake", + "NotEnoughStake", + "NotEnoughStakeToSetChildkeys", + "NotEnoughStakeToSetWeights", + "NotEnoughStakeToWithdraw", + "NotFound", + "NotNoted", + "NotOwner", + "NotPermittedOnRootSubnet", + "NotProxy", + "NotReadyToDissolve", + "NotRequested", + "NotRootSubnet", + "NotSubnetOwner", + "NothingAuthorized", + "OrderAlreadyProcessed", + "OrderCancelled", + "OrderExpired", + "OrderNetUidMismatch", + "OutOfBounds", + "OutOfGas", + "OutOfTransientStorage", + "OutputBufferTooSmall", + "Overflow", + "POWRegistrationDisabled", + "PalletHotkeyNotRegistered", + "PartialFillsNotEnabled", + "PauseFailed", + "PaymentOverflow", + "PreLogExists", + "PriceConditionNotMet", + "PriceLimitExceeded", + "ProportionOverflow", + "PulseVerificationError", + "RandomSubjectTooLong", + "ReentranceDenied", + "Reentrancy", + "RegistrationNotPermittedOnRootSubnet", + "RegistrationPriceLimitExceeded", + "RelayerMissMatch", + "RelayerRequiredForPartialFill", + "Requested", + "RequireSudo", + "RescheduleNoChange", + "ReservesOutOfBalance", + "ReservesTooLow", + "ResumeFailed", + "RevealPeriodTooLarge", + "RevealPeriodTooSmall", + "RevealTooEarly", + "RootNetUidNotAllowed", + "RootNetworkDoesNotExist", + "SameAutoStakeHotkeyAlreadySet", + "SameNetuid", + "SenderInSignatories", + "ServingRateLimitExceeded", + "SettingWeightsTooFast", + "SignatoriesOutOfOrder", + "SlippageTooHigh", + "SpaceLimitExceeded", + "SpecVersionNeedsToIncrease", + "StakeTooLowForRoot", + "StakeUnavailable", + "StakingRateLimitExceeded", + "StartCallNotReady", + "StateChangeDenied", + "StorageDepositLimitExhausted", + "StorageDepositNotEnoughFunds", + "StorageOverflow", + "SubNetRegistrationDisabled", + "SubnetBuybackRateLimitExceeded", + "SubnetDoesNotExist", + "SubnetLimitReached", + "SubnetNotExists", + "SubtokenDisabled", + "SwapInputTooLarge", + "SwapReturnedZero", + "SymbolAlreadyInUse", + "SymbolDoesNotExist", + "TargetBlockNumberInPast", + "TempoOutOfBounds", + "TerminatedInConstructor", + "TerminatedWhileReentrant", + "TooBig", + "TooFew", + "TooFewSignatories", + "TooMany", + "TooManyCalls", + "TooManyChildren", + "TooManyFieldsInCommitmentInfo", + "TooManyFreezes", + "TooManyHolds", + "TooManyPendingExtrinsics", + "TooManyRegistrationsThisBlock", + "TooManyRegistrationsThisInterval", + "TooManyReserves", + "TooManySignatories", + "TooManyTopics", + "TooManyUIDsPerMechanism", + "TooManyUnrevealedCommits", + "TooSoon", + "TransactionMustComeFromEOA", + "TransactorAccountShouldBeHotKey", + "TransferDisallowed", + "TransferFailed", + "TrimmingWouldExceedMaxImmunePercentage", + "TxChildkeyTakeRateLimitExceeded", + "TxRateLimitExceeded", + "UidMapCouldNotBeCleared", + "UidVecContainInvalidOne", + "UidsLengthExceedUidsInSubNet", + "UnableToRecoverPublicKey", + "Unannounced", + "Unauthorized", + "Undefined", + "Underflow", + "UnexpectedTimepoint", + "UnexpectedUnreserveLeftover", + "UnlockAmountTooHigh", + "Unproxyable", + "Unreachable", + "UnverifiedPulse", + "ValueNotInBounds", + "ValueTooLarge", + "VestingBalance", + "VotingPowerTrackingNotEnabled", + "WaitingForDissolvedSubnetCleanup", + "WeightExceedsAbsoluteMax", + "WeightVecLengthIsLow", + "WeightVecNotEqualSize", + "WithdrawFailed", + "WrongTimepoint", + "XCMDecodeFailed", + "ZeroBalanceAfterWithdrawn", + "ZeroShareInBatch" + ] +} diff --git a/docs/errors/disabled.mdx b/docs/errors/disabled.mdx new file mode 100644 index 0000000000..8fd6e6b1b1 --- /dev/null +++ b/docs/errors/disabled.mdx @@ -0,0 +1,40 @@ +--- +title: "disabled" +description: "This call or feature is switched off on this network" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The call or feature is switched off: registration is closed on the subnet, commit-reveal (or the legacy direct path) is not enabled, transfers are disallowed by the subnet, the extrinsic is deprecated, or an admin has disabled it temporarily. + +This is a deliberate chain- or subnet-level switch, not a problem with your arguments. use the supported alternative call if one exists (the error name usually points at it), or wait for the feature to be enabled. + +## Remediation + +This call or feature is switched off on this network + +## Chain errors + +The exact chain error names (from the extrinsic receipt) that classify to `disabled`; the description says what triggered the failure and where to check. Each name has its own page: + +| Chain error | Description | +| --- | --- | +| [`CallDisabled`](/docs/errors/chain/CallDisabled) | The extrinsic has been switched off in the current runtime and cannot be dispatched. There is no active raise site in current code; if seen, check release notes for whether the call was re-enabled in a newer runtime version. | +| [`CommitRevealDisabled`](/docs/errors/chain/CommitRevealDisabled) | A weight commit or reveal was submitted on a subnet where commit-reveal is turned off. Check the `commit_reveal_weights_enabled` hyperparameter for the netuid (`btcli sudo get --netuid `); use plain `set_weights` instead when it is disabled. | +| [`CommitRevealEnabled`](/docs/errors/chain/CommitRevealEnabled) | Plain `set_weights` was called on a subnet where commit-reveal is enabled, which requires the commit/reveal flow instead. Check the `commit_reveal_weights_enabled` hyperparameter for the netuid and switch to `commit_weights`/`reveal_weights`. | +| [`Deprecated`](/docs/errors/chain/Deprecated) | The extrinsic has been removed and always fails, e.g. `schedule_swap_coldkey`, the swap pallet's user-liquidity calls, or `sudo_set_total_issuance`. Migrate to the replacement call noted in the deprecation (for coldkey swaps, `announce_coldkey_swap` plus `coldkey_swap`). | +| [`DisabledTemporarily`](/docs/errors/chain/DisabledTemporarily) | The operation has been temporarily switched off in the runtime, usually as a hotfix measure. There is no active raise site in current code; if encountered, check the runtime version and release notes for when the feature is re-enabled. | +| [`DynamicTempoBlockedByCommitReveal`](/docs/errors/chain/DynamicTempoBlockedByCommitReveal) | `trigger_epoch` is refused while commit-reveal is enabled on the subnet, because an out-of-band epoch would desync the CRv3 reveal window from the Drand schedule and drop committed weights. Check the `commit_reveal_weights_enabled` hyperparameter; disable it before manually triggering epochs. | +| [`FaucetDisabled`](/docs/errors/chain/FaucetDisabled) | The `faucet` extrinsic was called on a runtime built without the pow-faucet feature, i.e. any real network. The faucet only works on local test chains compiled with that feature; use a funded wallet or testnet TAO instead. | +| [`IssuanceDeactivated`](/docs/errors/chain/IssuanceDeactivated) | Total issuance cannot be adjusted because issuance has already been deactivated. Check the Balances `InactiveIssuance` state before calling `force_adjust_total_issuance` again. | +| [`LimitOrdersDisabled`](/docs/errors/chain/LimitOrdersDisabled) | Order execution was attempted while the pallet's global switch is off. Check the `LimitOrdersEnabled` storage value; root must call `set_pallet_status` with true to enable the pallet. | +| [`LiquidAlphaDisabled`](/docs/errors/chain/LiquidAlphaDisabled) | Setting `alpha_low`/`alpha_high` was attempted while liquid alpha is disabled on the subnet. Check the `LiquidAlphaOn` hyperparameter (`btcli sudo get --netuid `) and have the subnet owner enable liquid alpha first. | +| [`NoChainExtension`](/docs/errors/chain/NoChainExtension) | The contract invoked `call_chain_extension` but this runtime registers no chain extension; such code is normally rejected at upload. Check that the target chain provides the chain extension the contract was built against. | +| [`NotConfigured`](/docs/errors/chain/NotConfigured) | The permissionless safe-mode operation is disabled because its config option is `None`: `EnterDepositAmount` for `enter`, `ExtendDepositAmount` for `extend`, or `ReleaseDelay` for `release_deposit`. Check the runtime config or use the root-only force variants. | +| [`POWRegistrationDisabled`](/docs/errors/chain/POWRegistrationDisabled) | `sudo_set_network_pow_registration_allowed` unconditionally fails because proof-of-work registration is deprecated and its toggle can no longer be changed. Nothing to check; the call is permanently disabled. | +| [`PartialFillsNotEnabled`](/docs/errors/chain/PartialFillsNotEnabled) | A `partial_fill` amount was supplied for an order whose signed payload has `partial_fills_enabled` set to false. Check that field in the order payload; partial execution requires the signer to have opted in when signing. | +| [`SubNetRegistrationDisabled`](/docs/errors/chain/SubNetRegistrationDisabled) | Neuron registration is switched off: either the subnet's `NetworkRegistrationAllowed` flag is false, or network creation has not opened yet (`NetworkRegistrationStartBlock` is in the future). Check the network_registration_allowed hyperparameter for the netuid. | +| [`TransferDisallowed`](/docs/errors/chain/TransferDisallowed) | A stake transfer or cross-subnet move was attempted while the origin or destination subnet has stake transfers switched off. Check the `TransferToggle` storage for both netuids involved; the subnet owner must enable transfers first. | +| [`VotingPowerTrackingNotEnabled`](/docs/errors/chain/VotingPowerTrackingNotEnabled) | Disabling voting power tracking was requested on a subnet where tracking is not currently active. Check the `VotingPowerTrackingEnabled` storage flag for the netuid before calling the disable extrinsic. | + +The same explanation is available in the terminal: `btcli explain disabled` (or `btcli explain ` for one exact chain error). diff --git a/docs/errors/expired.mdx b/docs/errors/expired.mdx new file mode 100644 index 0000000000..25f410c376 --- /dev/null +++ b/docs/errors/expired.mdx @@ -0,0 +1,28 @@ +--- +title: "expired" +description: "The window has closed; restart the flow with fresh state" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The call arrived after its window closed: a weights commit past its reveal period, an order past its expiry, or a contribution after the period ended. + +The expired object cannot be revived. restart the flow with fresh state — make a new commit, place a new order, or move on. + +## Remediation + +The window has closed; restart the flow with fresh state + +## Chain errors + +The exact chain error names (from the extrinsic receipt) that classify to `expired`; the description says what triggered the failure and where to check. Each name has its own page: + +| Chain error | Description | +| --- | --- | +| [`ContributionPeriodEnded`](/docs/errors/chain/ContributionPeriodEnded) | A contribution was made at or after the crowdloan's `end` block. Compare the `end` field of the `Crowdloans` entry with the current block number; the creator can extend it with `update_end` while the crowdloan is not finalized. | +| [`ExpiredWeightCommit`](/docs/errors/chain/ExpiredWeightCommit) | The hash supplied to `reveal_weights` matches a commit whose reveal window has already passed, so it can no longer be revealed. Check the `commit_reveal_period` hyperparameter and reveal within the allowed epochs after committing; re-commit and reveal on time. | +| [`InvalidRevealRound`](/docs/errors/chain/InvalidRevealRound) | A timelocked weights commit specified a `reveal_round` older than the latest stored DRAND round, so it could be decrypted immediately. Query the drand pallet's `LastStoredRound` and commit with a future round number. | +| [`OrderCancelled`](/docs/errors/chain/OrderCancelled) | The order was previously cancelled via `cancel_order` and can never be executed. Check the `Orders` storage entry for the order id; a `Cancelled` status is terminal, so the signer must sign and submit a fresh order. | +| [`OrderExpired`](/docs/errors/chain/OrderExpired) | The current chain time is past the order's `expiry` field, which is a unix timestamp in milliseconds, so the order can no longer execute. Compare the `expiry` in the signed order payload with the chain's current `Timestamp` value. | + +The same explanation is available in the terminal: `btcli explain expired` (or `btcli explain ` for one exact chain error). diff --git a/docs/errors/index.mdx b/docs/errors/index.mdx new file mode 100644 index 0000000000..56a039a0a1 --- /dev/null +++ b/docs/errors/index.mdx @@ -0,0 +1,29 @@ +--- +title: "Errors" +description: "Every failure carries a machine-readable code and a remediation hint." +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +A failed `execute` returns an `ExtrinsicResult` whose `error` has a semantic `code` (branch on it) and a `remediation` hint (what to try next). Every chain error name is deliberately classified — a CI gate keeps the mapping complete in both directions. Each code has its own explainer page listing the chain errors that map to it, and every exact chain error name has its own page under [chain errors](/docs/errors/chain). The machine-readable version is at [`/catalog/errors.json`](/catalog/errors.json). + +| Code | Chain errors | Remediation | +| --- | --- | --- | +| [`insufficient_balance`](/docs/errors/insufficient-balance) | 25 | Fund the signing account or reduce the amount; check with `btcli wallet balance` | +| [`insufficient_liquidity`](/docs/errors/insufficient-liquidity) | 8 | The pool cannot absorb this trade; reduce the amount or split it into smaller steps | +| [`rate_limited`](/docs/errors/rate-limited) | 17 | Wait for the rate-limit window to pass, then retry | +| [`not_registered`](/docs/errors/not-registered) | 4 | Register the hotkey on this subnet first with `btcli subnets register` | +| [`not_authorized`](/docs/errors/not-authorized) | 22 | Sign with the key or origin that owns the target object, then retry | +| [`already_exists`](/docs/errors/already-exists) | 27 | The object or state already exists; treat the goal as met or pick a different target | +| [`not_found`](/docs/errors/not-found) | 20 | The referenced object is not on-chain; check the identifier | +| [`subnet_not_exists`](/docs/errors/subnet-not-exists) | 4 | Use an existing netuid; `btcli subnets list` shows valid ones | +| [`subtoken_disabled`](/docs/errors/subtoken-disabled) | 1 | The subnet is not active yet; wait for start_call | +| [`disabled`](/docs/errors/disabled) | 17 | This call or feature is switched off on this network | +| [`too_early`](/docs/errors/too-early) | 18 | The required window has not opened yet; wait some blocks and retry | +| [`expired`](/docs/errors/expired) | 5 | The window has closed; restart the flow with fresh state | +| [`limit_exceeded`](/docs/errors/limit-exceeded) | 34 | A chain-side capacity limit was hit; reduce the size or count of the request | +| [`unit_mismatch`](/docs/errors/unit-mismatch) | — | Match the Balance netuid to the operation's currency | +| [`invalid_argument`](/docs/errors/invalid-argument) | 135 | Check the argument values against the operation schema | +| [`policy_violation`](/docs/errors/policy-violation) | — | The action exceeds a configured safety policy | +| [`internal`](/docs/errors/internal) | 18 | A chain-side invariant failed; nothing to fix client-side — report it if it persists | +| [`unknown`](/docs/errors/unknown) | — | Inspect the message for details | diff --git a/docs/errors/insufficient-balance.mdx b/docs/errors/insufficient-balance.mdx new file mode 100644 index 0000000000..818dcb365e --- /dev/null +++ b/docs/errors/insufficient-balance.mdx @@ -0,0 +1,50 @@ +--- +title: "insufficient_balance" +description: "Fund the signing account or reduce the amount; check with `btcli wallet balance`" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The signing account cannot cover the requested amount plus the transaction fee. + +This covers several chain-side variants: the free balance is too low for a transfer, the stake being withdrawn is larger than what is actually staked, or the transfer would drop the account below the existential deposit and reap it. + +Check the account with `btcli wallet balance`, then reduce the amount or fund the coldkey. for stake operations, `btcli stake list` shows what is actually staked per subnet. + +## Remediation + +Fund the signing account or reduce the amount; check with `btcli wallet balance` + +## Chain errors + +The exact chain error names (from the extrinsic receipt) that classify to `insufficient_balance`; the description says what triggered the failure and where to check. Each name has its own page: + +| Chain error | Description | +| --- | --- | +| [`BalanceLow`](/docs/errors/chain/BalanceLow) | The sender's mapped account cannot cover the transaction's value plus maximum gas fee, detected during validation or when withdrawing the fee. Check the account balance against `value` plus `gas_limit` times the effective gas price. | +| [`BalanceWithdrawalError`](/docs/errors/chain/BalanceWithdrawalError) | The requested TAO could not be withdrawn from the coldkey's free balance, typically due to insufficient funds, the existential deposit, or frozen/reserved balance. Check the coldkey's balance with `btcli wallet balance` and reduce the amount or top up. | +| [`CannotAffordLockCost`](/docs/errors/chain/CannotAffordLockCost) | The coldkey's free balance cannot cover the current dynamic subnet-creation lock cost. Compare `btcli subnets create-cost` (or `btcli query subnet-registration-cost`) against the coldkey balance from `btcli wallet balance` before registering a subnet. | +| [`ExistentialDeposit`](/docs/errors/chain/ExistentialDeposit) | The amount is too small to create the destination account: its resulting balance would sit below the existential deposit. Compare the transfer value plus the destination's current free balance against the `ExistentialDeposit` constant. | +| [`Expendability`](/docs/errors/chain/Expendability) | The transfer or payment would drop the sender below the existential deposit and kill the account while keep-alive semantics are required. Compare the sender's free balance minus the amount and fees against `ExistentialDeposit`, or use `transfer_allow_death` if reaping is acceptable. | +| [`InsufficientAlphaBalance`](/docs/errors/chain/InsufficientAlphaBalance) | A stake decrease asked to debit more alpha than the hotkey-coldkey pair holds on that subnet. Compare the requested amount against the pair's current stake on the netuid (`btcli stake list` or the `Alpha` storage entry). | +| [`InsufficientBalance`](/docs/errors/chain/InsufficientBalance) | The caller's spendable balance is below what the operation needs, whether a plain transfer, a crowdloan deposit or contribution, or a swap. Compare the account's free balance in `System.Account` (net of holds, freezes, and fees) against the amount being moved. | +| [`InsufficientStakeForLock`](/docs/errors/chain/InsufficientStakeForLock) | The requested lock amount exceeds the coldkey's total alpha stake on that subnet (existing locked mass included). Compare the amount against the coldkey's stake on the netuid, e.g. via `btcli stake list`, and lock less or add stake first. | +| [`InsufficientTaoBalance`](/docs/errors/chain/InsufficientTaoBalance) | The coldkey's free TAO balance is below the amount a TAO-side operation needs: a transfer between coldkeys, a burn or recycle, or a subnet-registration lock. Check the coldkey's balance with `btcli wallet balance` against the amount being moved. | +| [`LiquidityRestrictions`](/docs/errors/chain/LiquidityRestrictions) | The withdrawal is blocked by locks or freezes on the account even though the raw balance looks sufficient. Check the account's `Locks` and `Freezes` entries and compare the frozen amount against what would remain after the withdrawal. | +| [`NotEnoughBalanceToPaySwapColdKey`](/docs/errors/chain/NotEnoughBalanceToPaySwapColdKey) | The coldkey's free TAO balance cannot cover the coldkey swap cost, which is recycled when the swap executes. Check the balance with `btcli wallet balance` against the swap cost and top up before scheduling the swap. | +| [`NotEnoughBalanceToPaySwapHotKey`](/docs/errors/chain/NotEnoughBalanceToPaySwapHotKey) | The coldkey's free TAO balance is below the hotkey swap cost (a per-subnet cost applies when swapping on a single netuid). Check `btcli wallet balance` against the key swap cost and fund the coldkey before retrying. | +| [`NotEnoughBalanceToStake`](/docs/errors/chain/NotEnoughBalanceToStake) | The coldkey's free balance is less than the TAO required, either the stake amount in add_stake or the burn cost of a registration. Check `btcli wallet balance` against the amount or the current registration burn (`Burn` storage for the netuid). | +| [`NotEnoughStake`](/docs/errors/chain/NotEnoughStake) | The caller's hotkey holds less stake than the action requires; a generic insufficient-stake failure on staking-related calls. Check the hotkey's stake on the relevant subnet, e.g. `btcli stake list`, against the amount the extrinsic needs. | +| [`NotEnoughStakeToSetChildkeys`](/docs/errors/chain/NotEnoughStakeToSetChildkeys) | Raised by `set_children` when the parent hotkey's total stake is below `StakeThreshold` and it is not the subnet owner hotkey. Compare the hotkey's total stake (`btcli stake list`) against the `StakeThreshold` storage value. | +| [`NotEnoughStakeToSetWeights`](/docs/errors/chain/NotEnoughStakeToSetWeights) | Setting or committing weights failed because the hotkey's stake weight on the subnet is below `StakeThreshold` (the weights-min-stake floor); the subnet owner hotkey is exempt. Check the hotkey's stake on that netuid against `StakeThreshold`. | +| [`NotEnoughStakeToWithdraw`](/docs/errors/chain/NotEnoughStakeToWithdraw) | An unstake, stake move, swap, or transfer requested more alpha than the hotkey-coldkey pair holds on that subnet. Compare the requested amount against the pair's current stake on the netuid (`btcli stake list` or the `Alpha` storage). | +| [`StakeTooLowForRoot`](/docs/errors/chain/StakeTooLowForRoot) | `root_register` when the root network is full and the hotkey's stake on netuid 0 does not exceed the lowest-staked current root member. Compare your hotkey's root stake against existing root validators (`btcli subnets metagraph 0` or `btcli query neurons --netuid 0`). | +| [`StakeUnavailable`](/docs/errors/chain/StakeUnavailable) | An unstake would dip into stake that is still locked: the requested alpha exceeds the coldkey's unlocked balance on that subnet. Check the `Lock` entry for the coldkey and netuid; only total stake minus the decaying locked mass can be unstaked. | +| [`StorageDepositNotEnoughFunds`](/docs/errors/chain/StorageDepositNotEnoughFunds) | The origin's free balance cannot cover the storage deposit limit specified or required for this call. Check the caller's withdrawable balance against the `storage_deposit_limit` argument and the dry-run's reported deposit. | +| [`TransferFailed`](/docs/errors/chain/TransferFailed) | A balance transfer performed during the contract call failed, most likely because the sender lacks enough free balance. Check the transferring account's free balance against the `value` being sent, accounting for the existential deposit. | +| [`UnlockAmountTooHigh`](/docs/errors/chain/UnlockAmountTooHigh) | An unlock requested more alpha than remains locked for the coldkey on that subnet. Check the lock's remaining decaying locked mass in the `Lock` storage entry and request at most that amount. | +| [`VestingBalance`](/docs/errors/chain/VestingBalance) | The account's balance is locked under a vesting schedule, leaving too little usable balance to send the requested value. Check the account's `Vesting` schedules and its lock in `Locks`, and compare the unvested (still locked) amount against what the transfer needs. | +| [`WithdrawFailed`](/docs/errors/chain/WithdrawFailed) | Withdrawing the transaction fee from the sender's mapped account failed even though the balance check passed, e.g. due to locks, holds, or existential deposit constraints. Check the account's locks and its free versus withdrawable balance. | +| [`ZeroBalanceAfterWithdrawn`](/docs/errors/chain/ZeroBalanceAfterWithdrawn) | Withdrawing TAO from the coldkey (e.g. paying a registration burn or adding stake) would leave the account at zero, below what keeps it alive. Check the coldkey's free balance and leave at least the existential deposit after the amount withdrawn. | + +The same explanation is available in the terminal: `btcli explain insufficient_balance` (or `btcli explain ` for one exact chain error). diff --git a/docs/errors/insufficient-liquidity.mdx b/docs/errors/insufficient-liquidity.mdx new file mode 100644 index 0000000000..0c2f508989 --- /dev/null +++ b/docs/errors/insufficient-liquidity.mdx @@ -0,0 +1,33 @@ +--- +title: "insufficient_liquidity" +description: "The pool cannot absorb this trade; reduce the amount or split it into smaller steps" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The pool on the other side of the operation cannot absorb it: the swap reserves are too low or too imbalanced, the price would move past the configured limit, or the slippage exceeds the allowed tolerance. + +Unlike insufficient_balance this is not about your account — it is about the subnet's liquidity. reduce the amount, split it into smaller steps across blocks, or trade on a subnet with deeper reserves. + +Stake trades are slippage-protected by default with a 5% tolerance (SlippageTooHigh): raise it with `--rate-tolerance`, or disable the protection with `--no-slippage-protection` (slippage_protection=False in the SDK) to trade through the price move. + +## Remediation + +The pool cannot absorb this trade; reduce the amount or split it into smaller steps + +## Chain errors + +The exact chain error names (from the extrinsic receipt) that classify to `insufficient_liquidity`; the description says what triggered the failure and where to check. Each name has its own page: + +| Chain error | Description | +| --- | --- | +| [`InsufficientLiquidity`](/docs/errors/chain/InsufficientLiquidity) | The pool cannot absorb the operation: the swap simulation failed, reserves are smaller than the payout, or the amount exceeds the pool's supported input. Check the subnet pool reserves `SubnetTAO`, `SubnetAlphaIn` and `SubnetAlphaOut` against the amount. | +| [`NotEnoughAlphaOutToRecycle`](/docs/errors/chain/NotEnoughAlphaOutToRecycle) | A recycle or burn of alpha requested more than the subnet's outstanding alpha supply. Compare the amount against the `SubnetAlphaOut` storage value for the netuid and reduce the recycle amount. | +| [`PriceLimitExceeded`](/docs/errors/chain/PriceLimitExceeded) | The `limit_price` given to a swap is not beyond the current pool price in the trade's direction, so the swap would immediately breach it. Compare the limit price argument against the subnet's current alpha price before submitting. | +| [`ReservesOutOfBalance`](/docs/errors/chain/ReservesOutOfBalance) | Swap balancer initialization failed because the subnet's TAO and alpha reserves produce an invalid ratio, for example both reserves are zero when an initial price is supplied. Inspect the subnet's TAO and alpha reserves and the `SwapBalancer` entry. | +| [`ReservesTooLow`](/docs/errors/chain/ReservesTooLow) | The output-side reserve is below the swap pallet's `MinimumReserve`, or a swap step produced zero output for a nonzero input. Check the subnet's TAO and alpha reserves against `MinimumReserve` and reduce the trade size. | +| [`SlippageTooHigh`](/docs/errors/chain/SlippageTooHigh) | A stake, unstake, or move with a price limit would execute at a worse rate than the limit allows and `allow_partial` was false. Compare the `limit_price` argument with the subnet's current alpha price (`btcli subnets price`) or permit partial execution. | +| [`SwapInputTooLarge`](/docs/errors/chain/SwapInputTooLarge) | The swap's net input after fees exceeds 1000 times the input-side reserve, the pallet's hard per-trade cap. Compare the input amount against the subnet's input-side reserve (TAO or alpha) and split the trade if needed. | +| [`SwapReturnedZero`](/docs/errors/chain/SwapReturnedZero) | The netted pool swap in `execute_batched_orders` produced zero output for a non-zero input, meaning the pool lacks liquidity or the derived price limit clamped the swap entirely. Check the subnet pool's reserves and the batch's tightest slippage-derived price limit. | + +The same explanation is available in the terminal: `btcli explain insufficient_liquidity` (or `btcli explain ` for one exact chain error). diff --git a/docs/errors/internal.mdx b/docs/errors/internal.mdx new file mode 100644 index 0000000000..58eeb065e3 --- /dev/null +++ b/docs/errors/internal.mdx @@ -0,0 +1,41 @@ +--- +title: "internal" +description: "A chain-side invariant failed; nothing to fix client-side \u2014 report it if it persists" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The chain hit an internal invariant it could not uphold: an arithmetic overflow, an unreachable branch, a storage inconsistency, or a failure in an underlying pallet. + +There is usually nothing to fix client-side. retry once in case it was state-dependent; if it persists, report it upstream with the exact chain error name from the diagnostic. + +## Remediation + +A chain-side invariant failed; nothing to fix client-side — report it if it persists + +## Chain errors + +The exact chain error names (from the extrinsic receipt) that classify to `internal`; the description says what triggered the failure and where to check. Each name has its own page: + +| Chain error | Description | +| --- | --- | +| [`AnnouncementDepositInvariantViolated`](/docs/errors/chain/AnnouncementDepositInvariantViolated) | Internal invariant failure in `announce`: recomputing the announcement deposit returned nothing after the pending announcements were updated. Inspect the caller's `Announcements` entry and the announcement deposit constants; this indicates a pallet bug rather than bad input. | +| [`ArithmeticOverflow`](/docs/errors/chain/ArithmeticOverflow) | Converting a TAO amount to alpha during batched order execution overflowed the fixed-point range, typically when the pool price is tiny relative to the batch's total buy TAO. Check the subnet's current alpha price against the batch's aggregate buy amounts. | +| [`ContractReverted`](/docs/errors/chain/ContractReverted) | The contract ran to completion but returned with the REVERT flag set, rolling back its state changes; only extrinsics surface this as an error. Dry-run the call via RPC and decode the returned output data for the contract's error value. | +| [`ContractTrapped`](/docs/errors/chain/ContractTrapped) | The contract aborted with a WASM trap, e.g. a panic, unreachable instruction, or memory violation, instead of returning normally. Dry-run the call with debug messages enabled and check the input data against the contract's expectations. | +| [`CurrencyError`](/docs/errors/chain/CurrencyError) | A balance hold, release, or burn inside pallet-safe-mode failed while managing an enter/extend deposit. Check the account's free balance and existing holds under the safe-mode `EnterOrExtend` hold reason. | +| [`DrandConnectionFailure`](/docs/errors/chain/DrandConnectionFailure) | Declared for failures reaching the drand HTTP API, but pulse fetching happens in the offchain worker, which logs errors instead of raising this. Check the node's offchain worker logs and outbound connectivity to the drand endpoints. | +| [`FailedToSchedule`](/docs/errors/chain/FailedToSchedule) | The scheduler could not place the call into the agenda, typically because the target block's agenda is full or the schedule parameters are unusable. Check `Agenda` at the target block against `MaxScheduledPerBlock` and pick a different block if it is saturated. | +| [`FeeOverflow`](/docs/errors/chain/FeeOverflow) | Fee arithmetic overflowed, either multiplying the fee per gas by `gas_limit` or converting the EVM fee into Substrate balance decimals. Check for absurdly large `gas_limit` or fee-per-gas values in the transaction. | +| [`Overflow`](/docs/errors/chain/Overflow) | A checked arithmetic operation overflowed, e.g. incrementing `NextSubnetLeaseId` when registering a leased network, or adding to a crowdloan's `raised` amount or contributor count. Internal guard; inspect the amounts involved as this should not occur with realistic values. | +| [`PaymentOverflow`](/docs/errors/chain/PaymentOverflow) | Arithmetic overflowed while computing the total payment or refund for an EVM transaction, such as refunding remaining gas at the effective gas price. Check for extreme gas price or gas limit values in the transaction. | +| [`Reentrancy`](/docs/errors/chain/Reentrancy) | EVM execution re-entered the pallet while another EVM execution was already in progress on the same thread, e.g. a precompile or runtime call dispatching back into the EVM. Inspect precompiles and runtime code that invoke the EVM from within an EVM call. | +| [`StorageOverflow`](/docs/errors/chain/StorageOverflow) | Template leftover in the drand pallet for a counter increment overflowing `u32::MAX`; no current code path raises it. If seen, inspect the drand pallet's stored counters for values near the u32 limit. | +| [`TerminatedInConstructor`](/docs/errors/chain/TerminatedInConstructor) | The contract called `seal_terminate` inside its constructor, self-destructing during instantiation, which is forbidden. Inspect the constructor logic; termination is only allowed in regular message calls. | +| [`UidMapCouldNotBeCleared`](/docs/errors/chain/UidMapCouldNotBeCleared) | During a UID reshuffle or trim, clearing the subnet's `Uids` map left residual entries (the storage clear returned a cursor). Internal state inconsistency rather than a caller error; inspect the `Uids` storage for the netuid and report it. | +| [`Undefined`](/docs/errors/chain/Undefined) | Catch-all EVM validation error for cases without a dedicated variant, such as a malformed EIP-7702 authorization list or an unknown validation failure. Inspect the raw transaction for unsupported fields and check the node logs. | +| [`Underflow`](/docs/errors/chain/Underflow) | A checked subtraction underflowed in crowdloan accounting, e.g. `raised` exceeding `cap` when computing remaining room, or a contributor count decrement, indicating inconsistent state. Inspect the `Crowdloans` and `Contributions` entries for the `crowdloan_id`. | +| [`UnexpectedUnreserveLeftover`](/docs/errors/chain/UnexpectedUnreserveLeftover) | While lowering a commitment deposit, `Currency::unreserve` failed to return the full difference, leaving a leftover, which signals an internal inconsistency. Check the account's reserved balance against the deposit recorded in `CommitmentOf`. | +| [`Unreachable`](/docs/errors/chain/Unreachable) | `announce_next_key` could not identify the current block author via the `FindAuthors` lookup, which should be impossible in a normally authored block. Check the block's author digest and the shield pallet's authorship wiring. | + +The same explanation is available in the terminal: `btcli explain internal` (or `btcli explain ` for one exact chain error). diff --git a/docs/errors/invalid-argument.mdx b/docs/errors/invalid-argument.mdx new file mode 100644 index 0000000000..72f99c5aea --- /dev/null +++ b/docs/errors/invalid-argument.mdx @@ -0,0 +1,158 @@ +--- +title: "invalid_argument" +description: "Check the argument values against the operation schema" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +An argument was rejected before or at submission: a malformed address, an out-of-range value, a bad signature, or an extrinsic the node refused to accept into its pool. + +Check the argument values against the operation's schema (`btcli tools` prints the machine-readable catalog; `--help` on any command shows its parameters). + +## Remediation + +Check the argument values against the operation schema + +## Chain errors + +The exact chain error names (from the extrinsic receipt) that classify to `invalid_argument`; the description says what triggered the failure and where to check. Each name has its own page: + +| Chain error | Description | +| --- | --- | +| [`AccountRejectsLockedAlpha`](/docs/errors/chain/AccountRejectsLockedAlpha) | Locked alpha was being transferred to a coldkey whose `AccountFlags` do not have the accept-locked-alpha bit set, e.g. during a lock transfer or coldkey swap of locks. Check the destination coldkey's `AccountFlags` storage and have the recipient opt in to receiving locked alpha before retrying. | +| [`ActivityCutoffFactorMilliOutOfBounds`](/docs/errors/chain/ActivityCutoffFactorMilliOutOfBounds) | The `factor_milli` argument to set the activity-cutoff factor was outside the allowed 1000-50000 per-mille range (1 to 50 tempos). Adjust the argument to fall within those bounds before resubmitting. | +| [`ActivityCutoffTooLow`](/docs/errors/chain/ActivityCutoffTooLow) | An admin tried to set the subnet's activity cutoff below the chain-wide minimum. Compare the requested value against the `MinActivityCutoff` storage item and current `activity_cutoff` in `btcli sudo get --netuid `. | +| [`AlphaHighTooLow`](/docs/errors/chain/AlphaHighTooLow) | The `alpha_high` argument to set liquid-alpha values was below the minimum of roughly 0.025 (1638/65535 in u16 units). Raise `alpha_high` in the `sudo_set_alpha_values` call; current values are in the `AlphaValues` storage per netuid. | +| [`AlphaLowOutOfRange`](/docs/errors/chain/AlphaLowOutOfRange) | The `alpha_low` argument to set liquid-alpha values was below the ~0.025 minimum (1638/65535) or greater than `alpha_high`. Choose alpha_low within that range and not exceeding alpha_high; current settings are in the `AlphaValues` storage for the netuid. | +| [`AmountTooLow`](/docs/errors/chain/AmountTooLow) | A stake, unstake, move or swap amount was zero or its TAO equivalent fell below the minimum stake threshold after fees and slippage. Compare the amount against the `DefaultMinStake` storage item and the subnet's alpha price before retrying with a larger amount. | +| [`AnnouncedColdkeyHashDoesNotMatch`](/docs/errors/chain/AnnouncedColdkeyHashDoesNotMatch) | The `new_coldkey` passed to `coldkey_swap` hashes to a different value than the hash committed in the earlier `announce_coldkey_swap`. Verify the announced hash in the `ColdkeySwapAnnouncements` storage matches the BlakeTwo256 hash of the coldkey you are swapping to. | +| [`BadEncKeyLen`](/docs/errors/chain/BadEncKeyLen) | The `enc_key` passed to `announce_next_key` is not the exact ML-KEM-768 encapsulation key length (1184 bytes). Check the byte length of the `enc_key` argument before announcing. | +| [`BlockDurationTooLong`](/docs/errors/chain/BlockDurationTooLong) | The requested `end` block is more than `MaximumBlockDuration` blocks after the current block. Compare `end` minus the current block number against the `MaximumBlockDuration` pallet constant when calling `create` or `update_end`. | +| [`BlockDurationTooShort`](/docs/errors/chain/BlockDurationTooShort) | The requested `end` block is fewer than `MinimumBlockDuration` blocks after the current block. Compare `end` minus the current block number against the `MinimumBlockDuration` pallet constant when calling `create` or `update_end`. | +| [`CanNotSetRootNetworkWeights`](/docs/errors/chain/CanNotSetRootNetworkWeights) | `set_weights` was called with netuid 0, the root network, where normal weight setting is not allowed. Use a non-root `netuid` argument; root weights are handled by a separate mechanism. | +| [`CannotAddSelfAsDelegateDependency`](/docs/errors/chain/CannotAddSelfAsDelegateDependency) | A contract called `lock_delegate_dependency` with its own code hash, which is not permitted. Check the code hash argument passed to the delegate dependency API against the contract's own code hash. | +| [`CannotBurnOrRecycleOnRootSubnet`](/docs/errors/chain/CannotBurnOrRecycleOnRootSubnet) | `recycle_alpha` or `burn_alpha` was called with netuid 0, and TAO on the root subnet cannot be burned or recycled. Pass a non-root `netuid` argument for the subnet whose alpha you want to recycle or burn. | +| [`CannotEndInPast`](/docs/errors/chain/CannotEndInPast) | The `end` block passed to `create` or `update_end` is not after the current block. Compare the `end` argument against the current block number; it must be strictly greater. | +| [`CapTooLow`](/docs/errors/chain/CapTooLow) | On `create` the `cap` is not strictly greater than the initial `deposit`, or on `update_cap` the new cap is below the amount already raised. Compare the cap argument against the `deposit` or the `raised` field of the `Crowdloans` entry. | +| [`ChainIdMismatch`](/docs/errors/chain/ChainIdMismatch) | The order payload's `chain_id` field differs from this chain's configured EVM chain id, e.g. an order signed for testnet was submitted to mainnet. Compare the order's `chain_id` with the runtime's `pallet_evm_chain_id` value and re-sign if needed. | +| [`ChildParentInconsistency`](/docs/errors/chain/ChildParentInconsistency) | A `set_children` or parent-delegation call would make the same hotkey appear as both a child and a parent, or referenced a child missing from the proposed mapping. Inspect the `ChildKeys` and `ParentKeys` storage for the hotkeys involved and remove the overlap. | +| [`CodeInUse`](/docs/errors/chain/CodeInUse) | `remove_code` was refused because at least one contract instance still references the code hash. Check the code's reference count and terminate or `set_code` the contracts using it before removal. | +| [`CodeRejected`](/docs/errors/chain/CodeRejected) | The uploaded WASM failed validation, most often because it imports a host API the node does not support, e.g. newer ink! against an older node. Rerun the node with `-lruntime::contracts=debug` to see the detailed rejection reason. | +| [`ContributionTooLow`](/docs/errors/chain/ContributionTooLow) | The `amount` passed to `contribute` is below the crowdloan's configured minimum contribution. Check the `min_contribution` field of the `Crowdloans` entry for the `crowdloan_id` and contribute at least that amount. | +| [`DecodingFailed`](/docs/errors/chain/DecodingFailed) | Input bytes passed to a contract API host function could not be SCALE-decoded into the expected type. Check the encoding of the call's input data or the argument bytes the contract passes to the runtime API. | +| [`DelegateTakeTooHigh`](/docs/errors/chain/DelegateTakeTooHigh) | The `take` argument exceeds the maximum delegate take allowed by the chain (18% by default). Compare the requested value against the `MaxDelegateTake` storage item and lower it. | +| [`DelegateTakeTooLow`](/docs/errors/chain/DelegateTakeTooLow) | The `take` argument was below the `MinDelegateTake` minimum, or `increase_take`/`decrease_take` was not strictly increasing/decreasing relative to the current take. Check the hotkey's current take in the `Delegates` storage and the `MinDelegateTake` storage item. | +| [`DeltaZero`](/docs/errors/chain/DeltaZero) | The issuance adjustment was called with a delta of zero, which is meaningless. Check the `delta` argument to `force_adjust_total_issuance` and pass a strictly positive amount. | +| [`DepositCannotBeWithdrawn`](/docs/errors/chain/DepositCannotBeWithdrawn) | The creator called `withdraw` but holds nothing above the initial deposit, which stays locked until the crowdloan is dissolved. Compare the creator's `Contributions` entry with the `deposit` field of the `Crowdloans` entry. | +| [`DepositTooLow`](/docs/errors/chain/DepositTooLow) | The `deposit` argument to `create` is below the pallet's required minimum. Check the `MinimumDeposit` pallet constant and create the crowdloan with at least that initial deposit. | +| [`DuplicateChild`](/docs/errors/chain/DuplicateChild) | The children list passed to `set_children` contains the same child hotkey more than once. Deduplicate the `children` argument; current relations are visible in the `ChildKeys` storage for the parent hotkey and netuid. | +| [`DuplicateOrderInBatch`](/docs/errors/chain/DuplicateOrderInBatch) | Two entries in one `execute_batched_orders` call hash to the same order id, meaning the identical signed payload was included twice, which hard-fails the batch. Deduplicate by the blake2-256 hash of each SCALE-encoded `VersionedOrder`. | +| [`DuplicateUids`](/docs/errors/chain/DuplicateUids) | The `uids` vector passed to `set_weights` (or a reveal) contains the same UID more than once. Deduplicate the uids/values pairs before submitting; each target neuron may appear only once per weight vector. | +| [`FailedToExtractRuntimeVersion`](/docs/errors/chain/FailedToExtractRuntimeVersion) | The new runtime code passed to `set_code` did not yield a readable version: calling `Core_version` or decoding `RuntimeVersion` failed. Check that the submitted blob is a valid, complete runtime wasm and not truncated or compressed incorrectly. | +| [`FeeRateTooHigh`](/docs/errors/chain/FeeRateTooHigh) | `set_fee_rate` was called with a rate above the swap pallet's `MaxFeeRate` config constant. Compare the `rate` argument (u16-scaled fraction) against `MaxFeeRate` before submitting. | +| [`GasLimitTooHigh`](/docs/errors/chain/GasLimitTooHigh) | The transaction's `gas_limit` exceeds the block gas limit configured for the EVM. Compare the `gas_limit` argument against the chain's block gas limit and lower it. | +| [`GasLimitTooLow`](/docs/errors/chain/GasLimitTooLow) | The transaction's `gas_limit` is below the intrinsic gas required, or too small to cover the weight and proof-size base cost. Raise the `gas_limit` argument, comparing against an `eth_estimateGas` result. | +| [`GasPriceTooLow`](/docs/errors/chain/GasPriceTooLow) | The offered fee cannot satisfy the current base fee: `max_fee_per_gas` is below the block base fee, the priority fee exceeds the max fee, or the fee inputs are inconsistent. Check `max_fee_per_gas` and `max_priority_fee_per_gas` against the chain's base fee. | +| [`IncorrectCommitRevealVersion`](/docs/errors/chain/IncorrectCommitRevealVersion) | The `commit_reveal_version` argument does not match the chain's current commit-reveal weights version. Query the `CommitRevealWeightsVersion` storage item and upgrade or configure the client to commit with that version. | +| [`IncorrectPartialFillAmount`](/docs/errors/chain/IncorrectPartialFillAmount) | The `partial_fill` amount is zero or exceeds the order's remaining unfilled amount, or a full execution was submitted against an order already partially filled. Compare `partial_fill` with `order.amount` minus the filled amount recorded in `Orders`. | +| [`IncorrectWeightVersionKey`](/docs/errors/chain/IncorrectWeightVersionKey) | The `version_key` supplied with set_weights is older than the subnet's required weights version. Compare it against the `WeightsVersionKey` hyperparameter (`btcli sudo get --netuid `) and update the validator software or the key. | +| [`Indeterministic`](/docs/errors/chain/Indeterministic) | Code flagged as non-deterministic (e.g. using floating point) was used where determinism is enforced, such as on-chain instantiation or calls. Check the determinism mode the code was uploaded with and rebuild the contract deterministically. | +| [`InputForwarded`](/docs/errors/chain/InputForwarded) | The contract forwarded its input to a callee via `seal_call` with the FORWARD_INPUT flag and then tried to read or forward it again. Check the call flags used; use CLONE_INPUT when the input is still needed afterwards. | +| [`InputLengthsUnequal`](/docs/errors/chain/InputLengthsUnequal) | A batch weights call passed vectors of different lengths, e.g. netuids vs commit hashes, or uids vs values, salts and version_keys in batch reveal. Check that every parallel vector argument in the batch extrinsic has the same length. | +| [`InsufficientInputAmount`](/docs/errors/chain/InsufficientInputAmount) | Declared for swap inputs too small to execute, but no current code path raises it since the user-liquidity code was removed. If seen on an older runtime, check that the swap input amount is nonzero and large enough to produce output. | +| [`InvalidCallFlags`](/docs/errors/chain/InvalidCallFlags) | The flags bitmask given to `seal_call` or `seal_delegate_call` contains an unknown or disallowed combination; delegate calls accept only a restricted flag set. Check the flags argument against the supported `CallFlags` bit values. | +| [`InvalidChainId`](/docs/errors/chain/InvalidChainId) | The EIP-155 chain id encoded in the signed transaction does not match this chain's configured chain id. Compare the transaction's chain id with the value returned by `eth_chainId` and re-sign the transaction. | +| [`InvalidChild`](/docs/errors/chain/InvalidChild) | The children or parents list includes the pivot hotkey itself (a self-loop), including during a hotkey swap when the new hotkey is already a child or parent of the old one. Check the `children` argument and `ChildKeys`/`ParentKeys` for the hotkeys involved. | +| [`InvalidChildkeyTake`](/docs/errors/chain/InvalidChildkeyTake) | The childkey take is outside the allowed range for the subnet: below the effective minimum or above `MaxChildkeyTake`. Query `MinChildkeyTake` and `MaxChildkeyTake` and pick a `take` value within those bounds. | +| [`InvalidDerivedAccount`](/docs/errors/chain/InvalidDerivedAccount) | Deriving the sub-account for `as_derivative` failed to decode into a valid account id from the (caller, index) entropy. Check the `index` argument and the caller account used for derivation. | +| [`InvalidDerivedAccountId`](/docs/errors/chain/InvalidDerivedAccountId) | Deriving the pure proxy account id from the provided entropy failed to decode into a valid account. Check the spawner, `proxy_type`, and `index` arguments used with `create_pure` (or the equivalent lookup when destroying one). | +| [`InvalidDifficulty`](/docs/errors/chain/InvalidDifficulty) | The submitted proof-of-work hash does not meet the required difficulty (the faucet uses a fixed 1,000,000; PoW registration uses the subnet's difficulty). Check the `Difficulty` storage for the netuid and regenerate work against the current block. | +| [`InvalidEquivocationProof`](/docs/errors/chain/InvalidEquivocationProof) | The submitted GRANDPA equivocation proof does not demonstrate a real double-vote: the two votes may be identical, from different rounds or set ids, or badly signed. Verify the proof contains two distinct votes by the same authority in the same round and authority set. | +| [`InvalidFinalizationConfig`](/docs/errors/chain/InvalidFinalizationConfig) | Exactly one of `call` or `target_address` must be set, but both or neither were provided to `create`, or the stored crowdloan holds an inconsistent pair at `finalize` time. Check those two fields on the `create` arguments or the `Crowdloans` entry. | +| [`InvalidIdentity`](/docs/errors/chain/InvalidIdentity) | The submitted coldkey or subnet identity failed validation, typically a field exceeding its allowed byte length or malformed data. Check each identity field's length against the limits enforced by the chain before calling set_identity or set_subnet_identity. | +| [`InvalidIpAddress`](/docs/errors/chain/InvalidIpAddress) | The `ip` argument to serve_axon or serve_prometheus is not a valid address for the declared `ip_type` (prometheus additionally rejects the zero address). Verify the IP encodes correctly as IPv4 or IPv6 and matches the `ip_type` passed. | +| [`InvalidIpType`](/docs/errors/chain/InvalidIpType) | The `ip_type` argument to serve_axon or serve_prometheus is not 4 or 6. Check the value being sent by the miner or client; only IPv4 (4) and IPv6 (6) are accepted. | +| [`InvalidKeyOwnershipProof`](/docs/errors/chain/InvalidKeyOwnershipProof) | The key ownership proof does not establish that the offending GRANDPA key belonged to the claimed validator at that session. Regenerate the proof via the `generate_key_ownership_proof` runtime API for the exact set id and authority id in the report. | +| [`InvalidLeaseBeneficiary`](/docs/errors/chain/InvalidLeaseBeneficiary) | The account registering a leased network is not the creator of the crowdloan currently being finalized. Check the crowdloan's `creator` field in the crowdloan pallet storage and submit the call from that coldkey. | +| [`InvalidLiquidityValue`](/docs/errors/chain/InvalidLiquidityValue) | Legacy error from the removed V3 user-liquidity code, raised when an added or removed liquidity amount was below the pallet's `MinimumLiquidity`. Not raised on current runtimes; on older ones compare the `liquidity` argument to `MinimumLiquidity`. | +| [`InvalidNonce`](/docs/errors/chain/InvalidNonce) | The transaction nonce does not match the sender's current account nonce, being either too low (already used) or too high. Compare the transaction's `nonce` with the sender's on-chain nonce from `eth_getTransactionCount`. | +| [`InvalidNumRootClaim`](/docs/errors/chain/InvalidNumRootClaim) | The value passed to sudo_set_num_root_claims exceeds the compile-time maximum number of root claims. Check the `new_value` argument against the chain's `MAX_NUM_ROOT_CLAIMS` constant and pass a smaller number. | +| [`InvalidPort`](/docs/errors/chain/InvalidPort) | The `port` argument to serve_axon or serve_prometheus is 0, which is rejected. Check the miner or client axon configuration and serve on a non-zero port. | +| [`InvalidRecoveredPublicKey`](/docs/errors/chain/InvalidRecoveredPublicKey) | The EVM key association signature recovered to a public key whose keccak hash does not equal the supplied `evm_key`. Verify the signature was produced by the claimed EVM key over the hotkey plus block hash message (EIP-191 format). | +| [`InvalidRevealCommitHashNotMatch`](/docs/errors/chain/InvalidRevealCommitHashNotMatch) | The revealed uids, values, salt and version_key hash to a value that matches none of the hotkey's pending non-expired commits. Check that the reveal parameters and salt exactly match what was committed, and inspect `WeightCommits` for the hotkey and netuid. | +| [`InvalidRootClaimThreshold`](/docs/errors/chain/InvalidRootClaimThreshold) | The value passed to set the root claim threshold exceeds the chain's maximum allowed threshold. Check the `new_value` argument against the `MAX_ROOT_CLAIM_THRESHOLD` constant and the current `RootClaimableThreshold` for the netuid. | +| [`InvalidRoundNumber`](/docs/errors/chain/InvalidRoundNumber) | A submitted drand pulse has an unacceptable round: the first pulse ever stored must have a round greater than zero, and each later pulse must be exactly `LastStoredRound` plus one. Compare the pulse round against `LastStoredRound`. | +| [`InvalidSchedule`](/docs/errors/chain/InvalidSchedule) | The pallet's schedule is misconfigured, e.g. a zero weight or zero `ref_time_by_fuel` for a basic operation, making gas conversion impossible. This is a runtime configuration issue; inspect the `Schedule` constant rather than call arguments. | +| [`InvalidSeal`](/docs/errors/chain/InvalidSeal) | The seal hash recomputed from the supplied `block_number`, `nonce` and key does not equal the submitted `work`. Verify the PoW solver built the seal for the same key and block it submits, and that the work bytes were not corrupted in transit. | +| [`InvalidSignature`](/docs/errors/chain/InvalidSignature) | Signature verification failed: the sender of an Ethereum or EVM transaction could not be recovered from its signature, or a limit order's Sr25519 signature does not match the order payload and signer. Check the signing key, chain id, and the exact payload bytes that were signed. | +| [`InvalidSpecName`](/docs/errors/chain/InvalidSpecName) | The new runtime's `spec_name` does not match the current runtime, so `set_code` refuses the upgrade. Check the `RuntimeVersion` embedded in the new wasm and ensure the spec name is identical to the chain's current one. | +| [`InvalidSubnetNumber`](/docs/errors/chain/InvalidSubnetNumber) | A root-claim call passed an empty subnet set or more subnets than the per-call maximum (claim_root, or KeepSubnets in set_root_claim_type). Check the `subnets` argument is non-empty and within the `MAX_SUBNET_CLAIMS` limit. | +| [`InvalidTickRange`](/docs/errors/chain/InvalidTickRange) | Legacy error from the removed V3 user-liquidity code, raised when `tick_low` was not below `tick_high` or a tick failed to convert to a sqrt price. Not raised on current runtimes; check the tick range arguments on older ones. | +| [`InvalidValue`](/docs/errors/chain/InvalidValue) | A generic out-of-range parameter on an admin or sudo call, e.g. mechanism counts, emission splits summing away from 65535, max UIDs or take bounds. Check the specific argument against the min/max storage items the extrinsic validates (e.g. `MinAllowedUids`, `MaxMechanismCount`). | +| [`InvalidVotingPowerEmaAlpha`](/docs/errors/chain/InvalidVotingPowerEmaAlpha) | The alpha passed to set the voting power EMA exceeds 10^18, which represents 1.0. Check the `alpha` argument to sudo_set_voting_power_ema_alpha; it must be at most 10^18, and the current value is in `VotingPowerEmaAlpha` per netuid. | +| [`InvalidWorkBlock`](/docs/errors/chain/InvalidWorkBlock) | The `block_number` in the proof-of-work submission is in the future or more than 3 blocks old, so the work is stale. Compare the submitted block number with the current chain height and regenerate the PoW against a fresh block. | +| [`LeaseCannotEndInThePast`](/docs/errors/chain/LeaseCannotEndInThePast) | The `end_block` supplied when registering a leased network is not after the current block. Check the current chain height and pass an `end_block` in the future, or omit it for a perpetual lease. | +| [`LeaseHasNoEndBlock`](/docs/errors/chain/LeaseHasNoEndBlock) | The lease being terminated is perpetual (its `end_block` is None), so it can never be ended this way. Check the lease's `end_block` field in `SubnetLeases` for the given lease id. | +| [`LockHotkeyMismatch`](/docs/errors/chain/LockHotkeyMismatch) | The coldkey already has a conviction lock on this subnet bound to a different hotkey, and locks for one coldkey per subnet must target a single hotkey. Check the existing lock's hotkey in the lock storage for the coldkey and netuid, or move the lock first. | +| [`MaxAllowedUIdsLessThanCurrentUIds`](/docs/errors/chain/MaxAllowedUIdsLessThanCurrentUIds) | `sudo_set_max_allowed_uids` was given a value below the number of neurons already registered on the subnet. Compare the `max_allowed_uids` argument against `SubnetworkN` for that netuid. | +| [`MaxAllowedUidsGreaterThanDefaultMaxAllowedUids`](/docs/errors/chain/MaxAllowedUidsGreaterThanDefaultMaxAllowedUids) | `sudo_set_max_allowed_uids` was given a value above the chain-wide ceiling. Compare the `max_allowed_uids` argument against the `DefaultMaxAllowedUids` storage value. | +| [`MaxAllowedUidsLessThanMinAllowedUids`](/docs/errors/chain/MaxAllowedUidsLessThanMinAllowedUids) | `sudo_set_max_allowed_uids` was given a value below the subnet's configured minimum. Compare the `max_allowed_uids` argument against `MinAllowedUids` for that netuid. | +| [`MaxValidatorsLargerThanMaxUIds`](/docs/errors/chain/MaxValidatorsLargerThanMaxUIds) | `sudo_set_max_allowed_validators` was given a value exceeding the subnet's UID capacity. Compare the `max_allowed_validators` argument against `MaxAllowedUids` for that netuid. | +| [`MaxWeightTooLow`](/docs/errors/chain/MaxWeightTooLow) | The `max_weight` argument supplied with the final multisig approval is lower than the actual weight of the call being dispatched. Compute the call's real dispatch weight and pass a `max_weight` at least that large to `as_multi`. | +| [`MaximumContributionTooLow`](/docs/errors/chain/MaximumContributionTooLow) | The `new_max_contribution` passed to `set_max_contribution` is below the crowdloan's `min_contribution` or below the creator's existing contribution. Check both against the `Crowdloans` entry and the creator's `Contributions` entry. | +| [`MinAllowedUidsGreaterThanCurrentUids`](/docs/errors/chain/MinAllowedUidsGreaterThanCurrentUids) | `sudo_set_min_allowed_uids` was given a value not strictly below the number of neurons currently registered on the subnet. Compare the `min_allowed_uids` argument against `SubnetworkN` for that netuid. | +| [`MinAllowedUidsGreaterThanMaxAllowedUids`](/docs/errors/chain/MinAllowedUidsGreaterThanMaxAllowedUids) | `sudo_set_min_allowed_uids` was given a value not strictly below the subnet's maximum UID capacity. Compare the `min_allowed_uids` argument against `MaxAllowedUids` for that netuid. | +| [`MinimumContributionTooHigh`](/docs/errors/chain/MinimumContributionTooHigh) | The `new_min_contribution` passed to `update_min_contribution` exceeds the crowdloan's configured per-contributor maximum. Compare it with the `MaxContributions` entry for the `crowdloan_id`. | +| [`MinimumContributionTooLow`](/docs/errors/chain/MinimumContributionTooLow) | The minimum contribution given to `create` or `update_min_contribution` is below the chain-wide floor. Check the `AbsoluteMinimumContribution` pallet constant and raise the `min_contribution` argument to at least that value. | +| [`MinimumThreshold`](/docs/errors/chain/MinimumThreshold) | The multisig `threshold` argument was below 2, which these calls do not accept. Pass a threshold of 2 or more, or use `as_multi_threshold_1` for the single-approval case. | +| [`Named`](/docs/errors/chain/Named) | An unnamed scheduler function was used on a task that was scheduled with a name. Use the named variants (`cancel_named`, `schedule_named`) with the task's id, which you can find via the `Lookup` storage map. | +| [`NegativeSigmoidSteepness`](/docs/errors/chain/NegativeSigmoidSteepness) | A non-root caller (subnet owner) passed a negative value to `sudo_set_alpha_sigmoid_steepness`; negative steepness values are reserved for the root origin. Use a non-negative `steepness` or submit the call as root. | +| [`NewColdKeyIsHotkey`](/docs/errors/chain/NewColdKeyIsHotkey) | The proposed new coldkey in a coldkey swap is already an existing hotkey account, which is not allowed. Check the `Owner` storage for the candidate key to confirm it is not registered as a hotkey, and pick a fresh coldkey. | +| [`NewHotKeyIsSameWithOld`](/docs/errors/chain/NewHotKeyIsSameWithOld) | swap_hotkey was called with `new_hotkey` equal to the current hotkey, so there is nothing to swap. Check the extrinsic arguments and supply a different destination hotkey. | +| [`NewHotKeyNotCleanForRootSwap`](/docs/errors/chain/NewHotKeyNotCleanForRootSwap) | The destination hotkey has pending root claimable dividends, non-zero root stake, or root-claimed history, so root accounting cannot merge safely. Check `RootClaimable` and root-subnet stake for the new hotkey; claim or clear them, or use a fresh hotkey. | +| [`NoApprovalsNeeded`](/docs/errors/chain/NoApprovalsNeeded) | The multisig call does not need any more approvals; it is only waiting for final execution with the full call data. Instead of another `approve_as_multi`, submit `as_multi` with the complete call to dispatch it. | +| [`NoMigrationPerformed`](/docs/errors/chain/NoMigrationPerformed) | A `migrate` call ran but no migration step executed, either because no migration is pending or the supplied `weight_limit` is too small for one step. Check the in-progress migration status and increase the weight limit argument. | +| [`NoSelfProxy`](/docs/errors/chain/NoSelfProxy) | An account attempted to register itself as its own proxy, which is not allowed. Check the `delegate` argument to `add_proxy` and ensure it differs from the calling (delegator) account. | +| [`NoTimepoint`](/docs/errors/chain/NoTimepoint) | No timepoint was supplied but this multisig operation is already underway, so the approval cannot be matched to it. Read the operation's `when` field from the `Multisigs` entry for the call hash and pass that height and index as `maybe_timepoint`. | +| [`NonDefaultComposite`](/docs/errors/chain/NonDefaultComposite) | The account cannot be killed because its composite account data is not in the default state. Check the account's `System.Account` entry; all balance and data fields must be default before the account can be removed this way. | +| [`NonZeroRefCount`](/docs/errors/chain/NonZeroRefCount) | The account cannot be purged because other pallets still reference it. Check the `consumers`, `providers`, and `sufficients` counters in the account's `System.Account` record; all references must be released first. | +| [`NotPermittedOnRootSubnet`](/docs/errors/chain/NotPermittedOnRootSubnet) | An admin-utils call that only applies to regular subnets (burn half-life, burn increase multiplier, owner-cut flags, or the subnet emission toggle) was targeted at the root network. Check that the `netuid` argument is not the root netuid 0. | +| [`NotRootSubnet`](/docs/errors/chain/NotRootSubnet) | A call that only operates on the root network, such as setting root network weights, was given a non-root netuid. Check the netuid argument; root operations must target netuid 0. | +| [`OrderNetUidMismatch`](/docs/errors/chain/OrderNetUidMismatch) | An order inside an `execute_batched_orders` call has a `netuid` field different from the batch's `netuid` parameter, which hard-fails the entire batch. Check each order payload's `netuid` against the batch argument and split mismatched orders out. | +| [`OutOfBounds`](/docs/errors/chain/OutOfBounds) | A pointer and length pair passed to a contract API host function references memory outside the contract's sandbox. Check the buffer pointers and lengths the contract passes to seal functions; this usually indicates a low-level contract bug. | +| [`OutputBufferTooSmall`](/docs/errors/chain/OutputBufferTooSmall) | The output buffer the contract supplied to an API call is smaller than the data the runtime needs to write back. Check the output length pointer the contract passes and enlarge the buffer; usually a contract-side bug. | +| [`PauseFailed`](/docs/errors/chain/PauseFailed) | A GRANDPA pause was signalled while the authority set is not live, i.e. it is already paused or a pause is already pending. Check the Grandpa `State` storage before signalling a pause. | +| [`PreLogExists`](/docs/errors/chain/PreLogExists) | An `ethereum.transact` extrinsic was submitted in a block that already carries a pre-log digest, meaning an Ethereum transaction is being injected by other means. Check the block's digest for a pre-runtime Ethereum log; transact is not allowed alongside it. | +| [`ProportionOverflow`](/docs/errors/chain/ProportionOverflow) | The child proportions passed to `set_children` sum to more than u64::MAX. Reduce the per-child proportion values so their total fits in a u64; each proportion is a fraction of u64::MAX. | +| [`PulseVerificationError`](/docs/errors/chain/PulseVerificationError) | BLS signature verification of a submitted drand pulse against the stored beacon configuration failed. Check the pulse's signature and round against the `BeaconConfig` storage (drand quicknet public key). | +| [`ReentranceDenied`](/docs/errors/chain/ReentranceDenied) | A call tried to re-enter a contract already on the call stack without reentrancy being allowed, or contract code called back into the contracts pallet through the runtime. Check the callee address against the current call stack and the ALLOW_REENTRY call flag. | +| [`RegistrationNotPermittedOnRootSubnet`](/docs/errors/chain/RegistrationNotPermittedOnRootSubnet) | A neuron registration or child-hotkey operation (`register`, `burned_register`, `set_children`) was called with the root netuid, where these calls are invalid. Check the netuid argument; use a regular subnet, or `root_register` for root membership. | +| [`RelayerMissMatch`](/docs/errors/chain/RelayerMissMatch) | The order's `relayer` allowlist is set but the account that submitted the execution transaction is not in it. Compare the extrinsic's signing account against the `relayer` list in the signed order payload. | +| [`RelayerRequiredForPartialFill`](/docs/errors/chain/RelayerRequiredForPartialFill) | A `partial_fill` was requested for an order whose `relayer` field is empty; partial fills are only permitted on orders that restrict who may execute them. Check the order payload and either set a relayer list or execute the full amount. | +| [`Requested`](/docs/errors/chain/Requested) | The preimage cannot be unnoted while there are still outstanding requests for it. Check the request count in the hash's request status; all requests must be cleared before the preimage can be removed. | +| [`RescheduleNoChange`](/docs/errors/chain/RescheduleNoChange) | The reschedule was rejected because the new dispatch time equals the task's currently scheduled time. Check the task's existing slot in `Agenda` and pass a genuinely different `when` block. | +| [`ResumeFailed`](/docs/errors/chain/ResumeFailed) | A GRANDPA resume was signalled while the authority set is not paused, i.e. it is live or already pending a resume. Check the Grandpa `State` storage before signalling a resume. | +| [`RevealPeriodTooLarge`](/docs/errors/chain/RevealPeriodTooLarge) | `set_reveal_period` was given a commit-reveal period above the compiled-in maximum number of epochs. Lower the `reveal_period` argument; the current setting is readable from `RevealPeriodEpochs` for the netuid. | +| [`RevealPeriodTooSmall`](/docs/errors/chain/RevealPeriodTooSmall) | `set_reveal_period` was given a commit-reveal period below the compiled-in minimum number of epochs. Raise the `reveal_period` argument; the current setting is readable from `RevealPeriodEpochs` for the netuid. | +| [`RootNetUidNotAllowed`](/docs/errors/chain/RootNetUidNotAllowed) | The order or batch targets the root subnet, netuid 0, which the limit orders pallet does not serve. Check the `netuid` field of the order payload or the `netuid` parameter of the batch call and target a non-root subnet. | +| [`SameNetuid`](/docs/errors/chain/SameNetuid) | A stake swap or move where coldkey, hotkey, and subnet are all unchanged, so `origin_netuid` equals `destination_netuid` with nothing to transition. Check the call arguments; at least the subnet or one of the keys must differ. | +| [`SenderInSignatories`](/docs/errors/chain/SenderInSignatories) | The multisig sender was included in the `other_signatories` list, but that list must contain only the remaining signatories. Remove the sender's own account from `other_signatories` before resubmitting. | +| [`SignatoriesOutOfOrder`](/docs/errors/chain/SignatoriesOutOfOrder) | The `other_signatories` list is not sorted in strictly ascending account order, which the multisig pallet requires for a canonical account derivation. Sort the list and remove duplicates before resubmitting. | +| [`SpecVersionNeedsToIncrease`](/docs/errors/chain/SpecVersionNeedsToIncrease) | The new runtime's `spec_version` is not greater than the current one, so the upgrade is rejected. Check the `RuntimeVersion` in the new wasm and bump `spec_version` above the version currently on chain. | +| [`TargetBlockNumberInPast`](/docs/errors/chain/TargetBlockNumberInPast) | The scheduler was given a dispatch block that is not in the future. Compare the `when` argument against the current block number and choose a strictly later block. | +| [`TempoOutOfBounds`](/docs/errors/chain/TempoOutOfBounds) | The subnet owner gave `sudo_set_tempo` a tempo outside the allowed MIN_TEMPO to MAX_TEMPO range (360-50,400 blocks). Check the tempo argument against those chain constants and pick a value inside the bounds; only root may set a tempo outside them. | +| [`TerminatedWhileReentrant`](/docs/errors/chain/TerminatedWhileReentrant) | `seal_terminate` was called on a contract that appears more than once on the call stack, so termination was refused. Check the call chain for reentrant calls into the contract being terminated. | +| [`TooFew`](/docs/errors/chain/TooFew) | The bulk preimage upgrade was requested with zero hashes, so there is nothing to do. Pass at least one hash to `ensure_updated`. | +| [`TooFewSignatories`](/docs/errors/chain/TooFewSignatories) | The multisig signatory list is too short: `other_signatories` must contain at least one account besides the sender. Check the length of `other_signatories` against the threshold you are using. | +| [`UidVecContainInvalidOne`](/docs/errors/chain/UidVecContainInvalidOne) | The weight submission includes a UID that is not registered on the subnet, i.e. at least one entry is not below `SubnetworkN`. Check the `uids` argument against the subnet's neuron count in the metagraph (`btcli subnets metagraph`). | +| [`UnableToRecoverPublicKey`](/docs/errors/chain/UnableToRecoverPublicKey) | While associating an EVM key, the secp256k1 public key recovered from the supplied signature could not be parsed. Check that the signature was produced by signing the expected EIP-191 message (hotkey plus block hash) with the EVM private key. | +| [`UnexpectedTimepoint`](/docs/errors/chain/UnexpectedTimepoint) | A timepoint was supplied but no multisig operation is underway for this call hash; the first approval must open the operation with no timepoint. Pass `maybe_timepoint: None` on the opening call, or verify the call hash matches an existing `Multisigs` entry. | +| [`UnverifiedPulse`](/docs/errors/chain/UnverifiedPulse) | Declared for drand pulses that fail validity checks, but current code raises `PulseVerificationError` for verification failures and silently skips unverified pulses. If seen on an older runtime, check the pulse signature against `BeaconConfig`. | +| [`ValueNotInBounds`](/docs/errors/chain/ValueNotInBounds) | An admin-utils argument fell outside its allowed range: `min_burn` must be below `MinBurnUpperBound` and the subnet's max burn, `max_burn` above `MaxBurnLowerBound` and the min burn, and `max_epochs_per_block` at least 1. Check the argument against those bounds. | +| [`WeightVecLengthIsLow`](/docs/errors/chain/WeightVecLengthIsLow) | The weight submission has fewer entries than the subnet's minimum (setting only a self-weight is the one exception). Compare the vector length against the `MinAllowedWeights` (min_allowed_weights hyperparameter) for the netuid. | +| [`WeightVecNotEqualSize`](/docs/errors/chain/WeightVecNotEqualSize) | The `uids` and `values` vectors passed to a weight-setting call have different lengths, so they cannot be paired. Check the call arguments; both vectors must have exactly one value per UID. | +| [`WrongTimepoint`](/docs/errors/chain/WrongTimepoint) | The timepoint supplied does not match the one recorded when this multisig operation was opened. Read the correct `when` height and index from the `Multisigs` entry for the call hash and resubmit with that exact timepoint. | +| [`XCMDecodeFailed`](/docs/errors/chain/XCMDecodeFailed) | The bytes the contract passed to `xcm_execute` or `xcm_send` could not be decoded as a versioned XCM message. Check the XCM encoding and version the contract produces against what the runtime supports. | +| [`ZeroShareInBatch`](/docs/errors/chain/ZeroShareInBatch) | An order's pro-rata share of the batch output floored to zero, so the whole batch was rejected rather than consuming that order's input for no payout. Check the order's `amount` relative to the batch totals and retry it in a differently composed batch. | + +The same explanation is available in the terminal: `btcli explain invalid_argument` (or `btcli explain ` for one exact chain error). diff --git a/docs/errors/limit-exceeded.mdx b/docs/errors/limit-exceeded.mdx new file mode 100644 index 0000000000..4b50bf56b0 --- /dev/null +++ b/docs/errors/limit-exceeded.mdx @@ -0,0 +1,57 @@ +--- +title: "limit_exceeded" +description: "A chain-side capacity limit was hit; reduce the size or count of the request" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +A chain-side capacity limit was hit: too many children, weights above the maximum, no free UID slots on the subnet, the subnet limit reached, or a batch/queue at its configured maximum. + +These are hard caps rather than timing windows, so retrying unchanged will fail again. reduce the size or count of the request, or free capacity first (e.g. remove a child before adding another). + +## Remediation + +A chain-side capacity limit was hit; reduce the size or count of the request + +## Chain errors + +The exact chain error names (from the extrinsic receipt) that classify to `limit_exceeded`; the description says what triggered the failure and where to check. Each name has its own page: + +| Chain error | Description | +| --- | --- | +| [`BondsMovingAverageMaxReached`](/docs/errors/chain/BondsMovingAverageMaxReached) | A subnet owner called `sudo_set_bonds_moving_average` with a value above 975000, the cap for owner-set values; root is exempt. Lower the `bonds_moving_average` argument or submit the call as root. | +| [`CapRaised`](/docs/errors/chain/CapRaised) | A contribution was attempted on a crowdloan whose `raised` amount has already reached its `cap`, so no further contributions are accepted. Compare the `raised` and `cap` fields of the `Crowdloans` entry for the `crowdloan_id`. | +| [`CodeTooLarge`](/docs/errors/chain/CodeTooLarge) | The code blob passed to `instantiate_with_code` or `upload_code` exceeds the maximum code length in the pallet's schedule. Compare the WASM binary size against the schedule's code length limit and shrink the contract. | +| [`EvmKeyAssociationLimitExceeded`](/docs/errors/chain/EvmKeyAssociationLimitExceeded) | The EVM address is already associated with the maximum number of UIDs allowed on this subnet. Inspect the `AssociatedUidsByEvmAddress` storage for the (netuid, evm_key) pair and free a slot or use a different EVM address. | +| [`LockIdOverFlow`](/docs/errors/chain/LockIdOverFlow) | The global network-registration lock id counter reached its u32 maximum while queueing a subnet registration, so no new lock could be created. Check the `NetworkRegistrationLockId` storage value; this indicates lock id exhaustion, not a balance problem. | +| [`MaxCallDepthReached`](/docs/errors/chain/MaxCallDepthReached) | A nested contract call would exceed the maximum call stack depth defined in the pallet schedule. Inspect the cross-contract call chain for deep or unbounded recursion and flatten it or raise the configured depth. | +| [`MaxContributionReached`](/docs/errors/chain/MaxContributionReached) | The contributor's cumulative contribution already equals the crowdloan's per-contributor cap, so further contributions are rejected. Compare their `Contributions` entry with the `MaxContributions` value for the `crowdloan_id`. | +| [`MaxContributorsReached`](/docs/errors/chain/MaxContributorsReached) | The crowdloan already has the maximum number of distinct contributors, so accounts without an existing contribution are rejected. Compare the `contributors_count` field of the `Crowdloans` entry with the `MaxContributors` pallet constant. | +| [`MaxDelegateDependenciesReached`](/docs/errors/chain/MaxDelegateDependenciesReached) | `lock_delegate_dependency` failed because the contract already holds the maximum number of delegate dependencies allowed by the runtime. Check the `MaxDelegateDependencies` config value and unlock unused dependencies first. | +| [`MaxWeightExceeded`](/docs/errors/chain/MaxWeightExceeded) | After normalization, one of the submitted weights exceeds the subnet's maximum weight limit (self-weight is exempt). Check the `MaxWeightsLimit` hyperparameter (`btcli sudo get --netuid `) and flatten the weight vector before setting. | +| [`NoNeuronIdAvailable`](/docs/errors/chain/NoNeuronIdAvailable) | Registration could not obtain a uid: the subnet's `MaxAllowedUids` is 0, or the subnet is full and every neuron is immune from pruning. Check `SubnetworkN` versus `MaxAllowedUids` for the netuid and retry after immunity periods expire. | +| [`OutOfGas`](/docs/errors/chain/OutOfGas) | The contract exhausted the gas limit supplied for this execution before completing. Increase the `gas_limit` argument on `call` or `instantiate`; dry-run the call via RPC to estimate the required weight. | +| [`OutOfTransientStorage`](/docs/errors/chain/OutOfTransientStorage) | A write would exceed the per-execution byte limit for transient storage. Check how much data the contract places in transient storage during the call against the runtime's transient storage limit. | +| [`RandomSubjectTooLong`](/docs/errors/chain/RandomSubjectTooLong) | The subject buffer given to the deprecated `seal_random` API exceeds the schedule's `subject_len` limit. Shorten the randomness subject the contract passes or check the schedule's limits section. | +| [`RegistrationPriceLimitExceeded`](/docs/errors/chain/RegistrationPriceLimitExceeded) | `burned_register` with a price limit failed because the subnet's current registration burn cost exceeds the supplied `limit_price`. Check the current burn via `Burn` storage or `btcli subnets list` and raise the limit or wait for the cost to decay. | +| [`StorageDepositLimitExhausted`](/docs/errors/chain/StorageDepositLimitExhausted) | The execution created more storage than the caller's `storage_deposit_limit` allows to be charged. Raise the `storage_deposit_limit` argument or reduce storage usage; a dry-run reports the required `storage_deposit`. | +| [`SubnetLimitReached`](/docs/errors/chain/SubnetLimitReached) | `register_network` failed because the subnet count is at the network limit and no existing subnet is eligible to be pruned. Check the number of registered subnets (`btcli subnets list`) against the subnet limit and retry once a subnet becomes prunable. | +| [`TooBig`](/docs/errors/chain/TooBig) | The preimage exceeds the maximum size the pallet will store on-chain (4 MiB). Check the byte length of the preimage against the pallet's `MAX_SIZE` limit before noting it. | +| [`TooMany`](/docs/errors/chain/TooMany) | A limit was exceeded: more preimage hashes than `MAX_HASH_UPGRADE_BULK_COUNT` were passed to `ensure_updated`, or the account has hit its proxy or announcement cap. Check the account's `Proxies` and `Announcements` entries against `MaxProxies` and `MaxPending`. | +| [`TooManyCalls`](/docs/errors/chain/TooManyCalls) | The batch submitted to the utility pallet contains more calls than the batched-calls limit allows. Check the length of the `calls` vector and split the work across multiple smaller `batch`, `batch_all`, or `force_batch` submissions. | +| [`TooManyChildren`](/docs/errors/chain/TooManyChildren) | `set_children` was called with more than 5 child hotkeys for a parent on the subnet. Trim the children list to at most 5 entries. | +| [`TooManyFieldsInCommitmentInfo`](/docs/errors/chain/TooManyFieldsInCommitmentInfo) | The `CommitmentInfo` passed to `set_commitment` contains more entries in `fields` than the pallet's `MaxFields` config allows. Count the fields in the `info` argument and trim to the `MaxFields` limit. | +| [`TooManyFreezes`](/docs/errors/chain/TooManyFreezes) | The account already has the maximum number of balance freezes, so a new freeze cannot be added. Check the account's `Freezes` entry against the `MaxFreezes` constant; an existing freeze must be thawed first. | +| [`TooManyHolds`](/docs/errors/chain/TooManyHolds) | The account already carries the maximum number of balance holds, one per hold reason variant. Check the account's `Holds` entry; an existing hold must be released before a new reason can place another. | +| [`TooManyPendingExtrinsics`](/docs/errors/chain/TooManyPendingExtrinsics) | `store_encrypted` was rejected because the shield pallet's queue of encrypted extrinsics is already at capacity. Compare the `PendingExtrinsics` count against `MaxPendingExtrinsicsLimit` and wait for queued items to be processed or expire. | +| [`TooManyReserves`](/docs/errors/chain/TooManyReserves) | The account already has the maximum number of named reserves. Check the account's `Reserves` entry against the `MaxReserves` constant; a named reserve must be unreserved before adding another. | +| [`TooManySignatories`](/docs/errors/chain/TooManySignatories) | The multisig signatory list exceeds the maximum allowed. Compare the length of `other_signatories` plus the sender against the pallet's `MaxSignatories` constant. | +| [`TooManyTopics`](/docs/errors/chain/TooManyTopics) | The number of topics passed to `seal_deposit_event` exceeds the schedule's `event_topics` limit. Reduce the number of indexed topics in the contract's event definition or compare against the schedule limit. | +| [`TooManyUIDsPerMechanism`](/docs/errors/chain/TooManyUIDsPerMechanism) | Setting max UIDs or mechanism count would make max_uids times mechanism_count exceed the chain default of 256 UIDs per subnet. Check `MaxAllowedUids` and the subnet's mechanism count so their product stays within the limit. | +| [`TooManyUnrevealedCommits`](/docs/errors/chain/TooManyUnrevealedCommits) | `commit_weights` (or a CRv3 commit) failed because the hotkey already has 10 unrevealed commits queued on the subnet. Inspect `WeightCommits` for the hotkey and reveal or let old commits expire before committing again. | +| [`TrimmingWouldExceedMaxImmunePercentage`](/docs/errors/chain/TrimmingWouldExceedMaxImmunePercentage) | Trimming the subnet's UIDs cannot proceed because immune neurons would make up at least the maximum immune share (80%) of the reduced slot count. Check neurons still in `ImmunityPeriod` and retry after their immunity lapses or with a higher max UID target. | +| [`UidsLengthExceedUidsInSubNet`](/docs/errors/chain/UidsLengthExceedUidsInSubNet) | The weight submission contains more UID entries than there are neurons registered on the subnet. Compare the length of the `uids` argument against `SubnetworkN` for the netuid and trim the vector. | +| [`ValueTooLarge`](/docs/errors/chain/ValueTooLarge) | A value written to contract storage or emitted as event data exceeds the `MaxValueSize` limit. Compare the size of the stored value or event payload against the runtime's maximum value size constant. | +| [`WeightExceedsAbsoluteMax`](/docs/errors/chain/WeightExceedsAbsoluteMax) | `set_on_initialize_weight` or `set_max_extrinsic_weight` was given a value above the shield pallet's hard cap of half the total block weight. Compare the `value` argument against the `MAX_ON_INITIALIZE_WEIGHT` constant. | + +The same explanation is available in the terminal: `btcli explain limit_exceeded` (or `btcli explain ` for one exact chain error). diff --git a/docs/errors/meta.json b/docs/errors/meta.json new file mode 100644 index 0000000000..d760fdcc2b --- /dev/null +++ b/docs/errors/meta.json @@ -0,0 +1,25 @@ +{ + "title": "Errors", + "pages": [ + "index", + "insufficient-balance", + "insufficient-liquidity", + "rate-limited", + "not-registered", + "not-authorized", + "already-exists", + "not-found", + "subnet-not-exists", + "subtoken-disabled", + "disabled", + "too-early", + "expired", + "limit-exceeded", + "unit-mismatch", + "invalid-argument", + "policy-violation", + "internal", + "unknown", + "chain" + ] +} diff --git a/docs/errors/not-authorized.mdx b/docs/errors/not-authorized.mdx new file mode 100644 index 0000000000..f2f2ab34a0 --- /dev/null +++ b/docs/errors/not-authorized.mdx @@ -0,0 +1,45 @@ +--- +title: "not_authorized" +description: "Sign with the key or origin that owns the target object, then retry" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The signing key is not allowed to perform this call: the coldkey is not associated with the hotkey, the caller is not the subnet owner, the origin is filtered, a proxy lacks permission for this call type, or the call requires a root/sudo origin. + +Check which key actually owns the target object and sign with it. for proxy signing, the proxy type must permit the call; for subnet hyperparameters, the signer must be the subnet owner coldkey. + +## Remediation + +Sign with the key or origin that owns the target object, then retry + +## Chain errors + +The exact chain error names (from the extrinsic receipt) that classify to `not_authorized`; the description says what triggered the failure and where to check. Each name has its own page: + +| Chain error | Description | +| --- | --- | +| [`AccountNotAllowedCommit`](/docs/errors/chain/AccountNotAllowedCommit) | Raised by `set_commitment` when the runtime commit check fails: the subnet must exist and the signing hotkey must be registered on it. Verify the `netuid` and that the hotkey has a UID on that subnet. | +| [`BeneficiaryDoesNotOwnHotkey`](/docs/errors/chain/BeneficiaryDoesNotOwnHotkey) | When ending a subnet lease, the hotkey passed for the ownership handover is not owned by the lease's beneficiary coldkey. Check the `Owner` storage for that hotkey and pass a hotkey the beneficiary coldkey actually owns. | +| [`CallFiltered`](/docs/errors/chain/CallFiltered) | The runtime's origin call filter (e.g. `BaseCallFilter` or a restricted origin) rejected this call before dispatch. Check whether the specific call is permitted for the origin you used, including any proxy or safe-mode filtering in effect. | +| [`CannotUseSystemAccount`](/docs/errors/chain/CannotUseSystemAccount) | The hotkey supplied for registration, hotkey swap, or subnet-owner-hotkey assignment is a reserved subnet system account. Use a regular user-generated hotkey instead; system accounts are derived per-subnet and rejected by `is_subnet_account_id`. | +| [`ColdkeySwapDisputed`](/docs/errors/chain/ColdkeySwapDisputed) | All extrinsics from this coldkey are blocked because its pending coldkey swap is under dispute. Check the `ColdkeySwapDisputes` storage for the coldkey; the dispute must be resolved by root before the account can transact. | +| [`CreateOriginNotAllowed`](/docs/errors/chain/CreateOriginNotAllowed) | A CREATE, or a CALL that performs a nested CREATE, was attempted from an EVM address not permitted to deploy contracts. Check whether the deploying address is in the chain's allowed-deployers list. | +| [`ExpectedBeneficiaryOrigin`](/docs/errors/chain/ExpectedBeneficiaryOrigin) | A lease operation such as terminating a subnet lease was signed by an account other than the lease's beneficiary coldkey. Check the beneficiary recorded in the `SubnetLeases` storage for the lease id and sign with that coldkey. | +| [`InvalidOrigin`](/docs/errors/chain/InvalidOrigin) | The caller is not the crowdloan's creator, which is required for `finalize`, `refund`, `dissolve`, and the update calls. Compare the signing account with the `creator` field of the `Crowdloans` entry for the `crowdloan_id`. | +| [`NeuronNoValidatorPermit`](/docs/errors/chain/NeuronNoValidatorPermit) | The hotkey tried to set weights on other neurons without holding a validator permit on that subnet. Check `ValidatorPermit` for the neuron's uid (e.g. via the metagraph or `btcli subnets metagraph`) and whether its stake ranks it as a validator. | +| [`NoPermission`](/docs/errors/chain/NoPermission) | The proxy pallet refused the action: the proxied call could escalate privileges, or the caller lacks authority over the pure proxy (e.g. `kill_pure` by a non-spawner). Check the proxy type's call filter and the original `create_pure` arguments. | +| [`NonAssociatedColdKey`](/docs/errors/chain/NonAssociatedColdKey) | The signing coldkey does not own the hotkey it is trying to operate on (stake, swap, serve, children or take changes). Check the `Owner` storage entry for the hotkey, e.g. via `btcli wallet overview`, and sign with the coldkey that registered it. | +| [`NotAllowed`](/docs/errors/chain/NotAllowed) | Contract deployment was blocked because the source address is not in the `WhitelistedCreators` list while the whitelist check is enabled. Check the deployer address against `WhitelistedCreators` and the `DisableWhitelistCheck` storage value. | +| [`NotAuthorized`](/docs/errors/chain/NotAuthorized) | The caller is not permitted to manage this preimage; unnoting or unrequesting requires the pallet's manager origin or the account that originally deposited it. Check which origin noted or requested the preimage and use that origin or the configured `ManagerOrigin`. | +| [`NotOwner`](/docs/errors/chain/NotOwner) | Only the account that opened the multisig operation (its depositor) may cancel it or adjust its deposit. Compare the sender against the `depositor` field stored in the `Multisigs` entry for this call hash. | +| [`NotProxy`](/docs/errors/chain/NotProxy) | The sender is not registered as a proxy for the account it tried to act for. Check the `Proxies` entry of the `real` account and confirm the sender appears there with a proxy type and delay compatible with the call. | +| [`NotSubnetOwner`](/docs/errors/chain/NotSubnetOwner) | The signing coldkey is not the recorded owner of the subnet it tried to administer (e.g. setting subnet identity or owner-only hyperparameters). Compare the caller against the `SubnetOwner` storage entry for that netuid. | +| [`RequireSudo`](/docs/errors/chain/RequireSudo) | The call requires the sudo key but was signed by a different account. Compare the sender against the account stored in the Sudo pallet's `Key` storage item. | +| [`StateChangeDenied`](/docs/errors/chain/StateChangeDenied) | The contract invoked a state-modifying host function, such as a storage write, transfer, or value-bearing call, while executing in read-only mode. Check whether the enclosing call was made with the read-only flag or from a static context. | +| [`TransactionMustComeFromEOA`](/docs/errors/chain/TransactionMustComeFromEOA) | Rejected per EIP-3607: the sender address has contract code deployed, and transactions must originate from externally owned accounts. Check `eth_getCode` for the `source` address and sign with a plain EOA key instead. | +| [`TransactorAccountShouldBeHotKey`](/docs/errors/chain/TransactorAccountShouldBeHotKey) | The extrinsic must be signed by the hotkey itself, but a different account (typically the coldkey) was the origin. Check which key signs the transaction; calls like axon serving expect the hotkey as origin. | +| [`Unauthorized`](/docs/errors/chain/Unauthorized) | In System, the code passed to `apply_authorized_upgrade` does not hash to the value in `AuthorizedUpgrade`. In LimitOrders, the caller is not the order's signer, who alone may cancel it. Check the code hash against the authorization, or the sender against the order's `signer`. | +| [`Unproxyable`](/docs/errors/chain/Unproxyable) | The attempted call is not permitted by the registered proxy type's call filter. Check which `proxy_type` the sender holds in the real account's `Proxies` entry and whether that type's `InstanceFilter` allows this specific call. | + +The same explanation is available in the terminal: `btcli explain not_authorized` (or `btcli explain ` for one exact chain error). diff --git a/docs/errors/not-found.mdx b/docs/errors/not-found.mdx new file mode 100644 index 0000000000..87b5e0e93d --- /dev/null +++ b/docs/errors/not-found.mdx @@ -0,0 +1,43 @@ +--- +title: "not_found" +description: "The referenced object is not on-chain; check the identifier" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The call references an object that is not on-chain: a weights commit, lease, multisig operation, scheduled call, announcement, or contribution that was never created, was already consumed, or has been removed. + +Check the identifier and the current chain state. if the flow has a create step (commit before reveal, announce before execute), make sure it completed on the network you are connected to (check `--network`). + +## Remediation + +The referenced object is not on-chain; check the identifier + +## Chain errors + +The exact chain error names (from the extrinsic receipt) that classify to `not_found`; the description says what triggered the failure and where to check. Each name has its own page: + +| Chain error | Description | +| --- | --- | +| [`CallUnavailable`](/docs/errors/chain/CallUnavailable) | During `finalize` the crowdloan's stored call could not be fetched from preimage storage, so nothing was dispatched. Check that the preimage referenced by the `call` field of the `Crowdloans` entry still exists in the preimage pallet. | +| [`CodeInfoNotFound`](/docs/errors/chain/CodeInfoNotFound) | No `CodeInfoOf` entry exists for the supplied code hash, so its owner and deposit metadata cannot be read. Verify the code hash argument and that the code was uploaded and not since removed. | +| [`CodeNotFound`](/docs/errors/chain/CodeNotFound) | No uploaded WASM binary exists under the supplied code hash. Verify the `code_hash` argument used in instantiation, `set_code`, or a delegate call, and that the code was uploaded via `upload_code` and not removed. | +| [`ColdkeySwapAnnouncementNotFound`](/docs/errors/chain/ColdkeySwapAnnouncementNotFound) | `coldkey_swap`, `dispute_coldkey_swap`, or `clear_coldkey_swap_announcement` was called for a coldkey with no pending announcement. Check the `ColdkeySwapAnnouncements` storage; you must call `announce_coldkey_swap` first. | +| [`ContractNotFound`](/docs/errors/chain/ContractNotFound) | No contract instance exists at the destination address; the account has no `ContractInfoOf` entry. Verify the `dest` address and that the contract was instantiated and has not been terminated. | +| [`DeadAccount`](/docs/errors/chain/DeadAccount) | The beneficiary account does not exist and this operation is not allowed to create it. Check `System.Account` for the destination: it must already hold at least the existential deposit before the operation runs. | +| [`DelegateDependencyNotFound`](/docs/errors/chain/DelegateDependencyNotFound) | `unlock_delegate_dependency` was called for a code hash that is not among the contract's locked delegate dependencies. Check the code hash argument against the dependencies recorded in the contract's info. | +| [`InvalidCrowdloanId`](/docs/errors/chain/InvalidCrowdloanId) | No crowdloan exists in the `Crowdloans` storage map for the given `crowdloan_id`; it was never created or has been dissolved. Check `Crowdloans` for the id and `NextCrowdloanId` for the range of ids ever issued. | +| [`LeaseDoesNotExist`](/docs/errors/chain/LeaseDoesNotExist) | The `lease_id` argument does not correspond to any stored lease. Query the `SubnetLeases` storage map to confirm the lease id and whether it was already terminated. | +| [`LeaseNetuidNotFound`](/docs/errors/chain/LeaseNetuidNotFound) | After registering the leased network, no subnet owned by the lease's derived coldkey could be found, so the netuid lookup failed. Inspect `SubnetOwner` entries for the lease coldkey; this usually indicates the registration did not complete. | +| [`NoContribution`](/docs/errors/chain/NoContribution) | The account has no contribution recorded for this crowdloan, so `withdraw` has nothing to pay out (or `dissolve` finds no creator contribution). Check the `Contributions` double map under the `crowdloan_id` for the account in question. | +| [`NoDeposit`](/docs/errors/chain/NoDeposit) | No safe-mode deposit exists for the given account and block combination, so nothing can be released or slashed. Check the `Deposits` storage map for an entry under that account and deposit block. | +| [`NoExistingLock`](/docs/errors/chain/NoExistingLock) | move_lock was called but no conviction lock exists for the signing coldkey on that subnet. Check the lock storage for the coldkey and netuid, and create a lock before attempting to move it to another hotkey. | +| [`NoWeightsCommitFound`](/docs/errors/chain/NoWeightsCommitFound) | A weights reveal was submitted but no pending (non-expired) commit exists for the hotkey and netuid, possibly because it already expired. Query the `WeightCommits` storage map for the hotkey and check the commit hasn't passed the reveal window. | +| [`NoneValue`](/docs/errors/chain/NoneValue) | Template leftover in the drand pallet meaning a storage value was read before ever being set; no current code path raises it. If seen, inspect the drand pallet's storage items for missing initialization. | +| [`NotFound`](/docs/errors/chain/NotFound) | The referenced item does not exist in storage: no multisig operation for that call hash in `Multisigs`, no scheduled task at that slot or name in `Agenda`/`Lookup`, or no matching proxy registration in `Proxies`. Verify the identifier against current chain state. | +| [`NotNoted`](/docs/errors/chain/NotNoted) | The preimage cannot be unnoted because no preimage was ever noted for this hash. Check `RequestStatusFor` (or legacy `StatusFor`) for the hash to confirm what, if anything, is stored. | +| [`NotRequested`](/docs/errors/chain/NotRequested) | The preimage request cannot be removed because there are no outstanding requests for this hash. Check the request status for the hash in `RequestStatusFor` before calling `unrequest_preimage`. | +| [`NothingAuthorized`](/docs/errors/chain/NothingAuthorized) | No code upgrade has been authorized, so `apply_authorized_upgrade` has nothing to apply. Check the `AuthorizedUpgrade` storage item in System; an authorization must be recorded before applying the new code. | +| [`SymbolDoesNotExist`](/docs/errors/chain/SymbolDoesNotExist) | The requested token symbol is not in the chain's predefined symbol table, so it cannot be assigned to a subnet. Check the symbol argument against the chain's built-in `SYMBOLS` list and choose a valid entry. | + +The same explanation is available in the terminal: `btcli explain not_found` (or `btcli explain ` for one exact chain error). diff --git a/docs/errors/not-registered.mdx b/docs/errors/not-registered.mdx new file mode 100644 index 0000000000..10557b690f --- /dev/null +++ b/docs/errors/not-registered.mdx @@ -0,0 +1,27 @@ +--- +title: "not_registered" +description: "Register the hotkey on this subnet first with `btcli subnets register`" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The hotkey is not registered where the call expects it to be: either not on the specific subnet, or not anywhere on the network. + +Many calls (serving an axon, setting weights, some stake operations) require the hotkey to hold a UID on the target subnet. register it first with `btcli subnets register --netuid N`, or check where it is registered with `btcli query netuids-for-hotkey` / `btcli query uid --netuid N`. + +## Remediation + +Register the hotkey on this subnet first with `btcli subnets register` + +## Chain errors + +The exact chain error names (from the extrinsic receipt) that classify to `not_registered`; the description says what triggered the failure and where to check. Each name has its own page: + +| Chain error | Description | +| --- | --- | +| [`HotKeyAccountNotExists`](/docs/errors/chain/HotKeyAccountNotExists) | The hotkey has no on-chain account, meaning it was never created through registration, so staking or delegation operations cannot reference it. Check the `Owner` storage for the hotkey or `btcli wallet overview`; register the hotkey on a subnet first. | +| [`HotKeyNotRegisteredInNetwork`](/docs/errors/chain/HotKeyNotRegisteredInNetwork) | The hotkey is not registered on the relevant subnet, raised by `serve_axon`/`serve_prometheus` for the serving netuid or by identity calls requiring registration on any subnet. Verify registration with `btcli query uid --netuid ` (or `btcli query netuids-for-hotkey`) and register via `btcli subnets register` first. | +| [`HotKeyNotRegisteredInSubNet`](/docs/errors/chain/HotKeyNotRegisteredInSubNet) | The hotkey holds no UID on the given netuid, so weight setting, commits, or UID lookups fail there. Check the `Uids` storage for the (netuid, hotkey) pair or `btcli query uid --netuid `; confirm the netuid argument and register the hotkey if needed. | +| [`PalletHotkeyNotRegistered`](/docs/errors/chain/PalletHotkeyNotRegistered) | Root tried to enable the pallet via `set_pallet_status` before its hotkey was registered to the pallet's intermediary account. Check that the `PalletHotkey` constant is registered for the pallet account, which genesis or the `on_runtime_upgrade` migration performs. | + +The same explanation is available in the terminal: `btcli explain not_registered` (or `btcli explain ` for one exact chain error). diff --git a/docs/errors/policy-violation.mdx b/docs/errors/policy-violation.mdx new file mode 100644 index 0000000000..08419e5bfb --- /dev/null +++ b/docs/errors/policy-violation.mdx @@ -0,0 +1,20 @@ +--- +title: "policy_violation" +description: "The action exceeds a configured safety policy" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The action was blocked locally by a configured safety policy before reaching the chain. + +Policies cap what a session is allowed to do (spend limits, allowed operations). this is a deliberate guardrail: if the action is intended, raise or remove the relevant policy limit in your configuration and rerun. + +## Remediation + +The action exceeds a configured safety policy + +## Chain errors + +No chain error classifies to this code — it is raised client-side by the SDK before or after the call reaches the chain. + +The same explanation is available in the terminal: `btcli explain policy_violation` (or `btcli explain ` for one exact chain error). diff --git a/docs/errors/rate-limited.mdx b/docs/errors/rate-limited.mdx new file mode 100644 index 0000000000..4113d2d7f3 --- /dev/null +++ b/docs/errors/rate-limited.mdx @@ -0,0 +1,40 @@ +--- +title: "rate_limited" +description: "Wait for the rate-limit window to pass, then retry" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The chain enforces per-key rate limits on certain calls (registration, weight setting, stake movement, child hotkey changes) to keep the network stable. + +The limit is measured in blocks since the same key last performed the same kind of call, so retrying immediately will fail with the same error. wait for the window to pass (typically a few blocks to a few hundred blocks depending on the call), then retry. + +## Remediation + +Wait for the rate-limit window to pass, then retry + +## Chain errors + +The exact chain error names (from the extrinsic receipt) that classify to `rate_limited`; the description says what triggered the failure and where to check. Each name has its own page: + +| Chain error | Description | +| --- | --- | +| [`AddStakeBurnRateLimitExceeded`](/docs/errors/chain/AddStakeBurnRateLimitExceeded) | The add-stake-and-burn operation was submitted again before its per-key rate-limit window elapsed. Wait some blocks and retry; no active raise site exists in current code, so this mainly appears on older runtimes. | +| [`CommittingWeightsTooFast`](/docs/errors/chain/CommittingWeightsTooFast) | The neuron committed weights again before the per-UID rate limit elapsed since its last commit on that subnet. Compare blocks since the last commit against the `weights_rate_limit` hyperparameter (`btcli sudo get --netuid `) and wait. | +| [`DelegateTxRateLimitExceeded`](/docs/errors/chain/DelegateTxRateLimitExceeded) | The delegate changed its take again before the per-hotkey take-change rate limit elapsed. Compare blocks since the hotkey's last take transaction against the `TxDelegateTakeRateLimit` storage item and retry later. | +| [`EvmKeyAssociateRateLimitExceeded`](/docs/errors/chain/EvmKeyAssociateRateLimitExceeded) | `associate_evm_key` was called again before the per-UID rate limit since the last association elapsed. Compare blocks since the association recorded in the `AssociatedEvmAddress` storage against the `EvmKeyAssociateRateLimit` runtime constant and retry later. | +| [`HotKeySetTxRateLimitExceeded`](/docs/errors/chain/HotKeySetTxRateLimitExceeded) | The coldkey attempted a hotkey set/swap before `TxRateLimit` blocks had passed since its last such transaction. Check the coldkey's last transaction block against the `TxRateLimit` storage value and wait the remaining blocks. | +| [`HotKeySwapOnSubnetIntervalNotPassed`](/docs/errors/chain/HotKeySwapOnSubnetIntervalNotPassed) | A hotkey swap on a subnet was attempted before `HotkeySwapOnSubnetInterval` blocks passed since the coldkey's last swap on that netuid. Compare `LastHotkeySwapOnNetuid` for the coldkey with the current block and retry after the interval. | +| [`NetworkTxRateLimitExceeded`](/docs/errors/chain/NetworkTxRateLimitExceeded) | The coldkey attempted register_network again before the network registration rate limit elapsed since its previous registration. Check the coldkey's last register-network block against the `NetworkRateLimit` storage value and wait the remaining blocks. | +| [`ServingRateLimitExceeded`](/docs/errors/chain/ServingRateLimitExceeded) | `serve_axon` or `serve_prometheus` was called again before enough blocks passed since the neuron's last serving update. Check the axon's last update block in `Axons` against the `ServingRateLimit` (serving_rate_limit hyperparameter) and wait. | +| [`SettingWeightsTooFast`](/docs/errors/chain/SettingWeightsTooFast) | The neuron set weights again before `WeightsSetRateLimit` blocks elapsed since its last weight update on that subnet. Check the weights_rate_limit hyperparameter and the neuron's `LastUpdate` entry, then wait the remaining blocks. | +| [`SpaceLimitExceeded`](/docs/errors/chain/SpaceLimitExceeded) | The commitment would push the account's byte quota for the current epoch over the cap; each `set_commitment` consumes at least 100 bytes. Check `UsedSpaceOf` for the netuid and account against `MaxSpace`, or wait for the next epoch to reset usage. | +| [`StakingRateLimitExceeded`](/docs/errors/chain/StakingRateLimitExceeded) | Staking operations (add_stake, remove_stake, and similar) were submitted faster than the per-block staking rate limit allows for the hotkey-coldkey pair. Space the transactions out and retry in a later block. | +| [`SubnetBuybackRateLimitExceeded`](/docs/errors/chain/SubnetBuybackRateLimitExceeded) | A subnet buyback operation (staking TAO and immediately burning the acquired alpha, e.g. via `add_stake_burn`) was repeated within its rate-limit window. Wait for the window to pass before retrying the buyback. | +| [`TooManyRegistrationsThisBlock`](/docs/errors/chain/TooManyRegistrationsThisBlock) | Registrations in the current block already reached the subnet's per-block cap (`MaxRegistrationsPerBlock`, the max_regs_per_block hyperparameter); root registration enforces the same cap on netuid 0. Retry in the next block. | +| [`TooManyRegistrationsThisInterval`](/docs/errors/chain/TooManyRegistrationsThisInterval) | Registrations in the current interval reached the cap of three times `TargetRegistrationsPerInterval` for the subnet. Compare `RegistrationsThisInterval` against that hyperparameter and wait for the next interval to start. | +| [`TooSoon`](/docs/errors/chain/TooSoon) | A forced GRANDPA authority change was signalled too soon after the previous one. Check the Grandpa `NextForced` storage and wait until the current block passes it before signalling another forced change. | +| [`TxChildkeyTakeRateLimitExceeded`](/docs/errors/chain/TxChildkeyTakeRateLimitExceeded) | `set_childkey_take` was called again for the hotkey on this subnet before its rate-limit window elapsed. Check the block of the last childkey-take change against `TxChildkeyTakeRateLimit` and wait out the remainder. | +| [`TxRateLimitExceeded`](/docs/errors/chain/TxRateLimitExceeded) | An owner or admin transaction (e.g. `set_children`, tempo or hyperparameter updates, setting the owner hotkey) was repeated within its rate-limit window for that key and subnet. Check when the same transaction type last succeeded and wait for the limit to pass. | + +The same explanation is available in the terminal: `btcli explain rate_limited` (or `btcli explain ` for one exact chain error). diff --git a/docs/errors/subnet-not-exists.mdx b/docs/errors/subnet-not-exists.mdx new file mode 100644 index 0000000000..8b83d8f7cb --- /dev/null +++ b/docs/errors/subnet-not-exists.mdx @@ -0,0 +1,27 @@ +--- +title: "subnet_not_exists" +description: "Use an existing netuid; `btcli subnets list` shows valid ones" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The netuid does not name an existing subnet on this network. + +Netuids are not stable across networks: a subnet that exists on finney may not exist on testnet. `btcli subnets list` shows the valid netuids for the network you are connected to (check `--network`). + +## Remediation + +Use an existing netuid; `btcli subnets list` shows valid ones + +## Chain errors + +The exact chain error names (from the extrinsic receipt) that classify to `subnet_not_exists`; the description says what triggered the failure and where to check. Each name has its own page: + +| Chain error | Description | +| --- | --- | +| [`MechanismDoesNotExist`](/docs/errors/chain/MechanismDoesNotExist) | The target subnet or its sub-mechanism does not exist: the netuid is unknown, the mechanism index is at or above `MechanismCountCurrent`, or a non-dynamic `mechid` was requested. Check the netuid with `btcli subnets list` and the mechanism count for that subnet. | +| [`RootNetworkDoesNotExist`](/docs/errors/chain/RootNetworkDoesNotExist) | Root registration or root stake claiming found no root network in chain state, which only happens on misconfigured or freshly bootstrapped chains. Verify netuid 0 exists in `NetworksAdded`. | +| [`SubnetDoesNotExist`](/docs/errors/chain/SubnetDoesNotExist) | The admin-utils call targets a netuid with no registered subnet. Verify the `netuid` argument against `NetworksAdded` (the set of existing subnets) before setting hyperparameters. | +| [`SubnetNotExists`](/docs/errors/chain/SubnetNotExists) | The netuid passed to the call does not correspond to a registered subnet. Verify the netuid argument against `NetworksAdded` or `btcli subnets list`; the subnet may also have been dissolved. | + +The same explanation is available in the terminal: `btcli explain subnet_not_exists` (or `btcli explain ` for one exact chain error). diff --git a/docs/errors/subtoken-disabled.mdx b/docs/errors/subtoken-disabled.mdx new file mode 100644 index 0000000000..95c306cf46 --- /dev/null +++ b/docs/errors/subtoken-disabled.mdx @@ -0,0 +1,24 @@ +--- +title: "subtoken_disabled" +description: "The subnet is not active yet; wait for start_call" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The subnet exists but its token operations are not enabled yet. + +A newly registered subnet starts inactive: staking, unstaking, and other alpha operations are rejected until the subnet owner activates it with start_call. there is nothing to fix on your side; wait for the subnet to become active. + +## Remediation + +The subnet is not active yet; wait for start_call + +## Chain errors + +The exact chain error names (from the extrinsic receipt) that classify to `subtoken_disabled`; the description says what triggered the failure and where to check. Each name has its own page: + +| Chain error | Description | +| --- | --- | +| [`SubtokenDisabled`](/docs/errors/chain/SubtokenDisabled) | The subnet's alpha token is not yet enabled, so staking, swapping, and trading on it are blocked; `SubtokenEnabled` is false until the owner makes the `start_call` after registration. Check `SubtokenEnabled` for the netuid involved. | + +The same explanation is available in the terminal: `btcli explain subtoken_disabled` (or `btcli explain ` for one exact chain error). diff --git a/docs/errors/too-early.mdx b/docs/errors/too-early.mdx new file mode 100644 index 0000000000..53d6adaf9f --- /dev/null +++ b/docs/errors/too-early.mdx @@ -0,0 +1,41 @@ +--- +title: "too_early" +description: "The required window has not opened yet; wait some blocks and retry" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The call arrived before its window opened: a reveal before the reveal period, a coldkey swap before its delay elapsed, a start_call before enough blocks passed, or a finalization before the period ended. + +Unlike rate_limited this is not about calling too often — the flow has a built-in waiting period that has not elapsed yet. wait the required blocks and retry; the chain state (announcement block, commit block) tells you how long is left. + +## Remediation + +The required window has not opened yet; wait some blocks and retry + +## Chain errors + +The exact chain error names (from the extrinsic receipt) that classify to `too_early`; the description says what triggered the failure and where to check. Each name has its own page: + +| Chain error | Description | +| --- | --- | +| [`AdminActionProhibitedDuringWeightsWindow`](/docs/errors/chain/AdminActionProhibitedDuringWeightsWindow) | An owner or admin hyperparameter change was attempted inside the protected freeze window just before the subnet's epoch runs. Check `AdminFreezeWindow` and the blocks remaining until the next epoch (subnet tempo), then retry after the epoch fires. | +| [`AllNetworksInImmunity`](/docs/errors/chain/AllNetworksInImmunity) | Creating a new subnet required pruning an existing one, but every candidate subnet is still inside its network immunity period so none can be dissolved. Check `NetworkImmunityPeriod` and each subnet's `NetworkRegisteredAt`, and retry once a subnet leaves immunity. | +| [`CannotReleaseYet`](/docs/errors/chain/CannotReleaseYet) | `release_deposit` was called too early: the current block must exceed the deposit's block plus `ReleaseDelay`, and safe-mode must be exited. Check the block key of the entry in `Deposits` against the `ReleaseDelay` config. | +| [`CapNotRaised`](/docs/errors/chain/CapNotRaised) | `finalize` was called before the crowdloan's `raised` amount equals its `cap`. Compare the `raised` and `cap` fields of the `Crowdloans` entry; contribute the remainder or lower the cap with `update_cap` before finalizing. | +| [`ColdkeySwapClearTooEarly`](/docs/errors/chain/ColdkeySwapClearTooEarly) | The swap announcement cannot be cleared until the reannouncement delay after the announcement's execution block has passed. Compare the current block with the `when` stored in `ColdkeySwapAnnouncements` plus `ColdkeySwapReannouncementDelay` and retry later. | +| [`ColdkeySwapReannouncedTooEarly`](/docs/errors/chain/ColdkeySwapReannouncedTooEarly) | `announce_coldkey_swap` was called again before the reannouncement delay after the previous announcement's execution block elapsed. Compare the current block with the stored announcement time plus `ColdkeySwapReannouncementDelay` and retry later. | +| [`ColdkeySwapTooEarly`](/docs/errors/chain/ColdkeySwapTooEarly) | `coldkey_swap` was executed before the announcement delay had elapsed since `announce_coldkey_swap`. Check the execution block stored in `ColdkeySwapAnnouncements` (announcement time plus `ColdkeySwapAnnouncementDelay`) and wait until then. | +| [`ContributionPeriodNotEnded`](/docs/errors/chain/ContributionPeriodNotEnded) | The operation requires the crowdloan's contribution period to be over, but the current block is still before the crowdloan's `end` block. Compare the `end` field of the `Crowdloans` entry for the `crowdloan_id` with the current block number. | +| [`LeaseHasNotEnded`](/docs/errors/chain/LeaseHasNotEnded) | The lease termination was attempted before the lease's `end_block` was reached. Compare the current block height with the `end_block` stored in `SubnetLeases` for the lease id and retry after it passes. | +| [`MigrationInProgress`](/docs/errors/chain/MigrationInProgress) | A multi-block storage migration of the contracts pallet is still running, so other extrinsics of the pallet are rejected until it completes. Check the migration status in storage and retry once done, or submit `migrate` calls to advance it. | +| [`MultiBlockMigrationsOngoing`](/docs/errors/chain/MultiBlockMigrationsOngoing) | Runtime code replacement is blocked while a multi-block migration is still executing. Wait for the ongoing migrations to complete (check the multi-block migrations cursor) before retrying `set_code` or the authorized upgrade. | +| [`NeedWaitingMoreBlocksToStarCall`](/docs/errors/chain/NeedWaitingMoreBlocksToStarCall) | The subnet owner called start_call before enough blocks had passed since the subnet was registered. Compare the current block with `NetworkRegisteredAt` for the netuid plus the start-call delay and retry once the window opens. | +| [`NotReadyToDissolve`](/docs/errors/chain/NotReadyToDissolve) | `dissolve` was called while outside contributions remain: the crowdloan's `raised` amount still exceeds the creator's own contribution. Call `refund` until only the creator's `Contributions` entry remains and equals `raised` in the `Crowdloans` record. | +| [`PriceConditionNotMet`](/docs/errors/chain/PriceConditionNotMet) | The subnet's current alpha price does not satisfy the order's trigger: buys and stop-losses require price at or below `limit_price`, take-profits at or above it. Compare `current_alpha_price` for the order's `netuid`, scaled by 1e9, with the `limit_price` field. | +| [`RevealTooEarly`](/docs/errors/chain/RevealTooEarly) | A weight reveal was submitted before the commit's reveal window: the current epoch must equal the commit epoch plus the reveal period. Check the commit in `WeightCommits` and the subnet's `RevealPeriodEpochs`, then wait for the reveal epoch. | +| [`StartCallNotReady`](/docs/errors/chain/StartCallNotReady) | `start_call` was made before `StartCallDelay` blocks elapsed since the subnet was registered. Compare the current block against `NetworkRegisteredAt` for the netuid plus `StartCallDelay`, and wait for the remainder. | +| [`Unannounced`](/docs/errors/chain/Unannounced) | The proxied call was executed before its announcement matured, or no matching announcement exists at all. Check the proxy's `Announcements` entry for the call hash and the `delay` on the proxy registration; enough blocks must elapse between `announce` and `proxy_announced`. | +| [`WaitingForDissolvedSubnetCleanup`](/docs/errors/chain/WaitingForDissolvedSubnetCleanup) | The operation is blocked while a dissolved subnet's storage is still being torn down in the background. Check the `DissolveCleanupQueue` and retry after the on-idle cleanup for that netuid completes. | + +The same explanation is available in the terminal: `btcli explain too_early` (or `btcli explain ` for one exact chain error). diff --git a/docs/errors/unit-mismatch.mdx b/docs/errors/unit-mismatch.mdx new file mode 100644 index 0000000000..980cf3eadd --- /dev/null +++ b/docs/errors/unit-mismatch.mdx @@ -0,0 +1,22 @@ +--- +title: "unit_mismatch" +description: "Match the Balance netuid to the operation's currency" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +Two amounts denominated in different currencies were combined. + +Every Balance carries the netuid whose currency it denominates: netuid 0 is TAO, any other netuid is that subnet's alpha. arithmetic between different netuids raises instead of silently mixing currencies, because 1 alpha on one subnet is not worth 1 alpha on another. + +Construct the amount in the operation's own currency: Balance.from_tao for TAO, Balance.from_alpha(amount, netuid) for a subnet's alpha. + +## Remediation + +Match the Balance netuid to the operation's currency + +## Chain errors + +No chain error classifies to this code — it is raised client-side by the SDK before or after the call reaches the chain. + +The same explanation is available in the terminal: `btcli explain unit_mismatch` (or `btcli explain ` for one exact chain error). diff --git a/docs/errors/unknown.mdx b/docs/errors/unknown.mdx new file mode 100644 index 0000000000..a5ee0d9e41 --- /dev/null +++ b/docs/errors/unknown.mdx @@ -0,0 +1,20 @@ +--- +title: "unknown" +description: "Inspect the message for details" +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +The error did not match any known chain error name or message pattern. + +The original message is preserved in the diagnostic; read it for details. if it names a chain error that should be classified, the mapping lives in bittensor/error_map.py. + +## Remediation + +Inspect the message for details + +## Chain errors + +No chain error classifies to this code — it is raised client-side by the SDK before or after the call reaches the chain. + +The same explanation is available in the terminal: `btcli explain unknown` (or `btcli explain ` for one exact chain error). diff --git a/docs/guides/conviction.mdx b/docs/guides/conviction.mdx index f3d0b66ab1..a920fdccc2 100644 --- a/docs/guides/conviction.mdx +++ b/docs/guides/conviction.mdx @@ -102,7 +102,7 @@ btcli tx lock-stake --netuid 7 --amount-alpha 1000 --dry-run -w my_coldkey btcli tx lock-stake --netuid 7 --amount-alpha 1000 -w my_coldkey # Switch to perpetual mode (irreversible public signal when switching to decaying) -btcli tx set-perpetual-lock --netuid 7 --enabled true -w my_coldkey +btcli tx set-perpetual-lock --netuid 7 --enabled -w my_coldkey ``` The same flow in Python — lock, opt into perpetual, then read conviction back: diff --git a/docs/guides/evm/subnet-and-neuron.mdx b/docs/guides/evm/subnet-and-neuron.mdx index a0a51015b5..ccb04ca0d3 100644 --- a/docs/guides/evm/subnet-and-neuron.mdx +++ b/docs/guides/evm/subnet-and-neuron.mdx @@ -29,7 +29,7 @@ mirror as the hotkey. Start a localnet and fund an EVM key (via Alice, who is pre-funded): ```bash -btcli evm key new -w alice --no-password +btcli evm key new -w alice btcli evm fund --amount-tao 500 -w alice --network local -y btcli evm balance -w alice --rpc-url http://127.0.0.1:9945 ``` diff --git a/docs/guides/evm/verify-keys.mdx b/docs/guides/evm/verify-keys.mdx index 0ef5d2a5e3..6321cd42b5 100644 --- a/docs/guides/evm/verify-keys.mdx +++ b/docs/guides/evm/verify-keys.mdx @@ -95,7 +95,8 @@ resolve, and some subnets require it: ```bash btcli evm associate --netuid 1 -w my_wallet -H my_hotkey -btcli query associated-evm-key --netuid 1 --hotkey 5F… --json +btcli query uid --netuid 1 --hotkey 5F… --json # resolve UID +btcli query associated-evm-key --netuid 1 --uid 0 --json ``` Under the hood this is the reverse direction of proof: the **EVM key** signs diff --git a/docs/guides/meta.json b/docs/guides/meta.json index d6e6eb4a05..1b6cf847ed 100644 --- a/docs/guides/meta.json +++ b/docs/guides/meta.json @@ -1,4 +1,4 @@ { "title": "Guides", - "pages": ["staking", "conviction", "mining", "validating", "subnets", "signed-requests", "extension-signing", "ledger", "evm", "local-development", "running-a-node", "timelock"] + "pages": ["staking", "conviction", "mining", "validating", "subnets", "multisig", "proxies", "signed-requests", "extension-signing", "ledger", "evm", "local-development", "running-a-node", "timelock"] } diff --git a/docs/guides/mining.mdx b/docs/guides/mining.mdx index b746533868..b7dcc4ce8d 100644 --- a/docs/guides/mining.mdx +++ b/docs/guides/mining.mdx @@ -127,7 +127,7 @@ a channel for private data. ```bash btcli query metagraph --netuid 1 --json # your rank, incentive, emission -btcli query blocks-since-last-update --netuid 1 --json +btcli query blocks-since-last-update --netuid 1 --uid 0 --json btcli query immunity-period --netuid 1 ``` diff --git a/docs/guides/multisig.mdx b/docs/guides/multisig.mdx new file mode 100644 index 0000000000..0bee079988 --- /dev/null +++ b/docs/guides/multisig.mdx @@ -0,0 +1,421 @@ +--- +title: Multisig wallets +description: Put a coldkey under M-of-N control so no single stolen or lost key can move your funds. +--- + +A single coldkey is a single point of failure. If it leaks — a compromised +laptop, a phished mnemonic, a malicious dependency — everything it holds is +gone, irreversibly: subnet ownership, stake, free balance. A **multisig** +removes that single point of failure by deriving an account address from a +*set* of coldkeys plus a **threshold**: a call only executes from that account +once `M` of the `N` signatories have approved it. + +Conventionally written "M-of-N": a 2-of-3 multisig has three member keys and +requires any two of them to sign. That protects you in both directions — + +* **Compromise:** an attacker with one stolen key can open an operation but + cannot execute anything. You see the pending approval and simply never + co-sign it. +* **Loss:** with 2-of-3, losing any one key loses nothing. The remaining two + can move the funds to a new account. + +The signatories can be separate people (a subnet team's treasury) or separate +keys held by one person on separate machines (your own coldkey, hardened). +Nobody holds "the multisig key" — no such key exists. The address is pure math +over the member set and threshold. + +## How it works + +The multisig address is derived deterministically from the sorted signatory +set and the threshold. The same three keys with threshold 2 always produce the +same address, on any machine, with no on-chain registration step. + +Spending is an approval round: + +1. One signatory **opens** an operation by submitting the full call + ([`multisig-execute`](/docs/tx/multisig-execute)) or its approval + ([`multisig-approve`](/docs/tx/multisig-approve)) with no timepoint. This + reserves a deposit from the opener (returned on execution or cancellation) + and records the operation on-chain under the hash of the call. +2. Other signatories approve, quoting the **timepoint** (block height and + extrinsic index) of the opening approval. Every approval must describe the + identical call, threshold, and signatory set — the chain matches by hash. +3. Whoever brings the count to `threshold` must submit + [`multisig-execute`](/docs/tx/multisig-execute) with the full call, which + then dispatches *as the multisig account* in the same extrinsic. + +Two consequences worth internalizing: the inner call's arguments must be fully +explicit (it runs as the multisig, not as any signer's wallet), and because +the address is derived from the member set, **changing membership changes the +address**. If a signer leaves the team you must create a new multisig and move +the funds — unless you make the members [pure proxies](/docs/guides/proxies) +instead of raw coldkeys, which lets you swap the human behind a member slot +without changing the multisig address. + +## Prerequisites + +```bash +uv venv && source .venv/bin/activate +uv pip install bittensor +``` + +One install gives you both `btcli` and the Python SDK. Practice on testnet +(`-n test` on every command, or `btcli config set network test`) with test TAO +before touching mainnet. Each *signatory* needs a little TAO for fees and the +opener's deposit; the multisig account itself holds the actual funds. + +## Step 1 — the signatory keys + +For this walkthrough, a 2-of-3 with wallets `alice`, `bob`, and `carol`. In +production these live on different machines owned by different people; each +person only ever holds their own coldkey. + +```bash +btcli wallet create -w alice # repeat for bob and carol (on their machines) +``` + +Signers who are other people are just addresses to you — save them in the +address book so every later command can use names instead of raw ss58: + +```bash +btcli addresses add bob 5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty +btcli addresses add carol 5DAAnrj7VHTznn2AWBemMuyBwZWs6FNFjdyVXUeYum3PTXFy +btcli addresses list +``` + +## Step 2 — save the signer set and derive the address + +`btcli multisig add` stores a named signer set in your local multisig book and +derives the on-chain address. Signatories can be wallet names, address-book +names, or raw ss58 — order doesn't matter. + +```bash +btcli multisig add team-treasury --threshold 2 --signatories alice,bob,carol +btcli multisig show team-treasury # derives and prints the multisig address +btcli multisig list # all saved signer sets +btcli wallet list # multisigs appear alongside wallets on disk +``` + +The book entry is purely local convenience — every signer should run the same +`multisig add` on their own machine (same members, same threshold) and confirm +`multisig show` prints the **same address**. If it doesn't, someone has a +wrong member list, and funds sent there would belong to a different account. + +In Python, the same derivation: + +```python +import bittensor as sub + +async with sub.Client("test") as client: + ms = await client.multisig( + ["5Grw...alice", "5FHn...bob", "5DAA...carol"], + threshold=2, + ) + print(ms.address) # deterministic in the set + threshold +``` + +## Step 3 — fund and watch it + +Send TAO to the derived address like any other account: + +```bash +btcli addresses add team-treasury-addr 5Fmulti...sigAddress +btcli tx transfer --dest team-treasury-addr --amount-tao 100 -w alice +``` + +Balances and every other [query](/docs/query) work on the address directly — +no key needed for reads: + +```bash +btcli query balance --coldkey team-treasury-addr +``` + +Optionally register it as a watch-only wallet (public key only, no secrets), +so wallet-flavored commands work by name: + +```bash +btcli wallet regen-coldkeypub -w team-treasury --ss58 5Fmulti...sigAddress +btcli wallet balance team-treasury +``` + +## Step 4 — spend: open the operation + +Alice proposes paying 10 TAO out of the treasury. The inner call is an intent +spec — `{"op": , ...args}`, same shape as a batch child — with +fully explicit arguments. As always, `--dry-run` previews fee and effects +without submitting: + +```bash +btcli tx multisig-execute \ + --threshold 2 \ + --other-signatories bob,carol \ + --call '{"op": "transfer", "dest_ss58": "5DevPayee...", "amount_tao": 10}' \ + -w alice --dry-run + +btcli tx multisig-execute \ + --threshold 2 \ + --other-signatories bob,carol \ + --call '{"op": "transfer", "dest_ss58": "5DevPayee...", "amount_tao": 10}' \ + -w alice +``` + +`--other-signatories` lists every member *except* the signer; the CLI adds +Alice's coldkey and sorts the set. Since no timepoint was passed, this opens +the operation: Alice's deposit is reserved, and nothing executes yet. + +After submission the CLI prints the operation's `call_hash`, `call_data`, the +opening `timepoint`, and **ready-to-run commands for each remaining +co-signer** — copy-paste them to Bob and Carol over your team channel. The +opening extrinsic embeds the full call, so co-signers can also recover +everything from chain state alone (next step); no out-of-band call data is +required. + +## Step 5 — view pending operations + +Any signer can inspect what's awaiting approval. With the saved multisig book +entry: + +```bash +btcli multisig pending --multisig team-treasury -w bob +``` + +This lists every open operation on the multisig account with its target call, +decoded parameters, approval count, who has signed, who hasn't, the timepoint, +and a copy-paste command for each remaining co-signer. Filter to one operation +with `--call-hash 0x...`; if the call details can't be decoded locally (e.g. +the opening block is pruned on your RPC node), pass the `call_data` hex once +with `--call-data 0x...`. + +The raw chain state is also one query away: + +```bash +btcli query multisig --account team-treasury-addr --call-hash 0x... --json +``` + +which returns the pending operation's timepoint (`when`), the approvals so +far, the depositor, and the reserved deposit. **Check `pending` on a +schedule.** An operation you don't recognize means a member key is +compromised — don't co-sign, and rotate that member out. + +## Step 6 — approve and execute + +Bob was handed a ready-to-run command by `pending` (or by Alice). Because his +approval is the one that reaches the threshold, it must be +`multisig-execute` with the full call — the chain needs the call to run it. +The timepoint identifies the pending operation: + +```bash +btcli tx multisig-execute \ + --threshold 2 \ + --other-signatories alice,carol \ + --call '{"op": "transfer", "dest_ss58": "5DevPayee...", "amount_tao": 10}' \ + --timepoint '{"height": 5951841, "index": 6}' \ + -w bob +``` + +The transfer executes as the multisig account in this same extrinsic, and +Alice's deposit is returned. + +In a wider set (say 3-of-5), the signers *between* the first and the last can +use [`multisig-approve`](/docs/tx/multisig-approve) instead — it records +consent by hash only, which stays cheap even for huge calls, but never +executes. Opening approval: omit the timepoint. Intermediate approvals: pass +it. Last signer: always `multisig-execute` with the full call. + +## 1-of-N: shared access without approval rounds + +A threshold-1 multisig lets any single member act alone — useful as a shared +operational account where you still want membership to be explicit and +auditable. No approval round, timepoint, or deposit: + +```bash +btcli tx multisig-threshold-1 \ + --other-signatories bob,carol \ + --call '{"op": "transfer", "dest_ss58": "5DevPayee...", "amount_tao": 1}' \ + -w alice +``` + +Note that 1-of-N adds *no* compromise protection — any one stolen member key +spends. Use threshold ≥ 2 for security. See +[`multisig-threshold-1`](/docs/tx/multisig-threshold-1). + +## Cancel a pending operation + +Only the signatory who opened the operation (and paid the deposit) can cancel +it, which discards the stored approvals and returns the deposit. Threshold, +signatory set, call, and timepoint must all match exactly: + +```bash +btcli tx multisig-cancel \ + --threshold 2 \ + --other-signatories bob,carol \ + --call '{"op": "transfer", "dest_ss58": "5DevPayee...", "amount_tao": 10}' \ + --timepoint '{"height": 5951841, "index": 6}' \ + -w alice +``` + +If the *opener's* key is the compromised one, the honest members can't cancel +— but they don't need to: an operation below threshold never executes, and +the attacker's deposit stays locked until they cancel it themselves. Just +never co-sign it, and move the funds to a fresh multisig without the bad key. +See [`multisig-cancel`](/docs/tx/multisig-cancel). + +## Migrate an existing account to a multisig + +If the account you want to protect already exists — a subnet owner's coldkey +especially — funding the multisig is **not** a migration. A +[`transfer`](/docs/tx/transfer) moves transferable TAO and nothing else: +stake, conviction locks, hotkey registrations, and subnet ownership are +separate on-chain state that stays with the old coldkey. +([`transfer-stake`](/docs/tx/transfer-stake) moves a stake position, but +still not ownership.) The tool that moves *everything* is the announced +**coldkey swap**: + +```bash +# 1. Verify the destination first: every member derives the same address, +# and it must be a completely unused account — no balance history, stake, +# hotkeys, or locks. +btcli multisig show team-treasury + +# 2. Announce, committing to the multisig address. Costs 0.1 TAO and locks +# the old coldkey for the chain's delay (36,000 blocks, ~5 days) — only +# swap-related calls go through while it's pending. +btcli tx announce-coldkey-swap --new-coldkey 5Fmulti...sigAddress -w old-owner + +# 3. After the delay, execute. Balance, stake, locks, hotkeys, registrations, +# and subnet ownership all move to the multisig. Irreversible. +btcli tx swap-coldkey-announced --new-coldkey 5Fmulti...sigAddress -w old-owner +``` + +The delay exists so the real owner can catch a thief's announcement +([`dispute-coldkey-swap`](/docs/tx/dispute-coldkey-swap) freezes the key); +check a pending one with the +[`coldkey-swap-announcement`](/docs/query/coldkey-swap-announcement) read. +After the swap, every owner action goes through the multisig approval round — +for routine operations like hyperparameter changes, grant a scoped proxy from +the multisig instead (next section). + +## Day-to-day operations: pair it with a proxy + +An approval round for every hyperparameter tweak gets old fast. The standard +layering keeps the multisig as the high-impact control plane and delegates +the routine work: from the multisig, grant an operations key a narrowly +scoped [proxy](/docs/guides/proxies) — `Owner` for a subnet owner's identity +and hyperparameter calls, `Staking` for a treasury that manages positions. +The grant itself is just an inner call in the usual round: + +```bash +btcli tx multisig-execute \ + --threshold 2 \ + --other-signatories bob,carol \ + --call '{"op": "add_proxy", "delegate_ss58": "5F...opsKey", "proxy_type": "Owner", "delay": 0}' \ + -w alice +``` + +The ops key now handles routine changes alone (`--proxy-for` the multisig +address), but cannot transfer funds or rotate the owner — and if it leaks, +the multisig convenes once to revoke it (`{"op": "remove_proxy", ...}`) while +the principal never moves. The full pattern is in the +[proxy guide](/docs/guides/proxies). + +## Any call, including sudo: `btcli call --multisig` + +The `tx multisig-*` intents cover calls that have intents. For anything else — +raw pallet calls, or a chain whose sudo key is a multisig — the raw-call +escape hatch takes the same multisig flags: + +```bash +btcli call SubtensorModule.some_call --args '{"netuid": 3}' \ + --multisig team-treasury -w alice --yes +``` + +`--multisig NAME` pulls the threshold and signer set from your multisig book +(inline `--multisig-threshold 2 --other-signatories bob,carol` works too, and +`--sudo` wraps the call in `Sudo.sudo`). After each approval the CLI prints +`call_hash`, `call_data`, the timepoint, and the exact command for each +remaining co-signer — the same flow as above, for any call the chain exposes. + +## The SDK + +The high-level handle wraps the whole round. Each signatory calls `approve` +with the identical call; the approval that reaches the threshold executes it +and returns the inner call's events: + +```python +import bittensor as sub +from bittensor.wallet import Wallet + +async with sub.Client("test") as client: + ms = await client.multisig( + ["5Grw...alice", "5FHn...bob", "5DAA...carol"], + threshold=2, + ) + + call = sub.calls.Balances.transfer_keep_alive( + dest="5DevPayee...", + value=10 * 10**9, # raw calls take rao (1 TAO = 1e9 rao) + ) + + # on Alice's machine — opens the operation + result = await ms.approve(call, Wallet(name="alice")) + + # on Bob's machine — same call, reaches threshold, executes + result = await ms.approve(call, Wallet(name="bob")) + print(result.success, result.block_hash) +``` + +For the intent-shaped flow (human-unit arguments, `plan` preview, explicit +timepoints), the `Multisig*` intents mirror the CLI exactly: + +```python +intent = sub.MultisigExecute( + threshold=2, + other_signatories=["5FHn...bob", "5DAA...carol"], + call={"op": "transfer", "dest_ss58": "5DevPayee...", "amount_tao": 10}, + # timepoint={"height": ..., "index": ...} on every approval after the first +) +plan = await client.plan(intent, wallet) # fee, effects — nothing submitted +result = await client.execute(intent, wallet) +``` + +`sub.MultisigApprove`, `sub.MultisigCancel`, and `sub.MultisigThreshold1` are +the other three, and the executing approval surfaces `multisig_call_hash` and +`multisig_call_data` in `result.data`. Inspect pending state with the read: + +```python +pending = await client.read("multisig", account_ss58=ms.address, call_hash="0x...") +# → timepoint (when), approvals so far, depositor, reserved deposit +``` + +## Deposits and limits + +* Opening a pending operation reserves **0.132 TAO base + 0.032 TAO per unit + of threshold** from the opener, returned on execution or cancellation. +* A multisig can have at most **100 signatories**. +* Approving twice from the same signer, or with a mismatched timepoint, + threshold, or signatory set, fails — the (set, threshold, call-hash) triple + is the operation's identity. + +## Operating it safely + +* **Threshold ≥ 2, always, for anything valuable.** M=1 is a convenience + account, not a security boundary. 2-of-3 is the usual sweet spot: survives + one lost key *and* one stolen key. +* **Independent keys.** Signatories on the same laptop defeat the point. Give + each member key its own machine, and consider signing with a + [Ledger](/docs/guides/ledger) (`--ledger`) or a + [browser extension](/docs/guides/extension-signing) (`--signer extension`) + so member coldkeys never sit on the machine running `btcli` at all. +* **Verify the address independently.** Every member derives it themselves + (`btcli multisig show`) before anyone funds it. +* **Watch `pending`.** A pending operation nobody proposed is your compromise + alarm, and it fires *before* any funds move — that early warning is half + the value of a multisig. Check it on a schedule, or script it with + `--json`. +* **Verify before you co-sign.** The `pending` output decodes the target and + parameters — read them. Your approval is the security boundary. +* **Plan for membership change.** Swapping a member means a new address and a + funds migration, unless members are [pure proxies](/docs/guides/proxies) + with swappable controllers behind them. +* **Test the full round on testnet** — open, view, approve, cancel — before + moving real funds. Fees and deposits behave identically. diff --git a/docs/guides/proxies.mdx b/docs/guides/proxies.mdx new file mode 100644 index 0000000000..6855b64767 --- /dev/null +++ b/docs/guides/proxies.mdx @@ -0,0 +1,393 @@ +--- +title: Proxy accounts +description: Keep the coldkey offline and do daily work through scoped, revocable, optionally delayed delegate keys. +--- + +Every coldkey that sits on a working machine is one compromise away from a +total loss — and the loss is irreversible: nobody can claw back a drained +wallet. The **proxy** pallet exists so the coldkey holding your funds never +has to be that key. You register a *delegate* key that may sign on the real +account's behalf, scoped to a class of operations (**proxy type**) and +optionally forced through a time delay you can veto. The valuable key goes to +cold storage; the key that lives on your laptop or server is a limited, +revocable stand-in. + +What this buys you: + +* **Blast-radius control.** A leaked `Staking` proxy can shuffle your stake; + it cannot transfer a single TAO out. Revoke it, rotate, move on — your + principal never moved. +* **A veto window.** A delegation with `delay > 0` must announce each call + and wait; the real account can reject anything it doesn't recognize before + it executes. +* **An offline primary.** After a one-time setup, day-to-day operations never + need the primary coldkey on any networked machine. + +The related **pure proxy** is an account *nobody* has a key for — the chain +derives a fresh address controlled only through proxy relationships. Useful as +disposable role accounts and as swappable members of a +[multisig](/docs/guides/multisig). + +## How it works + +The real account signs [`add-proxy`](/docs/tx/add-proxy) once, naming a +delegate, a proxy type, and a delay. From then on the delegate submits +ordinary calls wrapped in `Proxy.proxy(real, ...)` — the CLI flag for this is +`--proxy-for`, the SDK keyword is `proxy_for=` — and the runtime checks each +call against the delegation's type before dispatching it *as the real +account*. Bookkeeping is paid with reserved deposits (returned on removal), +not fees. + +### Proxy types + +Prefer the narrowest type that covers the job: + +* **Any** — everything, including transfers. Avoid: leaking an `Any` proxy is + exactly as bad as leaking the coldkey. +* **NonTransfer** — everything *except* balance transfers, stake transfers, + and coldkey swaps. Notably it can manage the account's other proxies, which + makes it the natural "manager" proxy. +* **Transfer** — balance transfers and `transfer_stake` only. + **SmallTransfer** is the same but caps each transfer below 0.5 TAO + (0.5 alpha for stake transfers). +* **Staking** — stake add / remove / move / swap / unstake-all and their + limit variants, plus `set_root_claim_type`. Its allowlist does *not* + include `Utility.batch_all`, so batched staking through a Staking proxy + fails with `CallFiltered` — use NonTransfer for batched staking. +* **Registration** — neuron registration (`burned_register`, `register`, + `register_limit`). +* **ChildKeys** — `set_children` and `set_childkey_take`; + **RootClaim** — `claim_root` only. +* **Owner** — subnet-owner admin calls; **NonCritical** — everything except + registrations, coldkey swaps, and subnet dissolution; + **SubnetLeaseBeneficiary** — the lease-scoped subset of owner calls. + +`Triumvirate`, `Senate`, `Governance`, and `RootWeights` are deprecated on the +current runtime — they deny all calls. + +## Prerequisites + +```bash +uv venv && source .venv/bin/activate +uv pip install bittensor +``` + +One install gives you both `btcli` and the Python SDK. Practice on testnet +(`-n test` on every command, or `btcli config set network test`) before +mainnet. Two wallets for the walkthrough: + +* `safe` — the real account holding the funds. Its coldkey is the crown + jewel; on mainnet it should live in a hardware wallet or on an air-gapped + machine. +* `ops` — the delegate that will do daily work. Its coldkey lives on your + working machine and needs a little TAO for transaction fees. + +```bash +btcli wallet create -w safe +btcli wallet create -w ops +``` + +## Step 1 — grant the delegation + +The real account signs the grant — this is the one step that needs the `safe` +coldkey. Give `ops` staking powers with no delay: + +```bash +btcli proxy add --delegate ops --proxy-type Staking --delay 0 -w safe +``` + +`--delegate` accepts a wallet name, address-book name, or raw ss58. The same +operation exists as the [`add-proxy`](/docs/tx/add-proxy) intent +(`btcli tx add-proxy`), with `--dry-run` to preview the fee and reserved +deposit first. Multiple delegations can exist between the same pair of +accounts as long as each uses a different proxy type. + +**Keep the coldkey off this machine anyway.** The CLI can sign this grant on +a [Ledger](/docs/guides/ledger) (`--ledger`) or in a +[browser extension](/docs/guides/extension-signing) (`--signer extension` +with Talisman, Polkadot.js, SubWallet, ...), so even the initial `add-proxy` +never puts the `safe` mnemonic on a networked box: + +```bash +btcli proxy add --delegate ops --proxy-type Staking -w safe --ledger +``` + +After the first grant, a `NonTransfer` proxy can add and remove the account's +other proxies — so the primary coldkey is only ever needed once (see +[the recommended architecture](#a-recommended-architecture) below). + +## Step 2 — view delegations + +Straight from chain state — who may sign for the account, with what scope, +delay, and reserved deposit: + +```bash +btcli proxy list --coldkey safe +btcli query proxies --coldkey safe --json # same read, machine-readable +``` + +```python +import bittensor as sub + +async with sub.Client("test") as client: + delegations = await client.balances.proxies(coldkey_ss58="5F...safe") +``` + +For proxies you use often — especially ones on other machines or pure proxies +— the local **proxy book** gives them names that every `--proxy-for` and +address argument resolves: + +```bash +btcli proxy book add --name safe-staking --address 5F...ops \ + --proxy-type Staking --delay 0 --note "daily staking ops" +btcli proxy book list +``` + +(`update`, `remove`, and `clear` complete the group.) + +## Step 3 — act through the proxy + +Every `btcli tx` command takes `--proxy-for`. The wallet you sign with (`-w`) +is the *delegate*; `--proxy-for` names the *real account* being acted for: + +```bash +btcli tx add-stake --hotkey 5F...validator --netuid 1 --amount-tao 25 \ + -w ops --proxy-for safe --dry-run + +btcli tx add-stake --hotkey 5F...validator --netuid 1 --amount-tao 25 \ + -w ops --proxy-for safe +``` + +The stake moves from the `safe` account's balance; the `safe` coldkey was +never touched. `--force-proxy-type Staking` additionally requires the +delegation used to be exactly that type. In Python it's one keyword on +`execute`: + +```python +from bittensor.wallet import Wallet + +ops = Wallet(name="ops") + +async with sub.Client("test") as client: + intent = sub.AddStake(hotkey_ss58="5F...validator", netuid=1, amount_tao=25) + result = await client.execute(intent, ops, proxy_for="5F...safe") + if not result.success: + print(result.error.code, result.error.remediation) +``` + +A call outside the delegation's scope fails with `Unproxyable` / +`CallFiltered` — the runtime filter, not the CLI, is what enforces the scope. + +## Step 4 — delayed proxies: announce, wait, execute, veto + +For higher-risk powers, add a delay (in blocks; one block ≈ 12 seconds, so +300 blocks ≈ 1 hour). The delegate must then announce each call's hash and +wait out the delay before executing — and during that window the real account +(or its NonTransfer proxy) can veto: + +```bash +btcli proxy add --delegate ops --proxy-type Transfer --delay 300 -w safe +``` + +**Announce.** Announcing publishes the hash of the intended call +(blake2-256 of its SCALE encoding). Compose the call to get its hash, then +submit `Proxy.announce` as the delegate — via the raw-call escape hatch on +the CLI, or `submit_call` in Python: + +```python +ops = Wallet(name="ops") + +async with sub.Client("test") as client: + call = sub.calls.Balances.transfer_keep_alive( + dest="5DevPayee...", + value=10 * 10**9, # raw calls take rao (1 TAO = 1e9 rao) + ) + composed = await client.compose(call) + call_hash = "0x" + bytes(composed.call_hash).hex() + + await client.submit_call( + sub.calls.Proxy.announce(real="5F...safe", call_hash=call_hash), ops + ) +``` + +**Execute after the delay.** The announced call is rebuilt with byte-identical +parameters — anything else hashes differently and matches no announcement. +The convenient form takes an intent op and its args: + +```bash +btcli proxy execute \ + --delegate ops --real safe \ + --inner-op transfer \ + --inner-args '{"dest_ss58": "5DevPayee...", "amount_tao": 10}' \ + -w ops +``` + +(the same operation as the +[`execute-proxy-announced`](/docs/tx/execute-proxy-announced) intent, also +reachable as `btcli tx execute-proxy-announced` or `sub.ExecuteProxyAnnounced` +in Python). Executing before the delay elapses fails with `Unannounced`. + +**Monitor and veto.** A delay only protects you if you *watch* — check +pending announcements on a schedule shorter than the delay, otherwise the +window is only forensic. Announcements are keyed by delegate in the +`Proxy.Announcements` storage map: + +```python +async with sub.Client("test") as client: + for delegate, value in await client.query_map(sub.storage.Proxy.Announcements): + announcements, _deposit = value + for ann in announcements: + if str(ann["real"]) == "5F...safe": + print(delegate, ann["call_hash"], "announced at", ann["height"]) +``` + +Reject anything you don't recognize — rejection cancels the announcement +on-chain, signed by the real account or a `NonTransfer` proxy acting for it: + +```bash +btcli call Proxy.reject_announcement \ + --args '{"delegate": "5F...ops", "call_hash": "0x..."}' \ + -w safe +``` + +A delegate withdrawing its own mistaken announcement uses +`Proxy.remove_announcement` instead, signed by the delegate and passing the +*real* account: `--args '{"real": "5F...safe", "call_hash": "0x..."}'`. + +## Step 5 — pure proxies + +[`create-pure-proxy`](/docs/tx/create-pure-proxy) spawns a fresh account with +**no private key anywhere** — the spawner controls it purely through the +proxy relationship. That makes it a disposable, role-scoped account (a +staking-only treasury, a per-project operational account), and the standard +building block for multisigs with swappable members: make each multisig +member a pure proxy, and a departing teammate is handled by re-pointing one +pure proxy's delegate — the multisig address never changes. + +```bash +btcli proxy create --proxy-type Any --name treasury -w safe +``` + +`--name` saves the spawned address *and its creation block height / extrinsic +index* to the proxy book — record these; killing the account later requires +them. Fund the pure proxy, then act as it with the same `--proxy-for` flow, +signed by the spawner: + +```bash +btcli tx transfer --dest treasury --amount-tao 50 -w safe +btcli tx add-stake --hotkey 5F...validator --netuid 1 --amount-tao 20 \ + -w safe --proxy-for treasury +``` + +`btcli proxy list --coldkey treasury` shows the spawner as its delegate. +Additional delegates can be registered on the pure proxy itself by +dispatching `add-proxy` *through* it (`btcli tx add-proxy --delegate ... -w +safe --proxy-for treasury`). + +When it's no longer needed, empty it first, then destroy it. `kill` must be +dispatched through the pure proxy itself and must reproduce the exact +creation coordinates — which the proxy book entry supplies automatically: + +```bash +btcli tx transfer --dest safe --amount-tao all -w safe --proxy-for treasury +btcli proxy kill treasury -w safe +``` + +**Irreversible:** any funds left in a killed pure proxy are gone permanently. +And since the spawner relationship is the only control path, losing the +spawner key — or removing its delegation — strands the pure proxy and +everything it holds. See [`kill-pure-proxy`](/docs/tx/kill-pure-proxy). + +## Step 6 — revoke + +Removal must match the original grant's (delegate, type, delay) triple +exactly, and takes effect immediately regardless of the delegation's delay. +The deposit comes back: + +```bash +btcli proxy remove --delegate ops --proxy-type Staking --delay 0 -w safe +``` + +The panic switch drops every delegation at once +([`remove-proxies`](/docs/tx/remove-proxies)): + +```bash +btcli proxy remove --all -w safe +``` + +Careful with `--all` if the account spawned pure proxies — they are +controlled *through* delegations, so removing everything can strand them +permanently. Treat any leaked **zero-delay** proxy key as already spent: +revoke first, investigate second. + +## A recommended architecture + +The pattern the pieces are designed for, from a compromised-wallet +perspective: + +1. **Primary coldkey** in a hardware wallet or air-gapped machine. It signs + exactly one thing, once: `add-proxy` for a delayed **NonTransfer** manager + proxy (`--ledger` / `--signer extension` keep it off the CLI machine even + for that). +2. **Manager proxy** (`NonTransfer`, `delay > 0`) creates, removes, and + monitors all other proxies, and rejects hostile announcements. It cannot + move funds even if leaked, and its own actions are announced and delayed — + visible before they bite. +3. **Scoped daily proxies** (`Staking`, `Registration`, `ChildKeys`, ..., + `delay 0`) on working machines, one per job, revoked the moment they're + not actively needed. +4. **A monitor** — a cron job or agent polling `Proxy.Announcements` (and + your delegations via `btcli query proxies --json`) on a schedule shorter + than your shortest delay. + +With this in place, the key that can drain you touches a signer once in its +life, every routine key is individually expendable, and an attacker's move +announces itself before it can execute. For shared custody on top — +subnet-owner treasuries especially — put a [multisig](/docs/guides/multisig) +behind the real account. + +### Subnet owners specifically + +A subnet owner's coldkey controls the subnet's identity and owner-settable +hyperparameters — exactly the kind of authority that should never live in a +single key on an operational machine. The layering: + +* Make the owner account a [multisig](/docs/guides/multisig) from + registration (or migrate an existing owner to one via the announced + coldkey swap — see + [that guide](/docs/guides/multisig#migrate-an-existing-account-to-a-multisig); + a plain transfer moves TAO but *not* subnet ownership). +* From the owner, grant a dedicated operations key the narrow **Owner** proxy + type. It can manage subnet identity and hyperparameters but cannot transfer + funds or rotate the owner. Use it for all routine changes; the owner + account itself acts only to replace or revoke that proxy. +* Add an announcement delay on the Owner proxy when your operational cadence + permits, and monitor announcements more often than the delay. + +Crowd-funded subnets get a chain-native version of this model for free: a +crowdloan lease creates the lease-owner account itself and grants the +beneficiary a scoped `SubnetLeaseBeneficiary` proxy. + +## Deposits and limits + +* Adding a proxy reserves **0.06 TAO base + 0.033 TAO per delegation** from + the granting account, returned on removal. At most **20 proxies** per + account. +* Announcements reserve **0.036 TAO base + 0.068 TAO each** from the + delegate; at most **75 pending** per delegate. +* The delegate pays ordinary transaction fees on proxied calls, so keep a + little TAO on it. + +## Troubleshooting + +* `NotProxy` — no delegation matches; check `btcli proxy list` and that + you're signing with the delegate (`-w`) and naming the real account + (`--proxy-for`), not the reverse. +* `Unproxyable` / `CallFiltered` — the call is outside the delegation's proxy + type (remember `Staking` excludes `batch_all`). +* `Unannounced` — a delayed delegation executed without announcing, before + the delay elapsed, or with parameters that don't hash to the announcement. +* `Duplicate` — a delegation with the same (delegate, type, delay) already + exists. +* `TooMany` — over the 20-proxy or 75-announcement cap; remove unused + entries. diff --git a/docs/guides/running-a-node.mdx b/docs/guides/running-a-node.mdx index f69343699d..8f032c04de 100644 --- a/docs/guides/running-a-node.mdx +++ b/docs/guides/running-a-node.mdx @@ -168,7 +168,7 @@ head. Point the SDK and CLI at your node by passing the endpoint as the network: ```bash -btcli query block-info --network ws://127.0.0.1:9944 +btcli query block-info --block 0 --network ws://127.0.0.1:9944 ``` ```python diff --git a/docs/guides/staking.mdx b/docs/guides/staking.mdx index eb6000266f..fb4c940223 100644 --- a/docs/guides/staking.mdx +++ b/docs/guides/staking.mdx @@ -6,7 +6,9 @@ description: Back validators with TAO, manage positions across subnets, and clai Staking backs a validator (a **hotkey**) with your TAO. On a subnet, staking swaps TAO into the subnet's alpha at the pool price; your position's value then follows the pool price and the validator's performance. On the root network -(netuid 0) stake stays TAO-denominated. +(netuid 0) stake stays TAO-denominated. For how the pools themselves work — +price impact, fees, MEV — see +[staking and pools](/docs/concepts/staking-pools). ## Pick a validator diff --git a/docs/guides/subnets.mdx b/docs/guides/subnets.mdx index 2342e97c49..95ee8761b1 100644 --- a/docs/guides/subnets.mdx +++ b/docs/guides/subnets.mdx @@ -41,6 +41,36 @@ needs the [raw-call escape hatch](/docs/concepts/advanced). A new subnet is a two-step commitment: register, then activate with [`start-call`](/docs/tx/start-call) — until then it earns nothing. +## Secure the owner account + +A subnet owner's coldkey controls the subnet's identity and owner-settable +hyperparameters — authority that should never live in a single coldkey on an +operational machine. The standard layering: make the owner account a +[**multisig**](/docs/guides/multisig) from registration, and from it grant a +dedicated operations key the narrow **Owner** +[proxy type](/docs/guides/proxies#proxy-types) for routine changes. The ops +key can manage subnet identity and hyperparameters but cannot transfer funds +or rotate the owner; the multisig convenes only to manage that proxy, veto +delayed calls, and act outside the **Owner** scope. Both guides walk through +the setup — see +[Subnet owners specifically](/docs/guides/proxies#subnet-owners-specifically) +in the proxy guide for this exact pattern. + +For a new crowd-funded subnet, a +[crowdloan lease](#financing-leases-and-crowdloans) provides a different secure +ownership model: the chain creates the lease owner account and grants the +beneficiary a scoped `SubnetLeaseBeneficiary` proxy. + +If the subnet is already owned by a single coldkey, adopting this setup is a +one-time ownership migration via the announced **coldkey swap**. Sending TAO to +the multisig address is not enough: balance, stake, conviction locks, and +subnet ownership are separate on-chain state, and a plain +[`transfer`](/docs/tx/transfer) moves only the first. The full procedure — +verifying the derived address, what the swap moves, and the delay — is in +[Migrate an existing account to a multisig](/docs/guides/multisig#migrate-an-existing-account-to-a-multisig); +[Wallet key rotation](/docs/concepts/wallets#key-rotation-and-recovery) covers +what a coldkey swap does and doesn't move. + ## Activate Once the chain's activation delay has passed, flip the subnet on with @@ -102,13 +132,13 @@ The important owner-settable parameters, with chain defaults: | Parameter | Default | What it does | | --- | --- | --- | -| `tempo` * | 360 | Blocks per epoch. Owner changes bounded 360–50,400. | +| `tempo` | 360 | Blocks per epoch. Owner changes bounded 360–50,400 and reset the epoch cycle. | | `immunity_period` | 4,096 | Blocks a fresh UID is protected from pruning and trimming. | | `min_burn` / `max_burn` | 0.0005 τ / 100 τ | Clamp on the neuron registration price. Read the live price with [`burn`](/docs/query/burn). | | `burn_half_life` | 360 | Blocks for the registration price to decay halfway toward `min_burn` when nobody registers. | | `burn_increase_mult` | 1.26 | Multiplier applied to the registration price after each successful registration. | | `max_allowed_uids` | 256 | UID slots. Floor 64; times mechanism count must stay ≤ 256. | -| `activity_cutoff_factor` * | 13,889 | Per-mille of tempo; effective cutoff = factor × tempo / 1000 blocks (5,000 at tempo 360; bounds 1,000–50,000). A validator that hasn't set weights within the cutoff sits out until next epoch. Replaces the deprecated `activity_cutoff` (5,000). | +| `activity_cutoff_factor` | 13,889 | Per-mille of tempo; effective cutoff = factor × tempo / 1000 blocks (5,000 at tempo 360; bounds 1,000–50,000). A validator that hasn't set weights within the cutoff sits out until next epoch. Replaces the deprecated `activity_cutoff` (5,000). | | `serving_rate_limit` | 50 | Minimum blocks between `serve-axon` / `serve-prometheus` calls per key. | | `commit_reveal_weights_enabled` | true | Weights are committed hashed, revealed later. | | `commit_reveal_period` | 1 | Tempos between commit and reveal. | @@ -166,11 +196,11 @@ excess neurons. Rules: ## Mechanisms A subnet can run multiple scoring mechanisms, each with its own weights. -[`set-mechanism-count`](/docs/tx/set-mechanism-count) and -[`set-mechanism-emission-split`](/docs/tx/set-mechanism-emission-split) -configure them; validators target one via the `mechid` field on -[`set-weights`](/docs/tx/set-weights). Read the current count with -[`mechanism-count`](/docs/query/mechanism-count). +[`set-mechanism-count`](/docs/tx/set-mechanism-count) configures how many; +validators target one via the `mechid` field on +[`set-weights`](/docs/tx/set-weights). Read the current count and split with +[`mechanism-count`](/docs/query/mechanism-count) and +[`mechanism-emission-split`](/docs/query/mechanism-emission-split). - The chain currently caps subnets at **2** mechanisms. - Each mechanism runs Yuma Consensus independently, with its own weight matrix @@ -186,8 +216,7 @@ configure them; validators target one via the `mechid` field on - The emission split is a vector of u16 weights summing to 65,535 (e.g. `[13107, 52428]` is 20%/80%); unset means an even split. Changing the mechanism count resets the split to even. -- Count and split changes are each rate-limited to once per 7,200 blocks - (~24 hours). +- Count changes are rate-limited to once per 7,200 blocks (~24 hours). ## Deregistration diff --git a/docs/hyperparameters/activity-cutoff-factor.mdx b/docs/hyperparameters/activity-cutoff-factor.mdx new file mode 100644 index 0000000000..e20e4a95c6 --- /dev/null +++ b/docs/hyperparameters/activity-cutoff-factor.mdx @@ -0,0 +1,53 @@ +--- +title: activity_cutoff_factor +description: Tempo-relative activity cutoff — per-mille of tempo a validator can go without setting weights. +--- + +`activity_cutoff_factor` sets the inactivity window as a fraction of the +epoch length instead of an absolute block count: the effective +[`activity_cutoff`](/docs/hyperparameters/activity-cutoff) is +`factor × tempo / 1000` blocks. Because the window scales with +[`tempo`](/docs/hyperparameters/tempo), a subnet that changes its epoch +cadence keeps the same *number of epochs* of tolerance rather than a fixed +wall-clock window. + +## How it works + +`get_activity_cutoff_blocks` (`pallets/subtensor/src/utils/misc.rs`) computes +`factor_milli × tempo / 1000`, clamped to at least 1 block, and the epoch uses +that value to mask inactive validators out of consensus (see +[`activity_cutoff`](/docs/hyperparameters/activity-cutoff) for the masking +mechanics). + +The factor is per-mille: 1,000 means one full tempo, 13,889 (the default) +means ~13.9 tempos — 5,000 blocks (~16.7 hours) at the default tempo of 360, +matching the legacy absolute cutoff. Bounds are 1,000–50,000 (one to fifty +tempos). + +## Reading and setting + +Inspect with: + +``` +btcli sudo get --netuid N --name activity_cutoff_factor +``` + +Owner-settable: + +``` +btcli sudo set --netuid N --name activity_cutoff_factor --value 13889 +``` + +This dispatches `sudo_set_activity_cutoff_factor` (AdminUtils), owner-or-root: +the owner path is rate-limited (once per ~2 tempos by default) and respects +the admin freeze window; root bypasses both. It supersedes the legacy +absolute-blocks `sudo_set_activity_cutoff`, whose stored value the epoch +ignores. + +## Related + +[`activity_cutoff`](/docs/hyperparameters/activity-cutoff) · +[`tempo`](/docs/hyperparameters/tempo) · +[`kappa`](/docs/hyperparameters/kappa) · +[`max_validators`](/docs/hyperparameters/max-validators) · +[`weights_rate_limit`](/docs/hyperparameters/weights-rate-limit) diff --git a/docs/hyperparameters/activity-cutoff.mdx b/docs/hyperparameters/activity-cutoff.mdx new file mode 100644 index 0000000000..e6ee985bff --- /dev/null +++ b/docs/hyperparameters/activity-cutoff.mdx @@ -0,0 +1,62 @@ +--- +title: activity_cutoff +description: Blocks without setting weights after which a validator is inactive and excluded from consensus. +--- + +`activity_cutoff` is the window, in blocks, that a validator can go without +setting weights before the epoch marks it inactive and removes its stake from +consensus. Validators care because an expired cutoff silently zeroes their +influence and dividends; owners tune it to keep consensus limited to +validators that are actually evaluating miners. + +## How it works + +At each epoch (`pallets/subtensor/src/epoch/run_epoch.rs`), a neuron is +inactive when `last_update + activity_cutoff` is less than the current block, +where `last_update` is the block of its last accepted weight submission. +Inactive stake is masked out of the active-stake vector before consensus, so +an inactive validator contributes nothing to the +[`kappa`](/docs/hyperparameters/kappa)-weighted median and earns no +dividends until it sets weights again. + +The effective value is derived, not stored directly: +`get_activity_cutoff_blocks` (`pallets/subtensor/src/utils/misc.rs`) computes +`factor_milli × tempo / 1000`, clamped to at least 1 block. The per-mille +factor is bounded to 1,000–50,000 (one to fifty tempos); the default 13,889 +at tempo 360 yields the legacy 5,000-block cutoff (~16.7 hours). + +The chart below plots each validator's blocks-since-last-weights against the +cutoff — slide it to see who drops out of consensus. + + + +## Reading and setting + +Inspect with: + +``` +btcli sudo get --netuid N --name activity_cutoff +``` + +This returns the effective block count. It is not settable directly: the +epoch derives it from +[`activity_cutoff_factor`](/docs/hyperparameters/activity-cutoff-factor) +(per-mille of [`tempo`](/docs/hyperparameters/tempo)), so change the factor +instead: + +``` +btcli sudo set --netuid N --name activity_cutoff_factor --value 13889 +``` + +A legacy absolute-blocks extrinsic (`sudo_set_activity_cutoff`) still exists +on chain, but the value it writes is ignored by the epoch and it will be +removed; don't use it. + +## Related + +[`activity_cutoff_factor`](/docs/hyperparameters/activity-cutoff-factor) · +[`tempo`](/docs/hyperparameters/tempo) · +[`kappa`](/docs/hyperparameters/kappa) · +[`rho`](/docs/hyperparameters/rho) · +[`max_validators`](/docs/hyperparameters/max-validators) · +[`weights_rate_limit`](/docs/hyperparameters/weights-rate-limit) diff --git a/docs/hyperparameters/adjustment-alpha.mdx b/docs/hyperparameters/adjustment-alpha.mdx new file mode 100644 index 0000000000..4d5ac8860e --- /dev/null +++ b/docs/hyperparameters/adjustment-alpha.mdx @@ -0,0 +1,58 @@ +--- +title: adjustment_alpha +description: Smoothing factor of the legacy difficulty/burn adjustment, stored as u64 (u64::MAX = 1.0). +--- + +The smoothing factor of Bittensor's original registration-price controller. +When the chain adjusted burn and PoW difficulty once per +[`adjustment_interval`](/docs/hyperparameters/adjustment-interval), this value +blended the old price into the new one — an exponential moving average where +higher alpha meant slower movement. Owners still see it in the hyperparameter +listing; on the current chain it is inert. + +## How it works + +The value lives in `AdjustmentAlpha` storage as a u64 fraction where +`u64::MAX` encodes 1.0. The mainnet default is **0** +(`SubtensorInitialAdjustmentAlpha` in `runtime/src/lib.rs`) — in the legacy +formula `next = alpha × current + (1 − alpha) × proposed`, zero meant no +weight on the previous value, i.e. no smoothing at all. + +In the current runtime the legacy interval controller is gone, and **nothing +reads this value** outside of the RPC/metagraph views: registration pricing is +handled per block in `pallets/subtensor/src/subnets/registration.rs`, where +each registration bumps the burn by +[`burn_increase_mult`](/docs/hyperparameters/burn-increase-mult) and decay with +half-life [`burn_half_life`](/docs/hyperparameters/burn-half-life) pulls it +back toward [`min_burn`](/docs/hyperparameters/min-burn). The setter +(`sudo_set_adjustment_alpha` in `pallets/admin-utils/src/lib.rs`) remains +callable by the subnet owner, subject only to the owner rate limit and admin +freeze window — but setting it changes nothing about how registrations are +priced. + +The chart below replays the same registration burst under the old interval-EMA +controller (where alpha mattered) and the current continuous one (where it +does not). + + + +## Reading and setting + +```bash +btcli sudo get --netuid 12 --name adjustment_alpha +btcli sudo set --netuid 12 --name adjustment_alpha --value 0.5 +``` + +Settable by the subnet owner or root. A value with a decimal point is a 0..1 +fraction (`0.5` = half weight on the previous value); a plain integer is the +raw u64 (u64::MAX = 1.0). + +## Related + +- [`adjustment_interval`](/docs/hyperparameters/adjustment-interval) — the + legacy controller's cadence +- [`target_regs_per_interval`](/docs/hyperparameters/target-regs-per-interval) + — the target it steered toward +- [`burn_half_life`](/docs/hyperparameters/burn-half-life) / + [`burn_increase_mult`](/docs/hyperparameters/burn-increase-mult) — the + controller that replaced it diff --git a/docs/hyperparameters/adjustment-interval.mdx b/docs/hyperparameters/adjustment-interval.mdx new file mode 100644 index 0000000000..10881244b2 --- /dev/null +++ b/docs/hyperparameters/adjustment-interval.mdx @@ -0,0 +1,55 @@ +--- +title: adjustment_interval +description: Blocks between adjustments of the registration difficulty and burn cost (legacy cadence). +--- + +Historically the cadence of Bittensor's registration-price controller: every +`adjustment_interval` blocks, the chain compared registrations against +[`target_regs_per_interval`](/docs/hyperparameters/target-regs-per-interval) +and moved the burn cost and PoW difficulty up or down. It matters today mostly +as context — the current chain adjusts the burn price continuously instead. + +## How it works + +The parameter lives in `AdjustmentInterval` storage (u16 block count, default +100 blocks ≈ 20 minutes, from `SubtensorInitialAdjustmentInterval` in +`runtime/src/lib.rs`). In the current runtime, however, **no adjustment logic +reads it**: the interval-based controller was replaced by a per-block +mechanism in `pallets/subtensor/src/subnets/registration.rs` where each +registration multiplies the burn by +[`burn_increase_mult`](/docs/hyperparameters/burn-increase-mult) and the price +decays every block with half-life +[`burn_half_life`](/docs/hyperparameters/burn-half-life), clamped to +\[[`min_burn`](/docs/hyperparameters/min-burn), +[`max_burn`](/docs/hyperparameters/max-burn)\]. The value is still stored, +returned by the `subnet_hyperparameters` query, and settable — the extrinsic +(`sudo_set_adjustment_interval` in `pallets/admin-utils/src/lib.rs`) checks +only that the subnet exists — but changing it has no effect on registration +pricing. Even the root subnet's per-interval registration counter resets on +the root epoch boundary (`tempo`), not on this interval. + +The chart below replays the same registration burst under the old +once-per-interval controller and the current per-block one — drag the interval +to see what the cadence used to change. + + + +## Reading and setting + +```bash +btcli sudo get --netuid 12 --name adjustment_interval +``` + +Root/governance only — `sudo_set_adjustment_interval` requires the root +origin, so subnet owners cannot change it. The value is a plain block count +(12-second blocks). + +## Related + +- [`burn_half_life`](/docs/hyperparameters/burn-half-life) — the cadence's + replacement for burn decay +- [`burn_increase_mult`](/docs/hyperparameters/burn-increase-mult) — the + per-registration response +- [`adjustment_alpha`](/docs/hyperparameters/adjustment-alpha) — the smoothing + factor of the same legacy controller +- [`target_regs_per_interval`](/docs/hyperparameters/target-regs-per-interval) diff --git a/docs/hyperparameters/alpha-high.mdx b/docs/hyperparameters/alpha-high.mdx new file mode 100644 index 0000000000..d4508adee8 --- /dev/null +++ b/docs/hyperparameters/alpha-high.mdx @@ -0,0 +1,38 @@ +--- +title: alpha_high +description: Upper bound of the liquid-alpha bonds EMA range — the smoothing rate for weights that deviate most from consensus. +--- + +`alpha_high` is the ceiling of the per-pair bonds EMA rate when [`liquid_alpha_enabled`](/docs/hyperparameters/liquid-alpha-enabled) is on. Pairs whose weights deviate strongly from consensus approach this rate, so their bonds move fastest. Subnet owners tune it to decide how quickly a bold, off-consensus position translates into bonds; validators care because it caps how fast conviction can build or unwind. + +## How it works + +Each epoch, `alpha_sigmoid` (`pallets/subtensor/src/epoch/run_epoch.rs`) maps a pair's deviation from consensus into the EMA rate: + +``` +alpha = alpha_low + sigmoid(diff) × (alpha_high − alpha_low) +``` + +The deviation is `weight − consensus` when a validator is raising its bond, or `bond − weight` when lowering it (both clamped to 0..1). As deviation grows past 0.5 the sigmoid saturates toward 1 and alpha approaches `alpha_high`; the result is clamped into the `alpha_low`..`alpha_high` window. The resulting per-pair matrix drives the bonds EMA `B(t) = alpha × W + (1 − alpha) × B(t−1)` in `mat_ema_alpha`. Defaults are (45875, 58982) — 0.7 and 0.9 of `u16::MAX`. + +Validation in `do_set_alpha_values`: liquid alpha must already be enabled (`LiquidAlphaDisabled`), and `alpha_high` must be at least 1638 (`u16::MAX / 40` ≈ 0.025), else `AlphaHighTooLow`. `alpha_low` may not exceed it. + + + +## Reading and setting + +``` +btcli sudo get --netuid N --name alpha_high +``` + +Stored as u16 (65535 = 1.0); the CLI accepts the raw integer or a 0..1 fraction with a decimal point: + +``` +btcli sudo set --netuid N --name alpha_high --value 0.9 +``` + +On chain, [`alpha_low`](/docs/hyperparameters/alpha-low) and `alpha_high` live in one storage value set by a single `sudo_set_alpha_values` call. The CLI reads the current pair and resubmits it with only `alpha_high` changed, so the other side is preserved. + +## Related + +[`alpha_low`](/docs/hyperparameters/alpha-low), [`alpha_sigmoid_steepness`](/docs/hyperparameters/alpha-sigmoid-steepness), [`liquid_alpha_enabled`](/docs/hyperparameters/liquid-alpha-enabled), [`bonds_moving_avg`](/docs/hyperparameters/bonds-moving-avg), [`yuma3_enabled`](/docs/hyperparameters/yuma3-enabled) diff --git a/docs/hyperparameters/alpha-low.mdx b/docs/hyperparameters/alpha-low.mdx new file mode 100644 index 0000000000..233a32a0e7 --- /dev/null +++ b/docs/hyperparameters/alpha-low.mdx @@ -0,0 +1,38 @@ +--- +title: alpha_low +description: Lower bound of the liquid-alpha bonds EMA range — the smoothing rate for weights that sit at consensus. +--- + +`alpha_low` is the floor of the per-pair bonds EMA rate when [`liquid_alpha_enabled`](/docs/hyperparameters/liquid-alpha-enabled) is on. It is the rate applied to validator–miner pairs whose weights match consensus — the slow, steady end of the liquid-alpha range. Subnet owners tune it to set how sluggish bonds are in the default case; validators care because it governs how fast their conviction decays or accrues when they are not deviating. + +## How it works + +Each epoch, `alpha_sigmoid` (`pallets/subtensor/src/epoch/run_epoch.rs`) computes a per-pair rate: + +``` +alpha = alpha_low + sigmoid(diff) × (alpha_high − alpha_low) +``` + +where `diff` is the pair's deviation from consensus and the sigmoid rises with [`alpha_sigmoid_steepness`](/docs/hyperparameters/alpha-sigmoid-steepness). At zero deviation the sigmoid is near 0, so alpha sits at `alpha_low`; the result is always clamped into the `alpha_low`..`alpha_high` window. Defaults are (45875, 58982), i.e. 0.7 and 0.9 of `u16::MAX`. + +Validation in `do_set_alpha_values`: liquid alpha must already be enabled (`LiquidAlphaDisabled`), `alpha_low` must be at least 1638 (`u16::MAX / 40` ≈ 0.025) and at most `alpha_high`, else `AlphaLowOutOfRange`. + + + +## Reading and setting + +``` +btcli sudo get --netuid N --name alpha_low +``` + +Stored as u16 (65535 = 1.0); the CLI accepts the raw integer or a 0..1 fraction with a decimal point: + +``` +btcli sudo set --netuid N --name alpha_low --value 0.7 +``` + +On chain, `alpha_low` and [`alpha_high`](/docs/hyperparameters/alpha-high) live in one storage value set by a single `sudo_set_alpha_values` call. The CLI reads the current pair and resubmits it with only `alpha_low` changed, so the other side is preserved. + +## Related + +[`alpha_high`](/docs/hyperparameters/alpha-high), [`alpha_sigmoid_steepness`](/docs/hyperparameters/alpha-sigmoid-steepness), [`liquid_alpha_enabled`](/docs/hyperparameters/liquid-alpha-enabled), [`bonds_moving_avg`](/docs/hyperparameters/bonds-moving-avg), [`yuma3_enabled`](/docs/hyperparameters/yuma3-enabled) diff --git a/docs/hyperparameters/alpha-sigmoid-steepness.mdx b/docs/hyperparameters/alpha-sigmoid-steepness.mdx new file mode 100644 index 0000000000..617a06de65 --- /dev/null +++ b/docs/hyperparameters/alpha-sigmoid-steepness.mdx @@ -0,0 +1,37 @@ +--- +title: alpha_sigmoid_steepness +description: Slope of the liquid-alpha sigmoid mapping consensus deviation to the bonds EMA rate; negative values (root only) invert the curve. +--- + +`alpha_sigmoid_steepness` shapes the transition between [`alpha_low`](/docs/hyperparameters/alpha-low) and [`alpha_high`](/docs/hyperparameters/alpha-high) when [`liquid_alpha_enabled`](/docs/hyperparameters/liquid-alpha-enabled) is on. Subnet owners tune it to make the liquid-alpha response gradual (small values) or nearly a step function (large values); validators care because it decides how much deviation from consensus is needed before their bonds start moving at the fast rate. + +## How it works + +For each validator–miner pair, `alpha_sigmoid` (`pallets/subtensor/src/epoch/run_epoch.rs`) computes + +``` +sigmoid = 1 / (1 + e^(-(steepness / 100) × (diff − 0.5))) +alpha = alpha_low + sigmoid × (alpha_high − alpha_low) +``` + +where `diff` is the pair's deviation from consensus, clamped to 0..1. The stored i16 is divided by 100, so the default 1000 gives an effective slope of 10: alpha sits near `alpha_low` for small deviations, crosses the midpoint at `diff = 0.5`, and saturates near `alpha_high` beyond it. Steepness near 0 flattens the curve to the midpoint of the range; very large values approximate a hard threshold at 0.5. + +Negative steepness inverts the curve — consensus-aligned pairs get the fast rate — and `sudo_set_alpha_sigmoid_steepness` in `pallets/admin-utils/src/lib.rs` reserves that to root: a subnet owner passing a negative value gets `NegativeSigmoidSteepness`. + + + +## Reading and setting + +``` +btcli sudo get --netuid N --name alpha_sigmoid_steepness +``` + +Plain integer (i16); owners may set 0 or positive values: + +``` +btcli sudo set --netuid N --name alpha_sigmoid_steepness --value 1000 +``` + +## Related + +[`alpha_low`](/docs/hyperparameters/alpha-low), [`alpha_high`](/docs/hyperparameters/alpha-high), [`liquid_alpha_enabled`](/docs/hyperparameters/liquid-alpha-enabled), [`yuma3_enabled`](/docs/hyperparameters/yuma3-enabled) diff --git a/docs/hyperparameters/bonds-moving-avg.mdx b/docs/hyperparameters/bonds-moving-avg.mdx new file mode 100644 index 0000000000..a0d1ae0fff --- /dev/null +++ b/docs/hyperparameters/bonds-moving-avg.mdx @@ -0,0 +1,38 @@ +--- +title: bonds_moving_avg +description: Smoothing factor for the validator bonds EMA — higher retains more of past bonds each epoch. +--- + +`bonds_moving_avg` controls how slowly validator bonds track a validator's current weights. Subnet owners tune it to decide how long validators must hold a position before their bonds (and thus dividends) catch up; validators care because it sets how fast early, correct weight calls pay off. + +## How it works + +Bonds are an exponential moving average over each validator–miner pair. Every epoch the chain computes + +``` +B(t) = alpha × ΔB + (1 − alpha) × B(t−1) +``` + +where `alpha = 1 − bonds_moving_avg / 1,000,000` (`compute_ema_bonds_normal` in `pallets/subtensor/src/epoch/run_epoch.rs`). The default 900,000 gives alpha = 0.1: each epoch a bond moves 10% of the way toward the validator's instant bond. Higher values mean stickier bonds and slower dividend response; lower values make bonds chase current weights quickly, weakening the reward for early conviction. + +The flat rate applies on the classic consensus path, and also on the [`yuma3_enabled`](/docs/hyperparameters/yuma3-enabled) path whenever [`liquid_alpha_enabled`](/docs/hyperparameters/liquid-alpha-enabled) is off (`compute_disabled_liquid_alpha`). With liquid alpha on, the per-pair sigmoid rate replaces it entirely. + +Subnet owners can set at most 975,000 (`BondsMovingAverageMaxReached` in `pallets/admin-utils/src/lib.rs`); only root can go higher. + + + +## Reading and setting + +``` +btcli sudo get --netuid N --name bonds_moving_avg +``` + +Stored over 1,000,000 (900000 = 0.9). The set command accepts either the raw integer or the human fraction with a decimal point: + +``` +btcli sudo set --netuid N --name bonds_moving_avg --value 0.9 +``` + +## Related + +[`liquid_alpha_enabled`](/docs/hyperparameters/liquid-alpha-enabled), [`alpha_low`](/docs/hyperparameters/alpha-low), [`alpha_high`](/docs/hyperparameters/alpha-high), [`bonds_penalty`](/docs/hyperparameters/bonds-penalty), [`yuma3_enabled`](/docs/hyperparameters/yuma3-enabled) diff --git a/docs/hyperparameters/bonds-penalty.mdx b/docs/hyperparameters/bonds-penalty.mdx new file mode 100644 index 0000000000..dfa7ad3f34 --- /dev/null +++ b/docs/hyperparameters/bonds-penalty.mdx @@ -0,0 +1,39 @@ +--- +title: bonds_penalty +description: How much out-of-consensus weight is excluded from bond accrual, from none (0) to full clipping (1). +--- + +`bonds_penalty` decides whether validators build bonds from their raw weights or only from the consensus-clipped portion. Subnet owners use it to punish (or tolerate) weight-setting that deviates from the stake-weighted consensus; validators care because it determines whether an out-of-consensus bet still accrues bonds. + +## How it works + +Before the bonds EMA runs, the epoch interpolates between raw and clipped weight matrices (`pallets/subtensor/src/epoch/run_epoch.rs`, around the "Bonds and Dividends" step): + +``` +weights_for_bonds = (1 − penalty) × weights + penalty × clipped_weights +``` + +- `bonds_penalty = 0`: bonds accrue from raw weights — deviating from consensus costs nothing in bond terms. +- `bonds_penalty = 1` (the default, stored as 65535): bonds accrue only from consensus-clipped weights, so any weight above the stake-weighted median is ignored for bonding. + +Values in between blend the two. The interpolated matrix feeds the bonds EMA on both the classic path and the [`yuma3_enabled`](/docs/hyperparameters/yuma3-enabled) path, so the penalty applies regardless of which consensus variant is active. See the [Yuma Consensus overview](/docs/concepts/emissions#yuma-consensus) for where this sits in the epoch. + +Drag the penalty from 0 to 1 to watch a deviant validator's out-of-consensus weight get clipped away while an in-consensus validator is untouched: + + + +## Reading and setting + +``` +btcli sudo get --netuid N --name bonds_penalty +``` + +Stored as u16 (65535 = 1.0, full penalty). The set command accepts the raw integer or a 0..1 fraction with a decimal point: + +``` +btcli sudo set --netuid N --name bonds_penalty --value 1.0 +``` + +## Related + +[`bonds_moving_avg`](/docs/hyperparameters/bonds-moving-avg), [`bonds_reset_enabled`](/docs/hyperparameters/bonds-reset-enabled), [`liquid_alpha_enabled`](/docs/hyperparameters/liquid-alpha-enabled), [`yuma3_enabled`](/docs/hyperparameters/yuma3-enabled) diff --git a/docs/hyperparameters/bonds-reset-enabled.mdx b/docs/hyperparameters/bonds-reset-enabled.mdx new file mode 100644 index 0000000000..5f9c72f74e --- /dev/null +++ b/docs/hyperparameters/bonds-reset-enabled.mdx @@ -0,0 +1,34 @@ +--- +title: bonds_reset_enabled +description: Whether a neuron's accrued validator bonds are wiped when it commits new on-chain metadata. +--- + +`bonds_reset_enabled` makes metadata commitments costly for miners: when enabled, any hotkey that publishes a commitment on the subnet has all bonds pointing at it erased. Subnet owners enable it on subnets where a metadata commit signals a model or identity change that should reset accumulated validator conviction; validators care because their bonds on that miner — and the dividends they earn from them — restart from zero. + +## How it works + +When a hotkey commits metadata (the commitments pallet's `OnMetadataCommitment` hook, wired up as `ResetBondsOnCommit` in `runtime/src/lib.rs`), the chain calls `do_reset_bonds` in `pallets/subtensor/src/epoch/run_epoch.rs` for each mechanism of the subnet. If this flag is off, the call returns immediately. If it is on, the chain looks up the committing hotkey's UID and filters that UID's column out of every validator's bond vector — every bond held **on** the committing neuron is deleted, while bonds the neuron holds as a validator on others are untouched. + +Bonds then rebuild through the normal EMA at the rate set by [`bonds_moving_avg`](/docs/hyperparameters/bonds-moving-avg) (or the liquid-alpha rate), so validators who re-endorse the refreshed miner regain dividends over subsequent epochs. + +Here is what that looks like for a validator's bond on a miner that commits metadata mid-way — toggle the flag to compare: + + + +The flag is off by default and toggling it emits a `BondsResetToggled` event (`sudo_set_bonds_reset_enabled` in `pallets/admin-utils/src/lib.rs`). + +## Reading and setting + +``` +btcli sudo get --netuid N --name bonds_reset_enabled +``` + +Boolean — pass true or false: + +``` +btcli sudo set --netuid N --name bonds_reset_enabled --value true +``` + +## Related + +[`bonds_moving_avg`](/docs/hyperparameters/bonds-moving-avg), [`bonds_penalty`](/docs/hyperparameters/bonds-penalty), [`yuma3_enabled`](/docs/hyperparameters/yuma3-enabled), [`liquid_alpha_enabled`](/docs/hyperparameters/liquid-alpha-enabled) diff --git a/docs/hyperparameters/burn-half-life.mdx b/docs/hyperparameters/burn-half-life.mdx new file mode 100644 index 0000000000..bd0e1c3d73 --- /dev/null +++ b/docs/hyperparameters/burn-half-life.mdx @@ -0,0 +1,51 @@ +--- +title: burn_half_life +description: Blocks for the burn cost to decay halfway back toward min_burn. +--- + +Sets how quickly an elevated registration price cools off. After registrations +bump the burn cost up, it decays exponentially — this parameter is the number +of blocks for the price to halve. Owners tune it against +[`burn_increase_mult`](/docs/hyperparameters/burn-increase-mult) to pick the +registration rate their subnet sustains; miners watching an expensive subnet +can read it as "how long until the price is reasonable again." + +## How it works + +Every block, `update_registration_prices_for_networks` (in +`pallets/subtensor/src/subnets/registration.rs`, called from `block_step`) +multiplies each subnet's burn by a per-block factor `f` chosen so that +`f ^ burn_half_life = 1/2`, then clamps into +\[[`min_burn`](/docs/hyperparameters/min-burn), +[`max_burn`](/docs/hyperparameters/max-burn)\]. The factor is computed in Q32 +fixed-point by binary search (`decay_factor_q32` in +`pallets/subtensor/src/utils/misc.rs`), giving continuous exponential decay: +after `n` blocks without registrations the price is `burn × 0.5^(n / +half_life)`, until it hits the `min_burn` floor. + +The value is stored in `BurnHalfLife` as a u16 block count; the default is +**360 blocks** (~72 minutes at 12s blocks). A stored value of 0 disables decay +entirely, but the owner-set extrinsic (`sudo_set_burn_half_life` in +`pallets/admin-utils/src/lib.rs`) requires **1 to 36,100 blocks** (~5 days) and +rejects the root subnet. Together with the multiplier, the break-even +registration cadence is one registration per `half_life × log2(mult)` blocks — +faster than that and the price climbs, slower and it falls. + + + +## Reading and setting + +```bash +btcli sudo get --netuid 12 --name burn_half_life +btcli sudo set --netuid 12 --name burn_half_life --value 720 +``` + +Settable by the subnet owner or root; the value is a plain block count +(12-second blocks). + +## Related + +- [`burn_increase_mult`](/docs/hyperparameters/burn-increase-mult) — the bump + this decay works against +- [`min_burn`](/docs/hyperparameters/min-burn) — where the decay comes to rest +- [`max_burn`](/docs/hyperparameters/max-burn) — the ceiling it decays down from diff --git a/docs/hyperparameters/burn-increase-mult.mdx b/docs/hyperparameters/burn-increase-mult.mdx new file mode 100644 index 0000000000..8c165066e2 --- /dev/null +++ b/docs/hyperparameters/burn-increase-mult.mdx @@ -0,0 +1,56 @@ +--- +title: burn_increase_mult +description: Multiplier applied to the burn cost after each successful registration. +--- + +Controls how sharply the registration price reacts to demand: every successful +registration multiplies the current burn cost by this factor. Owners tune it to +decide how strongly a registration burst should price out the next entrant; +miners see it as the reason the cost quoted a moment ago can jump before their +own transaction lands. + +## How it works + +After each successful registration, `bump_registration_price_after_registration` +(in `pallets/subtensor/src/subnets/registration.rs`) sets + +``` +new_burn = clamp(burn × burn_increase_mult, min_burn, max_burn) +``` + +The bump is immediate — the very next registration, even in the same block, +pays the raised price. Values below 1 are treated as 1 at read time +(`max(mult, 1)`), so the multiplier can never shrink the price. Between +registrations, the per-block decay governed by +[`burn_half_life`](/docs/hyperparameters/burn-half-life) pulls the price back +toward [`min_burn`](/docs/hyperparameters/min-burn). The steady-state +registration rate the pair supports is where bumps and decay cancel: +`half_life × log2(mult)` blocks per registration. + +The value is stored in `BurnIncreaseMult` as U64F64 fixed-point (raw bits +divided by 2^64 give the real multiplier); the default is **1.26** (~3 +registrations to double the price). The owner-set extrinsic +(`sudo_set_burn_increase_mult` in `pallets/admin-utils/src/lib.rs`) requires +the value to be **between 1 and 3** inclusive and rejects the root subnet, +which does not use the bump path. + + + +## Reading and setting + +```bash +btcli sudo get --netuid 12 --name burn_increase_mult +btcli sudo set --netuid 12 --name burn_increase_mult --value 1.5 +``` + +Settable by the subnet owner or root. A value with a decimal point is the +multiplier itself (`1.5`); a plain integer is the raw U64F64 bits. + +## Related + +- [`burn_half_life`](/docs/hyperparameters/burn-half-life) — the decay that + counteracts these bumps +- [`min_burn`](/docs/hyperparameters/min-burn) / + [`max_burn`](/docs/hyperparameters/max-burn) — the clamp bounds +- [`max_regs_per_block`](/docs/hyperparameters/max-regs-per-block) — the hard + per-block cap behind the price mechanism diff --git a/docs/hyperparameters/commit-reveal-period.mdx b/docs/hyperparameters/commit-reveal-period.mdx new file mode 100644 index 0000000000..f1549ff3a7 --- /dev/null +++ b/docs/hyperparameters/commit-reveal-period.mdx @@ -0,0 +1,51 @@ +--- +title: commit_reveal_period +description: Epochs between committing a weights hash and revealing the weights, when commit-reveal is enabled. +--- + +`commit_reveal_period` (on chain: `RevealPeriodEpochs`) is how long committed +weights stay hidden before they are revealed, measured in **epochs (tempos), +not blocks**. With the default of 1 and a 360-block tempo, weights committed +in one epoch become public roughly one tempo (~72 minutes) later. Subnet +owners tune it to balance copy-resistance (longer hiding) against how stale +weights are when they finally land. + +## How it works + +Each commit is tagged with the subnet's current epoch index. The reveal is +valid in **exactly one epoch**: `is_reveal_block_range` +(`pallets/subtensor/src/subnets/weights.rs`) requires +`current_epoch == commit_epoch + commit_reveal_period`. Revealing earlier +fails with `RevealTooEarly`; once the current epoch moves past that target, +`is_commit_expired` drops the commit and a late reveal fails with +`ExpiredWeightCommit`. A hotkey may hold at most 10 unrevealed commits. + +The value is bounded on-chain: `set_reveal_period` accepts 1 to 100 epochs. + +In practice validators rarely reveal by hand. `btcli tx set-weights` (the +`set_weights` intent) uses timelocked commits: it reads `RevealPeriodEpochs` +and the subnet's epoch schedule, computes the drand round whose randomness +becomes available in the reveal epoch, and encrypts the payload to that round +— the chain then decrypts and applies the weights itself, with no reveal +transaction. + + + +## Reading and setting + +```bash +btcli sudo get --netuid N --name commit_reveal_period +``` + +Owner-settable; the value is an epoch (tempo) count: + +```bash +btcli sudo set --netuid N --name commit_reveal_period --value 2 +``` + +## Related + +- [`commit_reveal_weights_enabled`](/docs/hyperparameters/commit-reveal-weights-enabled) — the toggle that activates this delay +- [`tempo`](/docs/hyperparameters/tempo) — the block length of one epoch +- [`weights_rate_limit`](/docs/hyperparameters/weights-rate-limit) — cooldown applied at commit time +- [`weights_version`](/docs/hyperparameters/weights-version) — version gate checked when revealed weights land diff --git a/docs/hyperparameters/commit-reveal-weights-enabled.mdx b/docs/hyperparameters/commit-reveal-weights-enabled.mdx new file mode 100644 index 0000000000..4615b3e5a9 --- /dev/null +++ b/docs/hyperparameters/commit-reveal-weights-enabled.mdx @@ -0,0 +1,53 @@ +--- +title: commit_reveal_weights_enabled +description: Whether validators must submit weights through commit-reveal, hiding them from copiers until the reveal. +--- + +`commit_reveal_weights_enabled` decides how weights reach the chain. When on +(the default for new subnets), validators must commit a hidden form of their +weights first and the actual values only become public an epoch or more later +— so weight-copying validators cannot free-ride on others' scoring work by +mirroring their submissions in the same epoch. + +## How it works + +The flag flips the accepted submission path. With it **on**, the plain +`set_weights` extrinsic is rejected outright with `CommitRevealEnabled` +(`pallets/subtensor/src/macros/dispatches.rs`); validators must instead +commit — either a salted hash (revealed later by a matching +`reveal_weights` call) or a timelock-encrypted payload that the chain +auto-decrypts at a drand round, no reveal transaction needed. With it +**off**, commits are rejected with `CommitRevealDisabled` and plain +`set_weights` is the only path. + +The reveal delay is governed by +[`commit_reveal_period`](/docs/hyperparameters/commit-reveal-period), and the +[`weights_rate_limit`](/docs/hyperparameters/weights-rate-limit) cooldown +applies at commit time rather than set time. Revealed weights pass through +the full `set_weights` validation (length, cap, version key) when they land. + +Validators do not need to branch on this flag themselves: `btcli tx +set-weights` (the `set_weights` intent) reads it and automatically dispatches +to plaintext `set_weights` or a timelocked commit, so the same command works +in either regime. + + + +## Reading and setting + +```bash +btcli sudo get --netuid N --name commit_reveal_weights_enabled +``` + +Owner-settable; the value is a flag (`true`/`false` or `1`/`0`): + +```bash +btcli sudo set --netuid N --name commit_reveal_weights_enabled --value true +``` + +## Related + +- [`commit_reveal_period`](/docs/hyperparameters/commit-reveal-period) — how long weights stay hidden +- [`weights_rate_limit`](/docs/hyperparameters/weights-rate-limit) — cooldown applied at commit time +- [`weights_version`](/docs/hyperparameters/weights-version) — version gate on revealed weights +- [`min_allowed_weights`](/docs/hyperparameters/min-allowed-weights) — floor on submission size in either mode diff --git a/docs/hyperparameters/difficulty.mdx b/docs/hyperparameters/difficulty.mdx new file mode 100644 index 0000000000..e347fbfa24 --- /dev/null +++ b/docs/hyperparameters/difficulty.mdx @@ -0,0 +1,55 @@ +--- +title: difficulty +description: Current PoW registration difficulty; u64::MAX means PoW registration is effectively disabled. +--- + +`difficulty` is the proof-of-work target a miner's registration nonce must +beat to claim a UID via PoW registration. It is a raw u64: finding a valid +nonce takes about `difficulty` hash attempts on average, so the value is a +direct price in compute. Miners planning PoW registration care about it; +`u64::MAX` is the sentinel for "no nonce can ever pass" — PoW registration +effectively disabled, burned registration only. + +## How it works + +A PoW registration submits a seal built from a recent block hash, the +hotkey, and a nonce (`create_seal_hash` in +`pallets/subtensor/src/subnets/registration.rs`). The check is +`hash_meets_difficulty`: the seal hash times `difficulty` must not overflow +a 256-bit integer, which passes with probability roughly 1 in `difficulty`. +At `u64::MAX` essentially nothing passes. + +On the current runtime this check no longer gates subnet registration: the +`register` extrinsic (`pallets/subtensor/src/macros/dispatches.rs`, call +index 6) accepts the work arguments but ignores them and routes to the +burned-registration path (`do_register`). No controller adjusts `difficulty` +anymore either — the per-block price update +(`update_registration_prices_for_networks`) only decays the burn cost. The +value stays on-chain, is reported in the metagraph, and root can still +change it. The seal check itself survives in the testnet faucet +(`do_faucet`, fixed difficulty 1,000,000). + +Historically, `difficulty` walked between its floor and ceiling as the +chain steered registrations toward a target rate: + + + +## Reading and setting + +``` +btcli sudo get --netuid N --name difficulty +``` + +Root/governance only: `sudo_set_difficulty` in the AdminUtils pallet +requires the root origin and takes a raw u64. It is not settable by the +subnet owner via `btcli sudo set`. + +## Related + +[`min_difficulty`](/docs/hyperparameters/min-difficulty) · +[`max_difficulty`](/docs/hyperparameters/max-difficulty) · +[`network_pow_registration_allowed`](/docs/hyperparameters/network-pow-registration-allowed) · +[`registration_allowed`](/docs/hyperparameters/registration-allowed) · +[`max_regs_per_block`](/docs/hyperparameters/max-regs-per-block) · +[`min_burn`](/docs/hyperparameters/min-burn) · +[`max_burn`](/docs/hyperparameters/max-burn) diff --git a/docs/hyperparameters/immunity-period.mdx b/docs/hyperparameters/immunity-period.mdx new file mode 100644 index 0000000000..76142c8978 --- /dev/null +++ b/docs/hyperparameters/immunity-period.mdx @@ -0,0 +1,32 @@ +--- +title: immunity_period +description: Blocks a newly registered neuron is protected from being pruned as the lowest-scoring UID on a full subnet. +--- + +`immunity_period` is the number of blocks a freshly registered neuron cannot be pruned to make room for a new registration. Subnet owners tune it to give new miners time to earn emission before they compete for their slot; miners care because it is their grace window to prove themselves. + +## How it works + +When a subnet is at [`max_allowed_uids`](/docs/hyperparameters/max-allowed-uids), each new registration must recycle an existing slot. `get_neuron_to_prune` (`pallets/subtensor/src/subnets/registration.rs`) scans every UID and treats a neuron as immune while `current_block − registration_block` is less than `immunity_period`. It picks the lowest-emission non-immune neuron (ties broken by older registration, then lower UID). Owner-immune UIDs (see [`owner_immune_neuron_limit`](/docs/hyperparameters/owner-immune-neuron-limit)) are skipped entirely. + +Immunity is not absolute: if pruning a non-immune neuron would leave `MinNonImmuneUids` (default 10) or fewer non-immune UIDs, the chain instead prunes the lowest-emission immune neuron. If no candidate exists at all, registration fails with `NoNeuronIdAvailable`. + +The default is 4096 blocks, about 13.7 hours at 12-second blocks. Longer periods shelter newcomers but shrink the pool of prunable slots; with heavy registration churn, most of the subnet can end up immune. + + + +## Reading and setting + +``` +btcli sudo get --netuid N --name immunity_period +``` + +The value is a plain block count: + +``` +btcli sudo set --netuid N --name immunity_period --value 4096 +``` + +## Related + +[`max_allowed_uids`](/docs/hyperparameters/max-allowed-uids), [`owner_immune_neuron_limit`](/docs/hyperparameters/owner-immune-neuron-limit), [`serving_rate_limit`](/docs/hyperparameters/serving-rate-limit), [`min_burn`](/docs/hyperparameters/min-burn), [`max_regs_per_block`](/docs/hyperparameters/max-regs-per-block) diff --git a/docs/hyperparameters/index.mdx b/docs/hyperparameters/index.mdx new file mode 100644 index 0000000000..cc104281d0 --- /dev/null +++ b/docs/hyperparameters/index.mdx @@ -0,0 +1,55 @@ +--- +title: "Hyperparameters" +description: "What each subnet hyperparameter controls, its unit, and how to change it." +--- + +{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} + +Read them with `btcli sudo get --netuid N` (the [`subnet_hyperparameters`](/docs/query/subnet-hyperparameters) read); the subnet owner changes the owner-settable ones with `btcli sudo set` (the [`set_hyperparameter`](/docs/tx/set-hyperparameter) intent). Raw on-chain integers are primary; where a parameter is a fixed-point fraction or a rao amount, the value also accepts the human form with a decimal point. Each parameter has its own explainer page. + +| Hyperparameter | Unit | Owner-settable | What it controls | +| --- | --- | --- | --- | +| [`rho`](/docs/hyperparameters/rho) | integer | yes | trust curve steepness | +| [`kappa`](/docs/hyperparameters/kappa) | fraction (u16, 65535 = 1.0) | root only | consensus majority-stake threshold | +| [`immunity_period`](/docs/hyperparameters/immunity-period) | blocks (12s) | yes | prune-immunity window for new neurons | +| [`min_allowed_weights`](/docs/hyperparameters/min-allowed-weights) | integer | yes | minimum weights per submission | +| [`max_weights_limit`](/docs/hyperparameters/max-weights-limit) | fraction (u16, 65535 = 1.0) | root only | cap on a single miner's weight | +| [`tempo`](/docs/hyperparameters/tempo) | blocks (12s) | yes | blocks per consensus epoch | +| [`min_difficulty`](/docs/hyperparameters/min-difficulty) | PoW difficulty (u64) | root only | PoW registration difficulty floor | +| [`max_difficulty`](/docs/hyperparameters/max-difficulty) | PoW difficulty (u64) | yes | PoW registration difficulty ceiling | +| [`difficulty`](/docs/hyperparameters/difficulty) | PoW difficulty (u64) | root only | current PoW registration difficulty | +| [`weights_version`](/docs/hyperparameters/weights-version) | integer | yes | minimum version key for set_weights | +| [`weights_rate_limit`](/docs/hyperparameters/weights-rate-limit) | blocks (12s) | root only | wait between weight submissions | +| [`adjustment_interval`](/docs/hyperparameters/adjustment-interval) | blocks (12s) | root only | difficulty/burn adjustment cadence | +| [`activity_cutoff`](/docs/hyperparameters/activity-cutoff) | blocks (12s) | root only | no-weights window before inactive | +| [`activity_cutoff_factor`](/docs/hyperparameters/activity-cutoff-factor) | integer | yes | activity cutoff, per-mille of tempo | +| [`registration_allowed`](/docs/hyperparameters/registration-allowed) | flag | root only | new neuron registrations allowed | +| [`network_pow_registration_allowed`](/docs/hyperparameters/network-pow-registration-allowed) | flag | yes | PoW registration toggle | +| [`target_regs_per_interval`](/docs/hyperparameters/target-regs-per-interval) | integer | root only | registration-rate controller target | +| [`min_burn`](/docs/hyperparameters/min-burn) | TAO amount in rao | yes | burned-registration cost floor | +| [`max_burn`](/docs/hyperparameters/max-burn) | TAO amount in rao | yes | burned-registration cost ceiling | +| [`bonds_moving_avg`](/docs/hyperparameters/bonds-moving-avg) | fraction (1,000,000 = 1.0) | yes | bonds EMA smoothing factor | +| [`max_regs_per_block`](/docs/hyperparameters/max-regs-per-block) | integer | root only | per-block registration cap | +| [`serving_rate_limit`](/docs/hyperparameters/serving-rate-limit) | blocks (12s) | yes | cooldown between axon serve calls | +| [`max_validators`](/docs/hyperparameters/max-validators) | integer | root only | top-stake validator permit cap | +| [`adjustment_alpha`](/docs/hyperparameters/adjustment-alpha) | fraction (u64, u64::MAX = 1.0) | yes | difficulty/burn adjust smoothing | +| [`commit_reveal_period`](/docs/hyperparameters/commit-reveal-period) | epochs (tempos) | yes | weight commit-to-reveal delay | +| [`commit_reveal_weights_enabled`](/docs/hyperparameters/commit-reveal-weights-enabled) | flag | yes | commit-reveal weights toggle | +| [`alpha_high`](/docs/hyperparameters/alpha-high) | fraction (u16, 65535 = 1.0) | yes | liquid-alpha smoothing upper bound | +| [`alpha_low`](/docs/hyperparameters/alpha-low) | fraction (u16, 65535 = 1.0) | yes | liquid-alpha smoothing lower bound | +| [`liquid_alpha_enabled`](/docs/hyperparameters/liquid-alpha-enabled) | flag | yes | per-weight bonds EMA (liquid alpha) | +| [`bonds_penalty`](/docs/hyperparameters/bonds-penalty) | fraction (u16, 65535 = 1.0) | yes | penalty on out-of-consensus bonds | +| [`alpha_sigmoid_steepness`](/docs/hyperparameters/alpha-sigmoid-steepness) | integer | yes | liquid-alpha sigmoid steepness | +| [`min_childkey_take`](/docs/hyperparameters/min-childkey-take) | fraction (u16, 65535 = 1.0) | yes | floor for childkey take | +| [`owner_immune_neuron_limit`](/docs/hyperparameters/owner-immune-neuron-limit) | integer | yes | owner-designated prune-immune UIDs | +| [`max_allowed_uids`](/docs/hyperparameters/max-allowed-uids) | integer | yes | neuron slot capacity before pruning | +| [`burn_increase_mult`](/docs/hyperparameters/burn-increase-mult) | multiplier (U64F64 bits / 2^64) | yes | burn cost bump per registration | +| [`burn_half_life`](/docs/hyperparameters/burn-half-life) | blocks (12s) | yes | burn cost decay half-life | +| [`yuma3_enabled`](/docs/hyperparameters/yuma3-enabled) | flag | yes | yuma3 consensus variant toggle | +| [`yuma_version`](/docs/hyperparameters/yuma-version) | integer | root only | epoch consensus variant (2 or 3) | +| [`subnet_is_active`](/docs/hyperparameters/subnet-is-active) | flag | root only | subnet started (staking + emissions) | +| [`user_liquidity_enabled`](/docs/hyperparameters/user-liquidity-enabled) | flag | root only | legacy user-LP flag (always false) | +| [`bonds_reset_enabled`](/docs/hyperparameters/bonds-reset-enabled) | flag | yes | bonds reset on metadata commit | +| [`transfers_enabled`](/docs/hyperparameters/transfers-enabled) | flag | yes | stake transfers between coldkeys | +| [`owner_cut_enabled`](/docs/hyperparameters/owner-cut-enabled) | flag | yes | owner emission cut toggle | +| [`owner_cut_auto_lock_enabled`](/docs/hyperparameters/owner-cut-auto-lock-enabled) | flag | yes | auto-lock the owner's emission cut | diff --git a/docs/hyperparameters/kappa.mdx b/docs/hyperparameters/kappa.mdx new file mode 100644 index 0000000000..a2217d282d --- /dev/null +++ b/docs/hyperparameters/kappa.mdx @@ -0,0 +1,49 @@ +--- +title: kappa +description: Majority-stake threshold at which a weight counts as consensus-supported in Yuma Consensus. +--- + +`kappa` is the majority-stake threshold of Yuma Consensus: the fraction of +active stake that must support a weight level for it to count as consensus. +It shapes how much a stake majority can discipline outlier validators, so +validators and subnet designers both care — but only root governance can +change it. + +## How it works + +Each epoch, the chain computes a per-miner consensus score as a stake-weighted +median (`weighted_median_col` in `pallets/subtensor/src/epoch/math.rs`, +called from `epoch/run_epoch.rs` with `kappa` as the majority ratio): the +highest weight level such that validators holding at least `kappa` of active +stake set a weight at or above it. Every weight above that consensus value is +clipped down to it (`inplace_col_clip`), and the clipped matrix drives ranks, +incentive, and validator trust. In the classic sigmoid formulation, `kappa` +is also the midpoint of the trust curve that +[`rho`](/docs/hyperparameters/rho) steepens. + +Stored as u16 where 65535 = 1.0; the default 32767 means 0.5 — a simple +stake majority. Raising it demands broader agreement before a weight +survives clipping; lowering it lets smaller stake coalitions set consensus. +The full pipeline is described in +[Yuma Consensus](/docs/concepts/emissions#yuma-consensus). + + + +## Reading and setting + +Inspect with: + +``` +btcli sudo get --netuid N --name kappa +``` + +Not settable by the subnet owner — root/governance only +(`sudo_set_kappa` in the AdminUtils pallet requires the root origin). When +set, the raw value is a u16: 32767 ≈ 0.5. + +## Related + +[`rho`](/docs/hyperparameters/rho) · +[`tempo`](/docs/hyperparameters/tempo) · +[`activity_cutoff`](/docs/hyperparameters/activity-cutoff) · +[`max_validators`](/docs/hyperparameters/max-validators) diff --git a/docs/hyperparameters/liquid-alpha-enabled.mdx b/docs/hyperparameters/liquid-alpha-enabled.mdx new file mode 100644 index 0000000000..a490bcd962 --- /dev/null +++ b/docs/hyperparameters/liquid-alpha-enabled.mdx @@ -0,0 +1,32 @@ +--- +title: liquid_alpha_enabled +description: Switches the bonds EMA from one flat rate to a per-weight rate that varies between alpha_low and alpha_high. +--- + +`liquid_alpha_enabled` turns on "liquid alpha": instead of every validator–miner bond smoothing at the same flat rate, each pair gets its own EMA rate based on how far the validator's weight deviates from consensus. Subnet owners enable it to reward decisive, differentiated weight-setting; validators care because it changes how fast their bonds — and dividends — respond to weight changes. + +## How it works + +In the epoch's `compute_bonds` (`pallets/subtensor/src/epoch/run_epoch.rs`), if this flag is on and consensus is non-empty, the chain builds a full matrix of per-pair alphas via `compute_liquid_alpha_values`: each pair's deviation (weight above consensus when increasing a bond, bond above weight when decreasing) is pushed through a sigmoid and mapped into the [`alpha_low`](/docs/hyperparameters/alpha-low)..[`alpha_high`](/docs/hyperparameters/alpha-high) range, with [`alpha_sigmoid_steepness`](/docs/hyperparameters/alpha-sigmoid-steepness) controlling the transition sharpness. Larger deviations get a higher alpha, so those bonds move faster. + +When the flag is off, every pair uses the flat rate `1 − bonds_moving_avg / 1,000,000` (`compute_disabled_liquid_alpha`). + +One important dependency: `compute_bonds` only runs on the Yuma3 path. If [`yuma3_enabled`](/docs/hyperparameters/yuma3-enabled) is off, the classic bond code ignores this toggle entirely and always uses [`bonds_moving_avg`](/docs/hyperparameters/bonds-moving-avg). Setting `alpha_low`/`alpha_high` also requires this flag to be on first (`LiquidAlphaDisabled` error otherwise). + + + +## Reading and setting + +``` +btcli sudo get --netuid N --name liquid_alpha_enabled +``` + +Boolean — pass true or false: + +``` +btcli sudo set --netuid N --name liquid_alpha_enabled --value true +``` + +## Related + +[`alpha_low`](/docs/hyperparameters/alpha-low), [`alpha_high`](/docs/hyperparameters/alpha-high), [`alpha_sigmoid_steepness`](/docs/hyperparameters/alpha-sigmoid-steepness), [`yuma3_enabled`](/docs/hyperparameters/yuma3-enabled), [`bonds_moving_avg`](/docs/hyperparameters/bonds-moving-avg) diff --git a/docs/hyperparameters/max-allowed-uids.mdx b/docs/hyperparameters/max-allowed-uids.mdx new file mode 100644 index 0000000000..8214de8c50 --- /dev/null +++ b/docs/hyperparameters/max-allowed-uids.mdx @@ -0,0 +1,30 @@ +--- +title: max_allowed_uids +description: Neuron slot capacity of the subnet — registrations beyond it prune the lowest-scoring UID. +--- + +`max_allowed_uids` is the number of neuron slots (UIDs) a subnet has. Subnet owners size it to match how many miners and validators they actually want; participants care because a full subnet means every new registration evicts someone. + +## How it works + +While the neuron count is below `max_allowed_uids`, a registration simply appends a new UID (`append_neuron`). Once full, `get_neuron_to_prune` selects the lowest-emission prunable neuron and `replace_neuron` hands its slot to the entrant (`pallets/subtensor/src/subnets/registration.rs`) — see [`immunity_period`](/docs/hyperparameters/immunity-period) for who is protected. + +Raising the value with `btcli sudo set` is straightforward, but lowering it below the current neuron count is rejected (`MaxAllowedUIdsLessThanCurrentUIds` in `pallets/admin-utils/src/lib.rs`); the setter also enforces the range between the subnet's minimum and the chain-wide cap of 256. To shrink an occupied subnet, use `btcli sudo trim`, which calls `trim_to_max_allowed_uids` (`pallets/subtensor/src/subnets/uids.rs`): it removes the lowest-emission neurons while preserving temporally and owner-immune UIDs, refuses to act if immune UIDs would exceed 80% of the new capacity, then compacts the surviving UIDs to the left. Trimming is heavily rate-limited (about 30 days between trims). + + + +## Reading and setting + +``` +btcli sudo get --netuid N --name max_allowed_uids +``` + +The value is a plain integer slot count: + +``` +btcli sudo set --netuid N --name max_allowed_uids --value 256 +``` + +## Related + +[`immunity_period`](/docs/hyperparameters/immunity-period), [`owner_immune_neuron_limit`](/docs/hyperparameters/owner-immune-neuron-limit), [`serving_rate_limit`](/docs/hyperparameters/serving-rate-limit), [`min_burn`](/docs/hyperparameters/min-burn), [`max_regs_per_block`](/docs/hyperparameters/max-regs-per-block) diff --git a/docs/hyperparameters/max-burn.mdx b/docs/hyperparameters/max-burn.mdx new file mode 100644 index 0000000000..b3d8aeb0b8 --- /dev/null +++ b/docs/hyperparameters/max-burn.mdx @@ -0,0 +1,53 @@ +--- +title: max_burn +description: Ceiling for the burned-registration cost, in rao. +--- + +Caps how expensive a burned registration can get, no matter how many +registrations pile in. Miners care because it bounds their worst-case entry +cost during a registration rush; owners tune it to decide how hard a rush +should be throttled by price. + +## How it works + +The burn price moves in two ways, and both are clamped into +`[min_burn, max_burn]` in `pallets/subtensor/src/subnets/registration.rs`: + +- Each successful registration multiplies the price by + [`burn_increase_mult`](/docs/hyperparameters/burn-increase-mult) + (`bump_registration_price_after_registration`). Once the product exceeds + `max_burn`, the price is pinned at `max_burn` — further registrations in the + same burst cost exactly `max_burn` each. +- Each block, the price decays exponentially with half-life + [`burn_half_life`](/docs/hyperparameters/burn-half-life) + (`update_registration_prices_for_networks`), pulling it back down from the + ceiling toward [`min_burn`](/docs/hyperparameters/min-burn). + +The value lives in `MaxBurn` storage as a rao amount (1 TAO = 1e9 rao); the +chain default is 100 TAO. The owner-set extrinsic (`sudo_set_max_burn` in +`pallets/admin-utils/src/lib.rs`) requires the new value to be **above 0.1 TAO** +(`MaxBurnLowerBound`) and **strictly greater than the subnet's current +`min_burn`**. A low ceiling makes registration cheap but lets bursts through; a +high ceiling makes sustained bursts increasingly expensive. + + + +## Reading and setting + +```bash +btcli sudo get --netuid 12 --name max_burn +btcli sudo set --netuid 12 --name max_burn --value 50.0 +``` + +Settable by the subnet owner or root. A value with a decimal point is a TAO +amount (`50.0` = τ50); a plain integer is raw rao. + +## Related + +- [`min_burn`](/docs/hyperparameters/min-burn) — the matching floor +- [`burn_increase_mult`](/docs/hyperparameters/burn-increase-mult) — how fast a + rush drives the price toward this cap +- [`burn_half_life`](/docs/hyperparameters/burn-half-life) — how fast it comes + back down +- [`max_regs_per_block`](/docs/hyperparameters/max-regs-per-block) — the + non-price rate limit diff --git a/docs/hyperparameters/max-difficulty.mdx b/docs/hyperparameters/max-difficulty.mdx new file mode 100644 index 0000000000..02da701cf5 --- /dev/null +++ b/docs/hyperparameters/max-difficulty.mdx @@ -0,0 +1,56 @@ +--- +title: max_difficulty +description: Upper bound for the PoW registration difficulty controller; u64::MAX leaves difficulty unbounded above. +--- + +`max_difficulty` is the ceiling over +[`difficulty`](/docs/hyperparameters/difficulty), the proof-of-work target +for PoW neuron registration. However hot registration demand runs, the +difficulty controller never pushes the PoW price above this value. At +`u64::MAX` the ceiling is effectively removed — difficulty is unbounded +above. Subnet owners tune it to cap how expensive a PoW slot can become +during registration rushes. + +## How it works + +Storage is `MaxDifficulty` in `pallets/subtensor/src/lib.rs`, accessed via +`get_max_difficulty` / `set_max_difficulty` +(`pallets/subtensor/src/utils/misc.rs`). In the classic controller, +difficulty was rescaled each adjustment interval by registration pressure +against the target rate, then clamped to the +\[`min_difficulty`, `max_difficulty`\] band — sustained over-target demand +walked difficulty up until it hit this ceiling. The mainnet default is +`u64::MAX / 4` (`SubtensorInitialMaxDifficulty` in `runtime/src/lib.rs`). + +On the current runtime PoW registration is deprecated — the `register` +extrinsic routes to burned registration and nothing adjusts `difficulty` +between intervals — so this ceiling is dormant. The playground opens on a +registration-rush scenario where difficulty ratchets up until it pins +against the bold ceiling line: + + + +## Reading and setting + +``` +btcli sudo get --netuid N --name max_difficulty +``` + +Owner-settable (the AdminUtils extrinsic `sudo_set_max_difficulty` accepts +the subnet owner or root, inside the admin window and subject to the owner +rate limit): + +``` +btcli sudo set --netuid N --name max_difficulty --value 4611686018427387903 +``` + +The value is a raw u64 difficulty, no human form. + +## Related + +[`difficulty`](/docs/hyperparameters/difficulty) · +[`min_difficulty`](/docs/hyperparameters/min-difficulty) · +[`network_pow_registration_allowed`](/docs/hyperparameters/network-pow-registration-allowed) · +[`target_regs_per_interval`](/docs/hyperparameters/target-regs-per-interval) · +[`adjustment_interval`](/docs/hyperparameters/adjustment-interval) · +[`max_burn`](/docs/hyperparameters/max-burn) diff --git a/docs/hyperparameters/max-regs-per-block.mdx b/docs/hyperparameters/max-regs-per-block.mdx new file mode 100644 index 0000000000..c209778e02 --- /dev/null +++ b/docs/hyperparameters/max-regs-per-block.mdx @@ -0,0 +1,60 @@ +--- +title: max_regs_per_block +description: Maximum neuron registrations accepted on a subnet in a single block. +--- + +`max_regs_per_block` caps how many neurons can register on a subnet within +one 12-second block. It is a burst limiter: even when the burn cost is low +and demand spikes, at most this many UIDs can turn over per block, which +protects existing neurons from being pruned en masse in a single block. +Anyone scripting registrations should expect rejections beyond the cap; +root/governance sets it, with a mainnet default of 1 +(`SubtensorInitialMaxRegistrationsPerBlock` in `runtime/src/lib.rs`). + +## How it works + +Each successful registration increments the `RegistrationsThisBlock` +counter (`do_register`, step 11, in +`pallets/subtensor/src/subnets/registration.rs`), and the counter is reset +to zero every block by `update_registration_prices_for_networks` (same +file), which runs in `on_initialize`. + +The cap is checked against that counter in two places: + +- Root registration: `do_root_register` + (`pallets/subtensor/src/coinbase/root.rs`) fails with + `TooManyRegistrationsThisBlock` when the block's registrations reach + `get_max_registrations_per_block`, and additionally caps the interval at + 3 × [`target_regs_per_interval`](/docs/hyperparameters/target-regs-per-interval). +- The registration validity helper `checked_allowed_register` + (`pallets/subtensor/src/lib.rs`) reports a subnet as closed for the + block once the counter reaches the cap, alongside the + [`registration_allowed`](/docs/hyperparameters/registration-allowed) + gate and the same 3 × target interval cap. + +Because the burn cost also bumps multiplicatively after every registration +in a block ([`burn_increase_mult`](/docs/hyperparameters/burn-increase-mult)), +the price and the cap work together against registration floods. + +Drag the cap below to see how it slices bursts block by block: + + + +## Reading and setting + +``` +btcli sudo get --netuid N --name max_regs_per_block +``` + +Root/governance only: `sudo_set_max_registrations_per_block` in the +AdminUtils pallet requires the root origin and takes a plain integer (u16). +It is not settable by the subnet owner via `btcli sudo set`. + +## Related + +[`registration_allowed`](/docs/hyperparameters/registration-allowed) · +[`target_regs_per_interval`](/docs/hyperparameters/target-regs-per-interval) · +[`immunity_period`](/docs/hyperparameters/immunity-period) · +[`max_allowed_uids`](/docs/hyperparameters/max-allowed-uids) · +[`burn_increase_mult`](/docs/hyperparameters/burn-increase-mult) · +[`min_burn`](/docs/hyperparameters/min-burn) diff --git a/docs/hyperparameters/max-validators.mdx b/docs/hyperparameters/max-validators.mdx new file mode 100644 index 0000000000..09396c2f31 --- /dev/null +++ b/docs/hyperparameters/max-validators.mdx @@ -0,0 +1,53 @@ +--- +title: max_validators +description: Maximum validator permits — only the top-stake neurons up to this count may validate. +--- + +`max_validators` caps how many neurons on a subnet hold a validator permit at +once. Anyone deciding whether their stake is enough to validate on a subnet +needs this number: without a permit, a neuron's weights are ignored and it +earns no dividends. + +## How it works + +Every epoch recomputes permits from scratch +(`pallets/subtensor/src/epoch/run_epoch.rs`): `is_topk_nonzero(&stake, k)` +with `k = MaxAllowedValidators` grants permits to the top-`k` neurons by +stake weight (alpha stake plus TAO stake scaled by `tao_weight`), skipping +zero-stake neurons; the subnet owner's UID is always permitted. The mainnet +default is 128, and the value must not exceed `max_allowed_uids`. + +Losing a permit is immediate and total: non-permitted validators' stake is +masked from the active-stake vector, their weight rows are discarded before +consensus, and their accumulated bonds are cleared — so a validator that +slips out of the top-`k` re-enters later with no bond history. Permits +interact with [`activity_cutoff`](/docs/hyperparameters/activity-cutoff): +holding a permit is necessary but not sufficient, since an inactive permitted +validator is also excluded. See +[Yuma Consensus](/docs/concepts/emissions#yuma-consensus) for where permits +sit in the pipeline. + +The chart below sorts neurons by stake and draws the permit line at the +top-`max_validators` — slide the cap to move it. + + + +## Reading and setting + +Inspect with: + +``` +btcli sudo get --netuid N --name max_validators +``` + +Not settable by the subnet owner — root/governance only +(`sudo_set_max_allowed_validators` in AdminUtils requires the root origin +and enforces the `max_allowed_uids` ceiling). The value is a plain integer. + +## Related + +[`rho`](/docs/hyperparameters/rho) · +[`kappa`](/docs/hyperparameters/kappa) · +[`tempo`](/docs/hyperparameters/tempo) · +[`activity_cutoff`](/docs/hyperparameters/activity-cutoff) · +[`max_allowed_uids`](/docs/hyperparameters/max-allowed-uids) diff --git a/docs/hyperparameters/max-weights-limit.mdx b/docs/hyperparameters/max-weights-limit.mdx new file mode 100644 index 0000000000..247b6c4d3b --- /dev/null +++ b/docs/hyperparameters/max-weights-limit.mdx @@ -0,0 +1,46 @@ +--- +title: max_weights_limit +description: Cap on the share of a validator's total weight that any single miner may receive. +--- + +`max_weights_limit` caps how much of a validator's normalized weight can land +on one miner, forcing weight to spread across the pool. It is stored as a u16 +fraction: 65535 means 1.0 — no cap — and, say, 6553 means no miner may hold +more than ~10% of a submission's total weight. + +## How it works + +The check is on the **normalized** distribution, not the raw values. On +submission the chain first max-upscales the u16 values so the largest equals +65535 (`vec_u16_max_upscale_to_u16`), then `max_weight_limited` +(`pallets/subtensor/src/subnets/weights.rs`) sum-normalizes them and rejects +the whole submission with `MaxWeightExceeded` if the largest share exceeds +`max_weights_limit / 65535` (`check_vec_max_limited` in `epoch/math.rs`). A +single self-weight is exempt. + +In the current runtime the enforced limit is pinned: `get_max_weight_limit` +(`pallets/subtensor/src/utils/misc.rs`) is a constant returning `u16::MAX`, so +the on-chain check always passes and there is no extrinsic to change it. The +`MaxWeightsLimit` storage map still exists and is what queries return; the SDK +reads it and `btcli tx set-weights` (the `set_weights` intent) proactively +clips and renormalizes your weights to fit the stored value — redistributing +the excess mass rather than discarding it — and warns when clipping occurs. + + + +## Reading and setting + +```bash +btcli sudo get --netuid N --name max_weights_limit +``` + +Root/governance-only — not settable by the subnet owner. Where a value is +taken, it is a fraction: either the human 0..1 form with a decimal point +(e.g. `0.1`) or the raw integer 0..65535. + +## Related + +- [`min_allowed_weights`](/docs/hyperparameters/min-allowed-weights) — the floor on submission size +- [`weights_rate_limit`](/docs/hyperparameters/weights-rate-limit) — how often submissions are accepted +- [`commit_reveal_weights_enabled`](/docs/hyperparameters/commit-reveal-weights-enabled) — whether weights go through commit-reveal +- [`kappa`](/docs/hyperparameters/kappa) — consensus clipping applied later, at epoch time diff --git a/docs/hyperparameters/meta.json b/docs/hyperparameters/meta.json new file mode 100644 index 0000000000..6434cf2e4b --- /dev/null +++ b/docs/hyperparameters/meta.json @@ -0,0 +1,50 @@ +{ + "title": "Hyperparameters", + "pages": [ + "index", + "rho", + "kappa", + "immunity-period", + "min-allowed-weights", + "max-weights-limit", + "tempo", + "min-difficulty", + "max-difficulty", + "difficulty", + "weights-version", + "weights-rate-limit", + "adjustment-interval", + "activity-cutoff", + "activity-cutoff-factor", + "registration-allowed", + "network-pow-registration-allowed", + "target-regs-per-interval", + "min-burn", + "max-burn", + "bonds-moving-avg", + "max-regs-per-block", + "serving-rate-limit", + "max-validators", + "adjustment-alpha", + "commit-reveal-period", + "commit-reveal-weights-enabled", + "alpha-high", + "alpha-low", + "liquid-alpha-enabled", + "bonds-penalty", + "alpha-sigmoid-steepness", + "min-childkey-take", + "owner-immune-neuron-limit", + "max-allowed-uids", + "burn-increase-mult", + "burn-half-life", + "yuma3-enabled", + "yuma-version", + "subnet-is-active", + "user-liquidity-enabled", + "bonds-reset-enabled", + "transfers-enabled", + "owner-cut-enabled", + "owner-cut-auto-lock-enabled" + ] +} diff --git a/docs/hyperparameters/min-allowed-weights.mdx b/docs/hyperparameters/min-allowed-weights.mdx new file mode 100644 index 0000000000..d4b1764738 --- /dev/null +++ b/docs/hyperparameters/min-allowed-weights.mdx @@ -0,0 +1,48 @@ +--- +title: min_allowed_weights +description: Minimum number of distinct weights a validator must include in one weight submission. +--- + +`min_allowed_weights` is the floor on how many miners a validator must score in +a single weight submission. Subnet owners raise it to force validators to +evaluate a broad slice of the miner pool instead of concentrating weight on a +favored few; validators care because a submission with too few entries is +rejected outright. + +## How it works + +`set_weights` validates the submission length in `check_length` +(`pallets/subtensor/src/subnets/weights.rs`). The effective minimum is the +smaller of `min_allowed_weights` and the subnet's current neuron count, so a +freshly launched subnet with 5 neurons and a minimum of 50 still accepts +5-entry submissions. Anything below the effective minimum fails with +`WeightVecLengthIsLow`. A single **self-weight** — one entry naming the +validator's own UID — is exempt, as it is from most weight checks. + +What counts is the number of weights that actually reach the chain. The SDK +quantizes weights to u16 and drops zeros before submitting, then enforces the +minimum on what remains — so `btcli tx set-weights` (the `set_weights` intent) +fails fast client-side with the same rule instead of burning an on-chain +submission. Assigning zero to a miner does not help you meet the minimum. + + + +## Reading and setting + +```bash +btcli sudo get --netuid N --name min_allowed_weights +``` + +Owner-settable; the value is a plain integer count: + +```bash +btcli sudo set --netuid N --name min_allowed_weights --value 8 +``` + +## Related + +- [`max_weights_limit`](/docs/hyperparameters/max-weights-limit) — cap on any single weight in the same submission +- [`weights_rate_limit`](/docs/hyperparameters/weights-rate-limit) — how often submissions are accepted +- [`weights_version`](/docs/hyperparameters/weights-version) — version gate on submissions +- [`commit_reveal_weights_enabled`](/docs/hyperparameters/commit-reveal-weights-enabled) — whether weights go through commit-reveal +- [`max_allowed_uids`](/docs/hyperparameters/max-allowed-uids) — the pool of UIDs available to weight diff --git a/docs/hyperparameters/min-burn.mdx b/docs/hyperparameters/min-burn.mdx new file mode 100644 index 0000000000..a9d9e93d9f --- /dev/null +++ b/docs/hyperparameters/min-burn.mdx @@ -0,0 +1,48 @@ +--- +title: min_burn +description: Floor for the burned-registration cost, in rao. +--- + +Sets the lowest price a subnet can charge for a burned registration. Miners +watch it because it is the cost of joining a quiet subnet; owners tune it to +keep registration from ever becoming free while still letting the price decay +to something affordable. + +## How it works + +Every block, `update_registration_prices_for_networks` (in +`pallets/subtensor/src/subnets/registration.rs`, run from `block_step`) decays +the current burn price exponentially toward zero — and then clamps it into +`[min_burn, max_burn]`. The same clamp is applied after each registration bumps +the price by [`burn_increase_mult`](/docs/hyperparameters/burn-increase-mult). +So with no registrations arriving, the price settles at exactly `min_burn` and +stays there; `min_burn` is the resting price of an idle subnet. + +The value is stored in `MinBurn` as a rao amount (1 TAO = 1e9 rao). The chain +default is 500,000 rao (τ0.0005). The owner-set extrinsic +(`sudo_set_min_burn` in `pallets/admin-utils/src/lib.rs`) enforces two bounds: +the new value must be **below 1 TAO** (`MinBurnUpperBound`) and **strictly less +than the subnet's current `max_burn`**. Registration burns are recycled — they +are subtracted from total issuance and can be re-emitted, which pushes halvings +out (see [emissions](/docs/concepts/emissions)). + + + +## Reading and setting + +```bash +btcli sudo get --netuid 12 --name min_burn +btcli sudo set --netuid 12 --name min_burn --value 0.001 +``` + +Settable by the subnet owner or root. A value with a decimal point is a TAO +amount (`0.001` = τ0.001); a plain integer is raw rao (`1000000` = τ0.001). + +## Related + +- [`max_burn`](/docs/hyperparameters/max-burn) — the matching ceiling +- [`burn_half_life`](/docs/hyperparameters/burn-half-life) — how fast the price + decays back to this floor +- [`burn_increase_mult`](/docs/hyperparameters/burn-increase-mult) — the bump + per registration +- [`registration_allowed`](/docs/hyperparameters/registration-allowed) diff --git a/docs/hyperparameters/min-childkey-take.mdx b/docs/hyperparameters/min-childkey-take.mdx new file mode 100644 index 0000000000..45c3ff2834 --- /dev/null +++ b/docs/hyperparameters/min-childkey-take.mdx @@ -0,0 +1,51 @@ +--- +title: min_childkey_take +description: Per-subnet floor on the take a childkey may charge its parent hotkeys. +--- + +Sets the minimum take a childkey can charge on this subnet. It matters to +validators running parent–child hotkey setups +([`set-children`](/docs/tx/set-children)): the take is the share of +child-earned dividends the childkey keeps before passing the rest back to its +parents. A subnet owner can raise this floor to stop childkeys from +undercutting each other to zero. + +## How it works + +The value is stored per subnet as a `PerU16` fraction (65535 = 100%). The +chain combines it with the global, root-set minimum: the **effective floor is +the greater of the two**, so a subnet can only make the global minimum +stricter, never looser. Both defaults are currently 0. + +The floor is enforced in two places. Setting a take via +[`set-childkey-take`](/docs/tx/set-childkey-take) rejects values below the +effective floor (or above the global maximum, 11796/65535 ≈ 18%) with +`InvalidChildkeyTake`. And every read of a childkey's take clamps up to the +floor — so raising it immediately re-prices existing childkeys that were set +lower, without anyone resubmitting. + +The setter itself bounds the floor to the \[global min, global max\] range, so +an owner cannot set a floor above 18%. + +Raise the floor below and watch an existing childkey's take get pushed up: + + + +## Reading and setting + +```bash +btcli sudo get --netuid 12 --name min_childkey_take +btcli sudo set --netuid 12 --name min_childkey_take --value 0.05 +``` + +Settable by the subnet owner or root. The value is a fraction between 0 and 1 +written with a decimal point (`0.05` = 5%, stored as 3276/65535). The get +command reports the effective floor — the max of global and per-subnet values. + +## Related + +- [`transfers_enabled`](/docs/hyperparameters/transfers-enabled) +- [`owner_cut_enabled`](/docs/hyperparameters/owner-cut-enabled) +- [`set-children`](/docs/tx/set-children) and + [`set-childkey-take`](/docs/tx/set-childkey-take) — the transactions this + floor governs diff --git a/docs/hyperparameters/min-difficulty.mdx b/docs/hyperparameters/min-difficulty.mdx new file mode 100644 index 0000000000..44d9a4275d --- /dev/null +++ b/docs/hyperparameters/min-difficulty.mdx @@ -0,0 +1,55 @@ +--- +title: min_difficulty +description: Lower bound for the PoW registration difficulty controller; u64::MAX pins difficulty at maximum, disabling PoW registration. +--- + +`min_difficulty` is the floor under +[`difficulty`](/docs/hyperparameters/difficulty), the proof-of-work target +for PoW neuron registration. However cheap registrations get, the +difficulty controller never lets the PoW price fall below this value. +Setting it to `u64::MAX` pins difficulty at maximum, which effectively +disables PoW registration outright. Miners care because it fixes the +minimum compute cost of a PoW slot; root/governance sets it. + +## How it works + +Storage is `MinDifficulty` in `pallets/subtensor/src/lib.rs`, read and +written by `get_min_difficulty` / `set_min_difficulty` +(`pallets/subtensor/src/utils/misc.rs`). In the classic controller, +difficulty was rescaled each adjustment interval by how far registrations +ran over or under target, then clamped to the +\[`min_difficulty`, `max_difficulty`\] band — so with quiet demand, +difficulty decayed until it sat on this floor. + +The chain-wide migration `migrate_set_min_difficulty` +(`pallets/subtensor/src/migrations/migrate_set_min_difficulty.rs`) set the +floor to 10,000,000 on every subnet, matching the runtime default +(`SubtensorInitialMinDifficulty` in `runtime/src/lib.rs`). + +Note that on the current runtime PoW registration is deprecated: the +`register` extrinsic routes to burned registration and no controller +adjusts `difficulty` between intervals, so this floor is dormant. The +playground below opens on a quiet-demand scenario where difficulty decays +until it sits on the bold floor line — drag the floor to its top stop (or +use the button) to see the `u64::MAX` disabled state: + + + +## Reading and setting + +``` +btcli sudo get --netuid N --name min_difficulty +``` + +Root/governance only: `sudo_set_min_difficulty` in the AdminUtils pallet +requires the root origin and takes a raw u64. It is not settable by the +subnet owner via `btcli sudo set`. + +## Related + +[`difficulty`](/docs/hyperparameters/difficulty) · +[`max_difficulty`](/docs/hyperparameters/max-difficulty) · +[`network_pow_registration_allowed`](/docs/hyperparameters/network-pow-registration-allowed) · +[`target_regs_per_interval`](/docs/hyperparameters/target-regs-per-interval) · +[`adjustment_interval`](/docs/hyperparameters/adjustment-interval) · +[`min_burn`](/docs/hyperparameters/min-burn) diff --git a/docs/hyperparameters/network-pow-registration-allowed.mdx b/docs/hyperparameters/network-pow-registration-allowed.mdx new file mode 100644 index 0000000000..d295435b1c --- /dev/null +++ b/docs/hyperparameters/network-pow-registration-allowed.mdx @@ -0,0 +1,57 @@ +--- +title: network_pow_registration_allowed +description: Whether proof-of-work registration is allowed, as opposed to burned registration only. +--- + +`network_pow_registration_allowed` is the per-subnet flag that historically +let owners choose whether miners could register by proof-of-work (grinding +nonces against [`difficulty`](/docs/hyperparameters/difficulty)) or only by +burning TAO. Miners reading the metagraph still see the flag, but on the +current runtime it is a deprecated remnant: PoW registration has been +retired chain-wide. + +## How it works + +Storage is `NetworkPowRegistrationAllowed` in +`pallets/subtensor/src/lib.rs`, exposed through the metagraph RPC as +`pow_registration_allowed`. Two pieces of code make its deprecation +concrete: + +- The migration `migrate_clear_deprecated_registration_maps` + (`pallets/subtensor/src/migrations/`) cleared every stored entry, so all + subnets read the storage default. +- The AdminUtils setter `sudo_set_network_pow_registration_allowed` + (`pallets/admin-utils/src/lib.rs`, call index 20) no longer writes + anything — it unconditionally returns the `POWRegistrationDisabled` + error. + +Independently, the PoW `register` extrinsic +(`pallets/subtensor/src/macros/dispatches.rs`) ignores its work arguments +and routes to the burned-registration path, so the flag has nothing left to +gate. Both ends of the wire are cut: + + + +## Reading and setting + +``` +btcli sudo get --netuid N --name network_pow_registration_allowed +``` + +Nominally owner-settable as a boolean: + +``` +btcli sudo set --netuid N --name network_pow_registration_allowed --value false +``` + +but on the current runtime the underlying extrinsic always fails with +`POWRegistrationDisabled`, so the value cannot actually be changed. + +## Related + +[`registration_allowed`](/docs/hyperparameters/registration-allowed) · +[`difficulty`](/docs/hyperparameters/difficulty) · +[`min_difficulty`](/docs/hyperparameters/min-difficulty) · +[`max_difficulty`](/docs/hyperparameters/max-difficulty) · +[`min_burn`](/docs/hyperparameters/min-burn) · +[`max_burn`](/docs/hyperparameters/max-burn) diff --git a/docs/hyperparameters/owner-cut-auto-lock-enabled.mdx b/docs/hyperparameters/owner-cut-auto-lock-enabled.mdx new file mode 100644 index 0000000000..5db8a9377d --- /dev/null +++ b/docs/hyperparameters/owner-cut-auto-lock-enabled.mdx @@ -0,0 +1,51 @@ +--- +title: owner_cut_auto_lock_enabled +description: Per-subnet toggle that conviction-locks the owner's emission cut as it is paid. +--- + +Controls whether the subnet owner's emission cut is automatically placed under +a [conviction lock](/docs/guides/conviction) the moment it is paid out. It +matters to owners weighing liquidity against conviction, and to lockers +watching the ownership race — an auto-locking owner compounds conviction every +tempo without lifting a finger. + +## How it works + +The flag defaults to **false**. When it is on, each epoch's owner-cut payout is +followed by an automatic `lock-stake` on the owner's coldkey: the freshly +staked alpha is added to the owner's locked mass. If the owner already has a +lock on the subnet, the cut locks toward that lock's existing hotkey (one lock +per coldkey per subnet); otherwise a new lock is created toward the subnet +owner hotkey. A zero cut is silently skipped. + +Locked alpha still earns dividends — the lock is a floor on unstaking, not a +separate bucket — and it accrues conviction, which counts toward the +aggregate-conviction threshold for automatic subnet ownership transfer. Locks +on the owner hotkey mature instantly (conviction equals mass), so an owner +auto-locking toward their own hotkey is defending their seat with fully mature +conviction. Turning the flag off stops future auto-locking but does not +release anything already locked. + +The flag has no effect while +[`owner_cut_enabled`](/docs/hyperparameters/owner-cut-enabled) is off — no cut, +nothing to lock. + + + +## Reading and setting + +```bash +btcli sudo get --netuid 12 --name owner_cut_auto_lock_enabled +btcli sudo set --netuid 12 --name owner_cut_auto_lock_enabled --value true +``` + +Settable by the subnet owner or root; booleans take `true` or `false`. Not +permitted on the root subnet. + +## Related + +- [`owner_cut_enabled`](/docs/hyperparameters/owner-cut-enabled) — the cut + this flag locks +- [`transfers_enabled`](/docs/hyperparameters/transfers-enabled) +- [Conviction locks](/docs/guides/conviction) — lock mechanics and ownership + transfer diff --git a/docs/hyperparameters/owner-cut-enabled.mdx b/docs/hyperparameters/owner-cut-enabled.mdx new file mode 100644 index 0000000000..68a057d0b3 --- /dev/null +++ b/docs/hyperparameters/owner-cut-enabled.mdx @@ -0,0 +1,45 @@ +--- +title: owner_cut_enabled +description: Per-subnet toggle for the subnet owner's 18% share of alpha emission. +--- + +Controls whether the subnet owner receives their cut of the subnet's alpha +emission. Owners care because it is their revenue; stakers and miners care +because whatever the owner does not take is redistributed to them. + +## How it works + +The cut percentage itself is a **global, root-set** value (`SubnetOwnerCut`, +11796/65535 ≈ 18%). This flag only decides, per subnet, whether the deduction +happens at all. It defaults to **true**. + +Every block, when the chain accrues the subnet's `alpha_out`, it multiplies it +by the cut percentage, subtracts that from the amount destined for +participants, and accumulates it in a pending bucket. At the next epoch the +pending cut is staked directly to the subnet owner's hotkey and coldkey, while +the remainder goes through Yuma Consensus to miners, validators, and stakers +(see [the per-tempo split](/docs/concepts/emissions#the-per-tempo-split)). + +When the flag is off, no deduction happens: the full `alpha_out` flows to +participants and the owner receives nothing — the forgone cut is not stashed +or paid retroactively. Blocks are settled as they accrue, so flipping the flag +mid-tempo affects only the blocks after the change. + + + +## Reading and setting + +```bash +btcli sudo get --netuid 12 --name owner_cut_enabled +btcli sudo set --netuid 12 --name owner_cut_enabled --value false +``` + +Settable by the subnet owner or root; booleans take `true` or `false`. Not +permitted on the root subnet. + +## Related + +- [`owner_cut_auto_lock_enabled`](/docs/hyperparameters/owner-cut-auto-lock-enabled) + — lock the cut as it arrives +- [`transfers_enabled`](/docs/hyperparameters/transfers-enabled) +- [Emissions](/docs/concepts/emissions) — the full per-tempo split diff --git a/docs/hyperparameters/owner-immune-neuron-limit.mdx b/docs/hyperparameters/owner-immune-neuron-limit.mdx new file mode 100644 index 0000000000..b6a2e6a758 --- /dev/null +++ b/docs/hyperparameters/owner-immune-neuron-limit.mdx @@ -0,0 +1,30 @@ +--- +title: owner_immune_neuron_limit +description: How many of the subnet owner's own neurons are permanently immune from pruning. +--- + +`owner_immune_neuron_limit` is the number of neurons registered under the subnet owner's coldkey that can never be pruned, regardless of emission or age. Owners use it to keep their own validator or infrastructure hotkeys safe on a full subnet; everyone else cares because each owner-immune UID is one slot removed from the competition. + +## How it works + +`get_immune_owner_tuples` (`pallets/subtensor/src/subnets/registration.rs`) collects all of the owner coldkey's hotkeys that hold a UID, sorts them by registration block (earliest first, so older keys keep priority), and truncates the list to `owner_immune_neuron_limit`. The subnet owner hotkey itself is inserted at the front of the list if it holds a UID. Both `get_neuron_to_prune` and `trim_to_max_allowed_uids` skip these UIDs entirely — unlike [`immunity_period`](/docs/hyperparameters/immunity-period) immunity, this protection never expires and has no emission-based fallback. + +The limit must stay between 1 and 10 (`set_owner_immune_neuron_limit` in `pallets/subtensor/src/utils/misc.rs` rejects anything outside `MinImmuneOwnerUidsLimit`..`MaxImmuneOwnerUidsLimit` with `InvalidValue`). The default is 1: just the owner's primary hotkey. + + + +## Reading and setting + +``` +btcli sudo get --netuid N --name owner_immune_neuron_limit +``` + +The value is a plain integer between 1 and 10: + +``` +btcli sudo set --netuid N --name owner_immune_neuron_limit --value 3 +``` + +## Related + +[`immunity_period`](/docs/hyperparameters/immunity-period), [`max_allowed_uids`](/docs/hyperparameters/max-allowed-uids), [`serving_rate_limit`](/docs/hyperparameters/serving-rate-limit) diff --git a/docs/hyperparameters/registration-allowed.mdx b/docs/hyperparameters/registration-allowed.mdx new file mode 100644 index 0000000000..fdd77ed660 --- /dev/null +++ b/docs/hyperparameters/registration-allowed.mdx @@ -0,0 +1,54 @@ +--- +title: registration_allowed +description: Whether new neuron registrations are currently accepted on this subnet. +--- + +`registration_allowed` is the master on/off switch for joining a subnet. +When it is false, every neuron registration — burned or the legacy PoW +entry point — is rejected, no matter how much TAO the caller is willing to +burn. Prospective miners and validators should check it before attempting +to register; it is a root/governance-controlled gate, typically used to +close a subnet during incidents or before launch. + +## How it works + +Storage is `NetworkRegistrationAllowed` in `pallets/subtensor/src/lib.rs` +(default true; new subnets also set it explicitly to true during +initialization in `pallets/subtensor/src/subnets/subnet.rs`). The +gate is enforced at step 3 of `do_register` +(`pallets/subtensor/src/subnets/registration.rs`): the extrinsic fails with +`SubNetRegistrationDisabled` if `get_network_registration_allowed` returns +false. Both `burned_register` and the legacy PoW `register` extrinsic land +in `do_register`, so the flag closes every registration path at once. The +validity helper `checked_allowed_register` (`pallets/subtensor/src/lib.rs`) +applies the same check, alongside the per-block cap +([`max_regs_per_block`](/docs/hyperparameters/max-regs-per-block)) and the +per-interval cap of 3 × +[`target_regs_per_interval`](/docs/hyperparameters/target-regs-per-interval). + +Note the flag only stops new registrations; already-registered neurons are +unaffected, and pruning/immunity mechanics continue as normal. + +Flip the gate below to see how the same stream of registration attempts is +accepted or rejected: + + + +## Reading and setting + +``` +btcli sudo get --netuid N --name registration_allowed +``` + +Root/governance only: `sudo_set_network_registration_allowed` in the +AdminUtils pallet requires the root origin (a boolean). It is not settable +by the subnet owner via `btcli sudo set`. + +## Related + +[`network_pow_registration_allowed`](/docs/hyperparameters/network-pow-registration-allowed) · +[`max_regs_per_block`](/docs/hyperparameters/max-regs-per-block) · +[`target_regs_per_interval`](/docs/hyperparameters/target-regs-per-interval) · +[`min_burn`](/docs/hyperparameters/min-burn) · +[`max_burn`](/docs/hyperparameters/max-burn) · +[`immunity_period`](/docs/hyperparameters/immunity-period) diff --git a/docs/hyperparameters/rho.mdx b/docs/hyperparameters/rho.mdx new file mode 100644 index 0000000000..3e2e5112d8 --- /dev/null +++ b/docs/hyperparameters/rho.mdx @@ -0,0 +1,57 @@ +--- +title: rho +description: Steepness of the classic Yuma trust sigmoid mapping consensus alignment to trust. +--- + +`rho` sets the temperature (steepness) of the sigmoid that maps a validator's +consensus alignment to trust in the classic Yuma Consensus formulation. Subnet +owners tuning how sharply the network rewards agreement with consensus are the +main audience. + +## How it works + +The formula lives in `sigmoid_safe` (`pallets/subtensor/src/epoch/math.rs`): + +``` +trust = 1 / (1 + e^(−rho × (x − kappa))) +``` + +where `x` is the stake fraction aligned with a validator's view and +[`kappa`](/docs/hyperparameters/kappa) is the midpoint. Higher `rho` makes the +curve steeper: alignment just above `kappa` maps to trust near 1, just below +to near 0. The recommended range in the code is 0 to 40; the mainnet +default is 10. + +One caveat, straight from the source: the current epoch +(`pallets/subtensor/src/epoch/run_epoch.rs`) computes consensus as the +`kappa`-weighted median of weights and clips to it — the helper that would +feed `rho` into the sigmoid (`get_float_rho`) is defined but not called on +the active path. `rho` remains a stored, owner-settable parameter of the +Yuma formulation, but changing it does not alter today's epoch output. See +[Yuma Consensus](/docs/concepts/emissions#yuma-consensus) for the full +pipeline. + + + +## Reading and setting + +Inspect with: + +``` +btcli sudo get --netuid N --name rho +``` + +Owner-settable: + +``` +btcli sudo set --netuid N --name rho --value 10 +``` + +The value is a plain integer (stored as u16), not a fraction. + +## Related + +[`kappa`](/docs/hyperparameters/kappa) · +[`tempo`](/docs/hyperparameters/tempo) · +[`activity_cutoff`](/docs/hyperparameters/activity-cutoff) · +[`max_validators`](/docs/hyperparameters/max-validators) diff --git a/docs/hyperparameters/serving-rate-limit.mdx b/docs/hyperparameters/serving-rate-limit.mdx new file mode 100644 index 0000000000..61760ba24f --- /dev/null +++ b/docs/hyperparameters/serving-rate-limit.mdx @@ -0,0 +1,32 @@ +--- +title: serving_rate_limit +description: Minimum blocks a neuron must wait between axon (or prometheus) serve calls. +--- + +`serving_rate_limit` is the minimum number of blocks between consecutive `serve_axon` calls from the same neuron on a subnet. Miners care because it caps how often they can republish their IP/port endpoint; owners tune it to keep endpoint churn and extrinsic spam down without blocking legitimate updates. + +## How it works + +When a neuron posts its axon endpoint, `do_serve_axon` (`pallets/subtensor/src/subnets/serving.rs`) checks `axon_passes_rate_limit`: the call is accepted only if `current_block − last_serve_block >= serving_rate_limit`, where `last_serve_block` is stamped on the neuron's stored axon info at each successful serve. A first-ever serve always passes, and setting the parameter to 0 disables the check entirely. Violations fail with `ServingRateLimitExceeded`. The same limit applies independently to `serve_prometheus` via `prometheus_passes_rate_limit`. + +The default is 50 blocks — about 10 minutes at 12-second blocks. Unlike the pruning family of parameters, this one does not affect who keeps a UID, only how frequently a UID's published endpoint can change; a miner that misses the window just retries after the cooldown. + +Drag the slider to see which of a miner's serve attempts survive the cooldown: + + + +## Reading and setting + +``` +btcli sudo get --netuid N --name serving_rate_limit +``` + +The value is a plain block count: + +``` +btcli sudo set --netuid N --name serving_rate_limit --value 50 +``` + +## Related + +[`immunity_period`](/docs/hyperparameters/immunity-period), [`max_allowed_uids`](/docs/hyperparameters/max-allowed-uids), [`owner_immune_neuron_limit`](/docs/hyperparameters/owner-immune-neuron-limit), [`weights_rate_limit`](/docs/hyperparameters/weights-rate-limit) diff --git a/docs/hyperparameters/subnet-is-active.mdx b/docs/hyperparameters/subnet-is-active.mdx new file mode 100644 index 0000000000..82616e82be --- /dev/null +++ b/docs/hyperparameters/subnet-is-active.mdx @@ -0,0 +1,64 @@ +--- +title: subnet_is_active +description: Whether the subnet has been started — staking, alpha trading, and emissions are live. +--- + +`subnet_is_active` reports whether the subnet's owner has fired the one-shot +[`start-call`](/docs/tx/start-call) that brings a registered subnet to life. +Before it flips to true the subnet is a shell: no one can stake into it, its +alpha cannot be traded, and it receives no emissions. Stakers should check it +before trying to enter a freshly registered subnet; owners watch it as the +line between "registered" and "launched." + +## How it works + +The value is the `SubtokenEnabled` storage map in +`pallets/subtensor/src/lib.rs` — "is subtoken trading enabled" — which +defaults to **false** for a newly registered subnet. `do_start_call` +(`pallets/subtensor/src/subnets/subnet.rs`) sets it to true, records the +subnet's first emission block, and can only be called by the subnet owner +once `StartCallDelay` blocks have passed since registration (the +[`subnet_start_schedule`](/docs/query/subnet-start-schedule) read shows the +earliest block). + +While the flag is false: + +- **All staking paths are closed.** `ensure_subtoken_enabled` guards + add/remove stake, limit orders, and alpha recycle/burn, and both the origin + and destination subnets are checked on move/swap/transfer + (`pallets/subtensor/src/staking/stake_utils.rs`). Every one of them fails + with `SubtokenDisabled`. +- **No emissions.** `get_subnets_to_emit_to` + (`pallets/subtensor/src/coinbase/subnet_emissions.rs`) only emits to + subnets with the flag on, a first-emission block set, and + [`registration_allowed`](/docs/hyperparameters/registration-allowed) true. +- **Childkeys apply immediately.** `set_children` skips the pending-cooldown + schedule before start and applies at once + (`pallets/subtensor/src/staking/set_children.rs`). + +Dissolving a subnet removes the entry. Root can also force the flag either +way with `sudo_set_subtoken_enabled` (AdminUtils), but in normal operation it +is written exactly once, by `start-call`. + +## Reading and setting + +``` +btcli sudo get --netuid N --name subnet_is_active +``` + +Not settable through `btcli sudo set` — the owner flips it by starting the +subnet: + +``` +btcli tx start-call --netuid N --dry-run +``` + +Check when the subnet becomes eligible with +`btcli query subnet-start-schedule --netuid N`. + +## Related + +[`registration_allowed`](/docs/hyperparameters/registration-allowed) · +[`transfers_enabled`](/docs/hyperparameters/transfers-enabled) · +[`start-call`](/docs/tx/start-call) · +[`subnet_start_schedule`](/docs/query/subnet-start-schedule) diff --git a/docs/hyperparameters/target-regs-per-interval.mdx b/docs/hyperparameters/target-regs-per-interval.mdx new file mode 100644 index 0000000000..24afcfdd88 --- /dev/null +++ b/docs/hyperparameters/target-regs-per-interval.mdx @@ -0,0 +1,60 @@ +--- +title: target_regs_per_interval +description: Registrations per interval the registration controller steers toward; today a hard admission cap. +--- + +Originally the setpoint of the registration-rate controller: the number of +registrations per +[`adjustment_interval`](/docs/hyperparameters/adjustment-interval) the chain +tried to hit by moving burn and difficulty. On the current chain it survives +as a **hard cap**: an interval may admit at most three times this many +registrations. Root-subnet validators hit it most directly. + +## How it works + +The value lives in `TargetRegistrationsPerInterval` storage (u16, default +**2**, so the cap is 6). Two places enforce the 3× cap: + +- `do_root_register` (`pallets/subtensor/src/coinbase/root.rs`) rejects a root + registration with `TooManyRegistrationsThisInterval` once + `RegistrationsThisInterval ≥ 3 × target_regs_per_interval`. The counter + resets on the root subnet's epoch boundary — one "interval" is effectively + one root tempo, and `adjustment_interval` itself is not consulted. +- `checked_allowed_register` (`pallets/subtensor/src/lib.rs`) applies the same + 3× comparison when reporting whether a non-root subnet accepts + registrations; on those subnets pricing is done by the continuous burn + controller ([`burn_increase_mult`](/docs/hyperparameters/burn-increase-mult) + and [`burn_half_life`](/docs/hyperparameters/burn-half-life)) rather than by + steering toward this target. + +The legacy EMA adjustment that used this value as a setpoint (together with +[`adjustment_alpha`](/docs/hyperparameters/adjustment-alpha)) no longer exists +in the runtime. + +The chart below shows the cap in action: attempts pile up over an interval, and +admission stops once the count reaches 3× the target. + + + +## Reading and setting + +```bash +btcli sudo get --netuid 12 --name target_regs_per_interval +``` + +Root/governance only — `sudo_set_target_registrations_per_interval` in +`pallets/admin-utils/src/lib.rs` requires the root origin (plus the admin +freeze window being open), so subnet owners cannot change it. The value is a +plain integer. + +## Related + +- [`adjustment_interval`](/docs/hyperparameters/adjustment-interval) — the + legacy cadence this target was measured over +- [`adjustment_alpha`](/docs/hyperparameters/adjustment-alpha) — the legacy + smoothing factor +- [`max_regs_per_block`](/docs/hyperparameters/max-regs-per-block) — the + per-block admission cap +- [`min_burn`](/docs/hyperparameters/min-burn) / + [`max_burn`](/docs/hyperparameters/max-burn) — the price bounds of the + current controller diff --git a/docs/hyperparameters/tempo.mdx b/docs/hyperparameters/tempo.mdx new file mode 100644 index 0000000000..5eb592cd86 --- /dev/null +++ b/docs/hyperparameters/tempo.mdx @@ -0,0 +1,61 @@ +--- +title: tempo +description: Blocks per epoch — how often a subnet runs Yuma Consensus and distributes emissions. +--- + +`tempo` is the number of 12-second blocks between a subnet's consensus +epochs. It sets the payout cadence for everyone on the subnet: miners and +validators are paid once per tempo, not per block. + +## How it works + +Each subnet's epoch fires once `tempo` blocks have passed since its last +epoch: `should_run_epoch` (`pallets/subtensor/src/coinbase/run_coinbase.rs`) +checks `current_block − LastEpochBlock ≥ tempo`. The default is 360 blocks +(~72 minutes); at most 2 subnet epochs run per block, with extras deferred, +and the owner can trigger an early epoch. When the epoch fires, the alpha +accumulated since the previous epoch is distributed via +[Yuma Consensus](/docs/concepts/emissions#yuma-consensus); see +[Epochs](/docs/concepts/emissions#epochs) for scheduling details. + +`tempo` also anchors other parameters: the effective +[`activity_cutoff`](/docs/hyperparameters/activity-cutoff) is a per-mille +factor multiplied by `tempo`, and commit-reveal delays are counted in tempos. +Setting a new tempo resets the epoch cycle (`apply_tempo_with_cycle_reset` +re-anchors `LastEpochBlock` to the current block), so the next epoch lands a +full tempo after the change. + +The timeline below shows emission accruing block by block and paying out at +each epoch boundary — slide `tempo` across the full owner-settable range +(360 to 50,400 blocks, ~72 minutes to ~7 days) to see the cadence in +wall-clock time. + + + +## Reading and setting + +Inspect with: + +``` +btcli sudo get --netuid N --name tempo +``` + +Owner-settable: + +``` +btcli sudo set --netuid N --name tempo --value 720 +``` + +This dispatches `sudo_set_tempo` (AdminUtils), which is owner-or-root: the +owner is bounded to 360–50,400 blocks (~72 minutes to ~7 days) and +rate-limited to one change per 360 blocks; root may set any u16. Both paths +respect the admin freeze window, and a successful change resets the epoch +cycle so the next epoch lands a full new tempo after the change. + +## Related + +[`rho`](/docs/hyperparameters/rho) · +[`kappa`](/docs/hyperparameters/kappa) · +[`activity_cutoff_factor`](/docs/hyperparameters/activity-cutoff-factor) · +[`max_validators`](/docs/hyperparameters/max-validators) · +[`commit_reveal_period`](/docs/hyperparameters/commit-reveal-period) diff --git a/docs/hyperparameters/transfers-enabled.mdx b/docs/hyperparameters/transfers-enabled.mdx new file mode 100644 index 0000000000..bfa983f984 --- /dev/null +++ b/docs/hyperparameters/transfers-enabled.mdx @@ -0,0 +1,48 @@ +--- +title: transfers_enabled +description: Per-subnet toggle allowing stake to be transferred between coldkeys. +--- + +Controls whether staked alpha on this subnet can be moved from one coldkey to +another. It matters to stakers who shift positions between their own wallets or +hand alpha to someone else without unstaking, and to subnet owners who would +rather see alpha change hands only through the pool. + +## How it works + +The flag lives in `TransferToggle` storage and defaults to **true**. It is +checked only on the coldkey-changing path: +[`transfer-stake`](/docs/tx/transfer-stake) validates it and fails with +`TransferDisallowed` when the toggle is off. Moving stake between hotkeys or +subnets under the *same* coldkey ([`move-stake`](/docs/tx/move-stake), +[`swap-stake`](/docs/tx/swap-stake)) never consults the flag, and neither does +ordinary staking or unstaking — disabling transfers does not trap anyone's +stake, it only pins it to its current coldkey. + +For a cross-subnet transfer, **both** the origin and destination subnets must +have transfers enabled. If the transferred stake carries a +[conviction lock](/docs/guides/conviction), the lock follows the stake: locked +mass and conviction move to the destination coldkey in proportion to the amount +transferred. + +Flip the toggle below and try moving stake between the two coldkeys: + + + +## Reading and setting + +```bash +btcli sudo get --netuid 12 --name transfers_enabled +btcli sudo set --netuid 12 --name transfers_enabled --value false +``` + +Settable by the subnet owner or root; booleans take `true` or `false`. + +## Related + +- [`owner_cut_enabled`](/docs/hyperparameters/owner-cut-enabled) — the other + per-subnet economics toggle +- [`min_childkey_take`](/docs/hyperparameters/min-childkey-take) +- [Money](/docs/concepts/money) — how stake, pools, and valuation work +- [Conviction locks](/docs/guides/conviction) — what happens to locked stake + on transfer diff --git a/docs/hyperparameters/user-liquidity-enabled.mdx b/docs/hyperparameters/user-liquidity-enabled.mdx new file mode 100644 index 0000000000..a3d20443b4 --- /dev/null +++ b/docs/hyperparameters/user-liquidity-enabled.mdx @@ -0,0 +1,41 @@ +--- +title: user_liquidity_enabled +description: Legacy flag for user-provided swap liquidity; always false since the balancer migration. +--- + +`user_liquidity_enabled` once gated whether users could open their own +liquidity positions in a subnet's swap pool. That feature is gone: the chain +migrated the swap to a balancer design, and the flag is now hardcoded to +**false** for every subnet. It remains in the hyperparameter listing only so +existing clients keep decoding a familiar field — treat it as a tombstone, +not a setting. + +## How it works + +Under swap v3, `EnabledUserLiquidity` in the Swap pallet controlled per-subnet +access to user LP calls (`add_liquidity`, `remove_liquidity`, +`modify_position`). The balancer migration +(`pallets/swap/src/pallet/migrations/migrate_swapv3_to_balancer.rs`) deleted +that storage outright, and each of those extrinsics — along with the +`toggle_user_liquidity` call that flipped the flag — now fails immediately +with `Deprecated` (`pallets/swap/src/pallet/mod.rs`). + +The hyperparameter getter reflects this: `get_subnet_hyperparams_v3` +(`pallets/subtensor/src/rpc_info/subnet_info.rs`) reports a literal `false` +with no storage read behind it. There is nothing to enable and no one who can +enable it. + +## Reading and setting + +``` +btcli sudo get --netuid N --name user_liquidity_enabled +``` + +Always false. Not settable by anyone — the calls that once toggled it are +deprecated on-chain. + +## Related + +[`transfers_enabled`](/docs/hyperparameters/transfers-enabled) · +[`subnet_is_active`](/docs/hyperparameters/subnet-is-active) · +[Money](/docs/concepts/money) — how the swap pool prices alpha today diff --git a/docs/hyperparameters/weights-rate-limit.mdx b/docs/hyperparameters/weights-rate-limit.mdx new file mode 100644 index 0000000000..6638ee00f9 --- /dev/null +++ b/docs/hyperparameters/weights-rate-limit.mdx @@ -0,0 +1,53 @@ +--- +title: weights_rate_limit +description: Minimum number of blocks a validator must wait between weight submissions on a subnet. +--- + +`weights_rate_limit` (on chain: `WeightsSetRateLimit`) is the cooldown between +a validator's weight submissions, measured in 12-second blocks. The default is +100 blocks (20 minutes). It keeps validators from churning weights every +block, which would bloat chain state and make consensus inputs noisy. + +## How it works + +The chain tracks each UID's last submission block (`LastUpdate`). +`check_rate_limit` (`pallets/subtensor/src/subnets/weights.rs`) passes when +`current_block - last_update >= weights_rate_limit`; a UID that has never +submitted always passes. Too-frequent calls fail with `SettingWeightsTooFast`. + +Which call is limited depends on the commit-reveal setting. With commit-reveal +off, the limit applies to `set_weights` itself. With it on, the limit applies +to the **commit** (failing with `CommittingWeightsTooFast`), and the reveal is +exempt — the commit already paid the cooldown, and updates `LastUpdate` at +commit time. + +Because `LastUpdate` also feeds the [activity +cutoff](/docs/hyperparameters/activity-cutoff), the rate limit bounds how +fresh a validator's weights can be — a subnet's cutoff should comfortably +exceed its rate limit. + +`btcli tx set-weights` (the `set_weights` intent) preflights this check: it +reads `LastUpdate` before signing and, if you are inside the cooldown, fails +immediately with the number of blocks (and approximate seconds) left to wait +instead of submitting a doomed extrinsic. + +Drag the limit to see which submission attempts survive the cooldown: + + + +## Reading and setting + +```bash +btcli sudo get --netuid N --name weights_rate_limit +``` + +Root/governance-only — not settable by the subnet owner. The value is a plain +block count. + +## Related + +- [`commit_reveal_weights_enabled`](/docs/hyperparameters/commit-reveal-weights-enabled) — decides whether the limit hits set or commit +- [`min_allowed_weights`](/docs/hyperparameters/min-allowed-weights) — the floor on submission size +- [`max_weights_limit`](/docs/hyperparameters/max-weights-limit) — cap on any single weight +- [`activity_cutoff`](/docs/hyperparameters/activity-cutoff) — staleness window fed by the same LastUpdate +- [`tempo`](/docs/hyperparameters/tempo) — how often submitted weights are consumed diff --git a/docs/hyperparameters/weights-version.mdx b/docs/hyperparameters/weights-version.mdx new file mode 100644 index 0000000000..e8994bcaaf --- /dev/null +++ b/docs/hyperparameters/weights-version.mdx @@ -0,0 +1,54 @@ +--- +title: weights_version +description: Minimum version key a validator must send with set_weights; a forced-upgrade lever for subnet owners. +--- + +`weights_version` (on chain: `WeightsVersionKey`) is a version gate on weight +submissions. Subnet owners raise it when they ship a breaking change to their +validator code: any validator still running the old software — and therefore +sending an old version key — has its weights rejected until it upgrades. + +## How it works + +Every `set_weights` call carries a `version_key` argument. The chain compares +it in `check_version_key` (`pallets/subtensor/src/subnets/weights.rs`): the +submission passes when the subnet's `WeightsVersionKey` is 0 (the gate is +disabled) or when the submitted key is greater than or equal to it. Otherwise +the call fails with `IncorrectWeightVersionKey`. The value itself is opaque to +the chain — it is a plain u64 counter whose meaning is a convention between +the owner and their validator codebase. + +The key is checked at reveal time too: revealed commit-reveal weights pass +through the same `set_weights` validation, so a version bump between commit +and reveal invalidates in-flight commits. + +The SDK does not preflight this check — `btcli tx set-weights` (the +`set_weights` intent) sends `version_key` 0 unless told otherwise, which only +passes on subnets that leave the gate disabled. Validator code on gated +subnets must pass the current key explicitly. + +Changing it has a dedicated rate limit: the owner may update it at most once +per `WeightsVersionKeyRateLimit` tempos (currently 5). + +Raise the required key to watch validators on old software fall off: + + + +## Reading and setting + +```bash +btcli sudo get --netuid N --name weights_version +``` + +Owner-settable; the value is a plain integer: + +```bash +btcli sudo set --netuid N --name weights_version --value 1020 +``` + +## Related + +- [`min_allowed_weights`](/docs/hyperparameters/min-allowed-weights) — the floor on submission size +- [`max_weights_limit`](/docs/hyperparameters/max-weights-limit) — cap on any single weight +- [`weights_rate_limit`](/docs/hyperparameters/weights-rate-limit) — how often submissions are accepted +- [`commit_reveal_period`](/docs/hyperparameters/commit-reveal-period) — the commit-to-reveal delay the key must survive diff --git a/docs/hyperparameters/yuma-version.mdx b/docs/hyperparameters/yuma-version.mdx new file mode 100644 index 0000000000..bdb3b1236b --- /dev/null +++ b/docs/hyperparameters/yuma-version.mdx @@ -0,0 +1,39 @@ +--- +title: yuma_version +description: Which Yuma consensus variant the subnet's epoch runs — a derived reading of yuma3_enabled. +--- + +`yuma_version` names the consensus variant the subnet's epoch executes: **2** +for classic Yuma, **3** for the Yuma3 bond computation. It is a convenience +reading, not its own switch — the chain derives it from the +[`yuma3_enabled`](/docs/hyperparameters/yuma3-enabled) flag, and those are the +only two values it takes. Validators use it to know which bond math is paying +their dividends without decoding a boolean in their head. + +## How it works + +`get_subnet_hyperparams_v3` (`pallets/subtensor/src/rpc_info/subnet_info.rs`) +computes it inline: 3 when `Yuma3On` is set for the subnet, 2 otherwise. No +`YumaVersion` storage exists; the epoch itself branches on `Yuma3On` directly +(`pallets/subtensor/src/epoch/run_epoch.rs`). What the two variants actually +change — bond normalization, the EMA path, and whether liquid alpha applies — +is covered on the [`yuma3_enabled`](/docs/hyperparameters/yuma3-enabled) page. + +## Reading and setting + +``` +btcli sudo get --netuid N --name yuma_version +``` + +Not settable directly. The subnet owner switches variants through the flag: + +``` +btcli sudo set --netuid N --name yuma3_enabled --value true +``` + +## Related + +[`yuma3_enabled`](/docs/hyperparameters/yuma3-enabled) · +[`liquid_alpha_enabled`](/docs/hyperparameters/liquid-alpha-enabled) · +[`bonds_moving_avg`](/docs/hyperparameters/bonds-moving-avg) · +[Yuma Consensus overview](/docs/concepts/emissions#yuma-consensus) diff --git a/docs/hyperparameters/yuma3-enabled.mdx b/docs/hyperparameters/yuma3-enabled.mdx new file mode 100644 index 0000000000..fd2b22ee00 --- /dev/null +++ b/docs/hyperparameters/yuma3-enabled.mdx @@ -0,0 +1,35 @@ +--- +title: yuma3_enabled +description: Enables the Yuma3 consensus variant, which changes how bonds are stored and dividends computed — and unlocks liquid alpha. +--- + +`yuma3_enabled` switches a subnet's epoch from the classic Yuma bond computation to the Yuma3 variant. Subnet owners toggle it to change how validator dividends reward conviction; validators care because it alters both the bond math and whether liquid alpha can apply at all. + +## How it works + +The epoch function branches on `Yuma3On` (`pallets/subtensor/src/epoch/run_epoch.rs`, "Bonds and Dividends" section): + +- **Classic path (off).** Instant bonds `ΔB = W ∘ S` are column-normalized, smoothed by the flat EMA from [`bonds_moving_avg`](/docs/hyperparameters/bonds-moving-avg) (`compute_ema_bonds_normal`), and dividends are `d_i = Σ_j B_ij × I_j`. +- **Yuma3 path (on).** Bonds are read as fixed proportions (`get_bonds_fixed_proportion`) without column normalization, the EMA runs through `compute_bonds` — which is where [`liquid_alpha_enabled`](/docs/hyperparameters/liquid-alpha-enabled) can swap in per-pair alpha rates — and dividends are the row-sum of normalized EMA bonds times incentive, scaled by each validator's active stake, then renormalized. + +Practical consequence: liquid alpha and its parameters ([`alpha_low`](/docs/hyperparameters/alpha-low), [`alpha_high`](/docs/hyperparameters/alpha-high), [`alpha_sigmoid_steepness`](/docs/hyperparameters/alpha-sigmoid-steepness)) only take effect when this flag is on; the classic path never consults them. See the [Yuma Consensus overview](/docs/concepts/emissions#yuma-consensus) for the full epoch pipeline. + +The two paths split the same dividend pool differently whenever validators disagree — compare them on a minimal example: + + + +## Reading and setting + +``` +btcli sudo get --netuid N --name yuma3_enabled +``` + +Boolean — pass true or false: + +``` +btcli sudo set --netuid N --name yuma3_enabled --value true +``` + +## Related + +[`liquid_alpha_enabled`](/docs/hyperparameters/liquid-alpha-enabled), [`bonds_moving_avg`](/docs/hyperparameters/bonds-moving-avg), [`bonds_penalty`](/docs/hyperparameters/bonds-penalty), [`bonds_reset_enabled`](/docs/hyperparameters/bonds-reset-enabled) diff --git a/docs/internals/contributing.mdx b/docs/internals/contributing.mdx index 9be2fd6284..07492549a6 100644 --- a/docs/internals/contributing.mdx +++ b/docs/internals/contributing.mdx @@ -88,7 +88,7 @@ truth rendered to [bittensor.com/docs](https://bittensor.com/docs) by the website app (`website/apps/bittensor-website`). - User-facing concepts and guides: `docs/concepts/`, `docs/guides/` -- Generated SDK reference (`docs/tx/`, `docs/query/`, `docs/errors.mdx`): +- Generated SDK reference (`docs/tx/`, `docs/query/`, `docs/errors/`): never hand-edit; regenerate with `website/apps/bittensor-website/scripts/generate.py` - Runtime internals and contributor docs: `docs/internals/` diff --git a/docs/internals/release-process.mdx b/docs/internals/release-process.mdx index f9b889f8e2..4c886ba6f1 100644 --- a/docs/internals/release-process.mdx +++ b/docs/internals/release-process.mdx @@ -52,8 +52,11 @@ Human approval lives in GitHub **environment settings**, not workflow code: | mainnet | `mainnet` | environment reviewers **plus** the multisig ceremony below | Deploys to devnet and testnet are direct `setCode` transactions via the CI -deploy key; each is followed by an on-chain `spec_version` verification and a -smoke suite. +deploy key. After the on-chain `spec_version` is verified, the train moves the +corresponding network mirror branch. That branch push builds the exact deployed +commit and updates `ghcr.io/raofoundation/subtensor:devnet` or `:testnet`; it +does not move `:latest`. The smoke suite then validates the live network while +the independent image build runs. ### The mainnet multisig ceremony @@ -74,27 +77,20 @@ running your runtime with a public endpoint yet, it cuts the release train's artifacts: 1. **GitHub release** `v` -2. **Python SDK + bittensor-core wheels** to PyPI (trusted publishing) -3. **Publishable Rust crates** to crates.io (auto-discovered; workspace crates +2. **Production Docker images** tagged `v` and `latest` +3. **Python SDK + bittensor-core wheels** to PyPI (trusted publishing, with + PEP 740 provenance attestations) +4. **Publishable Rust crates** to crates.io (auto-discovered; workspace crates are `publish = false` while they depend on the patched polkadot-sdk fork) -4. **Production website/docs** to Vercel +5. **Production website/docs** to Vercel This ordering means the release always reflects what is *actually running on mainnet*, not what was merged. -### Known gap: Docker images don't publish on release - -`docker.yml` and `docker-localnet.yml` trigger on `release: published` and are -what tag `ghcr.io` images (including `:latest`) for a mainnet release. However, -the watcher creates the release with the default `GITHUB_TOKEN`, and **GitHub -does not trigger workflows from events created by the default token** (a -recursion guard). So the production node image is currently *not* published -when a release is cut. Until fixed (use a PAT/app token for `gh release -create`, or have the watcher call `gh workflow run docker.yml -f -branch-or-tag=v`), publish manually via `docker.yml`'s -`workflow_dispatch` with the release tag. Note the release also carries no -attached artifacts — the srtool wasm/digest live only as 90-day workflow -artifacts on the train run. +The watcher explicitly dispatches `docker.yml` and `docker-localnet.yml` +because releases created with the default `GITHUB_TOKEN` do not emit another +workflow event. The production build publishes both the immutable release tag +and `:latest`; the localnet build does the same in its separate package. ## Hotfixes diff --git a/docs/internals/repo-layout.mdx b/docs/internals/repo-layout.mdx index 2c137c9a04..fd3b1b7916 100644 --- a/docs/internals/repo-layout.mdx +++ b/docs/internals/repo-layout.mdx @@ -40,7 +40,7 @@ binding crate per language, and one product directory per language surface. | `sdk/bittensor-core/` | The chain-defined compute core for clients: sp-core key primitives, keyfiles, SCALE codec + runtime metadata engine, extrinsic assembly, RFC-0078 metadata digests, drand timelock, ML-KEM, Ledger transport — a cargo workspace member built against the same crate revisions as the runtime so client crypto and codec can never drift from the chain's | | `sdk/bittensor-core-py/` | PyO3 bindings for the core (bindings only, no logic) — published to PyPI as `bittensor-core` wheels ([Rust setup](/docs/internals/rust-setup)). Future surfaces add sibling binding crates (napi, uniffi) next to the core they mirror | | `sdk/python/` | The `bittensor` Python SDK and `btcli` — bindings generated from chain metadata ([SDK tests](/docs/internals/sdk-tests)) | -| `docs/` | This documentation — the single source of truth, rendered to bittensor.com/docs by the website app. `docs/tx/`, `docs/query/`, and `docs/errors.mdx` are generated; never hand-edit them | +| `docs/` | This documentation — the single source of truth, rendered to bittensor.com/docs by the website app. `docs/tx/`, `docs/query/`, and `docs/errors/` are generated; never hand-edit them | | `website/` | Yarn 4 + Turbo monorepo. `apps/bittensor-website` is the production site (marketing + these docs); `packages/` holds shared UI/API libraries | | `ink-contract/` | Standalone ink! contract source used by the wasm-contract tests | diff --git a/docs/meta.json b/docs/meta.json index 15e19926b0..ca39f3dd31 100644 --- a/docs/meta.json +++ b/docs/meta.json @@ -13,6 +13,7 @@ "tx", "query", "errors", + "hyperparameters", "---Contributing---", "internals" ] diff --git a/docs/migration.mdx b/docs/migration.mdx index ba441a89f2..3042750bf6 100644 --- a/docs/migration.mdx +++ b/docs/migration.mdx @@ -18,9 +18,10 @@ equivalent. [CLI mappings](#cli-migration) mechanically; where a row says "no direct equivalent", use the listed escape hatch. 3. Watch the semantic traps in [Money](#money-and-balance), - [Weights and take](#weights-children-and-take), and - [Results and errors](#results-and-errors) — these change behavior, not - just names. + [Weights and take](#weights-children-and-take), + [Results and errors](#results-and-errors), and + [Chain and runtime changes](#chain-and-runtime-changes) — these change + behavior, not just names. 4. Verify against the live catalogs rather than guessing: `sub.intents.list_tools()` and `sub.reads.list_reads()` in Python, `btcli tools` and `btcli query --help` on the CLI, or the generated @@ -321,6 +322,16 @@ mg.neuron(5), mg.by_hotkey("5F...") | `mg.save()` / `mg.load()` | gone — persist `mg.raw` yourself if needed | | `bt.Metagraph(netuid, network=...)` | always fetched through a client | +The metagraph record returned by the `metagraph` read (`client.read("metagraph", netuid=...)`, +`btcli query metagraph --netuid N --json`) carries `name` and `symbol` decoded to text; on +the wire they are compact-u16 vectors of utf-8 bytes, unlike every other text +field on the record. If you need the undecoded wire record, call the runtime +API directly: + +```python +await client.runtime(sub.runtime_api.SubnetInfoRuntimeApi.get_metagraph, [netuid]) +``` + ## Transactions Every old transaction method maps to an intent class executed with @@ -481,11 +492,18 @@ the legacy v10 header format (recipe in the guide). | `.extrinsic_fee` | `.fee` | | `.extrinsic_receipt` | `.block_hash`, `.extrinsic_id`, `.events`, `.explorer_url` | | `.data` (uid, reveal_round, ...) | `.data` (same idea) | -| `.error` (a Python exception) | `.error` — `ChainError` with `.name`, `.code` (`sub.ErrorCode`), `.remediation` | +| `.error` (a Python exception) | `.error` — `ChainError` with `.name`, `.code` (`sub.ErrorCode`), `.remediation`, plus `.description` / `.docs_url` when the name is classified | | `raise_error=True` kwarg | `result.raise_for_failure()` | | typed exceptions (`StakeError`, `NotRegisteredError`, `TxRateLimitExceeded`, ...) | branch on `result.error.code`: `ErrorCode.INSUFFICIENT_BALANCE`, `RATE_LIMITED`, `NOT_REGISTERED`, `SUBNET_NOT_EXISTS`, ... ([Errors](/docs/errors)) | | `BalanceTypeError` / `BalanceUnitMismatchError` | `TypeError` / `sub.UnitMismatchError` | +Prefer `.code` over matching `.name` or message strings. SubtensorModule renamed +`InsufficientBalance` → `InsufficientTaoBalance` (same error index) and added +`InsufficientAlphaBalance`; both still classify as +`ErrorCode.INSUFFICIENT_BALANCE`. Other pallets (Balances, Crowdloan, Swap) +may still emit `InsufficientBalance`. `btcli explain ` and +`result.error.description` give the per-name trigger text. + ```python result = await client.execute(intent, wallet) if not result.success: @@ -495,6 +513,105 @@ if not result.success: case _: log(result.error.remediation) ``` +## Chain and runtime changes + +The v11 package ships against a runtime that also changed behavior. These are +not rename mappings — scripts that still "work" can fail or mis-account after +the upgrade. Full narrative: +[The V431 Upgrade](/releases/v431-upgrade). + +### Ownership and emissions + +- **Subnet ownership.** For subnets at least one year old, ownership transfers + to the highest-conviction hotkey when total conviction reaches 10% of + `SubnetAlphaOut`. Existing conviction counts toward this threshold. See + [Conviction](/docs/guides/conviction). +- **Cross-subnet emissions.** Allocation uses EMA price adjusted by miner burn; + `root_proportion` is no longer included in the inter-subnet split (it still + applies inside each subnet for injection caps and root dividends). Update + emission calculations and forecasts. See [Emissions](/docs/concepts/emissions). + +### Proxies and coldkeys + +- **Deny-by-default allowlists.** Proxy types only permit listed call groups. + Audit every existing grant. Triumvirate / Senate / Governance / RootWeights + proxies deny all calls. A Staking proxy cannot wrap `Utility.batch_all` — + batched staking needs NonTransfer. See [Proxies](/docs/guides/proxies). +- **Coldkey-swap and dissolve scope.** NonTransfer, NonFungible, and + NonCritical proxies can no longer call `reset_coldkey_swap`, `swap_coldkey`, + or `schedule_swap_coldkey`. NonCritical can no longer call + `root_dissolve_network`. Owner proxies are limited to owner-settable subnet + configuration and identity calls (not owner-key rotation). +- **Reject locked alpha.** Coldkeys that should accept locked-alpha transfers + must opt in with `set_reject_locked_alpha` (raw call / `submit_call` — + no first-class intent yet). Default is to reject. + +### Staking and fees + +- **Default slippage protection.** `AddStake`, `RemoveStake`, and `SwapStake` + (and the matching CLI commands) apply a 5% price bound by default. Old + any-price scripts fail with `SlippageTooHigh` unless you pass + `slippage_protection=False` / `--no-slippage-protection`, or set an explicit + `rate_tolerance` / limit price. +- **Strict stake moves.** `move_stake` / `transfer_stake` / `swap_stake` no + longer silently cap the alpha amount to what remains after an alpha-paid + fee. Submitting the full stake balance can fail with + `NotEnoughStakeToWithdraw` — leave dust or query the post-fee balance first. +- **Limit-stake TAO refund.** When a price limit stops a stake-in swap before + the full TAO is consumed, the unswapped remainder is refunded to the coldkey + (it used to sit on the subnet pallet account). Prefer post-state balances + over summing `StakeAdded` amounts for partial fills — the event can still + report the pre-refund TAO, and `SubnetVolume` may overstate the same way. +- **Take changes pay fees.** `increase_take` and `decrease_take` are + `Pays::Yes`. Preview with `client.plan(...)` / `--dry-run`. +- **Hotkey-swap alpha fees.** `swap_hotkey` / `swap_hotkey_v2` can take the + transaction fee in alpha across the hotkey's stake subnets — same + "don't submit the entire balance" footgun as stake moves when paying in + alpha. +- **Stake move/transfer/swap extrinsics** also pay the ordinary transaction + fee (`Pays::Yes`). Same-subnet `move_stake` is not a pool swap, but it is + not fee-free. + +### Registration, hyperparameters, and randomness + +- **Queued subnet registration.** When capacity is full or cleanup is pending, + `register_network` may emit `NetworkRegistrationQueued` without creating the + subnet. Wait for `NetworkAdded` before treating the registration as live. +- **Tempo and activity cutoff.** Owner (and root) set these through + `AdminUtils::sudo_set_tempo` and `AdminUtils::sudo_set_activity_cutoff_factor`. + `SubtensorModule` call indices 139 (`set_tempo`) and 140 + (`set_activity_cutoff_factor`) remain as deprecated no-op compatibility entry + points: they charge a fee and return success without changing state. Use the + AdminUtils calls to update these values. The live inactivity window is + `activity_cutoff_factor` (per-mille of tempo); absolute `activity_cutoff` is + derived / legacy. Set via `sub.SetHyperparameter` / + `btcli sudo set --name activity_cutoff_factor|tempo`. +- **Hyperparams read API.** Prefer `get_subnet_hyperparams_v3` (name→value); + v1/v2 are deprecated. The SDK read already uses v3. +- **Drand submissions.** After the initial pulse, only `last_stored_round + 1` + is accepted. Missing rounds must be backfilled sequentially or commit-reveal + / timelock consumers stall. + +### Runtime API, metadata, and catalogs + +- **`get_proxy_filter` → `get_proxy_filters`.** Regenerate any client that + called the singular API. +- **Typed metadata.** TAO/Alpha balances, `NetUid`, and related fields use + distinct TypeInfo paths (SCALE bytes unchanged). Regenerate static bindings. +- **Error catalog shape.** `/catalog/errors.json` `chain_errors` values are + now `{code, description, docs_url}` objects, not bare code strings. Update + parsers that assumed a string. +- **Contract / EVM ABI.** Chain-extension output still collapses both + Subtensor insufficient-balance variants to the ABI `InsufficientBalance` + code — contracts cannot distinguish TAO vs Alpha from that code alone. + +### Dissolution (operators / indexers) + +- **Settlement cursor.** Multi-block dissolution stake settlement keeps the + cursor on the last *completed* hotkey and retries an unpaid one on the next + pass instead of skipping it. Cleanup progress that assumed "cursor always + advances" needs a re-check. + ## CLI migration One binary, still called `btcli`, now installed with `bittensor`. The @@ -566,7 +683,7 @@ with exit code 1** instead of hanging; missing required options exit 2. | `liquidity add/list/modify/remove` | **removed** (chain feature retired) | | `view dashboard` | **removed** | | `utils convert` / `latency` | same names | -| `deriv quote/positions/market/open/topup/close` | same names | +| `deriv quote/positions/market/open/topup/close` | **removed** — chain feature not available on current runtime | | any other chain read | `btcli query ` — `btcli query --help` lists all of them | | any other mutation | `btcli tx ` — `btcli tx --help`, or `btcli call Pallet.function --args '{...}'` for raw extrinsics | @@ -583,12 +700,17 @@ explains any error code. 2. Swap `bt.Subtensor` / `bt.AsyncSubtensor` for `sub.SyncClient` / `sub.Client`; move `block=`-pinned reads onto `client.at(block)`. 3. Rewrite reads using the tables above; anything unmapped goes through - `client.query(sub.storage...)`. + `client.query(sub.storage...)`. Prefer hyperparams v3 for custom RPC + clients. 4. Rewrite each transaction as an intent + `client.execute`; take values and child proportions keep working as 0–1 floats (plain integers mean the raw - u16/u64 wire values); replace safe-staking flags with `*Limit` intents. + u16/u64 wire values); replace safe-staking flags with `*Limit` intents or + the default 5% slippage protection (`--no-slippage-protection` to opt out). 5. Update result handling: `.success` still works, but branch on - `result.error.code` instead of exception types or message strings. + `result.error.code` instead of exception types or message strings. Treat + SubtensorModule `InsufficientTaoBalance` / + `InsufficientAlphaBalance` as the old `InsufficientBalance` (other pallets + may still emit that name). 6. Fix Balance usage: no `.tao` on alpha, no cross-unit arithmetic, `from_alpha` for subnet currency. 7. Delete `bt.config`/argparse plumbing, `bt.logging` calls (use standard @@ -601,5 +723,10 @@ explains any error code. that layer on your own HTTP stack with `sub.http_auth` for the hotkey signing ([Signed requests](/docs/guides/signed-requests)); only its chain calls move to this SDK's client. -10. Dry-run everything: `client.plan(...)` in Python, `--dry-run` on the +10. Apply the [chain and runtime changes](#chain-and-runtime-changes): audit + proxies; retarget owner tempo / `activity_cutoff_factor` calls to + AdminUtils; never submit a full alpha balance when fees are alpha-paid; + wait for `NetworkAdded` on queued registrations; regenerate metadata + bindings and any `errors.json` / `get_proxy_filter` consumers. +11. Dry-run everything: `client.plan(...)` in Python, `--dry-run` on the CLI, ideally against `test` or a local node first. diff --git a/docs/query/balances.mdx b/docs/query/balances.mdx index e375b81279..a8b36f57d5 100644 --- a/docs/query/balances.mdx +++ b/docs/query/balances.mdx @@ -18,7 +18,7 @@ description: "Free TAO balance for several coldkey addresses in one batched requ ## CLI ```bash -btcli query balances --coldkey-ss58s --json +btcli query balances --coldkeys --json ``` ## Python diff --git a/docs/query/hotkey-identities.mdx b/docs/query/hotkey-identities.mdx index 555d2d6c22..a63c4a4972 100644 --- a/docs/query/hotkey-identities.mdx +++ b/docs/query/hotkey-identities.mdx @@ -18,7 +18,7 @@ description: "On-chain identities for hotkeys (via each hotkey's owner coldkey), ## CLI ```bash -btcli query hotkey-identities --hotkey-ss58s --json +btcli query hotkey-identities --hotkeys --json ``` ## Python diff --git a/docs/query/index.mdx b/docs/query/index.mdx index 5031716dfa..1f8f0a82b8 100644 --- a/docs/query/index.mdx +++ b/docs/query/index.mdx @@ -137,7 +137,7 @@ Reads are free, unsigned, and safe to call at any time. Each CLI command is `btc | [`mechanism-emission-split`](/docs/query/mechanism-emission-split) | Emission split between mechanisms on a subnet. | | [`metagraph`](/docs/query/metagraph) | The full metagraph for a subnet in one call (stakes, ranks, emissions, axons, ...). | | [`subnet`](/docs/query/subnet) | Tempo, burn, and neuron count for one subnet (the three reads run concurrently). | -| [`subnet-hyperparameters`](/docs/query/subnet-hyperparameters) | All hyperparameters for a subnet (named fields; version-dependent set). | +| [`subnet-hyperparameters`](/docs/query/subnet-hyperparameters) | All hyperparameters for a subnet, as a flat name -> raw value mapping. | | [`subnet-identity`](/docs/query/subnet-identity) | The identity metadata of a subnet, or None. | | [`subnet-names`](/docs/query/subnet-names) | Registered name of every subnet that published an identity, keyed by netuid. | | [`subnet-registration-cost`](/docs/query/subnet-registration-cost) | Current cost to register a new subnet. | diff --git a/docs/query/metagraph.mdx b/docs/query/metagraph.mdx index 31efa10c70..7a5f27796b 100644 --- a/docs/query/metagraph.mdx +++ b/docs/query/metagraph.mdx @@ -5,7 +5,8 @@ description: "The full metagraph for a subnet in one call (stakes, ranks, emissi {/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} - +`name` and `symbol` are decoded to text (the wire carries them as +compact-u16 vectors of utf-8 bytes, unlike every other text field). **Category:** Subnets diff --git a/docs/query/neurons.mdx b/docs/query/neurons.mdx index 7644c4c6e2..8ec9193641 100644 --- a/docs/query/neurons.mdx +++ b/docs/query/neurons.mdx @@ -20,7 +20,7 @@ smaller and what almost every caller wants. ## CLI ```bash -btcli query neurons --netuid --lite --json +btcli query neurons --netuid --lite/--no-lite --json ``` ## Python diff --git a/docs/query/stake-for-coldkeys.mdx b/docs/query/stake-for-coldkeys.mdx index 517d7349ac..8ea3eff38f 100644 --- a/docs/query/stake-for-coldkeys.mdx +++ b/docs/query/stake-for-coldkeys.mdx @@ -19,7 +19,7 @@ alpha, or TAO when netuid is 0. ## CLI ```bash -btcli query stake-for-coldkeys --coldkey-ss58s --json +btcli query stake-for-coldkeys --coldkeys --json ``` ## Python diff --git a/docs/query/stake-value-for-coldkeys.mdx b/docs/query/stake-value-for-coldkeys.mdx index b0add0322b..c1a5acab27 100644 --- a/docs/query/stake-value-for-coldkeys.mdx +++ b/docs/query/stake-value-for-coldkeys.mdx @@ -18,7 +18,7 @@ description: "Spot-price stake valuation for several coldkeys at once, at one bl ## CLI ```bash -btcli query stake-value-for-coldkeys --coldkey-ss58s --json +btcli query stake-value-for-coldkeys --coldkeys --json ``` ## Python diff --git a/docs/query/subnet-hyperparameters.mdx b/docs/query/subnet-hyperparameters.mdx index 6721f51bce..d6b05ddd9d 100644 --- a/docs/query/subnet-hyperparameters.mdx +++ b/docs/query/subnet-hyperparameters.mdx @@ -1,11 +1,14 @@ --- title: "subnet-hyperparameters" -description: "All hyperparameters for a subnet (named fields; version-dependent set)." +description: "All hyperparameters for a subnet, as a flat name -> raw value mapping." --- {/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} - +The set of names is version-dependent; None if the subnet does not exist. +Uses the forward-compatible `get_subnet_hyperparams_v3` runtime API, so +newly added chain hyperparameters (e.g. `burn_half_life`) show up +without a client update. **Category:** Subnets diff --git a/docs/tx/index.mdx b/docs/tx/index.mdx index f7bb0df45f..8626be3723 100644 --- a/docs/tx/index.mdx +++ b/docs/tx/index.mdx @@ -13,7 +13,6 @@ Every mutation is an **intent**: preview it with `plan` / `--dry-run`, submit it | --- | --- | --- | | [`set-hyperparameter`](/docs/tx/set-hyperparameter) | coldkey | Set an owner-settable subnet hyperparameter (btcli `sudo set`). | | [`set-mechanism-count`](/docs/tx/set-mechanism-count) | coldkey | Set the number of mechanisms on a subnet. | -| [`set-mechanism-emission-split`](/docs/tx/set-mechanism-emission-split) | coldkey | Set emission split between mechanisms on a subnet. | | [`trim-subnet`](/docs/tx/trim-subnet) | coldkey | Trim a subnet to at most `max_n` UIDs (subnet owner). | ## Balances diff --git a/docs/tx/meta.json b/docs/tx/meta.json index 002263f53e..bbaba6824e 100644 --- a/docs/tx/meta.json +++ b/docs/tx/meta.json @@ -5,7 +5,6 @@ "---AdminUtils---", "set-hyperparameter", "set-mechanism-count", - "set-mechanism-emission-split", "trim-subnet", "---Balances---", "fund-evm-key", diff --git a/docs/tx/set-hyperparameter.mdx b/docs/tx/set-hyperparameter.mdx index c5eb742fe2..34daeaab4d 100644 --- a/docs/tx/set-hyperparameter.mdx +++ b/docs/tx/set-hyperparameter.mdx @@ -6,10 +6,12 @@ description: "Set an owner-settable subnet hyperparameter (btcli `sudo set`)." {/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} Dispatches the matching AdminUtils `sudo_set_*` call for the named -parameter. The signer must be the subnet's owner coldkey; root-only -parameters are not available here and must go through the raw-call escape -hatch. Changes take effect on chain immediately and shape subnet economics -and consensus (registration costs, weight rules, immunity, transfers), so +parameter (`alpha_low`/`alpha_high` read the current pair from chain and +set both via `sudo_set_alpha_values`). The signer must be the subnet's +owner coldkey; root-only parameters are not available here and must go +through the raw-call escape hatch. Changes take effect on chain immediately +and shape subnet economics and consensus (registration costs, weight +rules, immunity, transfers), so verify the raw value before sending — `value` accepts either the raw on-chain integer or a human form that is converted for you. Some parameters are rate-limited by the chain, so a quick follow-up change can @@ -17,15 +19,15 @@ fail. Read current values back with the `subnet_hyperparameters` read. | Signer | Pallet | Wraps | | --- | --- | --- | -| `coldkey` | AdminUtils | `AdminUtils.sudo_set_immunity_period`, `AdminUtils.sudo_set_min_allowed_weights`, `AdminUtils.sudo_set_weights_version_key`, `AdminUtils.sudo_set_activity_cutoff`, `AdminUtils.sudo_set_min_burn`, `AdminUtils.sudo_set_bonds_moving_average`, `AdminUtils.sudo_set_serving_rate_limit`, `AdminUtils.sudo_set_commit_reveal_weights_interval`, `AdminUtils.sudo_set_max_allowed_uids`, `AdminUtils.sudo_set_burn_increase_mult`, `AdminUtils.sudo_set_burn_half_life`, `AdminUtils.sudo_set_commit_reveal_weights_enabled`, `AdminUtils.sudo_set_liquid_alpha_enabled`, `AdminUtils.sudo_set_network_pow_registration_allowed`, `AdminUtils.sudo_set_yuma3_enabled`, `AdminUtils.sudo_set_bonds_reset_enabled`, `AdminUtils.sudo_set_toggle_transfer`, `AdminUtils.sudo_set_owner_cut_enabled`, `AdminUtils.sudo_set_owner_cut_auto_lock_enabled` | +| `coldkey` | AdminUtils | `AdminUtils.sudo_set_tempo`, `AdminUtils.sudo_set_immunity_period`, `AdminUtils.sudo_set_min_allowed_weights`, `AdminUtils.sudo_set_weights_version_key`, `AdminUtils.sudo_set_activity_cutoff_factor`, `AdminUtils.sudo_set_min_burn`, `AdminUtils.sudo_set_max_burn`, `AdminUtils.sudo_set_bonds_moving_average`, `AdminUtils.sudo_set_bonds_penalty`, `AdminUtils.sudo_set_serving_rate_limit`, `AdminUtils.sudo_set_commit_reveal_weights_interval`, `AdminUtils.sudo_set_max_allowed_uids`, `AdminUtils.sudo_set_burn_increase_mult`, `AdminUtils.sudo_set_burn_half_life`, `AdminUtils.sudo_set_adjustment_alpha`, `AdminUtils.sudo_set_rho`, `AdminUtils.sudo_set_max_difficulty`, `AdminUtils.sudo_set_alpha_sigmoid_steepness`, `AdminUtils.sudo_set_min_childkey_take_per_subnet`, `AdminUtils.sudo_set_owner_immune_neuron_limit`, `AdminUtils.sudo_set_alpha_values`, `AdminUtils.sudo_set_commit_reveal_weights_enabled`, `AdminUtils.sudo_set_liquid_alpha_enabled`, `AdminUtils.sudo_set_network_pow_registration_allowed`, `AdminUtils.sudo_set_yuma3_enabled`, `AdminUtils.sudo_set_bonds_reset_enabled`, `AdminUtils.sudo_set_toggle_transfer`, `AdminUtils.sudo_set_owner_cut_enabled`, `AdminUtils.sudo_set_owner_cut_auto_lock_enabled` | ## Parameters | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `netuid` | integer | yes | Subnet to configure; the signer must be its owner. | -| `name` | string | yes | Hyperparameter to set. One of: activity_cutoff, bonds_moving_avg, bonds_reset_enabled, burn_half_life, burn_increase_mult, commit_reveal_period, commit_reveal_weights_enabled, immunity_period, liquid_alpha_enabled, max_allowed_uids, min_allowed_weights, min_burn, network_pow_registration_allowed, owner_cut_auto_lock_enabled, owner_cut_enabled, serving_rate_limit, transfers_enabled, weights_version, yuma3_enabled. | -| `value` | integer \| number \| string | yes | New value. Give the raw on-chain integer, or the human form as a float or a string with a decimal point (a 0..1 fraction for normalized parameters, a TAO amount for rao parameters). Boolean parameters take true/false or 0/1. | +| `name` | string | yes | Hyperparameter to set. One of: activity_cutoff_factor, adjustment_alpha, alpha_high, alpha_low, alpha_sigmoid_steepness, bonds_moving_avg, bonds_penalty, bonds_reset_enabled, burn_half_life, burn_increase_mult, commit_reveal_period, commit_reveal_weights_enabled, immunity_period, liquid_alpha_enabled, max_allowed_uids, max_burn, max_difficulty, min_allowed_weights, min_burn, min_childkey_take, network_pow_registration_allowed, owner_cut_auto_lock_enabled, owner_cut_enabled, owner_immune_neuron_limit, rho, serving_rate_limit, tempo, transfers_enabled, weights_version, yuma3_enabled. | +| `value` | integer \| number \| string | yes | New value. Give the raw on-chain integer (within the parameter's codec bounds), or the human form as a float or a string with a decimal point (a 0..1 fraction for normalized parameters, a non-negative TAO amount for rao parameters). Boolean parameters take true/false or 0/1 only. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. diff --git a/docs/tx/set-mechanism-count.mdx b/docs/tx/set-mechanism-count.mdx index b0fedbda09..f3915b14b0 100644 --- a/docs/tx/set-mechanism-count.mdx +++ b/docs/tx/set-mechanism-count.mdx @@ -11,9 +11,8 @@ the subnet runs. The count must be greater than zero, and the chain caps how many mechanisms a subnet may have. Increasing the count opens new mechanisms; decreasing it removes the highest-numbered ones and the miner state in them. Any change to the count clears the emission split -back to an even division — reapply `set_mechanism_emission_split` -afterwards if you want an uneven split. Rate-limited and blocked during -the end-of-epoch admin freeze window. +back to an even division. Rate-limited and blocked during the +end-of-epoch admin freeze window. | Signer | Pallet | Wraps | | --- | --- | --- | diff --git a/docs/tx/set-mechanism-emission-split.mdx b/docs/tx/set-mechanism-emission-split.mdx deleted file mode 100644 index a8b4bd9ca5..0000000000 --- a/docs/tx/set-mechanism-emission-split.mdx +++ /dev/null @@ -1,65 +0,0 @@ ---- -title: "set-mechanism-emission-split" -description: "Set emission split between mechanisms on a subnet." ---- - -{/* GENERATED by scripts/generate.py from the SDK registries — do not edit. */} - -Owner-only: divides the subnet's emission between its mechanisms, one -entry per mechanism in order. Entries with a decimal point are relative -weights (e.g. `[0.5, 0.5]` or `[3.0, 1.0]`), normalized to the exact -u16 split the chain requires; plain integers are raw u16 weights and must -sum to exactly 65,535 themselves. The list may be at most as long as the -subnet's current mechanism count (see `set_mechanism_count`) — a -shorter list leaves the trailing mechanisms with zero. Changing the -split reallocates future emission only — nothing already emitted moves. - -| Signer | Pallet | Wraps | -| --- | --- | --- | -| `coldkey` | AdminUtils | `AdminUtils.sudo_set_mechanism_emission_split` | - -## Parameters - -| Parameter | Type | Required | Description | -| --- | --- | --- | --- | -| `netuid` | integer | yes | Subnet to configure; the signer must be its owner. | -| `split` | array | yes | Emission weights in mechanism order, at most one entry per mechanism: relative weights with a decimal point (normalized for you, e.g. [0.5, 0.5]), or raw u16 integers summing to exactly 65,535. | - -Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 -address, an address-book or proxy-book name, or a local wallet/hotkey name. - -## CLI - -Preview with `--dry-run` (shows fee, effects, and policy result without -submitting), then submit: - -```bash -btcli tx set-mechanism-emission-split \ - --netuid \ - --split --dry-run -btcli tx set-mechanism-emission-split \ - --netuid \ - --split -w my_coldkey -``` - -## Python - -```python -import bittensor as sub -from bittensor.wallet import Wallet - -wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") -intent = sub.SetMechanismEmissionSplit(netuid=1, split=[...]) - -async with sub.Client("finney") as client: - plan = await client.plan(intent, wallet) # fee, effects, policy — no submission - result = await client.execute(intent, wallet) - if not result.success: - print(result.error.code, result.error.remediation) -``` - -Or build it by op name, as an agent would: - -```python -await client.execute_tool("set_mechanism_emission_split", {...}, wallet) -``` diff --git a/docs/tx/set-perpetual-lock.mdx b/docs/tx/set-perpetual-lock.mdx index a892846b9d..35c300464f 100644 --- a/docs/tx/set-perpetual-lock.mdx +++ b/docs/tx/set-perpetual-lock.mdx @@ -35,10 +35,10 @@ submitting), then submit: ```bash btcli tx set-perpetual-lock \ --netuid \ - --enabled --dry-run + --enabled/--no-enabled --dry-run btcli tx set-perpetual-lock \ --netuid \ - --enabled -w my_coldkey + --enabled/--no-enabled -w my_coldkey ``` ## Python diff --git a/eco-tests/src/mock.rs b/eco-tests/src/mock.rs index d61a5d7287..66ab4b9f9c 100644 --- a/eco-tests/src/mock.rs +++ b/eco-tests/src/mock.rs @@ -240,7 +240,7 @@ parameter_types! { pub const InitialColdkeySwapAnnouncementDelay: u64 = 50; pub const InitialColdkeySwapReannouncementDelay: u64 = 10; pub const InitialDissolveNetworkScheduleDuration: u64 = 5 * 24 * 60 * 60 / 12; // Default as 5 days - pub const InitialTaoWeight: u64 = 0; // 100% global weight. + pub const InitialTaoWeight: u64 = 0; // 0% TAO weight. pub const InitialEmaPriceHalvingPeriod: u64 = 201_600_u64; // 4 weeks pub const InitialStartCallDelay: u64 = 0; // 0 days pub const InitialKeySwapOnSubnetCost: u64 = 10_000_000; diff --git a/node/Cargo.toml b/node/Cargo.toml index 333fc4a093..da20b81641 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -132,6 +132,19 @@ pallet-subtensor-swap-rpc = { workspace = true, features = ["std"] } pallet-subtensor-swap-runtime-api = { workspace = true, features = ["std"] } subtensor-macros.workspace = true +# Optional global allocator (off by default). Archive nodes serving sustained RPC load show +# unbounded anonymous-memory growth (~3-4 GB/h) that fits the glibc-malloc arena-fragmentation +# profile across the node's hundreds of threads; linking jemalloc as the global allocator (as the +# polkadot binary does) is the proposed mitigation (issue #2724). Gated behind a feature so the +# default release binary is unchanged until an operator opts in. +tikv-jemallocator = { version = "0.5", optional = true, default-features = false } +# Companion to `tikv-jemallocator`, used by the `jemalloc-allocator` feature's +# activation test to read allocator stats over `mallctl`. It already resolves +# transitively and shares the single compiled `tikv-jemalloc-sys` with +# `tikv-jemallocator` (see Cargo.lock), so enabling it adds no second copy of +# jemalloc - only this small ctl FFI shim. +tikv-jemalloc-ctl = { version = "0.5", optional = true } + [build-dependencies] substrate-build-script-utils.workspace = true @@ -145,6 +158,9 @@ rocksdb = [ "sc-cli/rocksdb", ] default = ["rocksdb", "sql", "txpool"] +# Use tikv-jemallocator as the global allocator in the release binary (off by default). +# See the `tikv-jemallocator` dependency note and issue #2724. +jemalloc-allocator = ["dep:tikv-jemallocator", "dep:tikv-jemalloc-ctl"] fast-runtime = [ "node-subtensor-runtime/fast-runtime", "subtensor-runtime-common/fast-runtime", diff --git a/node/src/main.rs b/node/src/main.rs index a6aa15038f..cd23935b24 100644 --- a/node/src/main.rs +++ b/node/src/main.rs @@ -1,6 +1,14 @@ //! Substrate Node Subtensor CLI library. #![warn(missing_docs)] +// Use jemalloc as the global allocator when the `jemalloc-allocator` feature is enabled (off by +// default). Archive nodes serving sustained RPC load exhibit unbounded anonymous-memory growth that +// matches the glibc-malloc arena-fragmentation profile across the node's hundreds of threads; +// jemalloc (the allocator the polkadot binary ships with) avoids it. See issue #2724. +#[cfg(all(unix, feature = "jemalloc-allocator"))] +#[global_allocator] +static GLOBAL: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; + #[cfg(feature = "runtime-benchmarks")] mod benchmarking; mod chain_spec; @@ -18,3 +26,71 @@ mod service; fn main() -> sc_cli::Result<()> { command::run() } + +// Regression guard for the optional `jemalloc-allocator` feature. +// +// The feature is off by default, so the default build never compiles it, and a +// `tikv-jemallocator` bump, a `#[global_allocator]` typo, or a `cfg` mistake can +// leave the binary *compiling* while jemalloc is no longer the active global +// allocator (glibc silently stays in charge). A compile check cannot catch that: +// only bytes routed *through* jemalloc are observable, so this test drives the +// global allocator with a large live allocation and asserts jemalloc tracked it. +// If the `#[global_allocator]`/`cfg` wiring is broken the Vec goes to glibc and +// the delta stays at zero - well below the threshold - and the test fails. +// +// The delta is read from jemalloc's *thread-local* "bytes ever allocated by this +// thread" counter, not the process-global `stats.allocated`. `cargo test` runs the +// node's tests in parallel inside one process, and the global stat is perturbed by +// every other test's allocations and frees, so a before/after delta around it is +// racy: a concurrent free can drop it below threshold (false failure) and a +// concurrent allocation can lift it (false pass). The thread-local counter only +// ever counts bytes the *calling* thread routes through the global allocator, so +// it is immune to concurrent tests; it is also monotonic (a free cannot decrease +// it), so the only way it stays flat across a large live allocation is if that +// allocation went to glibc because jemalloc is linked but not active. +#[cfg(all(test, unix, feature = "jemalloc-allocator"))] +mod jemalloc_allocator_feature { + use tikv_jemalloc_ctl::thread; + + // A live allocation routed through the *global* allocator. Large enough to + // dwarf any bookkeeping the stat reads do themselves. + const PAYLOAD: usize = 16 * 1024 * 1024; + + // The workspace denies `expect_used`/`unwrap_used`, and `tikv_jemalloc_ctl`'s + // Error is a transparent wrapper that does not implement std::error::Error, + // so the Result cannot be propagated with `?` either. Resolve the stat handle + // by hand instead. + fn allocated_pointer() -> thread::ThreadLocal { + match thread::allocatedp::read() { + Ok(pointer) => pointer, + Err(error) => panic!("read thread.allocatedp: {error:?}"), + } + } + + #[test] + fn jemalloc_is_the_active_global_allocator() { + // `read()` returns a thread-local pointer to the cumulative byte count + // for *this* thread; `get()` dereferences it (safe: the raw-pointer read + // is encapsulated in `tikv_jemalloc_ctl`). Thread stats are live, so - + // unlike `stats::allocated` - no epoch refresh is needed before a read. + let pointer = allocated_pointer(); + let before = pointer.get(); + + // Held across the second read so the allocator cannot reclaim it first. + let hold = vec![0u8; PAYLOAD]; + + let after = pointer.get(); + + assert!( + after.saturating_sub(before) >= PAYLOAD as u64, + "jemalloc did not track a {}-byte global-allocator allocation \ + (before={}, after={}): it is linked but not the active global \ + allocator, so the jemalloc-allocator feature wiring is broken", + PAYLOAD, + before, + after, + ); + + drop(hold); + } +} diff --git a/pallets/admin-utils/src/benchmarking.rs b/pallets/admin-utils/src/benchmarking.rs index f71c59a90d..3c4db84727 100644 --- a/pallets/admin-utils/src/benchmarking.rs +++ b/pallets/admin-utils/src/benchmarking.rs @@ -218,6 +218,19 @@ mod benchmarks { _(RawOrigin::Root, 1u16.into()/*netuid*/, 361u16/*activity_cutoff*/)/*sudo_set_activity_cutoff*/; } + #[benchmark] + fn sudo_set_activity_cutoff_factor() { + // disable admin freeze window + pallet_subtensor::Pallet::::set_admin_freeze_window(0); + pallet_subtensor::Pallet::::init_new_network( + 1u16.into(), /*netuid*/ + 1u16, /*tempo*/ + ); + + #[extrinsic_call] + _(RawOrigin::Root, 1u16.into()/*netuid*/, 5_000u32/*factor_milli*/)/*sudo_set_activity_cutoff_factor*/; + } + #[benchmark] fn sudo_set_rho() { // disable admin freeze window @@ -348,28 +361,23 @@ mod benchmarks { _(RawOrigin::Root, 1u16.into()/*netuid*/, true/*registration_allowed*/)/*sudo_set_network_registration_allowed*/; } - /* - benchmark_sudo_set_tempo { - let netuid = NetUid::from(1); - let tempo_default: u16 = 1; <------- unused? - let tempo: u16 = 15; - let modality: u16 = 0; - - pallet_subtensor::Pallet::::init_new_network(netuid, tempo); - - }: sudo_set_tempo(RawOrigin::>::Root, netuid, tempo) - */ #[benchmark] fn sudo_set_tempo() { - // disable admin freeze window + let netuid = NetUid::from(1); + let owner: T::AccountId = account("owner", 0, 0); + + // Benchmark the heavier owner path (bounds check + rate-limit read/write), + // not the root path which bypasses both. pallet_subtensor::Pallet::::set_admin_freeze_window(0); - pallet_subtensor::Pallet::::init_new_network( - 1u16.into(), /*netuid*/ - 1u16, /*tempo*/ - ); + pallet_subtensor::Pallet::::init_new_network(netuid, pallet_subtensor::MIN_TEMPO); + pallet_subtensor::SubnetOwner::::insert(netuid, &owner); #[extrinsic_call] - _(RawOrigin::Root, 1u16.into()/*netuid*/, 1u16/*tempo*/)/*sudo_set_tempo*/; + _( + RawOrigin::Signed(owner), + netuid, + pallet_subtensor::MIN_TEMPO + 1, + ); } #[benchmark] diff --git a/pallets/admin-utils/src/lib.rs b/pallets/admin-utils/src/lib.rs index 3238250130..f4cb6c0534 100644 --- a/pallets/admin-utils/src/lib.rs +++ b/pallets/admin-utils/src/lib.rs @@ -650,6 +650,31 @@ pub mod pallet { Ok(()) } + /// The extrinsic sets the activity-cutoff factor for a subnet, in per-mille + /// (1/1000) of the tempo: the effective cutoff in blocks is + /// `(factor × tempo) / 1000`. Bounded to `[MinActivityCutoffFactorMilli, + /// MaxActivityCutoffFactorMilli]`. It is callable by the subnet owner + /// (rate-limited via `OwnerHyperparamUpdate`, respects the admin freeze + /// window) or the root account (bypasses both). This supersedes the + /// absolute-blocks `sudo_set_activity_cutoff`. + #[pallet::call_index(97)] + #[pallet::weight(::WeightInfo::sudo_set_activity_cutoff_factor())] + pub fn sudo_set_activity_cutoff_factor( + origin: OriginFor, + netuid: NetUid, + factor_milli: u32, + ) -> DispatchResult { + pallet_subtensor::Pallet::::do_set_activity_cutoff_factor( + origin, + netuid, + factor_milli, + )?; + log::debug!( + "ActivityCutoffFactorMilliSet( netuid: {netuid:?} factor_milli: {factor_milli:?} ) " + ); + Ok(()) + } + /// The extrinsic sets the network registration allowed for a subnet. /// It is only callable by the root account or subnet owner. /// The extrinsic will call the Subtensor pallet to set the network registration allowed. @@ -975,18 +1000,23 @@ pub mod pallet { } /// The extrinsic sets the tempo for a subnet. - /// It is only callable by the root account. - /// The extrinsic will call the Subtensor pallet to set the tempo. + /// It is callable by the subnet owner (bounded to `[MinTempo, MaxTempo]` and + /// rate-limited to one change per `MinTempo` blocks) or the root account (any + /// u16, no rate limit). Both respect the admin freeze window. A successful + /// change resets the epoch cycle (`LastEpochBlock = current_block`). #[pallet::call_index(30)] - #[pallet::weight(::WeightInfo::sudo_set_tempo())] + #[pallet::weight( + ::WeightInfo::sudo_set_tempo().max( + // Conservative floor from the original owner-call benchmark, + // plus the additional subnet-existence read performed by this call. + // Remove once weights are regenerated with the owner-path benchmark. + Weight::from_parts(44_877_000, 4_498) + .saturating_add(::DbWeight::get().reads(7_u64)) + .saturating_add(::DbWeight::get().writes(3_u64)) + ) + )] pub fn sudo_set_tempo(origin: OriginFor, netuid: NetUid, tempo: u16) -> DispatchResult { - ensure_root(origin)?; - pallet_subtensor::Pallet::::ensure_admin_window_open(netuid)?; - ensure!( - pallet_subtensor::Pallet::::if_subnet_exist(netuid), - Error::::SubnetDoesNotExist - ); - pallet_subtensor::Pallet::::apply_tempo_with_cycle_reset(netuid, tempo); + pallet_subtensor::Pallet::::do_set_tempo(origin, netuid, tempo)?; log::debug!("TempoSet( netuid: {netuid:?} tempo: {tempo:?} ) "); Ok(()) } diff --git a/pallets/admin-utils/src/tests/mod.rs b/pallets/admin-utils/src/tests/mod.rs index 4fc2532b16..a0bf21ed04 100644 --- a/pallets/admin-utils/src/tests/mod.rs +++ b/pallets/admin-utils/src/tests/mod.rs @@ -685,6 +685,138 @@ fn test_sudo_set_activity_cutoff() { }); } +#[test] +fn test_sudo_set_activity_cutoff_factor() { + new_test_ext().execute_with(|| { + let netuid = NetUid::from(1); + add_network(netuid, 10); + let owner = U256::from(5); + SubnetOwner::::insert(netuid, owner); + SubtensorModule::set_admin_freeze_window(0); + + // A non-owner signed origin is rejected. + assert_eq!( + AdminUtils::sudo_set_activity_cutoff_factor( + <::RuntimeOrigin>::signed(U256::from(1)), + netuid, + 5_000 + ), + Err(DispatchError::BadOrigin) + ); + + // Out-of-bounds factors are rejected for owner and root alike. + assert_noop!( + AdminUtils::sudo_set_activity_cutoff_factor( + <::RuntimeOrigin>::root(), + netuid, + MAX_ACTIVITY_CUTOFF_FACTOR_MILLI + 1 + ), + SubtensorError::::ActivityCutoffFactorMilliOutOfBounds + ); + + // The owner can set a factor within bounds. + assert_ok!(AdminUtils::sudo_set_activity_cutoff_factor( + <::RuntimeOrigin>::signed(owner), + netuid, + 5_000 + )); + assert_eq!(ActivityCutoffFactorMilli::::get(netuid), 5_000); + + // A second owner change within the rate limit is rejected; root bypasses it. + assert_noop!( + AdminUtils::sudo_set_activity_cutoff_factor( + <::RuntimeOrigin>::signed(owner), + netuid, + 6_000 + ), + SubtensorError::::TxRateLimitExceeded + ); + assert_ok!(AdminUtils::sudo_set_activity_cutoff_factor( + <::RuntimeOrigin>::root(), + netuid, + 6_000 + )); + assert_eq!(ActivityCutoffFactorMilli::::get(netuid), 6_000); + }); +} + +#[test] +fn test_sudo_set_tempo_owner_and_root() { + new_test_ext().execute_with(|| { + let netuid = NetUid::from(1); + add_network(netuid, 10); + let owner = U256::from(5); + SubnetOwner::::insert(netuid, owner); + SubtensorModule::set_admin_freeze_window(0); + + // A non-owner signed origin is rejected. + assert_eq!( + AdminUtils::sudo_set_tempo( + <::RuntimeOrigin>::signed(U256::from(1)), + netuid, + MIN_TEMPO + ), + Err(DispatchError::BadOrigin) + ); + + // A nonexistent subnet is rejected for root. + assert_noop!( + AdminUtils::sudo_set_tempo( + <::RuntimeOrigin>::root(), + netuid.next(), + MIN_TEMPO + ), + SubtensorError::::SubnetNotExists + ); + + // The owner is bounded to [MIN_TEMPO, MAX_TEMPO]. + assert_noop!( + AdminUtils::sudo_set_tempo( + <::RuntimeOrigin>::signed(owner), + netuid, + MIN_TEMPO - 1 + ), + SubtensorError::::TempoOutOfBounds + ); + assert_noop!( + AdminUtils::sudo_set_tempo( + <::RuntimeOrigin>::signed(owner), + netuid, + MAX_TEMPO + 1 + ), + SubtensorError::::TempoOutOfBounds + ); + + // Within bounds the owner change lands and resets the cycle. + assert_ok!(AdminUtils::sudo_set_tempo( + <::RuntimeOrigin>::signed(owner), + netuid, + MIN_TEMPO + )); + assert_eq!(Tempo::::get(netuid), MIN_TEMPO); + let now = SubtensorModule::get_current_block_as_u64(); + assert_eq!(LastEpochBlock::::get(netuid), now); + + // A second owner change within the MIN_TEMPO cooldown is rate-limited. + assert_noop!( + AdminUtils::sudo_set_tempo( + <::RuntimeOrigin>::signed(owner), + netuid, + MIN_TEMPO + 1 + ), + SubtensorError::::TxRateLimitExceeded + ); + + // Root bypasses the bounds and the rate limit. + assert_ok!(AdminUtils::sudo_set_tempo( + <::RuntimeOrigin>::root(), + netuid, + 10 + )); + assert_eq!(Tempo::::get(netuid), 10); + }); +} + #[test] fn test_sudo_set_target_registrations_per_interval() { new_test_ext().execute_with(|| { diff --git a/pallets/admin-utils/src/weights.rs b/pallets/admin-utils/src/weights.rs index bb29c2efb2..7bb37a0f6b 100644 --- a/pallets/admin-utils/src/weights.rs +++ b/pallets/admin-utils/src/weights.rs @@ -51,6 +51,7 @@ pub trait WeightInfo { fn sudo_set_adjustment_interval() -> Weight; fn sudo_set_target_registrations_per_interval() -> Weight; fn sudo_set_activity_cutoff() -> Weight; + fn sudo_set_activity_cutoff_factor() -> Weight; fn sudo_set_rho() -> Weight; fn sudo_set_kappa() -> Weight; fn sudo_set_min_allowed_uids() -> Weight; @@ -374,6 +375,31 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().reads(6_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: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 sudo_set_activity_cutoff_factor() -> Weight { + // Proof Size summary in bytes: + // Measured: `899` + // Estimated: `4364` + // Minimum execution time: 36_214_000 picoseconds. + Weight::from_parts(37_446_000, 4364) + .saturating_add(T::DbWeight::get().reads(7_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) @@ -1340,6 +1366,31 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(6_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: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 sudo_set_activity_cutoff_factor() -> Weight { + // Proof Size summary in bytes: + // Measured: `899` + // Estimated: `4364` + // Minimum execution time: 36_214_000 picoseconds. + Weight::from_parts(37_446_000, 4364) + .saturating_add(RocksDbWeight::get().reads(7_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) diff --git a/pallets/subtensor/src/benchmarks.rs b/pallets/subtensor/src/benchmarks.rs index 87a9b579bd..ad73946035 100644 --- a/pallets/subtensor/src/benchmarks.rs +++ b/pallets/subtensor/src/benchmarks.rs @@ -2302,39 +2302,6 @@ mod pallet_benchmarks { ); } - #[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); diff --git a/pallets/subtensor/src/coinbase/root.rs b/pallets/subtensor/src/coinbase/root.rs index 18c7cca674..7aeb0430b2 100644 --- a/pallets/subtensor/src/coinbase/root.rs +++ b/pallets/subtensor/src/coinbase/root.rs @@ -42,15 +42,15 @@ impl Pallet { Self::get_max_allowed_uids(NetUid::ROOT) } - /// Checks for any UIDs in the given list that are either equal to the root netuid or exceed the total number of subnets. + /// Checks whether any netuid in the given list does not exist. /// - /// It's important to check for invalid UIDs to ensure data integrity and avoid referencing nonexistent subnets. + /// It's important to check for invalid netuids to ensure data integrity and avoid referencing nonexistent subnets. /// /// # Arguments - /// * `uids`: A reference to a vector of UIDs to check. + /// * `netuids`: A reference to a slice of netuids to check. /// /// # Returns - /// * `bool`: 'true' if any of the UIDs are invalid, 'false' otherwise. + /// * `bool`: 'true' if any of the netuids are invalid, 'false' otherwise. /// pub fn contains_invalid_root_uids(netuids: &[NetUid]) -> bool { for netuid in netuids { diff --git a/pallets/subtensor/src/coinbase/tao.rs b/pallets/subtensor/src/coinbase/tao.rs index 384ac56613..d9c7d43590 100644 --- a/pallets/subtensor/src/coinbase/tao.rs +++ b/pallets/subtensor/src/coinbase/tao.rs @@ -65,7 +65,10 @@ impl Pallet { ) -> DispatchResult { // Get full balance including ED let max_transferrable = Self::get_coldkey_balance(origin_coldkey); - ensure!(amount <= max_transferrable, Error::::InsufficientBalance); + ensure!( + amount <= max_transferrable, + Error::::InsufficientTaoBalance + ); Self::transfer_allow_death_update_ti(origin_coldkey, destination_coldkey, amount) } @@ -118,7 +121,7 @@ impl Pallet { /// Returns the actual amount transferred. /// /// # Errors - /// Returns [`Error::::InsufficientBalance`] if no positive amount can be + /// Returns [`Error::::InsufficientTaoBalance`] if no positive amount can be /// transferred while preserving the origin account. /// /// Propagates any other transfer error from the underlying currency. @@ -144,7 +147,7 @@ impl Pallet { ensure!( !amount_to_transfer.is_zero(), - Error::::InsufficientBalance + Error::::InsufficientTaoBalance ); ::Currency::transfer( @@ -188,7 +191,7 @@ impl Pallet { ); ensure!( amount <= max_preserving_amount, - Error::::InsufficientBalance + Error::::InsufficientTaoBalance ); // Decrease subtensor pallet total issuance @@ -330,7 +333,7 @@ impl Pallet { ) -> DispatchResult { ensure!( Self::can_remove_balance_from_coldkey_account(coldkey, amount), - Error::::InsufficientBalance + Error::::InsufficientTaoBalance ); let identifier = Self::get_network_registration_lock_identifier(lock_id); diff --git a/pallets/subtensor/src/coinbase/tempo_control.rs b/pallets/subtensor/src/coinbase/tempo_control.rs index c43c53019e..98c0081114 100644 --- a/pallets/subtensor/src/coinbase/tempo_control.rs +++ b/pallets/subtensor/src/coinbase/tempo_control.rs @@ -8,28 +8,39 @@ use crate::system::pallet_prelude::OriginFor; use crate::utils::rate_limiting::{Hyperparameter, TransactionType}; impl Pallet { - /// Owner-side `set_tempo` implementation. + /// `set_tempo` implementation, dispatched by `AdminUtils::sudo_set_tempo`. + /// Callable by the subnet owner (bounded to `[MinTempo, MaxTempo]` and + /// cooled down by a fixed `MinTempo` blocks via `TransactionType::TempoUpdate`) + /// or by root (any u16, no rate limit). Both respect the admin freeze window + /// and reset the cycle (`LastEpochBlock = current_block`) on success. pub fn do_set_tempo(origin: OriginFor, netuid: NetUid, tempo: u16) -> DispatchResult { - let who = Self::ensure_subnet_owner(origin, netuid)?; + let maybe_who = Self::ensure_subnet_owner_or_root(origin, netuid)?; - ensure!( - (MIN_TEMPO..=MAX_TEMPO).contains(&tempo), - Error::::TempoOutOfBounds - ); + ensure!(Self::if_subnet_exist(netuid), Error::::SubnetNotExists); + + if maybe_who.is_some() { + 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(); + if let Some(who) = maybe_who.as_ref() { + ensure!( + tx.passes_rate_limit_on_subnet::(who, netuid), + Error::::TxRateLimitExceeded + ); + } Self::apply_tempo_with_cycle_reset(netuid, tempo); - tx.set_last_block_on_subnet::(&who, netuid, now); + 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(()) } diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index ded0b72d74..0d9fa2743d 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -469,7 +469,7 @@ pub mod pallet { false } - /// Default value for false. + /// Default value for true. #[pallet::type_value] pub fn DefaultTrue() -> bool { true @@ -1122,7 +1122,7 @@ pub mod pallet { 2 } - /// Default value for ck burn, 18%. + /// Default value for ck burn, 0%. #[pallet::type_value] pub fn DefaultCKBurn() -> u64 { 0 @@ -1632,7 +1632,7 @@ pub mod pallet { } /// MAP ( netuid ) --> bool | Whether subnet owner cut should be auto-locked. - /// Missing entries default to true, so auto-locking is enabled unless explicitly disabled. + /// Missing entries default to false, so auto-locking is disabled unless explicitly enabled. #[pallet::storage] pub type OwnerCutAutoLockEnabled = StorageMap<_, Identity, NetUid, bool, ValueQuery, DefaultOwnerCutAutoLockEnabled>; @@ -1830,7 +1830,7 @@ 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`. + /// Lower bound for owner-set tempo. Also the fixed cooldown for `sudo_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; @@ -1851,7 +1851,7 @@ pub mod pallet { /// 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). + /// and on a successful `sudo_set_tempo` (cycle reset). #[pallet::storage] pub type LastEpochBlock = StorageMap<_, Identity, NetUid, u64, ValueQuery, DefaultZeroU64>; @@ -2968,7 +2968,7 @@ impl> ensure!( Self::get_stake_for_hotkey_and_coldkey_on_subnet(hotkey, coldkey, netuid) >= alpha, - Error::::InsufficientBalance + Error::::InsufficientAlphaBalance ); // Decrese alpha out counter diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index 3907c29e01..a875eeeeea 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -26,16 +26,14 @@ mod dispatches { /// Sets the caller weights for the incentive mechanism. The call can be /// made from the hotkey account so is potentially insecure, however, the damage /// of changing weights is minimal if caught early. This function includes all the - /// checks that the passed weights meet the requirements. Stored as u16s they represent - /// rational values in the range [0,1] which sum to 1 and can be interpreted as - /// probabilities. The specific weights determine how inflation propagates outward + /// checks that the passed weights meet the requirements. Stored weights are u16s + /// max-upscaled by the pallet, so the largest non-zero supplied weight is stored + /// as `u16::MAX`. The weights determine how inflation propagates outward /// from this peer. /// /// # Note - /// The 16 bit integers weights should represent 1.0 as the max u16. - /// However, the function normalizes all integers to u16_max anyway. This means that if the sum of all - /// elements is larger or smaller than the amount of elements * u16_max, all elements - /// will be corrected for this deviation. + /// Input weights are relative. They do not need to sum to a particular + /// value before submission. /// /// # Arguments /// * `origin`: The caller, a hotkey who wishes to set their weights. @@ -44,8 +42,7 @@ mod dispatches { /// /// * `dests`: The edge endpoint for the weight, i.e. j for w_ij. /// - /// * `weights`: The u16 integer encoded weights. Interpreted as rational - /// values in the range [0,1]. They must sum to in32::MAX. + /// * `weights`: Relative u16-encoded weights, max-upscaled by the pallet before storage. /// /// * `version_key`: The network version key to check if the validator is up to date. /// @@ -87,16 +84,14 @@ mod dispatches { /// Sets the caller weights for the incentive mechanism for mechanisms. The call /// can be made from the hotkey account so is potentially insecure, however, the damage /// of changing weights is minimal if caught early. This function includes all the - /// checks that the passed weights meet the requirements. Stored as u16s they represent - /// rational values in the range [0,1] which sum to 1 and can be interpreted as - /// probabilities. The specific weights determine how inflation propagates outward + /// checks that the passed weights meet the requirements. Stored weights are u16s + /// max-upscaled by the pallet, so the largest non-zero supplied weight is stored + /// as `u16::MAX`. The weights determine how inflation propagates outward /// from this peer. /// /// # Note - /// The 16 bit integers weights should represent 1.0 as the max u16. - /// However, the function normalizes all integers to u16_max anyway. This means that if the sum of all - /// elements is larger or smaller than the amount of elements * u16_max, all elements - /// will be corrected for this deviation. + /// Input weights are relative. They do not need to sum to a particular + /// value before submission. /// /// # Arguments /// * `origin`: The caller, a hotkey who wishes to set their weights. @@ -107,8 +102,7 @@ mod dispatches { /// /// * `dests`: The edge endpoint for the weight, i.e. j for w_ij. /// - /// * `weights`: The u16 integer encoded weights. Interpreted as rational - /// values in the range [0,1]. They must sum to in32::MAX. + /// * `weights`: Relative u16-encoded weights, max-upscaled by the pallet before storage. /// /// * `version_key`: The network version key to check if the validator is up to date. /// @@ -2337,28 +2331,28 @@ mod dispatches { 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. + /// Deprecated compatibility entry point retained for call-index stability. + /// This call charges a fee, returns success, and does not modify state. + /// Use `AdminUtils::sudo_set_tempo` to change subnet tempo. + #[deprecated(note = "Use `AdminUtils::sudo_set_tempo` instead")] #[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) + #[pallet::weight((Weight::from_parts(0, 0), DispatchClass::Normal, Pays::Yes))] + pub fn set_tempo(_origin: OriginFor, _netuid: NetUid, _tempo: u16) -> DispatchResult { + Ok(()) } - /// `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). + /// Deprecated compatibility entry point retained for call-index stability. + /// This call charges a fee, returns success, and does not modify state. + /// Use `AdminUtils::sudo_set_activity_cutoff_factor` to change the factor. + #[deprecated(note = "Use `AdminUtils::sudo_set_activity_cutoff_factor` instead")] #[pallet::call_index(140)] - #[pallet::weight(::WeightInfo::set_activity_cutoff_factor())] + #[pallet::weight((Weight::from_parts(0, 0), DispatchClass::Normal, Pays::Yes))] pub fn set_activity_cutoff_factor( - origin: OriginFor, - netuid: NetUid, - factor_milli: u32, + _origin: OriginFor, + _netuid: NetUid, + _factor_milli: u32, ) -> DispatchResult { - Self::do_set_activity_cutoff_factor(origin, netuid, factor_milli) + Ok(()) } /// Owner-side `trigger_epoch`. Schedules an epoch to fire after `AdminFreezeWindow` diff --git a/pallets/subtensor/src/macros/errors.rs b/pallets/subtensor/src/macros/errors.rs index 9ebb94999a..33f8d5e5eb 100644 --- a/pallets/subtensor/src/macros/errors.rs +++ b/pallets/subtensor/src/macros/errors.rs @@ -214,8 +214,8 @@ mod errors { HotKeySwapOnSubnetIntervalNotPassed, /// Invalid netuid duplication SameNetuid, - /// The caller does not have enough balance for the operation. - InsufficientBalance, + /// The caller does not have enough TAO balance for the operation. + InsufficientTaoBalance, /// Invalid lease beneficiary to register the leased network. InvalidLeaseBeneficiary, /// Lease cannot end in the past. @@ -330,5 +330,7 @@ mod errors { LockIdOverFlow, /// Need to wait more blocks to do the start call. StartCallNotReady, + /// The caller does not have enough Alpha stake for the operation. + InsufficientAlphaBalance, } } diff --git a/pallets/subtensor/src/macros/hooks.rs b/pallets/subtensor/src/macros/hooks.rs index ed0b46314b..d50da3353d 100644 --- a/pallets/subtensor/src/macros/hooks.rs +++ b/pallets/subtensor/src/macros/hooks.rs @@ -179,7 +179,12 @@ mod hooks { // Fix lock state left behind by subnet-scoped hotkey swaps. .saturating_add(migrations::migrate_fix_subnet_hotkey_lock_swaps::migrate_fix_subnet_hotkey_lock_swaps::()) // Populate reverse lookup index for EVM address associations. - .saturating_add(migrations::migrate_associated_evm_address_index::migrate_associated_evm_address_index::()); + .saturating_add(migrations::migrate_associated_evm_address_index::migrate_associated_evm_address_index::()) + // Fold deprecated SubnetTaoProvided / SubnetAlphaInProvided residuals into the + // main AMM reserves (issue #2793). Guarded by HasMigrationRun, so it only runs once. + .saturating_add(migrations::migrate_cleanup_swap_v3::migrate_cleanup_swap_v3::()) + // Remove orphan SubnetIdentitiesV3 entries left for recycled netuids. + .saturating_add(migrations::migrate_clear_orphan_subnet_identities_v3::migrate_clear_orphan_subnet_identities_v3::()); weight } diff --git a/pallets/subtensor/src/migrations/migrate_clear_orphan_subnet_identities_v3.rs b/pallets/subtensor/src/migrations/migrate_clear_orphan_subnet_identities_v3.rs new file mode 100644 index 0000000000..293dd34833 --- /dev/null +++ b/pallets/subtensor/src/migrations/migrate_clear_orphan_subnet_identities_v3.rs @@ -0,0 +1,71 @@ +use crate::{Config, Event, HasMigrationRun, NetworksAdded, Pallet, SubnetIdentitiesV3, Weight}; +use frame_support::traits::Get; +use scale_info::prelude::string::String; +use sp_std::vec::Vec; +use subtensor_runtime_common::NetUid; + +/// Remove `SubnetIdentitiesV3` entries that belong to netuids which are no +/// longer registered networks (`!NetworksAdded`). +/// +/// Such orphan identities accumulate when a subnet slot is recycled: a new +/// owner registers a subnet in a previously-used netuid without supplying an +/// identity, and the stale identity from the prior owner is left in place, +/// misleading participants about what the subnet is. The registration path +/// (`set_new_network_state`) only writes on `Some(identity)` and never clears a +/// pre-existing entry, and the historical `migrate_subnet_identities_to_v3` +/// migration copied V2 entries unconditionally. +/// +/// This mirrors the identity-clear in `do_dissolve_network` as a one-shot +/// upgrade migration: orphan entries are removed and a `SubnetIdentityRemoved` +/// event is emitted for each; identities of live subnets are untouched. +pub fn migrate_clear_orphan_subnet_identities_v3() -> Weight { + let migration_name = b"migrate_clear_orphan_subnet_identities_v3".to_vec(); + let mut weight = T::DbWeight::get().reads(1); + + if HasMigrationRun::::get(&migration_name) { + log::info!( + target: "runtime", + "Migration '{}' already run - skipping.", + String::from_utf8_lossy(&migration_name) + ); + return weight; + } + + log::info!( + target: "runtime", + "Running migration '{}'", + String::from_utf8_lossy(&migration_name), + ); + + // Collect identity netuids that no longer correspond to a registered subnet. + // Every scanned key costs an identity-map read plus a NetworksAdded lookup, + // even when the identity is kept for a live subnet. + let mut scanned = 0u64; + let orphan_netuids: Vec = SubnetIdentitiesV3::::iter_keys() + .filter(|netuid| { + scanned = scanned.saturating_add(1); + !NetworksAdded::::get(netuid) + }) + .collect(); + let removed = orphan_netuids.len() as u64; + weight = weight.saturating_add(T::DbWeight::get().reads(scanned.saturating_mul(2))); + + for netuid in orphan_netuids { + SubnetIdentitiesV3::::remove(netuid); + Pallet::::deposit_event(Event::SubnetIdentityRemoved(netuid)); + } + weight = weight.saturating_add(T::DbWeight::get().writes(removed)); + + HasMigrationRun::::insert(&migration_name, true); + weight = weight.saturating_add(T::DbWeight::get().writes(1)); + + log::info!( + target: "runtime", + "Migration '{}' completed. scanned_identities={}, removed_orphans={}", + String::from_utf8_lossy(&migration_name), + scanned, + removed, + ); + + weight +} diff --git a/pallets/subtensor/src/migrations/mod.rs b/pallets/subtensor/src/migrations/mod.rs index 98699ccbce..ff9829ec1f 100644 --- a/pallets/subtensor/src/migrations/mod.rs +++ b/pallets/subtensor/src/migrations/mod.rs @@ -8,6 +8,7 @@ pub mod migrate_associated_evm_address_index; pub mod migrate_auto_stake_destination; pub mod migrate_cleanup_swap_v3; pub mod migrate_clear_deprecated_registration_maps; +pub mod migrate_clear_orphan_subnet_identities_v3; pub mod migrate_coldkey_swap_scheduled; pub mod migrate_coldkey_swap_scheduled_to_announcements; pub mod migrate_commit_reveal_settings; diff --git a/pallets/subtensor/src/staking/move_stake.rs b/pallets/subtensor/src/staking/move_stake.rs index e203dcf0bf..47e85a6e70 100644 --- a/pallets/subtensor/src/staking/move_stake.rs +++ b/pallets/subtensor/src/staking/move_stake.rs @@ -303,15 +303,6 @@ impl Pallet { maybe_allow_partial: Option, check_transfer_toggle: 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. - let alpha_available = Self::get_stake_for_hotkey_and_coldkey_on_subnet( - origin_hotkey, - origin_coldkey, - origin_netuid, - ); - let alpha_amount = alpha_amount.min(alpha_available); - // Calculate the maximum amount that can be executed let max_amount = if origin_netuid != destination_netuid { if let Some(limit_price) = maybe_limit_price { diff --git a/pallets/subtensor/src/staking/remove_stake.rs b/pallets/subtensor/src/staking/remove_stake.rs index 0b36dbf224..0c8ed6f6cc 100644 --- a/pallets/subtensor/src/staking/remove_stake.rs +++ b/pallets/subtensor/src/staking/remove_stake.rs @@ -580,15 +580,24 @@ impl Pallet { total_alpha_value_u128 = protocol_alpha_value_u128; } + // Preserve the inbound cursor so a pass that only skips other-netuid rows (or + // exhausts weight before finishing another matching hotkey) cannot return None + // and restart from the beginning — that would double-count into + // status.subnet_total_alpha_value. + let mut last_completed_key = last_key.clone(); + // Once a hotkey's Alpha/AlphaV2 prefix scan is started, finish it even if the + // remaining on_idle budget is exhausted. A single hotkey can have more prefix + // entries across all subnets than one block's leftover weight; aborting mid-hotkey + // and retrying forever livelocks dissolution. + let mut exhausted = false; + let iter = match last_key { Some(key) => TotalHotkeyAlpha::::iter_from(key), None => TotalHotkeyAlpha::::iter(), }; - let mut last_hot = None; - for (hot, this_netuid, _) in iter { - if !weight_meter.can_consume(r) { + if exhausted || !weight_meter.can_consume(r) { read_all = false; break; } @@ -598,13 +607,12 @@ impl Pallet { continue; } - let mut iterate_all = true; for (cold, this_netuid, share_u64f64) in Self::alpha_iter_single_prefix(&hot) { - if !weight_meter.can_consume(r) { - iterate_all = false; - break; + if weight_meter.can_consume(r) { + weight_meter.consume(r); + } else { + exhausted = true; } - weight_meter.consume(r); if this_netuid != netuid { continue; @@ -627,20 +635,16 @@ impl Pallet { } } - if !iterate_all { + last_completed_key = Some(TotalHotkeyAlpha::::hashed_key_for(&hot, netuid)); + if exhausted { read_all = false; break; - } else { - last_hot = Some(hot); } } status.subnet_total_alpha_value = Some(total_alpha_value_u128); - ( - read_all, - last_hot.map(|hot| TotalHotkeyAlpha::::hashed_key_for(&hot, netuid)), - ) + (read_all, last_completed_key) } pub fn destroy_alpha_in_out_stakes_settle_stakes( @@ -666,7 +670,10 @@ impl Pallet { let mut hotkeys_in_subnet: Vec = Vec::new(); let mut coldkeys = BTreeSet::::new(); - let mut last_hot = None; + let mut last_completed_key = last_key.clone(); + // Finish the current hotkey even if weight runs out mid-prefix; otherwise a fat + // Alpha/AlphaV2 hotkey prefix livelocks dissolution across blocks. + let mut exhausted = false; let iter = match last_key { Some(key) => TotalHotkeyAlpha::::iter_from(key), @@ -674,7 +681,7 @@ impl Pallet { }; for (hot, this_netuid, _) in iter { - if !weight_meter.can_consume(r) { + if exhausted || !weight_meter.can_consume(r) { read_all = false; break; } @@ -685,18 +692,17 @@ impl Pallet { } hotkeys_in_subnet.push(hot.clone()); - let mut inner_read_all = true; let mut coldkey_value_vec: Vec<(T::AccountId, u128)> = Vec::new(); - // Handle one hotkey and all its coldkeys or skip the hotkey if the weight is not enough - // Then we just need to record the hotkey as checkpoint + // Drain the whole hotkey prefix once started. Weight is accounted when it + // still fits; overshoot is allowed so the cursor can advance past this hotkey. for (cold, this_netuid, share_u64f64) in Self::alpha_iter_single_prefix(&hot) { - if !weight_meter.can_consume(r.saturating_mul(2_u64)) { - inner_read_all = false; - break; + let inner_reads = r.saturating_mul(2_u64); + if weight_meter.can_consume(inner_reads) { + weight_meter.consume(inner_reads); + } else { + exhausted = true; } - - weight_meter.consume(r.saturating_mul(2_u64)); if this_netuid != netuid { continue; } @@ -722,26 +728,23 @@ impl Pallet { coldkeys.insert(cold.clone()); } - // reserve the weight for the add_balance_to_coldkey_account function call later - if !weight_meter.can_consume(need_to_consume_weight) { - inner_read_all = false; - last_hot = Some(hot.clone()); - break; + if weight_meter.can_consume(need_to_consume_weight) { + weight_meter.consume(need_to_consume_weight); + } else { + exhausted = true; } - weight_meter.consume(need_to_consume_weight); let val_u128 = val_u64 as u128; coldkey_value_vec.push((cold.clone(), val_u128)); } } - if !inner_read_all { + for (cold, value) in coldkey_value_vec { + stakers.push((hot.clone(), cold, value)); + } + last_completed_key = Some(TotalHotkeyAlpha::::hashed_key_for(&hot, netuid)); + if exhausted { read_all = false; break; - } else { - for (cold, value) in coldkey_value_vec { - stakers.push((hot.clone(), cold, value)); - } - last_hot = Some(hot.clone()); } } @@ -825,10 +828,7 @@ impl Pallet { // ignore the weight for handling the final operation, we must set the correct status for the next run status.subnet_distributed_tao = Some(distributed_tao_value_u128); - ( - read_all, - last_hot.map(|hot| TotalHotkeyAlpha::::hashed_key_for(&hot, netuid)), - ) + (read_all, last_completed_key) } pub fn destroy_alpha_in_out_stakes_clean_alpha( @@ -840,16 +840,20 @@ impl Pallet { let w = T::DbWeight::get().writes(1); let mut read_all = true; + // Same cursor preserve as settle/get_total: do not wipe last_key when a pass + // only skips non-matching rows or runs out of weight before the next hotkey. + let mut last_completed_key = last_key.clone(); + // Finish the current hotkey's Alpha/AlphaV2 cleanup even if weight is exhausted. + let mut exhausted = false; + let iter = match last_key { Some(key) => TotalHotkeyAlpha::::iter_from(key), None => TotalHotkeyAlpha::::iter(), }; - let mut last_hot = None; - for (hot, this_netuid, _) in iter { let mut coldkeys: Vec = Vec::new(); - if !weight_meter.can_consume(r) { + if exhausted || !weight_meter.can_consume(r) { read_all = false; break; } @@ -859,46 +863,39 @@ impl Pallet { continue; } - let mut iterate_all = true; - // handle all coldkeys for the hotkey as transactional, it is overdesigned to record two layers of checkpoints for (cold, this_netuid, _) in Self::alpha_iter_single_prefix(&hot) { - if !weight_meter.can_consume(r) { - read_all = false; - iterate_all = false; - break; + if weight_meter.can_consume(r) { + weight_meter.consume(r); + } else { + exhausted = true; } - weight_meter.consume(r); if this_netuid != netuid { continue; } coldkeys.push(cold.clone()); } - if !iterate_all { - read_all = false; - break; - } - let weight_for_all_remove = w.saturating_mul(coldkeys.len() as u64); - - if !weight_meter.can_consume(weight_for_all_remove) { - read_all = false; - break; + if weight_meter.can_consume(weight_for_all_remove) { + weight_meter.consume(weight_for_all_remove); + } else { + exhausted = true; } - weight_meter.consume(weight_for_all_remove); - last_hot = Some(hot.clone()); + last_completed_key = Some(TotalHotkeyAlpha::::hashed_key_for(&hot, netuid)); for cold in coldkeys { Alpha::::remove((&hot, &cold, netuid)); AlphaV2::::remove((&hot, &cold, netuid)); } + + if exhausted { + read_all = false; + break; + } } - ( - read_all, - last_hot.map(|hot| TotalHotkeyAlpha::::hashed_key_for(&hot, netuid)), - ) + (read_all, last_completed_key) } pub fn destroy_alpha_in_out_stakes_clear_hotkey_totals( diff --git a/pallets/subtensor/src/staking/set_children.rs b/pallets/subtensor/src/staking/set_children.rs index 8131d2ac99..2c516eda5d 100644 --- a/pallets/subtensor/src/staking/set_children.rs +++ b/pallets/subtensor/src/staking/set_children.rs @@ -771,8 +771,7 @@ impl Pallet { /// * `hotkey` (&T::AccountId): The hotkey for which to retrieve the childkey take. /// /// # Returns - /// * `u16`: The childkey take value. This is a percentage represented as a value between 0 - /// and 10000, where 10000 represents 100%. + /// * `u16`: The childkey take value, scaled so `u16::MAX` represents 100%. pub fn get_childkey_take(hotkey: &T::AccountId, netuid: NetUid) -> u16 { ChildkeyTake::::get(hotkey, netuid) .deconstruct() diff --git a/pallets/subtensor/src/staking/stake_utils.rs b/pallets/subtensor/src/staking/stake_utils.rs index 0381257b12..8ff0e47a4d 100644 --- a/pallets/subtensor/src/staking/stake_utils.rs +++ b/pallets/subtensor/src/staking/stake_utils.rs @@ -143,10 +143,10 @@ impl Pallet { } } - /// Retrieves the global global weight as a normalized value between 0 and 1. + /// Retrieves the TAO weight as a normalized value between 0 and 1. /// /// This function performs the following steps: - /// 1. Fetches the global weight from storage using the TaoWeight storage item. + /// 1. Fetches the TAO weight from storage using the TaoWeight storage item. /// 2. Converts the retrieved u64 value to a fixed-point number (U96F32). /// 3. Normalizes the weight by dividing it by the maximum possible u64 value. /// 4. Returns the normalized weight as an U96F32 fixed-point number. @@ -155,12 +155,12 @@ impl Pallet { /// regardless of the actual stored weight value. /// /// # Returns - /// * `U96F32`: The normalized global global weight as a fixed-point number between 0 and 1. + /// * `U96F32`: The normalized TAO weight as a fixed-point number between 0 and 1. /// /// # Note /// This function uses saturating division to prevent potential overflow errors. pub fn get_tao_weight() -> U96F32 { - // Step 1: Fetch the global weight from storage + // Step 1: Fetch the TAO weight from storage let stored_weight = TaoWeight::::get(); // Step 2: Convert the u64 weight to U96F32 @@ -176,14 +176,14 @@ impl Pallet { weight_fixed.safe_div(U96F32::saturating_from_num(u64::MAX)) } - /// Sets the global global weight in storage. + /// Sets the TAO weight in storage. /// /// This function performs the following steps: /// 1. Takes the provided weight value as a u64. /// 2. Updates the TaoWeight storage item with the new value. /// /// # Arguments - /// * `weight`: The new global weight value to be set, as a u64. + /// * `weight`: The new TAO weight value to be set, as a u64. /// /// # Effects /// This function modifies the following storage item: @@ -202,13 +202,13 @@ impl Pallet { CKBurn::::set(weight); } - /// Calculates the weighted combination of alpha and global tao for a single hotkey onet a subnet. + /// Calculates the weighted combination of alpha and TAO stake for a single hotkey on a subnet. /// pub fn get_stake_weights_for_hotkey_on_subnet( hotkey: &T::AccountId, netuid: NetUid, ) -> (I64F64, I64F64, I64F64) { - // Retrieve the global tao weight. + // Retrieve the TAO weight. let tao_weight = I64F64::saturating_from_num(Self::get_tao_weight()); log::debug!("tao_weight: {tao_weight:?}"); @@ -217,7 +217,7 @@ impl Pallet { I64F64::saturating_from_num(Self::get_inherited_for_hotkey_on_subnet(hotkey, netuid)); log::debug!("alpha_stake: {alpha_stake:?}"); - // Step 2: Get the global tao stake for the hotkey + // Step 2: Get the TAO stake for the hotkey let tao_stake = I64F64::saturating_from_num(Self::get_tao_inherited_for_hotkey_on_subnet( hotkey, netuid, )); @@ -230,12 +230,12 @@ impl Pallet { (total_stake, alpha_stake, tao_stake) } - /// Calculates the weighted combination of alpha and global tao for hotkeys on a subnet. + /// Calculates the weighted combination of alpha and TAO stake for hotkeys on a subnet. /// pub fn get_stake_weights_for_network( netuid: NetUid, ) -> (Vec, Vec, Vec) { - // Retrieve the global tao weight. + // Retrieve the TAO weight. let tao_weight: I64F64 = I64F64::saturating_from_num(Self::get_tao_weight()); log::debug!("tao_weight: {tao_weight:?}"); @@ -257,8 +257,8 @@ impl Pallet { .collect(); log::debug!("alpha_stake: {alpha_stake:?}"); - // Step 3: Calculate the global tao stake vector. - // Initialize a vector to store global tao stakes for each neuron. + // Step 3: Calculate the TAO stake vector. + // Initialize a vector to store TAO stakes for each neuron. let tao_stake: Vec = (0..n) .map(|uid| { if Keys::::contains_key(netuid, uid) { @@ -273,8 +273,8 @@ impl Pallet { .collect(); log::trace!("tao_stake: {tao_stake:?}"); - // Step 4: Combine alpha and root tao stakes. - // Calculate the weighted average of alpha and global tao stakes for each neuron. + // Step 4: Combine alpha and TAO stakes. + // Calculate the weighted average of alpha and TAO stakes for each neuron. let total_stake: Vec = alpha_stake .iter() .zip(tao_stake.iter()) @@ -913,6 +913,21 @@ impl Pallet { } } + // Refund the TAO the AMM could not consume (e.g. when the user-supplied + // price limit is hit before the full `tao_staked` is swapped). Without + // this, the unswapped remainder is stranded on the subnet PalletId + // account. Mirrors the alpha refund in `unstake_from_subnet`. + let consumed_tao = swap_result + .amount_paid_in + .saturating_add(swap_result.fee_paid); + let refund_tao = tao_staked.saturating_sub(consumed_tao); + if !refund_tao.is_zero() { + Self::transfer_tao_from_subnet(netuid, coldkey, refund_tao)?; + // `swap_tao_for_alpha` bumped `TotalStake` by the full `tao_staked`; + // only `consumed_tao` actually became stake, so back out the refund. + TotalStake::::mutate(|total| *total = total.saturating_sub(refund_tao)); + } + // Record TAO inflow Self::record_tao_inflow(netuid, swap_result.amount_paid_in.into()); diff --git a/pallets/subtensor/src/subnets/subnet.rs b/pallets/subtensor/src/subnets/subnet.rs index 0f50df54d3..0d2e8ef52b 100644 --- a/pallets/subtensor/src/subnets/subnet.rs +++ b/pallets/subtensor/src/subnets/subnet.rs @@ -46,7 +46,7 @@ impl Pallet { /// This iterates through all the networks and returns a list of netuids. /// /// # Returns - /// * `Vec`: Netuids of all subnets. + /// * `Vec`: Netuids of all subnets. /// pub fn get_all_subnet_netuids() -> Vec { NetworksAdded::::iter() @@ -60,7 +60,7 @@ impl Pallet { /// This checks the Mechanism map for the value, defaults to 0. /// /// # Arguments - /// * `u16`: The subnet netuid + /// * `NetUid`: The subnet netuid. /// /// # Returns /// * `u16`: The subnet mechanism @@ -69,13 +69,13 @@ impl Pallet { SubnetMechanism::::get(netuid) } - /// Finds the next available mechanism ID. + /// Finds the next available subnet netuid. /// - /// This function iterates through possible mechanism IDs starting from 0 + /// This function iterates through possible subnet netuids starting from 1 /// until it finds an ID that is not currently in use. /// /// # Returns - /// * `u16`: The next available mechanism ID. + /// * `NetUid`: The next available subnet netuid. pub fn get_next_netuid() -> NetUid { let mut next_netuid = NetUid::from(1); // do not allow creation of root let netuids = Self::get_all_subnet_netuids(); diff --git a/pallets/subtensor/src/tests/claim_root.rs b/pallets/subtensor/src/tests/claim_root.rs index 71b97d9fcd..bc756f50a0 100644 --- a/pallets/subtensor/src/tests/claim_root.rs +++ b/pallets/subtensor/src/tests/claim_root.rs @@ -826,7 +826,7 @@ fn test_claim_root_swap_failure_does_not_consume_claim() { assert_noop!( SubtensorModule::claim_root(RuntimeOrigin::signed(coldkey), BTreeSet::from([netuid])), - Error::::InsufficientBalance + Error::::InsufficientTaoBalance ); assert_eq!( diff --git a/pallets/subtensor/src/tests/destroy_alpha_tests.rs b/pallets/subtensor/src/tests/destroy_alpha_tests.rs index d7186f1f16..bc4211c79e 100644 --- a/pallets/subtensor/src/tests/destroy_alpha_tests.rs +++ b/pallets/subtensor/src/tests/destroy_alpha_tests.rs @@ -439,8 +439,184 @@ fn test_destroy_alpha_in_out_stakes_settle_stakes_multi_block_total_issuance() { assert!(completed, "settle_stakes should finish"); assert!( - iterations >= 5, + iterations >= 3, "should need multiple blocks, completed in {iterations}" ); }); } + +#[test] +fn test_destroy_alpha_in_out_stakes_settle_stakes_finishes_hotkey_past_weight() { + new_test_ext(0).execute_with(|| { + let cold_base = U256::from(7000); + let hot_base = U256::from(8000); + let netuid = add_dynamic_network(&hot_base, &cold_base); + + setup_reserves(netuid, 1_000_000u64.into(), 10_000_000u64.into()); + + for index in 1..=3 { + let cold = U256::from(cold_base + index); + let hot = U256::from(hot_base + index); + assert_ok!(SubtensorModule::create_account_if_non_existent(&cold, &hot)); + add_balance_to_coldkey_account(&cold, 1_000u64.into()); + assert_ok!(SubtensorModule::stake_into_subnet( + &hot, + &cold, + netuid, + 1_000u64.into(), + ::SwapInterface::max_price(), + false, + )); + } + + let mut status = dissolve_cleanup_status(netuid); + let mut meter = WeightMeter::with_limit(Weight::from_parts(u64::MAX, u64::MAX)); + assert!(run_resumable_netuid_cleanup_with_status( + netuid, + &mut meter, + &mut status, + |netuid, weight_meter, last_key, status| { + SubtensorModule::destroy_alpha_in_out_stakes_get_total_alpha_value( + netuid, + weight_meter, + last_key, + status, + ) + }, + )); + status.subnet_distributed_tao = Some(0); + + let first_hot = TotalHotkeyAlpha::::iter() + .find_map(|(hot, this_netuid, _)| (this_netuid == netuid).then_some(hot)) + .expect("staked hotkey should exist"); + let first_cold = SubtensorModule::alpha_iter_single_prefix(&first_hot) + .find_map(|(cold, this_netuid, _)| (this_netuid == netuid).then_some(cold)) + .expect("staked coldkey should exist"); + let first_balance_before = SubtensorModule::get_coldkey_balance(&first_cold); + + // Enough to start the first hotkey, not enough to reserve its payout weight. + // Cleanup must still finish and pay that hotkey so dissolution cannot livelock. + let tight = ::DbWeight::get() + .reads(3) + .saturating_add(::DbWeight::get().writes(1)); + let mut tight_meter = WeightMeter::with_limit(tight); + let (done, new_key) = SubtensorModule::destroy_alpha_in_out_stakes_settle_stakes( + netuid, + &mut tight_meter, + None, + &mut status, + ); + + assert!(!done); + assert_eq!( + new_key, + Some(TotalHotkeyAlpha::::hashed_key_for(first_hot, netuid)), + "cursor must advance past the hotkey that was finished over budget" + ); + assert!( + SubtensorModule::get_coldkey_balance(&first_cold) > first_balance_before, + "started hotkey must be paid even when weight is exhausted mid-prefix" + ); + + let mut full_meter = WeightMeter::with_limit(Weight::from_parts(u64::MAX, u64::MAX)); + let (done, _) = SubtensorModule::destroy_alpha_in_out_stakes_settle_stakes( + netuid, + &mut full_meter, + new_key, + &mut status, + ); + + assert!(done); + }); +} + +#[test] +fn test_destroy_alpha_in_out_stakes_settle_stakes_keeps_previous_cursor() { + new_test_ext(0).execute_with(|| { + let cold_base = U256::from(9000); + let hot_base = U256::from(10000); + let netuid = add_dynamic_network(&hot_base, &cold_base); + + setup_reserves(netuid, 1_000_000u64.into(), 10_000_000u64.into()); + + for index in 1..=3 { + let cold = U256::from(cold_base + index); + let hot = U256::from(hot_base + index); + assert_ok!(SubtensorModule::create_account_if_non_existent(&cold, &hot)); + add_balance_to_coldkey_account(&cold, 1_000u64.into()); + assert_ok!(SubtensorModule::stake_into_subnet( + &hot, + &cold, + netuid, + 1_000u64.into(), + ::SwapInterface::max_price(), + false, + )); + } + + let mut status = dissolve_cleanup_status(netuid); + let mut meter = WeightMeter::with_limit(Weight::from_parts(u64::MAX, u64::MAX)); + assert!(run_resumable_netuid_cleanup_with_status( + netuid, + &mut meter, + &mut status, + |netuid, weight_meter, last_key, status| { + SubtensorModule::destroy_alpha_in_out_stakes_get_total_alpha_value( + netuid, + weight_meter, + last_key, + status, + ) + }, + )); + status.subnet_distributed_tao = Some(0); + + let first_hot = TotalHotkeyAlpha::::iter() + .find_map(|(hot, this_netuid, _)| (this_netuid == netuid).then_some(hot)) + .expect("staked hotkey should exist"); + let first_cold = SubtensorModule::alpha_iter_single_prefix(&first_hot) + .find_map(|(cold, this_netuid, _)| (this_netuid == netuid).then_some(cold)) + .expect("staked coldkey should exist"); + let first_balance_before = SubtensorModule::get_coldkey_balance(&first_cold); + + let one_hotkey = ::DbWeight::get() + .reads(14) + .saturating_add(::DbWeight::get().writes(4)); + let mut first_meter = WeightMeter::with_limit(one_hotkey); + let (done, previous_key) = SubtensorModule::destroy_alpha_in_out_stakes_settle_stakes( + netuid, + &mut first_meter, + None, + &mut status, + ); + let previous_key = previous_key.expect("first pass should complete one hotkey"); + + assert!(!done); + assert!( + SubtensorModule::get_coldkey_balance(&first_cold) > first_balance_before, + "first pass should pay the completed hotkey" + ); + let first_balance_after = SubtensorModule::get_coldkey_balance(&first_cold); + + // Not enough weight to start another hotkey outer read — cursor must stay put. + let mut tight_meter = WeightMeter::with_limit(Weight::zero()); + let (done, retry_key) = SubtensorModule::destroy_alpha_in_out_stakes_settle_stakes( + netuid, + &mut tight_meter, + Some(previous_key.clone()), + &mut status, + ); + + assert!(!done); + assert_eq!( + retry_key, + Some(previous_key), + "cursor should stay at the previous completed hotkey when no new hotkey can start" + ); + assert_eq!( + SubtensorModule::get_coldkey_balance(&first_cold), + first_balance_after, + "empty-budget pass should not rewind and pay the completed hotkey again" + ); + }); +} diff --git a/pallets/subtensor/src/tests/migration.rs b/pallets/subtensor/src/tests/migration.rs index 85eb1290c8..98bc48be59 100644 --- a/pallets/subtensor/src/tests/migration.rs +++ b/pallets/subtensor/src/tests/migration.rs @@ -78,6 +78,51 @@ fn test_migrate_associated_evm_address_index() { }); } +#[test] +fn test_migrate_clear_orphan_subnet_identities_v3() { + new_test_ext(1).execute_with(|| { + let migration_name = b"migrate_clear_orphan_subnet_identities_v3".to_vec(); + HasMigrationRun::::remove(&migration_name); + + let orphan_netuid = NetUid::from(1); + let live_netuid = NetUid::from(2); + + // live_netuid is a registered network; orphan_netuid is not. + NetworksAdded::::insert(live_netuid, true); + + let orphan_identity = SubnetIdentityV3 { + subnet_name: b"orphan".to_vec(), + ..Default::default() + }; + let live_identity = SubnetIdentityV3 { + subnet_name: b"live".to_vec(), + ..Default::default() + }; + + SubnetIdentitiesV3::::insert(orphan_netuid, orphan_identity); + SubnetIdentitiesV3::::insert(live_netuid, live_identity.clone()); + + crate::migrations::migrate_clear_orphan_subnet_identities_v3::migrate_clear_orphan_subnet_identities_v3::(); + + // The orphan identity is removed; the live subnet identity is preserved. + assert!(!SubnetIdentitiesV3::::contains_key(orphan_netuid)); + assert_eq!( + SubnetIdentitiesV3::::get(live_netuid), + Some(live_identity.clone()) + ); + + // Migration is marked as run. + assert!(HasMigrationRun::::get(&migration_name)); + + // Idempotent: re-running is a no-op (live identity still present). + crate::migrations::migrate_clear_orphan_subnet_identities_v3::migrate_clear_orphan_subnet_identities_v3::(); + assert_eq!( + SubnetIdentitiesV3::::get(live_netuid), + Some(live_identity) + ); + }); +} + #[test] fn test_migrate_associated_evm_address_index_reconciles_over_cap_buckets() { new_test_ext(1).execute_with(|| { @@ -3352,6 +3397,42 @@ fn test_migrate_cleanup_swap_v3() { }); } +// Regression test for issue #2793: migrate_cleanup_swap_v3 must be wired into the pallet +// on_runtime_upgrade hook. Seeds a *Provided residual, runs the full upgrade hook, and asserts +// the residual is folded into the main reserves. Without the wiring line in hooks.rs this fails. +#[test] +fn test_migrate_cleanup_swap_v3_runs_on_runtime_upgrade() { + use crate::migrations::migrate_cleanup_swap_v3::deprecated_swap_maps; + use frame_support::traits::Hooks; + + new_test_ext(1).execute_with(|| { + let netuid = NetUid::from(1); + let provided: u64 = 9876; + + deprecated_swap_maps::SubnetTaoProvided::::insert(netuid, TaoBalance::from(provided)); + deprecated_swap_maps::SubnetAlphaInProvided::::insert( + netuid, + AlphaBalance::from(provided), + ); + + let tao_before = u64::from(SubnetTAO::::get(netuid)); + let alpha_before = u64::from(SubnetAlphaIn::::get(netuid)); + + let _ = as Hooks>::on_runtime_upgrade(); + + assert!(!deprecated_swap_maps::SubnetTaoProvided::::contains_key(netuid)); + assert!(!deprecated_swap_maps::SubnetAlphaInProvided::::contains_key(netuid)); + assert_eq!( + u64::from(SubnetTAO::::get(netuid)), + tao_before + provided + ); + assert_eq!( + u64::from(SubnetAlphaIn::::get(netuid)), + alpha_before + provided + ); + }); +} + #[test] fn test_migrate_coldkey_swap_scheduled_to_announcements() { new_test_ext(1000).execute_with(|| { diff --git a/pallets/subtensor/src/tests/mock.rs b/pallets/subtensor/src/tests/mock.rs index a0368483ef..8f0f7b5e04 100644 --- a/pallets/subtensor/src/tests/mock.rs +++ b/pallets/subtensor/src/tests/mock.rs @@ -291,7 +291,7 @@ parameter_types! { pub const InitialColdkeySwapAnnouncementDelay: u64 = 50; pub const InitialColdkeySwapReannouncementDelay: u64 = 10; pub const InitialDissolveNetworkScheduleDuration: u64 = 5 * 24 * 60 * 60 / 12; // Default as 5 days - pub const InitialTaoWeight: u64 = 0; // 100% global weight. + pub const InitialTaoWeight: u64 = 0; // 0% TAO weight. pub const InitialEmaPriceHalvingPeriod: u64 = 201_600_u64; // 4 weeks pub const InitialStartCallDelay: u64 = 0; // 0 days pub const InitialKeySwapOnSubnetCost: u64 = 10_000_000; diff --git a/pallets/subtensor/src/tests/mock_high_ed.rs b/pallets/subtensor/src/tests/mock_high_ed.rs index 84229cbca1..94c7d2ad26 100644 --- a/pallets/subtensor/src/tests/mock_high_ed.rs +++ b/pallets/subtensor/src/tests/mock_high_ed.rs @@ -213,7 +213,7 @@ parameter_types! { pub const InitialColdkeySwapAnnouncementDelay: u64 = 50; pub const InitialColdkeySwapReannouncementDelay: u64 = 10; pub const InitialDissolveNetworkScheduleDuration: u64 = 5 * 24 * 60 * 60 / 12; // Default as 5 days - pub const InitialTaoWeight: u64 = 0; // 100% global weight. + pub const InitialTaoWeight: u64 = 0; // 0% TAO weight. pub const InitialEmaPriceHalvingPeriod: u64 = 201_600_u64; // 4 weeks pub const InitialStartCallDelay: u64 = 0; // 0 days pub const InitialKeySwapOnSubnetCost: u64 = 10_000_000; diff --git a/pallets/subtensor/src/tests/move_stake.rs b/pallets/subtensor/src/tests/move_stake.rs index 957b44077b..22f5dc5677 100644 --- a/pallets/subtensor/src/tests/move_stake.rs +++ b/pallets/subtensor/src/tests/move_stake.rs @@ -516,7 +516,7 @@ fn test_do_move_wrong_origin() { netuid, alpha, ), - Error::::AmountTooLow + Error::::NotEnoughStakeToWithdraw ); // Check that no stake was moved @@ -1024,17 +1024,45 @@ fn test_do_transfer_insufficient_stake() { ) .unwrap(); - // Amount over available stake succeeds (because fees can be paid in Alpha, - // this limitation is removed) - let alpha = stake_amount * 2; - assert_ok!(SubtensorModule::do_transfer_stake( - RuntimeOrigin::signed(origin_coldkey), - destination_coldkey, - hotkey, + let origin_alpha_before = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &origin_coldkey, netuid, + ); + let destination_alpha_before = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &destination_coldkey, netuid, - alpha.into() - )); + ); + + let alpha = stake_amount * 2; + assert_noop!( + SubtensorModule::do_transfer_stake( + RuntimeOrigin::signed(origin_coldkey), + destination_coldkey, + hotkey, + netuid, + netuid, + alpha.into() + ), + Error::::NotEnoughStakeToWithdraw + ); + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &origin_coldkey, + netuid + ), + origin_alpha_before + ); + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &destination_coldkey, + netuid + ), + destination_alpha_before + ); }); } @@ -1073,7 +1101,7 @@ fn test_do_transfer_wrong_origin() { netuid, stake_amount.into() ), - Error::::AmountTooLow + Error::::NotEnoughStakeToWithdraw ); }); } @@ -1323,13 +1351,16 @@ fn test_do_swap_insufficient_stake() { ) .unwrap(); - assert_ok!(SubtensorModule::do_swap_stake( - RuntimeOrigin::signed(coldkey), - hotkey, - netuid1, - netuid2, - attempted_swap.into() - )); + assert_noop!( + SubtensorModule::do_swap_stake( + RuntimeOrigin::signed(coldkey), + hotkey, + netuid1, + netuid2, + attempted_swap.into() + ), + Error::::NotEnoughStakeToWithdraw + ); }); } @@ -1366,7 +1397,7 @@ fn test_do_swap_wrong_origin() { netuid2, stake_amount.into() ), - Error::::AmountTooLow + Error::::NotEnoughStakeToWithdraw ); }); } diff --git a/pallets/subtensor/src/tests/staking.rs b/pallets/subtensor/src/tests/staking.rs index 54ba2333c7..19af1e22b7 100644 --- a/pallets/subtensor/src/tests/staking.rs +++ b/pallets/subtensor/src/tests/staking.rs @@ -1435,7 +1435,7 @@ fn test_remove_balance_from_coldkey_account_failed() { // as there is no balance, nor does the account exist let result = SubtensorModule::transfer_tao_to_subnet(netuid, &coldkey_account_id, amount.into()); - assert_eq!(result, Err(Error::::InsufficientBalance.into())); + assert_eq!(result, Err(Error::::InsufficientTaoBalance.into())); }); } diff --git a/pallets/subtensor/src/tests/staking2.rs b/pallets/subtensor/src/tests/staking2.rs index 536a14579a..1e05a3a3d2 100644 --- a/pallets/subtensor/src/tests/staking2.rs +++ b/pallets/subtensor/src/tests/staking2.rs @@ -77,6 +77,63 @@ fn test_stake_base_case() { }); } +// Regression for #2735: when the AMM price limit is hit before the full stake is +// swapped, the unswapped TAO must be refunded to the staker and TotalStake must +// reflect only the consumed portion. Without the fix the remainder is stranded on +// the subnet PalletId account and TotalStake over-counts by the refunded amount. +#[test] +fn test_stake_into_subnet_refunds_unswapped_tao_at_price_limit() { + new_test_ext(1).execute_with(|| { + let hotkey = U256::from(561337); + let coldkey = U256::from(61337); + let netuid = add_dynamic_network(&hotkey, &coldkey); + + // Symmetric reserves => starting price ~1.0; a 1.1 limit allows only a + // partial swap of a 1 TAO stake, so a refund must occur. + SubnetMechanism::::insert(netuid, 1); + mock::setup_reserves( + netuid, + TaoBalance::from(10_000_000_000_u64), + AlphaBalance::from(10_000_000_000_u64), + ); + SubnetAlphaOut::::insert(netuid, AlphaBalance::from(10_000_000_000_u64)); + + let stake = TaoBalance::from(1_000_000_000_u64); // 1 TAO + add_balance_to_coldkey_account(&coldkey, (2_000_000_000_u64).into()); + + let balance_before = SubtensorModule::get_coldkey_balance(&coldkey); + let total_stake_before = TotalStake::::get(); + + // Tight limit (1.1 RAO/Alpha) so the AMM cannot consume the full stake. + assert_ok!(SubtensorModule::stake_into_subnet( + &hotkey, + &coldkey, + netuid, + stake, + TaoBalance::from(1_100_000_000_u64), + false, + )); + + let balance_after = SubtensorModule::get_coldkey_balance(&coldkey); + let total_stake_after = TotalStake::::get(); + + // Only the consumed TAO (amount_paid_in + fee) may leave the coldkey; + // the unswapped remainder must be refunded, not stranded. + let consumed = balance_before.saturating_sub(balance_after); + assert!( + consumed < stake, + "unswapped TAO must be refunded: consumed {consumed:?} >= stake {stake:?}" + ); + + // TotalStake must track the consumed TAO exactly, not the full stake. + assert_eq!( + total_stake_after.saturating_sub(total_stake_before), + consumed, + "TotalStake must equal consumed TAO (no stranded over-count)" + ); + }); +} + // Test: Share-based Staking System // This test verifies the functionality of the share-based staking system where: // 1. Stakes are represented as shares in a pool @@ -448,6 +505,42 @@ fn test_share_based_staking_denominator_precision() { }); } +// Regression test for #1960: removing more Alpha than is staked must return the +// Alpha-specific `InsufficientAlphaBalance`, distinct from the TAO-side +// `InsufficientTaoBalance`. Without the split this returns `InsufficientBalance`. +#[test] +fn test_decrease_stake_insufficient_alpha_returns_distinct_error() { + new_test_ext(1).execute_with(|| { + use frame_support::assert_noop; + use subtensor_runtime_common::BalanceOps; + + let netuid = NetUid::from(1); + let hotkey = U256::from(1); + let coldkey = U256::from(2); + + add_network(netuid, 13, 0); + register_ok_neuron(netuid, hotkey, coldkey, 0); + + // Credit a known amount of Alpha stake for the (hotkey, coldkey, netuid). + let staked = AlphaBalance::from(1_000u64); + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, &coldkey, netuid, staked, + ); + + // Removing more than is staked hits the Alpha balance guard and must fail + // with the Alpha-specific error, not the shared/TAO one. + assert_noop!( + SubtensorModule::decrease_stake( + &coldkey, + &hotkey, + netuid, + staked + AlphaBalance::from(1u64), + ), + Error::::InsufficientAlphaBalance + ); + }); +} + // SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::staking2::test_share_based_staking_stake_unstake_inject --exact --show-output --nocapture #[test] fn test_share_based_staking_stake_unstake_inject() { diff --git a/pallets/subtensor/src/tests/tao.rs b/pallets/subtensor/src/tests/tao.rs index 0220bf258d..7d4be6c963 100644 --- a/pallets/subtensor/src/tests/tao.rs +++ b/pallets/subtensor/src/tests/tao.rs @@ -95,7 +95,7 @@ fn test_transfer_tao_zero_balance_non_zero_amount_fails() { assert_noop!( SubtensorModule::transfer_tao(&origin, &dest, 1u64.into()), - Error::::InsufficientBalance + Error::::InsufficientTaoBalance ); assert_eq!(total_balance(&origin), 0.into()); @@ -114,7 +114,7 @@ fn test_transfer_tao_amount_greater_than_transferrable_fails() { assert_noop!( SubtensorModule::transfer_tao(&origin, &dest, amount.into()), - Error::::InsufficientBalance + Error::::InsufficientTaoBalance ); }); } @@ -307,7 +307,7 @@ fn test_burn_tao_insufficient_balance_fails() { assert_noop!( SubtensorModule::burn_tao(&coldkey, 1u64.into()), - Error::::InsufficientBalance + Error::::InsufficientTaoBalance ); }); } @@ -357,7 +357,7 @@ fn test_recycle_tao_amount_greater_than_max_preserving_fails() { assert_noop!( SubtensorModule::recycle_tao(&coldkey, too_much.into()), - Error::::InsufficientBalance + Error::::InsufficientTaoBalance ); assert_eq!(balances_total_issuance(), subtensor_total_issuance()); @@ -567,7 +567,7 @@ fn test_recycle_tao_cannot_cross_preserve_threshold_in_high_ed_runtime() { assert_noop!( SubtensorModule::recycle_tao(&origin, max_preserving + 1u64.into()), - Error::::InsufficientBalance + Error::::InsufficientTaoBalance ); }); } diff --git a/pallets/subtensor/src/tests/tempo_control.rs b/pallets/subtensor/src/tests/tempo_control.rs index 25d3abc691..6921bf7c75 100644 --- a/pallets/subtensor/src/tests/tempo_control.rs +++ b/pallets/subtensor/src/tests/tempo_control.rs @@ -1,5 +1,10 @@ #![allow(clippy::expect_used)] -use frame_support::{assert_noop, assert_ok}; +#![allow(deprecated)] +use codec::Encode; +use frame_support::{ + assert_noop, assert_ok, + dispatch::{GetDispatchInfo, Pays}, +}; use frame_system::Config; use sp_core::U256; use subtensor_runtime_common::NetUid; @@ -13,6 +18,53 @@ use crate::{ const DEFAULT_TEMPO: u16 = 360; const NEW_TEMPO: u16 = 720; +#[test] +fn deprecated_tempo_extrinsics_keep_their_call_indices() { + let netuid = NetUid::from(1); + let tempo_call = crate::Call::::set_tempo { + netuid, + tempo: NEW_TEMPO, + }; + let activity_call = crate::Call::::set_activity_cutoff_factor { + netuid, + factor_milli: 5_000, + }; + + assert_eq!(tempo_call.encode().first().copied(), Some(139)); + assert_eq!(activity_call.encode().first().copied(), Some(140)); + assert_eq!(tempo_call.get_dispatch_info().pays_fee, Pays::Yes); + assert_eq!(activity_call.get_dispatch_info().pays_fee, Pays::Yes); +} + +#[test] +fn deprecated_tempo_extrinsics_return_ok_without_changing_state() { + new_test_ext(1).execute_with(|| { + let owner = U256::from(1); + let netuid = setup_subnet(owner); + let tempo_before = Tempo::::get(netuid); + let factor_before = ActivityCutoffFactorMilli::::get(netuid); + let last_epoch_before = LastEpochBlock::::get(netuid); + + assert_ok!(crate::Pallet::::set_tempo( + <::RuntimeOrigin>::signed(owner), + netuid, + NEW_TEMPO, + )); + assert_ok!(crate::Pallet::::set_activity_cutoff_factor( + <::RuntimeOrigin>::signed(owner), + netuid, + 5_000, + )); + + assert_eq!(Tempo::::get(netuid), tempo_before); + assert_eq!( + ActivityCutoffFactorMilli::::get(netuid), + factor_before + ); + assert_eq!(LastEpochBlock::::get(netuid), last_epoch_before); + }); +} + fn setup_subnet(owner: U256) -> NetUid { let netuid = NetUid::from(1); add_network(netuid, DEFAULT_TEMPO, 0); diff --git a/pallets/subtensor/src/utils/misc.rs b/pallets/subtensor/src/utils/misc.rs index 4e689237f3..4300f1a806 100644 --- a/pallets/subtensor/src/utils/misc.rs +++ b/pallets/subtensor/src/utils/misc.rs @@ -108,9 +108,9 @@ impl Pallet { // ==== Global Setters ==== // ======================== /// 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. + /// Does NOT reset `LastEpochBlock` — that is the responsibility of + /// `AdminUtils::sudo_set_tempo` (owner-or-root), which performs the cycle + /// reset explicitly via `apply_tempo_with_cycle_reset`. pub fn set_tempo_unchecked(netuid: NetUid, tempo: u16) { Tempo::::insert(netuid, tempo); Self::deposit_event(Event::TempoSet(netuid, tempo)); diff --git a/pallets/subtensor/src/weights.rs b/pallets/subtensor/src/weights.rs index ee9ad4ca90..575f5898a0 100644 --- a/pallets/subtensor/src/weights.rs +++ b/pallets/subtensor/src/weights.rs @@ -94,8 +94,6 @@ pub trait WeightInfo { 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; @@ -2471,52 +2469,6 @@ impl WeightInfo for SubstrateWeight { } /// 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: 43_154_000 picoseconds. - Weight::from_parts(44_877_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: 36_214_000 picoseconds. - Weight::from_parts(37_446_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) @@ -5009,52 +4961,6 @@ impl WeightInfo for () { } /// 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: 43_154_000 picoseconds. - Weight::from_parts(44_877_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: 36_214_000 picoseconds. - Weight::from_parts(37_446_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) diff --git a/pallets/transaction-fee/src/lib.rs b/pallets/transaction-fee/src/lib.rs index fbbe2cd805..918d9cd319 100644 --- a/pallets/transaction-fee/src/lib.rs +++ b/pallets/transaction-fee/src/lib.rs @@ -305,6 +305,33 @@ impl SubtensorTxFeeHandler { origin_netuid, .. }) => alpha_vec.push((hotkey.clone(), *origin_netuid)), + Some(SubtensorCall::swap_hotkey { + hotkey, + new_hotkey: _, + netuid, + }) => match netuid { + Some(netuid) => alpha_vec.push((hotkey.clone(), *netuid)), + None => { + let netuids = OU::get_all_netuids_for_coldkey_and_hotkey(who, hotkey); + netuids + .into_iter() + .for_each(|netuid| alpha_vec.push((hotkey.clone(), netuid))); + } + }, + Some(SubtensorCall::swap_hotkey_v2 { + hotkey, + new_hotkey: _, + netuid, + keep_stake: _, + }) => match netuid { + Some(netuid) => alpha_vec.push((hotkey.clone(), *netuid)), + None => { + let netuids = OU::get_all_netuids_for_coldkey_and_hotkey(who, hotkey); + netuids + .into_iter() + .for_each(|netuid| alpha_vec.push((hotkey.clone(), netuid))); + } + }, Some(SubtensorCall::recycle_alpha { hotkey, amount: _, diff --git a/pallets/transaction-fee/src/tests/mod.rs b/pallets/transaction-fee/src/tests/mod.rs index f452634cfe..e16a594ed2 100644 --- a/pallets/transaction-fee/src/tests/mod.rs +++ b/pallets/transaction-fee/src/tests/mod.rs @@ -10,6 +10,7 @@ use sp_runtime::{ transaction_validity::{InvalidTransaction, TransactionValidityError}, }; use subtensor_runtime_common::AlphaBalance; +use subtensor_swap_interface::SwapHandler; use mock::*; mod mock; @@ -204,6 +205,72 @@ fn test_rejects_multi_subnet_alpha_fee_deduction() { assert_eq!(alpha_before_1, alpha_after_1); }); } +// cargo test --package subtensor-transaction-fee --lib -- tests::test_swap_hotkey_fees_alpha --exact --show-output +#[test] +fn test_swap_hotkey_fees_alpha() { + new_test_ext().execute_with(|| { + let sn = setup_subnets(2, 2); + let stake_amount = TAO; + setup_stake( + sn.subnets[0].netuid, + &sn.coldkey, + &sn.hotkeys[0], + stake_amount, + ); + setup_stake( + sn.subnets[1].netuid, + &sn.coldkey, + &sn.hotkeys[0], + stake_amount, + ); + + // swap_hotkey and swap_hotkey_v2 move alpha stake off the origin hotkey, + // so their fees must be eligible to be paid in alpha on every subnet that + // hotkey has stake. Before the fix `fees_in_alpha` returned an empty vec + // for these calls, forcing a TAO fee (and rejecting alpha-only callers). + + // netuid = None -> every subnet the origin hotkey has stake on (2 here). + let call_all = RuntimeCall::SubtensorModule(pallet_subtensor::Call::swap_hotkey { + hotkey: sn.hotkeys[0], + new_hotkey: sn.hotkeys[1], + netuid: None, + }); + let alpha_vec_all = + SubtensorTxFeeHandler::>::fees_in_alpha::( + &sn.coldkey, + &call_all, + ); + assert_eq!(alpha_vec_all.len(), 2); + + // netuid = Some(single) -> only that subnet. + let call_one = RuntimeCall::SubtensorModule(pallet_subtensor::Call::swap_hotkey { + hotkey: sn.hotkeys[0], + new_hotkey: sn.hotkeys[1], + netuid: Some(sn.subnets[0].netuid), + }); + let alpha_vec_one = + SubtensorTxFeeHandler::>::fees_in_alpha::( + &sn.coldkey, + &call_one, + ); + assert_eq!(alpha_vec_one.len(), 1); + + // swap_hotkey_v2 moves the same alpha and must be eligible too. + let call_v2 = RuntimeCall::SubtensorModule(pallet_subtensor::Call::swap_hotkey_v2 { + hotkey: sn.hotkeys[0], + new_hotkey: sn.hotkeys[1], + netuid: None, + keep_stake: false, + }); + let alpha_vec_v2 = + SubtensorTxFeeHandler::>::fees_in_alpha::( + &sn.coldkey, + &call_v2, + ); + assert_eq!(alpha_vec_v2.len(), 2); + }); +} + // cargo test --package subtensor-transaction-fee --lib -- tests::test_remove_stake_fees_alpha --exact --show-output #[test] fn test_remove_stake_fees_alpha() { @@ -1258,6 +1325,135 @@ fn test_transfer_stake_fees_alpha() { }); } +// cargo test --package subtensor-transaction-fee --lib -- tests::test_transfer_stake_full_amount_fails_when_alpha_fee_reduces_available_stake --exact --show-output +#[test] +fn test_transfer_stake_full_amount_fails_when_alpha_fee_reduces_available_stake() { + new_test_ext().execute_with(|| { + let destination_coldkey = U256::from(100000); + let stake_amount = TAO; + let sn = setup_subnets(2, 2); + setup_stake( + sn.subnets[0].netuid, + &sn.coldkey, + &sn.hotkeys[0], + stake_amount, + ); + + let current_balance = Balances::free_balance(sn.coldkey); + remove_balance_from_coldkey_account(&sn.coldkey, current_balance); + assert_eq!(Balances::free_balance(sn.coldkey), TaoBalance::ZERO); + + let alpha_before = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &sn.hotkeys[0], + &sn.coldkey, + sn.subnets[0].netuid, + ); + let call = RuntimeCall::SubtensorModule(pallet_subtensor::Call::transfer_stake { + destination_coldkey, + hotkey: sn.hotkeys[0], + origin_netuid: sn.subnets[0].netuid, + destination_netuid: sn.subnets[1].netuid, + alpha_amount: alpha_before, + }); + let info = call.get_dispatch_info(); + let ext = pallet_transaction_payment::ChargeTransactionPayment::::from(0.into()); + + let inner = ext + .dispatch_transaction(RuntimeOrigin::signed(sn.coldkey).into(), call, &info, 0, 0) + .expect("alpha fee payment should validate"); + assert_eq!( + inner.unwrap_err().error, + Error::::NotEnoughStakeToWithdraw.into() + ); + + let alpha_after = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &sn.hotkeys[0], + &sn.coldkey, + sn.subnets[0].netuid, + ); + let actual_alpha_fee = alpha_before - alpha_after; + assert!(actual_alpha_fee > AlphaBalance::ZERO); + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &sn.hotkeys[0], + &destination_coldkey, + sn.subnets[1].netuid, + ), + AlphaBalance::ZERO + ); + }); + + new_test_ext().execute_with(|| { + let destination_coldkey = U256::from(100000); + let stake_amount = TAO; + let sn = setup_subnets(2, 2); + setup_stake( + sn.subnets[0].netuid, + &sn.coldkey, + &sn.hotkeys[0], + stake_amount, + ); + + let current_balance = Balances::free_balance(sn.coldkey); + remove_balance_from_coldkey_account(&sn.coldkey, current_balance); + assert_eq!(Balances::free_balance(sn.coldkey), TaoBalance::ZERO); + + let alpha_before = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &sn.hotkeys[0], + &sn.coldkey, + sn.subnets[0].netuid, + ); + let full_amount_call = + RuntimeCall::SubtensorModule(pallet_subtensor::Call::transfer_stake { + destination_coldkey, + hotkey: sn.hotkeys[0], + origin_netuid: sn.subnets[0].netuid, + destination_netuid: sn.subnets[1].netuid, + alpha_amount: alpha_before, + }); + let info = full_amount_call.get_dispatch_info(); + let tao_fee = pallet_transaction_payment::Pallet::::compute_fee(0, &info, 0.into()); + let alpha_fee = pallet_subtensor_swap::Pallet::::get_alpha_amount_for_tao( + sn.subnets[0].netuid, + tao_fee, + ); + assert!(alpha_fee > AlphaBalance::ZERO); + + let transfer_amount = alpha_before - alpha_fee; + let call = RuntimeCall::SubtensorModule(pallet_subtensor::Call::transfer_stake { + destination_coldkey, + hotkey: sn.hotkeys[0], + origin_netuid: sn.subnets[0].netuid, + destination_netuid: sn.subnets[1].netuid, + alpha_amount: transfer_amount, + }); + let info = call.get_dispatch_info(); + let ext = pallet_transaction_payment::ChargeTransactionPayment::::from(0.into()); + + let inner = ext + .dispatch_transaction(RuntimeOrigin::signed(sn.coldkey).into(), call, &info, 0, 0) + .expect("alpha fee payment should validate"); + assert_ok!(inner); + + assert_eq!(Balances::free_balance(sn.coldkey), TaoBalance::ZERO); + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &sn.hotkeys[0], + &sn.coldkey, + sn.subnets[0].netuid, + ), + AlphaBalance::ZERO + ); + assert!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &sn.hotkeys[0], + &destination_coldkey, + sn.subnets[1].netuid, + ) > AlphaBalance::ZERO + ); + }); +} + // cargo test --package subtensor-transaction-fee --lib -- tests::test_swap_stake_fees_alpha --exact --show-output #[test] fn test_swap_stake_fees_alpha() { diff --git a/precompiles/src/neuron.rs b/precompiles/src/neuron.rs index 8856acdbbd..8a7eac497f 100644 --- a/precompiles/src/neuron.rs +++ b/precompiles/src/neuron.rs @@ -11,6 +11,14 @@ use sp_std::vec::Vec; use crate::{PrecompileExt, PrecompileHandleExt}; +/// Neuron precompile for smart-contract (EVM) access to neuron management operations. +/// +/// Each method maps to a `pallet-subtensor` dispatchable and is dispatched as a +/// signed runtime call on behalf of the EVM caller (its mapped Substrate account), +/// so the caller pays the underlying extrinsic weight and is subject to the same +/// authorization rules (e.g. the caller coldkey must own the addressed hotkey). +/// All methods are marked `payable` so calls carrying EVM value do not revert, +/// but none of these methods consume the attached value. pub struct NeuronPrecompile(PhantomData); impl PrecompileExt for NeuronPrecompile @@ -61,6 +69,22 @@ where + IsSubType>, ::AddressMapping: AddressMapping, { + /// Set inter-neuron weights for the calling neuron on a subnet. + /// + /// Dispatches `set_weights`. This direct path is only honored when commit-reveal + /// weights are **disabled** for the subnet; when commit-reveal is enabled the + /// weights must instead be committed and revealed via `commitWeights` / + /// `revealWeights`. + /// + /// # Arguments + /// * `netuid` - The subnet identifier (uint16) + /// * `dests` - Destination UIDs the weights apply to (uint16[]) + /// * `weights` - Weight values, one per destination UID (uint16[]) + /// * `version_key` - Weights version key; rejected if it is lower than the + /// subnet's configured weights version key (uint64) + /// + /// # Returns + /// * `()` on success, or an EVM error reverts the call #[precompile::public("setWeights(uint16,uint16[],uint16[],uint64)")] #[precompile::payable] pub fn set_weights( @@ -83,6 +107,20 @@ where ) } + /// Commit a hash of intended weights for the commit-reveal-v2 flow. + /// + /// Dispatches `commit_weights`. Stores a commitment for the caller's neuron on + /// the subnet so the weights can later be revealed during the correct reveal + /// epoch. Requires commit-reveal weights to be enabled for the subnet and the + /// caller to meet the subnet's stake threshold. + /// + /// # Arguments + /// * `netuid` - The subnet identifier (uint16) + /// * `commit_hash` - Hash of `(hotkey, netuid, uids, values, salt, version_key)` + /// committing to the weights that will be revealed (bytes32) + /// + /// # Returns + /// * `()` on success, or an EVM error reverts the call #[precompile::public("commitWeights(uint16,bytes32)")] #[precompile::payable] pub fn commit_weights( @@ -101,6 +139,22 @@ where ) } + /// Reveal previously committed weights and set them for the calling neuron. + /// + /// Dispatches `reveal_weights`. Verifies the reveal matches a prior + /// `commitWeights` commitment for the current reveal epoch, then sets the + /// weights and consumes the commitment. The revealed tuple must hash (under the + /// same scheme used to build the commit) to the stored commit hash. + /// + /// # Arguments + /// * `netuid` - The subnet identifier (uint16) + /// * `uids` - Destination UIDs the weights apply to (uint16[]) + /// * `values` - Weight values, one per destination UID (uint16[]) + /// * `salt` - Salts, one per destination UID, binding the commit (uint16[]) + /// * `version_key` - Neuron version key, must match the committed value (uint64) + /// + /// # Returns + /// * `()` on success, or an EVM error reverts the call #[precompile::public("revealWeights(uint16,uint16[],uint16[],uint16[],uint64)")] #[precompile::payable] pub fn reveal_weights( @@ -125,6 +179,21 @@ where ) } + /// Register a hotkey on a subnet by burning TAO from the caller coldkey. + /// + /// Dispatches `burned_register`. The EVM caller is used as the owning coldkey; + /// `hotkey` is the neuron hotkey to register. The subnet's current registration + /// burn is charged to the caller; on success the hotkey is assigned a UID on + /// the subnet (pruning the lowest-scoring neuron if the subnet is full) and the + /// coldkey becomes its owner. + /// + /// # Arguments + /// * `netuid` - The subnet identifier (uint16) + /// * `hotkey` - The hotkey account ID to register (bytes32) + /// + /// # Returns + /// * `()` on success, or an EVM error reverts the call (e.g. insufficient + /// balance to cover the burn, registration disabled, or no UID available) #[precompile::public("burnedRegister(uint16,bytes32)")] #[precompile::payable] fn burned_register( @@ -142,6 +211,21 @@ where handle.try_dispatch_runtime_call::(call, RawOrigin::Signed(coldkey)) } + /// Register a hotkey on a subnet with a maximum acceptable burn price. + /// + /// Dispatches `register_limit`. Like `burnedRegister`, but the registration only + /// proceeds if the subnet's current burn is less than or equal to `limit_price`, + /// so a surging burn cannot over-charge the caller. The EVM caller is the owning + /// coldkey; `hotkey` is the neuron hotkey to register. + /// + /// # Arguments + /// * `netuid` - The subnet identifier (uint16) + /// * `hotkey` - The hotkey account ID to register (bytes32) + /// * `limit_price` - Maximum burn, in RAO, the caller is willing to pay (uint64) + /// + /// # Returns + /// * `()` on success, or an EVM error reverts the call (e.g. current burn + /// exceeds `limit_price`, insufficient balance, registration disabled) #[precompile::public("registerLimit(uint16,bytes32,uint64)")] #[precompile::payable] fn register_limit( @@ -161,6 +245,24 @@ where handle.try_dispatch_runtime_call::(call, RawOrigin::Signed(coldkey)) } + /// Publish the calling neuron's Axon endpoint metadata for a subnet. + /// + /// Dispatches `serve_axon`. Stores the network location of the neuron's Axon + /// (its query/forward RPC server) so validators and other neurons on the subnet + /// can discover and reach it. + /// + /// # Arguments + /// * `netuid` - The subnet identifier (uint16) + /// * `version` - Axon protocol version (uint32) + /// * `ip` - IPv4/IPv6 address as a packed integer (uint128) + /// * `port` - TCP port (uint16) + /// * `ip_type` - Address family: 4 for IPv4, 6 for IPv6 (uint8) + /// * `protocol` - Transport protocol (uint8) + /// * `placeholder1` - Reserved field (uint8) + /// * `placeholder2` - Reserved field (uint8) + /// + /// # Returns + /// * `()` on success, or an EVM error reverts the call #[precompile::public("serveAxon(uint16,uint32,uint128,uint16,uint8,uint8,uint8,uint8)")] #[precompile::payable] #[allow(clippy::too_many_arguments)] @@ -192,6 +294,25 @@ where ) } + /// Publish the calling neuron's Axon endpoint metadata together with a TLS certificate. + /// + /// Dispatches `serve_axon_tls`. Like `serveAxon`, and additionally stores a TLS + /// certificate so the Axon can be reached over a mutually-authenticated TLS + /// connection. + /// + /// # Arguments + /// * `netuid` - The subnet identifier (uint16) + /// * `version` - Axon protocol version (uint32) + /// * `ip` - IPv4/IPv6 address as a packed integer (uint128) + /// * `port` - TCP port (uint16) + /// * `ip_type` - Address family: 4 for IPv4, 6 for IPv6 (uint8) + /// * `protocol` - Transport protocol (uint8) + /// * `placeholder1` - Reserved field (uint8) + /// * `placeholder2` - Reserved field (uint8) + /// * `certificate` - TLS certificate bytes (bytes) + /// + /// # Returns + /// * `()` on success, or an EVM error reverts the call #[precompile::public( "serveAxonTls(uint16,uint32,uint128,uint16,uint8,uint8,uint8,uint8,bytes)" )] @@ -227,6 +348,20 @@ where ) } + /// Publish the calling neuron's Prometheus metrics endpoint metadata for a subnet. + /// + /// Dispatches `serve_prometheus`. Stores the network location of the neuron's + /// Prometheus metrics server so its operational metrics can be scraped. + /// + /// # Arguments + /// * `netuid` - The subnet identifier (uint16) + /// * `version` - Prometheus endpoint version (uint32) + /// * `ip` - IPv4/IPv6 address as a packed integer (uint128) + /// * `port` - TCP port (uint16) + /// * `ip_type` - Address family: 4 for IPv4, 6 for IPv6 (uint8) + /// + /// # Returns + /// * `()` on success, or an EVM error reverts the call #[precompile::public("servePrometheus(uint16,uint32,uint128,uint16,uint8)")] #[precompile::payable] #[allow(clippy::too_many_arguments)] diff --git a/precompiles/src/subnet.rs b/precompiles/src/subnet.rs index 32ecf82595..5948438471 100644 --- a/precompiles/src/subnet.rs +++ b/precompiles/src/subnet.rs @@ -505,7 +505,7 @@ where netuid: u16, factor_milli: u32, ) -> EvmResult<()> { - let call = pallet_subtensor::Call::::set_activity_cutoff_factor { + let call = pallet_admin_utils::Call::::sudo_set_activity_cutoff_factor { netuid: netuid.into(), factor_milli, }; diff --git a/runtime/src/check_nonce.rs b/runtime/src/check_nonce.rs index 0d28d33252..7ec9488d0d 100644 --- a/runtime/src/check_nonce.rs +++ b/runtime/src/check_nonce.rs @@ -5,6 +5,7 @@ use codec::{Decode, DecodeWithMemTracking, Encode}; use frame_support::{ RuntimeDebugNoBound, dispatch::{DispatchInfo, Pays}, + traits::Get, }; use frame_system::Config; use scale_info::TypeInfo; @@ -88,8 +89,11 @@ where type Pre = Pre; fn weight(&self, _: &T::RuntimeCall) -> Weight { - // TODO: benchmark transaction extension - Weight::zero() + // Account for the account-nonce storage ops the extension performs on + // signed transactions: one `Account::get` read in `validate`, plus one + // `Account::mutate` (read + write) in `prepare` to bump the nonce. + // Non-signed calls refund this weight in full via `Val::Refund`. + T::DbWeight::get().reads_writes(2, 1) } fn validate( @@ -174,3 +178,22 @@ where } } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::{Runtime, RuntimeCall}; + use sp_runtime::traits::Zero; + + #[test] + fn check_nonce_weight_accounts_for_account_storage_ops() { + let ext = CheckNonce::::from(<::Nonce>::zero()); + let call = RuntimeCall::System(frame_system::Call::remark { remark: vec![] }); + // validate performs one `Account::get` read; prepare performs one + // `Account::mutate` (read + write). The declared extension weight must + // reflect those ops, not zero. + let expected = ::DbWeight::get().reads_writes(2, 1); + assert_eq!(ext.weight(&call), expected); + assert!(!ext.weight(&call).is_zero()); + } +} diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 399d60658c..9eb98732e4 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -235,7 +235,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: 430, + spec_version: 431, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, @@ -844,7 +844,7 @@ parameter_types! { pub const InitialColdkeySwapAnnouncementDelay: BlockNumber = prod_or_fast!(5 * 24 * 60 * 60 / 12, 50); // 5 days pub const InitialColdkeySwapReannouncementDelay: BlockNumber = prod_or_fast!(24 * 60 * 60 / 12, 10); // 1 day pub const InitialDissolveNetworkScheduleDuration: BlockNumber = 5 * 24 * 60 * 60 / 12; // 5 days - pub const SubtensorInitialTaoWeight: u64 = 971_718_665_099_567_868; // 0.05267697438728329% tao weight. + pub const SubtensorInitialTaoWeight: u64 = 971_718_665_099_567_868; // ~5.27% TAO weight. pub const InitialEmaPriceHalvingPeriod: u64 = 201_600_u64; // 4 weeks // 0 days pub const InitialStartCallDelay: u64 = 0; diff --git a/runtime/src/proxy_filters/call_groups.rs b/runtime/src/proxy_filters/call_groups.rs index 6099b2ad65..b23fc2d5b9 100644 --- a/runtime/src/proxy_filters/call_groups.rs +++ b/runtime/src/proxy_filters/call_groups.rs @@ -3,6 +3,9 @@ //! Keep this file boring: one generated group per call-bearing runtime pallet. //! Proxy-specific policy belongs in `mod.rs`. +// The Owner proxy keeps deprecated Subtensor tempo calls for encoded-call compatibility. +#![allow(deprecated)] + use frame_system::Call as SystemCall; use pallet_admin_utils::Call as AdminUtilsCall; use pallet_balances::Call as BalancesCall; @@ -471,8 +474,6 @@ call_filter_group!( RuntimeCall::SubtensorModule(SubtensorCall::sudo_set_min_childkey_take), RuntimeCall::SubtensorModule(SubtensorCall::sudo_set_max_childkey_take), RuntimeCall::SubtensorModule(SubtensorCall::terminate_lease), - RuntimeCall::SubtensorModule(SubtensorCall::set_tempo), - RuntimeCall::SubtensorModule(SubtensorCall::set_activity_cutoff_factor), RuntimeCall::SubtensorModule(SubtensorCall::trigger_epoch), RuntimeCall::SubtensorModule(SubtensorCall::sudo_set_num_root_claims), RuntimeCall::SubtensorModule(SubtensorCall::sudo_set_root_claim_threshold), @@ -482,12 +483,14 @@ call_filter_group!( ] ); -// Subnet parameters a subnet owner may set directly (the admin-utils calls -// guarded by `ensure_sn_owner_or_root`). These are the genuine owner/lease -// management surface, as opposed to the root-only `RootConfigCalls`. +// Subnet parameters a subnet owner may set directly. This includes deprecated +// Subtensor compatibility calls plus AdminUtils calls guarded by +// `ensure_sn_owner_or_root`, as opposed to the root-only `RootConfigCalls`. call_filter_group!( SubnetManagementCalls, [ + RuntimeCall::SubtensorModule(SubtensorCall::set_tempo), + RuntimeCall::SubtensorModule(SubtensorCall::set_activity_cutoff_factor), RuntimeCall::AdminUtils(AdminUtilsCall::sudo_set_serving_rate_limit), RuntimeCall::AdminUtils(AdminUtilsCall::sudo_set_max_difficulty), RuntimeCall::AdminUtils(AdminUtilsCall::sudo_set_weights_version_key), @@ -496,6 +499,8 @@ call_filter_group!( RuntimeCall::AdminUtils(AdminUtilsCall::sudo_set_min_allowed_weights), RuntimeCall::AdminUtils(AdminUtilsCall::sudo_set_rho), RuntimeCall::AdminUtils(AdminUtilsCall::sudo_set_activity_cutoff), + RuntimeCall::AdminUtils(AdminUtilsCall::sudo_set_activity_cutoff_factor), + RuntimeCall::AdminUtils(AdminUtilsCall::sudo_set_tempo), RuntimeCall::AdminUtils(AdminUtilsCall::sudo_set_min_burn), RuntimeCall::AdminUtils(AdminUtilsCall::sudo_set_max_burn), RuntimeCall::AdminUtils(AdminUtilsCall::sudo_set_bonds_moving_average), @@ -545,7 +550,6 @@ call_filter_group!( RuntimeCall::AdminUtils(AdminUtilsCall::sudo_set_max_registrations_per_block), RuntimeCall::AdminUtils(AdminUtilsCall::sudo_set_subnet_owner_cut), RuntimeCall::AdminUtils(AdminUtilsCall::sudo_set_network_rate_limit), - RuntimeCall::AdminUtils(AdminUtilsCall::sudo_set_tempo), RuntimeCall::AdminUtils(AdminUtilsCall::sudo_set_total_issuance), RuntimeCall::AdminUtils(AdminUtilsCall::sudo_set_network_immunity_period), RuntimeCall::AdminUtils(AdminUtilsCall::sudo_set_network_min_lock_cost), @@ -680,7 +684,7 @@ type SubtensorSplitCalls = ( SubtensorCommonCalls, ); -// admin-utils, split for the Owner and SubnetLeaseBeneficiary proxies. +// AdminUtils calls and deprecated owner-call shims, split for owner proxies. #[cfg(test)] type AdminUtilsSplitCalls = (SubnetManagementCalls, RootConfigCalls, OwnerKeyCalls); diff --git a/runtime/src/proxy_filters/mod.rs b/runtime/src/proxy_filters/mod.rs index df1d936066..fa34890139 100644 --- a/runtime/src/proxy_filters/mod.rs +++ b/runtime/src/proxy_filters/mod.rs @@ -415,8 +415,13 @@ mod tests { assert!(owner.contains("AdminUtils::sudo_set_serving_rate_limit")); assert!(owner.contains("AdminUtils::sudo_set_max_difficulty")); assert!(owner.contains("SubtensorModule::set_subnet_identity")); + // Canonical owner-or-root tempo control lives in AdminUtils; deprecated + // Subtensor entry points remain available for encoded-call compatibility. + assert!(owner.contains("AdminUtils::sudo_set_tempo")); + assert!(owner.contains("AdminUtils::sudo_set_activity_cutoff_factor")); + assert!(owner.contains("SubtensorModule::set_tempo")); + assert!(owner.contains("SubtensorModule::set_activity_cutoff_factor")); // Root-only admin is not owner-settable (gated by `ensure_root`). - assert!(!owner.contains("AdminUtils::sudo_set_tempo")); assert!(!owner.contains("AdminUtils::sudo_set_kappa")); assert!(!owner.contains("AdminUtils::sudo_set_total_issuance")); assert!(!owner.contains("AdminUtils::swap_authorities")); diff --git a/scripts/srtool/build-srtool-image.sh b/scripts/srtool/build-srtool-image.sh index e2c9580973..e73061ff82 100755 --- a/scripts/srtool/build-srtool-image.sh +++ b/scripts/srtool/build-srtool-image.sh @@ -1,3 +1,8 @@ #!/bin/bash -docker build --build-arg RUSTC_VERSION="1.89.0" -t srtool https://github.com/paritytech/srtool.git#refs/tags/v0.18.3 +# Pinned to the commit behind srtool v0.18.3 so the builder cannot change +# under a re-used tag. Optional first argument overrides the image tag. +IMAGE_TAG="${1:-srtool}" +SRTOOL_COMMIT="0a446889c5e60abe92a41e426377276f5c7295e6" + +docker build --build-arg RUSTC_VERSION="1.89.0" -t "$IMAGE_TAG" "https://github.com/paritytech/srtool.git#${SRTOOL_COMMIT}" diff --git a/scripts/verify-upgrade.sh b/scripts/verify-upgrade.sh new file mode 100755 index 0000000000..593f2dd1c6 --- /dev/null +++ b/scripts/verify-upgrade.sh @@ -0,0 +1,176 @@ +#!/usr/bin/env bash +# One-command independent verification of a proposed runtime upgrade. +# +# For sudo-multisig (triumvirate) signers: fetches the proposal tag, +# rebuilds the runtime from a pristine clone of the proposal commit, +# byte-compares it against the artifact CI published in the proposal +# pre-release, and prints the exact sign command pinned to YOUR build. +# Never signs anything itself. +# +# Run this script from a checkout you already trust (reviewed main), NOT +# from the proposal tag: a malicious proposal could otherwise supply the +# very verifier that vouches for it. +# +# Usage: +# git fetch origin && git checkout origin/main +# ./scripts/verify-upgrade.sh # verifies the newest release +# ./scripts/verify-upgrade.sh --url # explicit release URL +# +# Requires: docker, curl, jq, git. Uses btcli for the final chain check if +# it is installed; prints the command to run manually otherwise. + +set -euo pipefail + +RED=$'\033[31m'; GREEN=$'\033[32m'; YELLOW=$'\033[33m'; RESET=$'\033[0m' +die() { echo "${RED}error:${RESET} $*" >&2; exit 1; } +ok() { echo "${GREEN}ok:${RESET} $*"; } +note() { echo "${YELLOW}note:${RESET} $*"; } + +url="" +while [ $# -gt 0 ]; do + case "$1" in + --url) url="${2:?--url needs a value}"; shift 2 ;; + -h|--help) sed -n '2,20p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;; + *) die "unknown argument: $1 (only --url is accepted)" ;; + esac +done + +for cmd in docker curl jq git; do + command -v "$cmd" >/dev/null || die "$cmd is required" +done +docker info >/dev/null 2>&1 || die "docker daemon is not running" + +repo_root=$(git rev-parse --show-toplevel) +cd "$repo_root" + +# ---------------------------------------------------------------- release --- +origin=$(git remote get-url origin) +slug=$(echo "$origin" | sed -E 's#(git@github.com:|https://github.com/)##; s#\.git$##') +if [ -z "$url" ]; then + tag=$(curl -fsSL "https://api.github.com/repos/${slug}/releases?per_page=1" \ + | jq -r '.[0].tag_name // empty') + [ -n "$tag" ] || die "could not discover the newest release; pass --url " + url="https://github.com/${slug}/releases/tag/${tag}" +fi +# Strict allowlist: a URL that fails this cannot smuggle shell metacharacters +# into anything we print or run below. +printf '%s' "$url" | grep -Eq \ + '^https://github\.com/[A-Za-z0-9._-]+/[A-Za-z0-9._-]+/releases/tag/[A-Za-z0-9._-]+$' \ + || die "unrecognized release URL: $url" +slug=$(echo "$url" | sed -E 's#https://github.com/([^/]+/[^/]+)/releases/tag/.*#\1#') +tag=${url##*/} +dl="https://github.com/${slug}/releases/download/${tag}" + +work=$(mktemp -d /tmp/verify-upgrade.XXXXXX) +trap 'rm -rf "$work"' EXIT + +echo "Verifying proposal ${tag} from ${url}" +curl -fsSL "${dl}/upgrade-manifest.json" -o "$work/manifest.json" \ + || die "could not download upgrade-manifest.json from the release" +curl -fsSL "${dl}/subtensor.wasm" -o "$work/release.wasm" \ + || die "could not download subtensor.wasm from the release" + +commit=$(jq -r '.commit // empty' "$work/manifest.json") +expected_sha=$(jq -r '.wasm_sha256 // empty' "$work/manifest.json" | sed 's/^0x//') +spec=$(jq -r '.spec_version // empty' "$work/manifest.json") +rustc_line=$(jq -r '.srtool_rustc // empty' "$work/manifest.json") +printf '%s' "$commit" | grep -Eq '^[0-9a-f]{40}$' \ + || die "manifest has no valid commit hash — do not sign" +printf '%s' "$expected_sha" | grep -Eq '^[0-9a-f]{64}$' \ + || die "manifest has no valid wasm_sha256 — do not sign" + +# The release wasm must itself match the manifest before we compare anything +# against it. +release_sha=$(shasum -a 256 "$work/release.wasm" | cut -d' ' -f1) +[ "$release_sha" = "$expected_sha" ] \ + || die "release asset subtensor.wasm (sha256 $release_sha) does not match manifest wasm_sha256 ($expected_sha) — do not sign" +ok "release wasm matches manifest sha256" + +# ----------------------------------------------------------------- source --- +git fetch --quiet --force origin "refs/tags/${tag}:refs/tags/${tag}" \ + || die "could not fetch tag ${tag} from origin — do not sign" +tag_commit=$(git rev-parse "refs/tags/${tag}^{commit}" 2>/dev/null) \ + || die "tag ${tag} does not resolve to a commit — do not sign" +# Bind the tag to the manifest: without this, a manifest could point the +# build at any commit in the repository and still get a green verdict. +[ "$tag_commit" = "$commit" ] \ + || die "tag ${tag} points at $tag_commit but the manifest claims $commit — do not sign" +ok "tag ${tag} resolves to the manifest commit" + +# Build from a pristine clone of the proposal commit, never the working +# tree: local modifications, untracked files, and ignored inputs such as a +# repo-level .cargo/ directory cannot influence the build or the verdict. +# A clone (rather than an archive) keeps .git present, matching the CI +# checkout the released artifact was built from. +src="$work/src" +git clone --quiet "$repo_root" "$src" +git -C "$src" checkout --quiet "$commit" \ + || die "could not check out $commit in the verification clone" +ok "created pristine source clone at proposal commit ${commit}" + +# ------------------------------------------------------------------ build --- +# The builder image recipe comes from the checkout this script runs in (the +# signer's trusted checkout), never from the proposal commit: a malicious +# proposal must not be able to swap out the toolchain that verifies it. +image_script="$repo_root/scripts/srtool/build-srtool-image.sh" +pinned_rustc=$(grep -Eo 'RUSTC_VERSION="[0-9.]+"' "$image_script" | grep -Eo '[0-9.]+') +if [ -n "$rustc_line" ]; then + echo "manifest toolchain: ${rustc_line}; local srtool pin: ${pinned_rustc}" +fi + +# Always (re)build the builder image from the commit-pinned srtool source +# under a dedicated tag, so a stale or hostile pre-existing `srtool` image +# cannot stand in as the verifier. With a warm docker layer cache this is +# fast; the first run takes ~10 min. +verify_image="srtool-verify" +note "building the pinned srtool builder image as ${verify_image} (cached after first run)" +DOCKER_DEFAULT_PLATFORM=linux/amd64 "$image_script" "$verify_image" + +note "running the deterministic srtool build (30+ min on Apple Silicon under emulation)" +( cd "$src/runtime" && { [ -L node-subtensor ] || ln -s . node-subtensor; } ) +# Fresh cargo home: a signer's ~/.cargo/config.toml could redirect registries +# or patch sources, so the verification build must not see it. +mkdir -p "$work/cargo-home" +docker run --rm --user root --platform=linux/amd64 \ + -e PACKAGE=node-subtensor-runtime \ + -e BUILD_OPTS="--features=metadata-hash" \ + -e PROFILE=production \ + -v "$work/cargo-home":/cargo-home \ + -v "$src":/build \ + "$verify_image" bash -c "git config --global --add safe.directory /build && \ + /srtool/build --app > /build/runtime/node-subtensor/srtool-output.log; \ + code=\$?; [ \$code -ne 0 ] && cat /build/runtime/node-subtensor/srtool-output.log && exit \$code; \ + exit 0" + +built_wasm=$(find "$src/runtime" -name 'node_subtensor_runtime.compact.compressed.wasm' -path '*srtool*' | head -n 1) +[ -n "$built_wasm" ] || die "srtool build produced no compact.compressed.wasm" +# $work (and the wasm inside it) is deleted on exit; keep a copy beside the +# repo so the printed sign command references a path that still exists. +local_wasm="$repo_root/verified-${tag}.wasm" +cp "$built_wasm" "$local_wasm" + +# ---------------------------------------------------------------- compare --- +local_sha=$(shasum -a 256 "$local_wasm" | cut -d' ' -f1) +echo "your build: sha256 $local_sha" +echo "release: sha256 $release_sha" +if [ "$local_sha" != "$release_sha" ]; then + die "your srtool build does NOT match the released runtime — do not sign; check toolchain pin and report the mismatch" +fi +cmp -s "$local_wasm" "$work/release.wasm" \ + || die "sha256 matched but bytes differ (should be impossible) — do not sign" +ok "your build is byte-identical to the released runtime (spec ${spec})" + +# ------------------------------------------------------------ chain check --- +if command -v btcli >/dev/null; then + echo + echo "Running btcli upgrade check against the chain..." + btcli upgrade check --url "$url" --wasm "$local_wasm" +else + note "btcli not found; to also verify call data and on-chain state, install it and run:" + printf ' btcli upgrade check --url %q --wasm %q\n' "$url" "$local_wasm" +fi + +echo +echo "All verifications passed. To sign, pinning the call data to your own build:" +echo +printf ' btcli upgrade sign --url %q --wasm %q -w \n' "$url" "$local_wasm" diff --git a/sdk/bittensor-core/src/client.rs b/sdk/bittensor-core/src/client.rs index 3bd7d431dc..15af650c0a 100644 --- a/sdk/bittensor-core/src/client.rs +++ b/sdk/bittensor-core/src/client.rs @@ -720,12 +720,13 @@ impl Client { netuid: u16, block_hash: Option<&str>, ) -> Result { - self.runtime_call( + let entries = self.runtime_call( "SubnetInfoRuntimeApi", - "get_subnet_hyperparams", + "get_subnet_hyperparams_v3", &[Value::Uint(u128::from(netuid))], block_hash, - ) + )?; + flatten_subnet_hyperparams_v3(entries) } pub fn stake_rao( @@ -1442,6 +1443,75 @@ fn event_attributes(record: &Value) -> Option<&Value> { .or_else(|| field(record, "event").and_then(|event| field(event, "attributes"))) } +/// Flatten `get_subnet_hyperparams_v3`'s `[{name, value}, ...]` list into a +/// `{name: raw}` map, matching the Python `subnet_hyperparameters` read. +fn flatten_subnet_hyperparams_v3(raw: Value) -> Result { + if matches!(raw, Value::Null) { + return Ok(Value::Null); + } + let Value::List(entries) = raw else { + return Err(CoreError::Codec( + "SubnetInfoRuntimeApi.get_subnet_hyperparams_v3 returned a non-list".into(), + )); + }; + let mut out = Vec::with_capacity(entries.len()); + for entry in entries { + let name = field(&entry, "name") + .and_then(as_str) + .ok_or_else(|| { + CoreError::Codec( + "SubnetInfoRuntimeApi.get_subnet_hyperparams_v3 entry omitted name".into(), + ) + })? + .to_owned(); + let tagged = field(&entry, "value").ok_or_else(|| { + CoreError::Codec( + "SubnetInfoRuntimeApi.get_subnet_hyperparams_v3 entry omitted value".into(), + ) + })?; + out.push((Value::Str(name), flatten_hyperparam_value(tagged.clone()))); + } + Ok(Value::Dict(out)) +} + +/// Peel one v3 `{TypeTag: payload}` value down to the raw payload. +/// +/// `U64F64` keeps the raw `bits` (what `sudo set` writes for +/// `burn_increase_mult`). `I32F32` is only used for +/// `alpha_sigmoid_steepness`, whose setter takes the integer part +/// (`bits / 2^32`), not the raw bits. +fn flatten_hyperparam_value(tagged: Value) -> Value { + const I32F32_ONE: i128 = 1 << 32; + let Value::Dict(entries) = &tagged else { + return tagged; + }; + let [(tag, payload)] = entries.as_slice() else { + return tagged; + }; + let Some(tag) = as_str(tag) else { + return tagged; + }; + if let Value::Dict(inner) = payload { + if let [(Value::Str(key), bits)] = inner.as_slice() { + if key == "bits" { + if tag == "I32F32" { + let bits = match bits { + Value::Int(v) => *v, + Value::Uint(v) => match i128::try_from(*v) { + Ok(v) => v, + Err(_) => return tagged, + }, + _ => return tagged, + }; + return Value::Int(bits.div_euclid(I32F32_ONE)); + } + return bits.clone(); + } + } + } + payload.clone() +} + pub fn field<'a>(value: &'a Value, name: &str) -> Option<&'a Value> { let Value::Dict(entries) = value else { return None; diff --git a/sdk/bittensor-core/tests/e2e-manifest.json b/sdk/bittensor-core/tests/e2e-manifest.json index f9add51f30..2232fc7683 100644 --- a/sdk/bittensor-core/tests/e2e-manifest.json +++ b/sdk/bittensor-core/tests/e2e-manifest.json @@ -149,9 +149,6 @@ { "test": "intent_set_mechanism_count" }, - { - "test": "intent_set_mechanism_emission_split" - }, { "test": "intent_set_perpetual_lock" }, diff --git a/sdk/bittensor-core/tests/e2e.rs b/sdk/bittensor-core/tests/e2e.rs index 25e913c624..4f165cea23 100644 --- a/sdk/bittensor-core/tests/e2e.rs +++ b/sdk/bittensor-core/tests/e2e.rs @@ -137,10 +137,6 @@ intent_plan_test!( intent_plan_test!(intent_set_hyperparameter, "set_hyperparameter"); intent_plan_test!(intent_set_identity, "set_identity"); intent_plan_test!(intent_set_mechanism_count, "set_mechanism_count"); -intent_plan_test!( - intent_set_mechanism_emission_split, - "set_mechanism_emission_split" -); intent_plan_test!(intent_set_perpetual_lock, "set_perpetual_lock"); intent_plan_test!(intent_set_root_claim_type, "set_root_claim_type"); intent_plan_test!(intent_set_subnet_identity, "set_subnet_identity"); @@ -1088,6 +1084,10 @@ fn test_typed_reads() { .subnet_hyperparameters(1, None) .expect("hyperparameters read"); assert!(field(&hp, "tempo").is_some()); + assert!( + field(&hp, "burn_half_life").is_some(), + "v3 hyperparams must include burn_half_life; got {hp:#?}" + ); let rate = ctx .client diff --git a/sdk/bittensor-core/tests/e2e_support/mod.rs b/sdk/bittensor-core/tests/e2e_support/mod.rs index 93aec396f9..3c74c87722 100644 --- a/sdk/bittensor-core/tests/e2e_support/mod.rs +++ b/sdk/bittensor-core/tests/e2e_support/mod.rs @@ -824,16 +824,6 @@ pub fn sample_intent(ctx: &TestContext, op: &str, netuid: u16) -> Result make( - SignerRole::Coldkey, - "AdminUtils", - "sudo_set_mechanism_emission_split", - record([ - ("netuid", u(netuid)), - ("maybe_split", list([u16v(32_768), u16v(32_767)])), - ]), - ) - .touches([netuid]), "set_perpetual_lock" => make( SignerRole::Coldkey, "SubtensorModule", diff --git a/sdk/python/bittensor/_generated/calls.py b/sdk/python/bittensor/_generated/calls.py index 31084b8998..dddadb71b5 100644 --- a/sdk/python/bittensor/_generated/calls.py +++ b/sdk/python/bittensor/_generated/calls.py @@ -1,7 +1,7 @@ """Generated from runtime metadata by codegen. DO NOT EDIT BY HAND. Regenerate with: python -m codegen -Spec version: 429 +Spec version: 431 """ from typing import Any, NamedTuple @@ -422,7 +422,7 @@ def serve_prometheus(netuid: 'NetUid', version: 'u32', ip: 'u128', port: 'u16', @staticmethod def set_activity_cutoff_factor(netuid: 'NetUid', factor_milli: 'u32') -> Call: - '`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).' + 'Deprecated compatibility entry point retained for call-index stability. This call charges a fee, returns success, and does not modify state. Use `AdminUtils::sudo_set_activity_cutoff_factor` to change the factor.' return Call('SubtensorModule', 'set_activity_cutoff_factor', {'netuid': netuid, 'factor_milli': factor_milli}) @staticmethod @@ -452,7 +452,7 @@ def set_identity(name: 'Any', url: 'Any', github_repo: 'Any', image: 'Any', disc @staticmethod def set_mechanism_weights(netuid: 'NetUid', mecid: 'MechId', dests: 'Any', weights: 'Any', version_key: 'u64') -> Call: - 'Sets the caller weights for the incentive mechanism for mechanisms. The call can be made from the hotkey account so is potentially insecure, however, the damage of changing weights is minimal if caught early. This function includes all the checks that the passed weights meet the requirements. Stored as u16s they represent rational values in the range [0,1] which sum to 1 and can be interpreted as probabilities. The specific weights determine how inflation propagates outward from this peer. # Note The 16 bit integers weights should represent 1.0 as the max u16. However, the function normalizes all integers to u16_max anyway. This means that if the sum of all elements is larger or smaller than the amount of elements * u16_max, all elements will be corrected for this deviation. # Arguments * `origin`: The caller, a hotkey who wishes to set their weights. * `netuid`: The network uid we are setting these weights on. * `mecid`: The u8 mechnism identifier. * `dests`: The edge endpoint for the weight, i.e. j for w_ij. * `weights`: The u16 integer encoded weights. Interpreted as rational values in the range [0,1]. They must sum to in32::MAX. * `version_key`: The network version key to check if the validator is up to date. # Events * `WeightsSet`: On successfully setting the weights on chain. # Errors * `MechanismDoesNotExist`: Attempting to set weights on a non-existent network. * `NotRegistered`: Attempting to set weights from a non registered account. * `WeightVecNotEqualSize`: Attempting to set weights with uids not of same length. * `DuplicateUids`: Attempting to set weights with duplicate uids. * `UidsLengthExceedUidsInSubNet`: Attempting to set weights above the max allowed uids. * `UidVecContainInvalidOne`: Attempting to set weights with invalid uids. * `WeightVecLengthIsLow`: Attempting to set weights with fewer weights than min. * `MaxWeightExceeded`: Attempting to set weights with max value exceeding limit.' + 'Sets the caller weights for the incentive mechanism for mechanisms. The call can be made from the hotkey account so is potentially insecure, however, the damage of changing weights is minimal if caught early. This function includes all the checks that the passed weights meet the requirements. Stored weights are u16s max-upscaled by the pallet, so the largest non-zero supplied weight is stored as `u16::MAX`. The weights determine how inflation propagates outward from this peer. # Note Input weights are relative. They do not need to sum to a particular value before submission. # Arguments * `origin`: The caller, a hotkey who wishes to set their weights. * `netuid`: The network uid we are setting these weights on. * `mecid`: The u8 mechnism identifier. * `dests`: The edge endpoint for the weight, i.e. j for w_ij. * `weights`: Relative u16-encoded weights, max-upscaled by the pallet before storage. * `version_key`: The network version key to check if the validator is up to date. # Events * `WeightsSet`: On successfully setting the weights on chain. # Errors * `MechanismDoesNotExist`: Attempting to set weights on a non-existent network. * `NotRegistered`: Attempting to set weights from a non registered account. * `WeightVecNotEqualSize`: Attempting to set weights with uids not of same length. * `DuplicateUids`: Attempting to set weights with duplicate uids. * `UidsLengthExceedUidsInSubNet`: Attempting to set weights above the max allowed uids. * `UidVecContainInvalidOne`: Attempting to set weights with invalid uids. * `WeightVecLengthIsLow`: Attempting to set weights with fewer weights than min. * `MaxWeightExceeded`: Attempting to set weights with max value exceeding limit.' return Call('SubtensorModule', 'set_mechanism_weights', {'netuid': netuid, 'mecid': mecid, 'dests': dests, 'weights': weights, 'version_key': version_key}) @staticmethod @@ -482,12 +482,12 @@ def set_subnet_identity(netuid: 'NetUid', subnet_name: 'Any', github_repo: 'Any' @staticmethod def set_tempo(netuid: 'NetUid', tempo: 'u16') -> Call: - '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.' + 'Deprecated compatibility entry point retained for call-index stability. This call charges a fee, returns success, and does not modify state. Use `AdminUtils::sudo_set_tempo` to change subnet tempo.' return Call('SubtensorModule', 'set_tempo', {'netuid': netuid, 'tempo': tempo}) @staticmethod def set_weights(netuid: 'NetUid', dests: 'Any', weights: 'Any', version_key: 'u64') -> Call: - 'Sets the caller weights for the incentive mechanism. The call can be made from the hotkey account so is potentially insecure, however, the damage of changing weights is minimal if caught early. This function includes all the checks that the passed weights meet the requirements. Stored as u16s they represent rational values in the range [0,1] which sum to 1 and can be interpreted as probabilities. The specific weights determine how inflation propagates outward from this peer. # Note The 16 bit integers weights should represent 1.0 as the max u16. However, the function normalizes all integers to u16_max anyway. This means that if the sum of all elements is larger or smaller than the amount of elements * u16_max, all elements will be corrected for this deviation. # Arguments * `origin`: The caller, a hotkey who wishes to set their weights. * `netuid`: The network uid we are setting these weights on. * `dests`: The edge endpoint for the weight, i.e. j for w_ij. * `weights`: The u16 integer encoded weights. Interpreted as rational values in the range [0,1]. They must sum to in32::MAX. * `version_key`: The network version key to check if the validator is up to date. # Events * `WeightsSet`: On successfully setting the weights on chain. # Errors * `MechanismDoesNotExist`: Attempting to set weights on a non-existent network. * `NotRegistered`: Attempting to set weights from a non registered account. * `WeightVecNotEqualSize`: Attempting to set weights with uids not of same length. * `DuplicateUids`: Attempting to set weights with duplicate uids. * `UidsLengthExceedUidsInSubNet`: Attempting to set weights above the max allowed uids. * `UidVecContainInvalidOne`: Attempting to set weights with invalid uids. * `WeightVecLengthIsLow`: Attempting to set weights with fewer weights than min. * `MaxWeightExceeded`: Attempting to set weights with max value exceeding limit.' + 'Sets the caller weights for the incentive mechanism. The call can be made from the hotkey account so is potentially insecure, however, the damage of changing weights is minimal if caught early. This function includes all the checks that the passed weights meet the requirements. Stored weights are u16s max-upscaled by the pallet, so the largest non-zero supplied weight is stored as `u16::MAX`. The weights determine how inflation propagates outward from this peer. # Note Input weights are relative. They do not need to sum to a particular value before submission. # Arguments * `origin`: The caller, a hotkey who wishes to set their weights. * `netuid`: The network uid we are setting these weights on. * `dests`: The edge endpoint for the weight, i.e. j for w_ij. * `weights`: Relative u16-encoded weights, max-upscaled by the pallet before storage. * `version_key`: The network version key to check if the validator is up to date. # Events * `WeightsSet`: On successfully setting the weights on chain. # Errors * `MechanismDoesNotExist`: Attempting to set weights on a non-existent network. * `NotRegistered`: Attempting to set weights from a non registered account. * `WeightVecNotEqualSize`: Attempting to set weights with uids not of same length. * `DuplicateUids`: Attempting to set weights with duplicate uids. * `UidsLengthExceedUidsInSubNet`: Attempting to set weights above the max allowed uids. * `UidVecContainInvalidOne`: Attempting to set weights with invalid uids. * `WeightVecLengthIsLow`: Attempting to set weights with fewer weights than min. * `MaxWeightExceeded`: Attempting to set weights with max value exceeding limit.' return Call('SubtensorModule', 'set_weights', {'netuid': netuid, 'dests': dests, 'weights': weights, 'version_key': version_key}) @staticmethod @@ -867,6 +867,11 @@ def sudo_set_activity_cutoff(netuid: 'NetUid', activity_cutoff: 'u16') -> Call: '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.' return Call('AdminUtils', 'sudo_set_activity_cutoff', {'netuid': netuid, 'activity_cutoff': activity_cutoff}) + @staticmethod + def sudo_set_activity_cutoff_factor(netuid: 'NetUid', factor_milli: 'u32') -> Call: + 'The extrinsic sets the activity-cutoff factor for a subnet, in per-mille (1/1000) of the tempo: the effective cutoff in blocks is `(factor × tempo) / 1000`. Bounded to `[MinActivityCutoffFactorMilli, MaxActivityCutoffFactorMilli]`. It is callable by the subnet owner (rate-limited via `OwnerHyperparamUpdate`, respects the admin freeze window) or the root account (bypasses both). This supersedes the absolute-blocks `sudo_set_activity_cutoff`.' + return Call('AdminUtils', 'sudo_set_activity_cutoff_factor', {'netuid': netuid, 'factor_milli': factor_milli}) + @staticmethod def sudo_set_adjustment_alpha(netuid: 'NetUid', adjustment_alpha: 'u64') -> Call: 'The extrinsic sets the adjustment alpha for a subnet. It is only callable by the root account or subnet owner. The extrinsic will call the Subtensor pallet to set the adjustment alpha.' @@ -1209,7 +1214,7 @@ def sudo_set_target_registrations_per_interval(netuid: 'NetUid', target_registra @staticmethod def sudo_set_tempo(netuid: 'NetUid', tempo: 'u16') -> Call: - 'The extrinsic sets the tempo for a subnet. It is only callable by the root account. The extrinsic will call the Subtensor pallet to set the tempo.' + 'The extrinsic sets the tempo for a subnet. It is callable by the subnet owner (bounded to `[MinTempo, MaxTempo]` and rate-limited to one change per `MinTempo` blocks) or the root account (any u16, no rate limit). Both respect the admin freeze window. A successful change resets the epoch cycle (`LastEpochBlock = current_block`).' return Call('AdminUtils', 'sudo_set_tempo', {'netuid': netuid, 'tempo': tempo}) @staticmethod diff --git a/sdk/python/bittensor/_generated/constants.py b/sdk/python/bittensor/_generated/constants.py index 46022f8c0b..4c4c7f8e25 100644 --- a/sdk/python/bittensor/_generated/constants.py +++ b/sdk/python/bittensor/_generated/constants.py @@ -1,7 +1,7 @@ """Generated from runtime metadata by codegen. DO NOT EDIT BY HAND. Regenerate with: python -m codegen -Spec version: 429 +Spec version: 431 Pallet constant descriptors: unpack into substrate.constant. """ diff --git a/sdk/python/bittensor/_generated/errors.py b/sdk/python/bittensor/_generated/errors.py index 369c44a8c7..58b67f0612 100644 --- a/sdk/python/bittensor/_generated/errors.py +++ b/sdk/python/bittensor/_generated/errors.py @@ -1,7 +1,7 @@ """Generated from runtime metadata by codegen. DO NOT EDIT BY HAND. Regenerate with: python -m codegen -Spec version: 429 +Spec version: 431 """ from dataclasses import dataclass @@ -138,7 +138,7 @@ class ErrorInfo: (7, 94): ErrorInfo('SubtensorModule', 'SubtokenDisabled', 'SubToken disabled now'), (7, 95): ErrorInfo('SubtensorModule', 'HotKeySwapOnSubnetIntervalNotPassed', 'Too frequent hotkey swap on subnet'), (7, 96): ErrorInfo('SubtensorModule', 'SameNetuid', 'Invalid netuid duplication'), - (7, 97): ErrorInfo('SubtensorModule', 'InsufficientBalance', 'The caller does not have enough balance for the operation.'), + (7, 97): ErrorInfo('SubtensorModule', 'InsufficientTaoBalance', 'The caller does not have enough TAO balance for the operation.'), (7, 98): ErrorInfo('SubtensorModule', 'InvalidLeaseBeneficiary', 'Invalid lease beneficiary to register the leased network.'), (7, 99): ErrorInfo('SubtensorModule', 'LeaseCannotEndInThePast', 'Lease cannot end in the past.'), (7, 100): ErrorInfo('SubtensorModule', 'LeaseNetuidNotFound', "Couldn't find the lease netuid."), @@ -194,6 +194,7 @@ class ErrorInfo: (7, 150): ErrorInfo('SubtensorModule', 'AccountRejectsLockedAlpha', 'The destination coldkey rejects incoming locked alpha.'), (7, 151): ErrorInfo('SubtensorModule', 'LockIdOverFlow', 'The coldkey has already registered too many subnets'), (7, 152): ErrorInfo('SubtensorModule', 'StartCallNotReady', 'Need to wait more blocks to do the start call.'), + (7, 153): ErrorInfo('SubtensorModule', 'InsufficientAlphaBalance', 'The caller does not have enough Alpha stake for the operation.'), (11, 0): ErrorInfo('Utility', 'TooManyCalls', 'Too many calls batched.'), (11, 1): ErrorInfo('Utility', 'InvalidDerivedAccount', 'Bad input data for derived account ID'), (12, 0): ErrorInfo('Sudo', 'RequireSudo', 'Sender must be the Sudo account.'), diff --git a/sdk/python/bittensor/_generated/runtime_apis.py b/sdk/python/bittensor/_generated/runtime_apis.py index 3c7a55460b..0f294e6174 100644 --- a/sdk/python/bittensor/_generated/runtime_apis.py +++ b/sdk/python/bittensor/_generated/runtime_apis.py @@ -1,7 +1,7 @@ """Generated from runtime metadata by codegen. DO NOT EDIT BY HAND. Regenerate with: python -m codegen -Spec version: 429 +Spec version: 431 Runtime API method descriptors: unpack into substrate.runtime_call. """ diff --git a/sdk/python/bittensor/_generated/storage.py b/sdk/python/bittensor/_generated/storage.py index d54cf3aeea..75d6afdfb2 100644 --- a/sdk/python/bittensor/_generated/storage.py +++ b/sdk/python/bittensor/_generated/storage.py @@ -1,7 +1,7 @@ """Generated from runtime metadata by codegen. DO NOT EDIT BY HAND. Regenerate with: python -m codegen -Spec version: 429 +Spec version: 431 Storage item descriptors: unpack into substrate.query/query_map. Each carries its VALUE's type identity (value_type_ident) so normalization can key on the runtime's own type names without a node round-trip. """ diff --git a/sdk/python/bittensor/cli/commands/deriv.py b/sdk/python/bittensor/cli/commands/deriv.py deleted file mode 100644 index e07662ecd3..0000000000 --- a/sdk/python/bittensor/cli/commands/deriv.py +++ /dev/null @@ -1,231 +0,0 @@ -"""`btcli deriv`: covered long/short derivatives (runtime API + raw calls).""" - -from __future__ import annotations - -import json -from typing import Optional - -import typer - -from ...balance import Balance -from ..call import _resolve_builder -from ..context import AppContext, address_cli_name, ctx_of, ss58_param_help -from ..globals import with_globals, with_tx_globals -from ..tx import _parse_money - -app = typer.Typer(no_args_is_help=True, help="Covered long/short derivatives.") - - -def _side(side: str) -> str: - normalized = side.lower() - if normalized not in ("short", "long"): - raise typer.BadParameter("side must be 'short' or 'long'") - return normalized - - -def _amount_rao(raw: str) -> int: - """Parse a TAO amount option exactly (no float round-trip) into rao.""" - try: - return Balance.from_tao(_parse_money(raw, False)).rao - except ValueError as error: - raise typer.BadParameter(str(error), param_hint="--amount-tao") - - -def _submit_raw_call( - app_ctx: AppContext, *, target: str, params: dict, signer: str, success: str -) -> None: - """Confirm and submit a raw call, honoring --dry-run like `btcli call`.""" - builder = _resolve_builder(target) - if app_ctx.dry_run: - app_ctx.run(lambda client: client.compose(builder(**params))) - app_ctx.output.detail( - "dry run: raw call", {"target": target, "signer": signer, "params": params} - ) - return - app_ctx.confirm(f"submit {target} with {json.dumps(params)}?") - - async def _op(client): - call = await client.compose(builder(**params)) - return await client.submit_call(call, app_ctx.signer(signer), signer=signer) - - result = app_ctx.run(_op) - if not app_ctx.output.result(result, success): - raise typer.Exit(1) - - -@app.command("quote") -@with_globals -def quote_open( - ctx: typer.Context, - side: str = typer.Option(..., "--side", help="Position side: short or long."), - netuid: int = typer.Option(..., "--netuid", help="Subnet whose derivative market to quote."), - amount_tao: str = typer.Option(..., "--amount-tao", "--amount", help="Position input, in TAO."), -): - """Quote opening a derivative position. - - Read-only: asks the runtime API what opening the position would cost - right now, without signing or submitting anything. - """ - app_ctx: AppContext = ctx_of(ctx) - side_name = _side(side) - rao = _amount_rao(amount_tao) - - async def _op(client): - method = f"quote_open_{side_name}" - return await client.runtime(("DerivativesRuntimeApi", method), [netuid, rao]) - - quote = app_ctx.run(_op) - app_ctx.output.detail( - f"{side_name} open quote", quote if isinstance(quote, dict) else {"quote": quote} - ) - - -@app.command("positions") -@with_globals -def show_positions( - ctx: typer.Context, - side: str = typer.Option(..., "--side", help="Position side: short or long."), - netuid: Optional[int] = typer.Option( - None, "--netuid", help="Show only the position on this subnet; omit to list all." - ), - coldkey_ss58: Optional[str] = typer.Option( - None, address_cli_name("coldkey_ss58"), help=ss58_param_help("coldkey_ss58") - ), -): - """List derivative positions for a coldkey.""" - app_ctx: AppContext = ctx_of(ctx) - owner = app_ctx.resolve_address("coldkey_ss58", coldkey_ss58) - side_name = _side(side) - - async def _op(client): - if netuid is None: - method = f"get_{side_name}_positions" - return await client.runtime(("DerivativesRuntimeApi", method), [owner]) - method = f"get_{side_name}_position" - return await client.runtime(("DerivativesRuntimeApi", method), [owner, netuid]) - - positions = app_ctx.run(_op) - app_ctx.output.detail(f"{side_name} positions", positions) - - -@app.command("market") -@with_globals -def show_market( - ctx: typer.Context, - side: str = typer.Option(..., "--side", help="Position side: short or long."), - netuid: int = typer.Option(..., "--netuid", help="Subnet whose derivative market to show."), -): - """Show derivative market state for a subnet.""" - app_ctx: AppContext = ctx_of(ctx) - side_name = _side(side) - - async def _op(client): - method = f"get_subnet_{side_name}_state" - return await client.runtime(("DerivativesRuntimeApi", method), [netuid]) - - state = app_ctx.run(_op) - app_ctx.output.detail(f"{side_name} market netuid {netuid}", state) - - -@app.command("open") -@with_tx_globals -def open_position( - ctx: typer.Context, - side: str = typer.Option(..., "--side", help="Position side: short or long."), - netuid: int = typer.Option(..., "--netuid", help="Subnet to open the position on."), - amount_tao: str = typer.Option(..., "--amount-tao", "--amount", help="Position input, in TAO."), - limit_price: Optional[int] = typer.Option( - None, - "--limit-price", - help="Limit price in parts-per-billion; omitted means no price limit.", - ), - hotkey_ss58: Optional[str] = typer.Option( - None, address_cli_name("hotkey_ss58"), help=ss58_param_help("hotkey_ss58") - ), -): - """Open a derivative position via raw call (requires chain support). - - Composes the raw SubtensorModule.open_short/open_long call, prompts for - confirmation, and signs with the hotkey. - """ - app_ctx: AppContext = ctx_of(ctx) - hotkey = app_ctx.resolve_address("hotkey_ss58", hotkey_ss58) - side_name = _side(side) - rao = _amount_rao(amount_tao) - target = f"SubtensorModule.open_{side_name}" - params = { - "hotkey": hotkey, - "netuid": netuid, - "position_input": rao, - "limit_price": limit_price, - } - _submit_raw_call( - app_ctx, - target=target, - params=params, - signer="hotkey", - success=f"opened {side_name} position", - ) - - -@app.command("topup") -@with_tx_globals -def top_up( - ctx: typer.Context, - side: str = typer.Option(..., "--side", help="Position side: short or long."), - netuid: int = typer.Option(..., "--netuid", help="Subnet the position lives on."), - amount_tao: str = typer.Option(..., "--amount-tao", "--amount", help="Top-up amount, in TAO."), -): - """Top up an existing derivative position.""" - app_ctx: AppContext = ctx_of(ctx) - side_name = _side(side) - rao = _amount_rao(amount_tao) - target = f"SubtensorModule.top_up_{side_name}" - params = {"netuid": netuid, "amount": rao, "limit_price": None} - _submit_raw_call( - app_ctx, - target=target, - params=params, - signer="coldkey", - success=f"topped up {side_name} position", - ) - - -@app.command("close") -@with_tx_globals -def close_position( - ctx: typer.Context, - side: str = typer.Option(..., "--side", help="Position side: short or long."), - netuid: int = typer.Option(..., "--netuid", help="Subnet the position lives on."), - fraction: float = typer.Option( - 1.0, - "--fraction", - help="Fraction of the position to close, above 0 and up to 1 (the default, all of it).", - ), - from_holdings: bool = typer.Option( - False, - "--from-holdings", - help="Submit the close_short/close_long call instead of the self-closing variant.", - ), -): - """Close (part of) a derivative position.""" - app_ctx: AppContext = ctx_of(ctx) - side_name = _side(side) - if not 0 < fraction <= 1: - raise typer.BadParameter( - f"must be above 0 and up to 1, got {fraction}", param_hint="--fraction" - ) - suffix = "" if from_holdings else "_self" - target = f"SubtensorModule.close_{side_name}{suffix}" - fraction_ppb = int(fraction * 1_000_000_000) - owner = app_ctx.resolve_address("coldkey_ss58", None) - params = {"netuid": netuid, "fraction_ppb": fraction_ppb} - if from_holdings: - params["coldkey"] = owner - _submit_raw_call( - app_ctx, - target=target, - params=params, - signer="coldkey", - success=f"closed {side_name} position", - ) diff --git a/sdk/python/bittensor/cli/commands/subnets.py b/sdk/python/bittensor/cli/commands/subnets.py index 8eb0f06067..b682bfbd6b 100644 --- a/sdk/python/bittensor/cli/commands/subnets.py +++ b/sdk/python/bittensor/cli/commands/subnets.py @@ -15,7 +15,7 @@ from ..context import AppContext, address_cli_name, ctx_of, ss58_param_help from ..globals import with_globals, with_tx_globals from ..helpers import chain_identity_names, local_address_names -from ..hyperparams_view import show_hyperparameters +from ..hyperparams_view import fetch_hyperparameters, show_hyperparameters from ..metagraph_view import show_metagraph app = typer.Typer(no_args_is_help=True, help="Inspect subnets.") @@ -120,7 +120,7 @@ def hyperparameters( ): """Show subnet hyperparameters.""" app_ctx: AppContext = ctx_of(ctx) - params = app_ctx.run(lambda c: c.read("subnet_hyperparameters", netuid=netuid)) + params = app_ctx.run(lambda c: fetch_hyperparameters(c, netuid)) show_hyperparameters(app_ctx, netuid, params, name) diff --git a/sdk/python/bittensor/cli/commands/sudo.py b/sdk/python/bittensor/cli/commands/sudo.py index 476c5b7ea7..7b7a030d8a 100644 --- a/sdk/python/bittensor/cli/commands/sudo.py +++ b/sdk/python/bittensor/cli/commands/sudo.py @@ -10,7 +10,6 @@ from ...intents import ( SetHyperparameter, SetMechanismCount, - SetMechanismEmissionSplit, SetSubnetIdentity, SetTake, StakeBurn, @@ -23,7 +22,7 @@ from ...settings import U16_MAX from ..context import AppContext, address_cli_name, ctx_of, ss58_param_help from ..globals import with_globals, with_tx_globals -from ..hyperparams_view import show_hyperparameters +from ..hyperparams_view import fetch_hyperparameters, show_hyperparameters from ..prompt import PromptSpec, fill_missing, interactive from ..tx import _parse_money @@ -61,7 +60,7 @@ def _prompt_set_args( specs: list[PromptSpec] = [] if name is None: if interactive(app_ctx): - params = app_ctx.run(lambda c: c.read("subnet_hyperparameters", netuid=netuid)) + params = app_ctx.run(lambda c: fetch_hyperparameters(c, netuid)) show_hyperparameters( app_ctx, netuid, @@ -116,7 +115,7 @@ def sudo_set( name, value = _prompt_set_args(app_ctx, netuid, name, value) try: intent = SetHyperparameter(netuid=netuid, name=name, value=value) - except ValueError as error: + except (ValueError, OverflowError) as error: app_ctx.output.error( str(error), help=f"`btcli sudo get --netuid {netuid} --name {name}` explains " @@ -139,7 +138,7 @@ def sudo_get( ): """Show subnet hyperparameters.""" app_ctx: AppContext = ctx_of(ctx) - params = app_ctx.run(lambda c: c.read("subnet_hyperparameters", netuid=netuid)) + params = app_ctx.run(lambda c: fetch_hyperparameters(c, netuid)) show_hyperparameters(app_ctx, netuid, params, name) @@ -420,30 +419,3 @@ def mechanism_emissions( app_ctx: AppContext = ctx_of(ctx) split = app_ctx.run(lambda c: c.read("mechanism_emission_split", netuid=netuid)) app_ctx.output.detail(None, {"netuid": netuid, "split": split}) - - -@mechanisms_app.command("split-emissions") -@with_tx_globals -def set_mechanism_emissions( - ctx: typer.Context, - netuid: int = typer.Option( - ..., "--netuid", help=SetMechanismEmissionSplit.field_help("netuid") - ), - split: str = typer.Option( - ..., - "--split", - help="Comma-separated integer weights, one per mechanism; the subnet's " - "emission is divided between mechanisms in proportion to them.", - ), -): - """Set mechanism emission split. - - Only the subnet owner can call this. It changes how the subnet's - emission is divided between its mechanisms from the next epoch on. - """ - app_ctx: AppContext = ctx_of(ctx) - values = [int(part.strip()) for part in split.split(",") if part.strip()] - app_ctx.submit(SetMechanismEmissionSplit(netuid=netuid, split=values)) - - -mechanisms_app.command("emissions-split", hidden=True)(set_mechanism_emissions) diff --git a/sdk/python/bittensor/cli/commands/wallet.py b/sdk/python/bittensor/cli/commands/wallet.py index cbc40d5a91..0272482fb6 100644 --- a/sdk/python/bittensor/cli/commands/wallet.py +++ b/sdk/python/bittensor/cli/commands/wallet.py @@ -221,16 +221,20 @@ def create( ) coldkey_crypto = _resolve_crypto_type(app_ctx, crypto_type) hotkey_crypto = _resolve_crypto_type(app_ctx, hotkey_crypto_type) - wallet = wallets.create( - name=app_ctx.wallet_name, - hotkey=app_ctx.hotkey_name, - path=app_ctx.wallet_path, - n_words=n_words, - use_password=not no_password, - overwrite=overwrite, - coldkey_crypto_type=coldkey_crypto, - hotkey_crypto_type=hotkey_crypto, - ) + try: + wallet = wallets.create( + name=app_ctx.wallet_name, + hotkey=app_ctx.hotkey_name, + path=app_ctx.wallet_path, + n_words=n_words, + use_password=not no_password, + overwrite=overwrite, + coldkey_crypto_type=coldkey_crypto, + hotkey_crypto_type=hotkey_crypto, + ) + except ValueError as error: + app_ctx.output.error(str(error)) + raise typer.Exit(1) app_ctx.output.detail( "created wallet", { @@ -262,14 +266,18 @@ def new_coldkey( app_ctx: AppContext = ctx_of(ctx) confirm_wallet(app_ctx, help_text="Wallet to create the coldkey in.", must_exist=False) crypto = _resolve_crypto_type(app_ctx, crypto_type) - wallet = wallets.new_coldkey( - name=app_ctx.wallet_name, - path=app_ctx.wallet_path, - n_words=n_words, - use_password=not no_password, - overwrite=overwrite, - crypto_type=crypto, - ) + try: + wallet = wallets.new_coldkey( + name=app_ctx.wallet_name, + path=app_ctx.wallet_path, + n_words=n_words, + use_password=not no_password, + overwrite=overwrite, + crypto_type=crypto, + ) + except ValueError as error: + app_ctx.output.error(str(error)) + raise typer.Exit(1) app_ctx.output.detail( "created coldkey", { @@ -366,16 +374,20 @@ def regen_coldkey( ) confirm_wallet(app_ctx, help_text="Wallet to regenerate the coldkey in.", must_exist=False) crypto = _resolve_crypto_type(app_ctx, crypto_type) - wallet = wallets.regen_coldkey( - mnemonic=mnemonic, - seed=seed, - json=json_keystore, - name=app_ctx.wallet_name, - path=app_ctx.wallet_path, - use_password=not no_password, - overwrite=overwrite, - crypto_type=crypto, - ) + try: + wallet = wallets.regen_coldkey( + mnemonic=mnemonic, + seed=seed, + json=json_keystore, + name=app_ctx.wallet_name, + path=app_ctx.wallet_path, + use_password=not no_password, + overwrite=overwrite, + crypto_type=crypto, + ) + except ValueError as error: + app_ctx.output.error(str(error)) + raise typer.Exit(1) reported_crypto = wallet.coldkey.crypto_type if json_keystore else crypto app_ctx.output.detail( "regenerated coldkey", diff --git a/sdk/python/bittensor/cli/context.py b/sdk/python/bittensor/cli/context.py index ee0985c108..5307c5494e 100644 --- a/sdk/python/bittensor/cli/context.py +++ b/sdk/python/bittensor/cli/context.py @@ -459,22 +459,29 @@ async def _execute(client): ) use_shield = False signer = await self.resolve_signing_wallet(intent.signer) + result = None try: if use_shield: - return await client.submit_shielded( + result = await client.submit_shielded( intent, signer, wait_for_finalization=False, ) - result = await client.execute( - intent, - signer, - wait_for_finalization=False, - **options, - ) - await self._attach_multisig_followup(client, intent, result) + else: + result = await client.execute( + intent, + signer, + wait_for_finalization=False, + **options, + ) + await self._attach_multisig_followup(client, intent, result) return result finally: + if self.uses_extension_signer() and hasattr(signer, "report_transaction_result"): + with contextlib.suppress(Exception): + await signer.report_transaction_result( + bool(result is not None and result.success) + ) if hasattr(signer, "close"): await signer.close() diff --git a/sdk/python/bittensor/cli/hyperparams_view.py b/sdk/python/bittensor/cli/hyperparams_view.py index 7ca3f07575..dc031b00eb 100644 --- a/sdk/python/bittensor/cli/hyperparams_view.py +++ b/sdk/python/bittensor/cli/hyperparams_view.py @@ -9,19 +9,49 @@ from __future__ import annotations +import asyncio from typing import Any, Optional import typer from .. import hyperparams as hp from ..intents.hyperparameters import OWNER_HYPERPARAMETERS +from ..settings import DOCS_URL from .context import AppContext +HYPERPARAMS_DOCS_URL = f"{DOCS_URL}/hyperparameters" + + +async def fetch_hyperparameters(client, netuid: int) -> Optional[dict[str, Any]]: + """The ``subnet_hyperparameters`` read plus the owner-settable values it + omits (queried from their storage items), so the listing shows everything + ``sudo set`` can change. + + Returns ``None`` when the subnet does not exist. + """ + params = await client.read("subnet_hyperparameters", netuid=netuid) + if params is None: + return None + extras = sorted( + name for name in OWNER_HYPERPARAMETERS if name not in params and name in hp.STORAGE_ITEMS + ) + values = await asyncio.gather( + *(client.query(hp.STORAGE_ITEMS[name], [netuid]) for name in extras) + ) + for name, value in zip(extras, values): + if isinstance(value, dict) and set(value) == {"bits"}: + # Fixed-point newtypes (U64F64) decode as {'bits': raw}; the raw + # bits are the value `sudo set` takes and `annotate` reads. + value = value["bits"] + if value is not None: + params[name] = value + return params + def show_hyperparameters( app_ctx: AppContext, netuid: int, - params: dict[str, Any], + params: Optional[dict[str, Any]], name: Optional[str], hint: Optional[str] = None, ) -> None: @@ -30,6 +60,9 @@ def show_hyperparameters( ``hint`` overrides the listing's default help line (the `sudo set` prompt flow reuses the listing with its own guidance). """ + if params is None: + app_ctx.output.error(f"subnet {netuid} does not exist") + raise typer.Exit(1) if name is None: _listing(app_ctx, netuid, params, hint) return @@ -45,13 +78,17 @@ def show_hyperparameters( def _listing( app_ctx: AppContext, netuid: int, params: dict[str, Any], hint: Optional[str] = None ) -> None: - rows = [(key, str(value), hp.annotate(key, value)) for key, value in params.items()] + rows = [ + (key, str(value), hp.annotate(key, value), hp.short_of(key)) + for key, value in params.items() + ] example = "kappa" if "kappa" in params else next(iter(params), "tempo") app_ctx.output.hyperparameters( f"hyperparameters netuid {netuid}", rows, params, hint=hint or f"`--name {example}` explains one parameter and how to set it", + docs=HYPERPARAMS_DOCS_URL, ) @@ -91,6 +128,7 @@ def _single(app_ctx: AppContext, netuid: int, name: str, raw: Any) -> None: }, help=help_text, note=note, + see=f"{HYPERPARAMS_DOCS_URL}/{name.replace('_', '-')}", ) @@ -100,6 +138,8 @@ def _example_value(kind: str, fraction: Optional[float], raw: Any) -> str: return _with_decimal_point(f"{fraction:.4g}") if kind == "rao" and isinstance(raw, int): return _with_decimal_point(f"{raw / hp.RAO_PER_TAO:g}") + if kind == "fixed128" and isinstance(raw, int): + return _with_decimal_point(f"{raw / hp.FIXED128_ONE:g}") if kind == "bool": return "true" if raw else "false" return str(raw) diff --git a/sdk/python/bittensor/cli/main.py b/sdk/python/bittensor/cli/main.py index a5bf86f889..cb43f8c1fb 100644 --- a/sdk/python/bittensor/cli/main.py +++ b/sdk/python/bittensor/cli/main.py @@ -22,9 +22,10 @@ from .. import __version__, wallets from .._generated.errors import ERRORS from ..config import get as config_default +from ..error_descriptions import DESCRIPTIONS from ..intents import list_tools -from ..result import EXPLANATIONS, REMEDIATION, ErrorCode, classify_error -from ..settings import DEFAULT_NETWORK +from ..result import EXPLANATIONS, REMEDIATION, ChainError, ErrorCode, classify_error +from ..settings import DEFAULT_NETWORK, chain_error_docs_url, error_docs_url from . import globals as g from . import help_theme # noqa: F401 (restyles typer's --help at import) from .call import call as call_command @@ -33,7 +34,6 @@ axon, config, crowd, - deriv, evm, extension, lock, @@ -95,7 +95,6 @@ def list_commands(self, ctx) -> list[str]: app.add_typer(utils.app, name="utils") app.add_typer(lock.app, name="lock", rich_help_panel=PANEL_STAKING) -app.add_typer(deriv.app, name="deriv", rich_help_panel=PANEL_STAKING) app.add_typer(crowd.app, name="crowd", rich_help_panel=PANEL_STAKING) app.add_typer(weights.app, name="weights", rich_help_panel=PANEL_OPERATORS) @@ -119,7 +118,6 @@ def list_commands(self, ctx) -> list[str]: (config.app, ("c", "conf")), (weights.app, ("wt", "weight")), (crowd.app, ("cr", "crowdloan")), - (deriv.app, ("d",)), ): for _alias in _aliases: app.add_typer(_sub_app, name=_alias, hidden=True) @@ -326,7 +324,17 @@ def _chain_error_matches(query: str) -> list[dict[str, str]]: "name": info.name, "code": code.value, "docs": info.docs, - "help": REMEDIATION[code], + "description": DESCRIPTIONS.get(info.name, ""), + # Same remediation a failure would print (per-name overrides + # like SlippageTooHigh's tolerance flags included). + "help": ChainError("", name=info.name).remediation, + # The exact name's page when it is classified (and thus has + # one), the semantic code's page otherwise. + "docs_url": ( + chain_error_docs_url(info.name) + if info.name in DESCRIPTIONS + else error_docs_url(code.value) + ), } ) return matches @@ -347,6 +355,7 @@ def _chain_error_catalog(out: Output, pallet: Optional[str]) -> None: "name": info.name, "code": classify_error(info.docs, info.name).value, "description": info.docs, + "docs_url": (chain_error_docs_url(info.name) if info.name in DESCRIPTIONS else None), } for info in ERRORS.values() if pallet is None or info.pallet.lower() == pallet.lower() diff --git a/sdk/python/bittensor/cli/metagraph_view.py b/sdk/python/bittensor/cli/metagraph_view.py index 1967c84630..3ef561c3cb 100644 --- a/sdk/python/bittensor/cli/metagraph_view.py +++ b/sdk/python/bittensor/cli/metagraph_view.py @@ -4,7 +4,8 @@ primary, dim readings beside them — the hyperparameters convention); the per-uid arrays collapse into one tree branch per neuron, largest stake first, hotkeys labeled with their local wallet name when one exists, else their -on-chain identity name. JSON carries the raw metagraph record untouched. +on-chain identity name. JSON carries the runtime record as the read returns +it (untouched except `name`/`symbol`, which the read decodes to text). """ from __future__ import annotations diff --git a/sdk/python/bittensor/cli/output.py b/sdk/python/bittensor/cli/output.py index d614b0e4fa..945c4549cb 100644 --- a/sdk/python/bittensor/cli/output.py +++ b/sdk/python/bittensor/cli/output.py @@ -27,7 +27,12 @@ from ..balance import Balance from ..intents import Plan from ..result import ErrorCode, ExtrinsicResult -from ..settings import explorer_account_url, explorer_extrinsic_url, explorer_subnet_url +from ..settings import ( + error_docs_url, + explorer_account_url, + explorer_extrinsic_url, + explorer_subnet_url, +) from . import multisig_helpers as ms_helpers # Stripe-muted palette: almost monochrome — dim for structure (titles, keys, @@ -427,33 +432,79 @@ def detail( return self._print_fields(fields) - def _print_fields(self, fields: dict[str, Any]) -> None: - """Aligned key/value block: dim keys, values colored by semantic role.""" - width = max((len(k) for k in fields), default=0) - for key, value in fields.items(): - # Built with Text (not markup) so values containing "[" render as-is. - line = Text(" ") - line.append(key.rjust(width), style=STYLE_KEY) + def _print_fields(self, fields: dict[str, Any], indent: int = 2) -> None: + """Aligned key/value block: dim keys, values colored by semantic role. + + Structured values stay legible instead of printing as raw reprs: dict + values recurse as indented sub-blocks, lists of records render one + compact line per record, and lists of scalars join with commas. + """ + # Reads like subnet_names / weights key by int; coerce for display only. + labels = [(str(key), value) for key, value in fields.items()] + width = max((len(label) for label, _ in labels), default=0) + for label, value in labels: + # Built with Text (not markup) so values containing "[" render + # as-is; never wrapped (addresses and hashes are copy targets). + line = Text(" " * indent, overflow="ignore", no_wrap=True) + line.append(label.rjust(width), style=STYLE_KEY) + if isinstance(value, (dict, list)) and not value: + line.append(" ") + line.append("none", style="dim") + self._out.print(line) + continue + if isinstance(value, dict): + self._out.print(line) + self._print_fields(value, indent + 2) + continue + if isinstance(value, list) and all(isinstance(item, dict) for item in value): + self._out.print(line) + for item in value: + self._out.print(self._record_line(item, indent + 2), soft_wrap=True) + continue line.append(" ") - if key.endswith("netuid") and str(value).isdigit(): + if isinstance(value, list): + value = ", ".join(str(item) for item in value) + if label.endswith("netuid") and str(value).isdigit(): line.append_text(self.subnet_text(value)) else: line.append_text( self._linked_text( - str(value), _value_style(key, value), _address_kind_for_key(key) + str(value), + _value_style(label, value), + _address_kind_for_key(label), ) ) - self._out.print(line) + self._out.print(line, soft_wrap=True) + + def _record_line(self, record: dict[str, Any], indent: int) -> Text: + """One record as a compact ``key value key value`` line (list items + inside a detail block, e.g. proxies or stake positions).""" + line = Text(" " * indent, overflow="ignore", no_wrap=True) + for index, (key, value) in enumerate(record.items()): + label = str(key) + if index: + line.append(" ") + line.append(f"{label} ", style=STYLE_KEY) + line.append_text( + self._linked_text( + str(value), _value_style(label, value), _address_kind_for_key(label) + ) + ) + return line def hyperparameters( self, title: str, - rows: list[tuple[str, str, Optional[str]]], + rows: list[tuple[str, str, Optional[str], Optional[str]]], json_fields: dict[str, Any], hint: Optional[str] = None, + docs: Optional[str] = None, ) -> None: - """Aligned hyperparameter listing: raw value plus its dim human reading - (``kappa 32767 ≈ 0.5``). JSON carries the raw record untouched.""" + """Aligned hyperparameter listing: raw value, its dim human reading, and + a one-line description (``kappa 32767 ≈ 0.5 consensus majority-stake + threshold``). ``docs`` links each name (OSC-8) to its explainer page + under that URL and prints as a ``see:`` footer. JSON carries the raw + record untouched.""" if self.json_mode: self._json(json_fields) return @@ -461,19 +512,28 @@ def hyperparameters( if not rows: self._out.print(" [dim]none[/dim]") return - name_width = max(len(name) for name, _, _ in rows) - value_width = max(len(value) for _, value, _ in rows) - for name, value, note in rows: + name_width = max(len(name) for name, _, _, _ in rows) + value_width = max(len(value) for _, value, _, _ in rows) + note_width = max((len(note) for _, _, note, _ in rows if note), default=0) + for name, value, note, short in rows: line = Text(" ", overflow="ignore", no_wrap=True) - line.append(name.rjust(name_width), style=STYLE_KEY) + name_style = f"{STYLE_KEY} link {docs}/{name.replace('_', '-')}" if docs else STYLE_KEY + line.append(name.rjust(name_width), style=name_style) line.append(" ") line.append(value.rjust(value_width)) - if note: + if short: + # The reading column is padded so descriptions align. + line.append(f" {(note or '').ljust(note_width)}", style=STYLE_HINT) + line.append(f" {short}", style=STYLE_INCIDENTAL) + elif note: line.append(f" {note}", style=STYLE_HINT) self._out.print(line, soft_wrap=True) - if hint: + if hint or docs: self._out.print() + if hint: self._sub_diag("help", hint, console=self._out) + if docs: + self._sub_diag("see", docs, console=self._out) def hyperparameter( self, @@ -484,6 +544,7 @@ def hyperparameter( *, help: Optional[str] = None, note: Optional[str] = None, + see: Optional[str] = None, ) -> None: """One hyperparameter in full: value fields, the explanation paragraph, and the how-to-set diagnostics.""" @@ -495,12 +556,14 @@ def hyperparameter( if doc: self._out.print() self._out.print(Padding(_prose(doc), (0, 0, 0, 2))) - if help or note: + if help or note or see: self._out.print() if help: self._sub_diag("help", help, console=self._out) if note: self._sub_diag("note", note, console=self._out) + if see: + self._sub_diag("see", see, console=self._out) def table( self, @@ -1496,7 +1559,11 @@ def _print_failure(self, result: ExtrinsicResult) -> None: return if error.name: self._sub_diag("note", f"the chain rejected the call with `{error.name}`") + if error.description: + self._sub_diag("note", error.description) self._sub_diag("help", error.remediation) + if error.docs_url: + self._sub_diag("see", error.docs_url) if result.explorer_url: self._sub_diag("see", result.explorer_url) # The exact chain name gives the most specific explanation; the semantic @@ -1514,13 +1581,21 @@ def _print_failure(self, result: ExtrinsicResult) -> None: def explain(self, code: str, explanation: str, help_text: str) -> None: """Long-form explanation of one error code (`rustc --explain` convention).""" if self.json_mode: - self._json({"code": code, "explanation": explanation, "help": help_text}) + self._json( + { + "code": code, + "explanation": explanation, + "help": help_text, + "see": error_docs_url(code), + } + ) return self._out.print(Text(f"error[{code}]", style=STYLE_ERROR)) self._out.print() self._out.print(_prose(explanation)) self._out.print() self._sub_diag("help", help_text, console=self._out) + self._sub_diag("see", error_docs_url(code), console=self._out) def error_catalog(self, title: str, records: list[dict[str, str]]) -> None: """The chain error catalog as a gh-style listing: one dim pallet header @@ -1541,7 +1616,8 @@ def error_catalog(self, title: str, records: list[dict[str, str]]) -> None: self._out.print() self._out.print(Text(pallet, style=STYLE_KEY)) line = Text(" ", overflow="ignore", no_wrap=True) - line.append(record["name"].ljust(width)) + url = record.get("docs_url") + line.append(record["name"].ljust(width), style=f"link {url}" if url else "") line.append(" ") line.append(record["code"], style=STYLE_KEY) self._out.print(line, soft_wrap=True) @@ -1554,8 +1630,10 @@ def error_catalog(self, title: str, records: list[dict[str, str]]) -> None: self._out.print(f"[dim]{len(records)} {suffix}[/dim]") def explain_chain(self, matches: list[dict[str, str]]) -> None: - """Explain exact chain error names: the on-chain docs plus the semantic - code each classifies to. One block per pallet when a name collides.""" + """Explain exact chain error names: the researched description (trigger + plus where to check) with the on-chain docs as fallback, and the + semantic code each classifies to. One block per pallet when a name + collides.""" if self.json_mode: self._json(matches[0] if len(matches) == 1 else matches) return @@ -1568,9 +1646,12 @@ def explain_chain(self, matches: list[dict[str, str]]) -> None: header.append(f"{match['pallet']}.{match['name']}") self._out.print(header) self._out.print() - self._out.print(_prose(_diagnostic(match["docs"] or match["name"]))) + body = match.get("description") or match["docs"] or match["name"] + self._out.print(_prose(_diagnostic(body))) self._out.print() self._sub_diag("help", match["help"], console=self._out) + see = match.get("docs_url") or error_docs_url(match["code"]) + self._sub_diag("see", see, console=self._out) for code in dict.fromkeys(match["code"] for match in matches): tail = _prose(f"for more information about this code, run `btcli explain {code}`") tail.style = STYLE_HINT diff --git a/sdk/python/bittensor/cli/query.py b/sdk/python/bittensor/cli/query.py index 688b7cadc1..9d91aad1aa 100644 --- a/sdk/python/bittensor/cli/query.py +++ b/sdk/python/bittensor/cli/query.py @@ -15,9 +15,10 @@ import typer from ..balance import Balance -from ..reads import REGISTRY +from ..reads import REGISTRY, Grouped, Matrix from . import globals as g from .context import address_cli_name, ctx_of, ss58_param_help +from .output import Output _TYPES = {"string": str, "integer": int, "number": float, "boolean": bool} @@ -37,6 +38,22 @@ def _jsonable(obj: Any) -> Any: return obj +def _records_table(output: Output, name: str, records: list[dict]) -> None: + """Render a list of records as a table, hiding opaque columns. + + Columns whose values are themselves dicts/lists (e.g. the neurons read's + ``raw`` blob) are unreadable in a table cell, so they are dropped from the + human view — ``--json`` carries the full records. + """ + if not records: + output.detail(name, {}) # title + the dim `none` convention + return + cols = [c for c, v in records[0].items() if not isinstance(v, (dict, list))] + cols = cols or list(records[0].keys()) + rows = [[r.get(c) for c in cols] for r in records] + output.table(name, cols, rows, records) + + def _make_command(name: str, spec): array_params = [p for p, t in spec.params.items() if t == "array"] @@ -50,16 +67,35 @@ def command(ctx: typer.Context, **kwargs: Any) -> None: kwargs[pname] = app_ctx.resolve_address(pname, kwargs.get(pname)) result = app_ctx.run(lambda client: client.read(name, **kwargs)) payload = _jsonable(result) - if app_ctx.output.json_mode: - app_ctx.output.value(payload) - elif isinstance(payload, list) and payload and isinstance(payload[0], dict): - cols = list(payload[0].keys()) - rows = [[r.get(c) for c in cols] for r in payload] - app_ctx.output.table(name, cols, rows, payload) + output = app_ctx.output + if output.json_mode: + output.value(payload) + elif payload is None or (isinstance(payload, (list, dict)) and not payload): + output.detail(name, {}) # title + the dim `none` convention + elif isinstance(spec.render, Grouped): + key = spec.render.key + _records_table( + output, + name, + [{key: group, **item} for group, items in payload.items() for item in items or []], + ) + elif isinstance(spec.render, Matrix): + hint = spec.render + _records_table( + output, + name, + [ + {hint.row: row, hint.col: col, hint.value: value} + for row, cells in payload.items() + for col, value in cells.items() + ], + ) + elif isinstance(payload, list) and isinstance(payload[0], dict): + _records_table(output, name, payload) elif isinstance(payload, dict): - app_ctx.output.detail(name, payload) + output.detail(name, payload) else: - app_ctx.output.value(payload) + output.value(payload) params = [ inspect.Parameter("ctx", inspect.Parameter.POSITIONAL_OR_KEYWORD, annotation=typer.Context) @@ -94,11 +130,14 @@ def command(ctx: typer.Context, **kwargs: Any) -> None: else: option_default = ... annotations[pname] = Optional[base_type] if wallet_defaulted else base_type + # Bool options get a negated twin (--flag/--no-flag) so default-True + # fields (e.g. lite) can be turned off — same pattern as `tx.py`. + flag_name = f"{cli_name}/--no-{cli_name[2:]}" if ptype == "boolean" else cli_name params.append( inspect.Parameter( pname, inspect.Parameter.KEYWORD_ONLY, - default=typer.Option(option_default, cli_name, help=help_text), + default=typer.Option(option_default, flag_name, help=help_text), annotation=annotations[pname], ) ) diff --git a/sdk/python/bittensor/error_descriptions/__init__.py b/sdk/python/bittensor/error_descriptions/__init__.py new file mode 100644 index 0000000000..0e92a20313 --- /dev/null +++ b/sdk/python/bittensor/error_descriptions/__init__.py @@ -0,0 +1,66 @@ +"""Per-chain-error descriptions: what the error means and where to check. + +Every name classified in :mod:`bittensor.error_map` has a description here that +tells an agent what condition triggered the error and which state, argument, or +query to inspect. ``python -m codegen.check --names`` enforces that this table +and ``NAME_TO_CODE`` cover exactly the same names, so a newly classified error +must be described before it can ship. + +Descriptions are split by the pallet that (first) declares the error. Import +:data:`DESCRIPTIONS` from this package — the public API is unchanged. +""" + +from __future__ import annotations + +from .admin_utils import DESCRIPTIONS as _ADMIN_UTILS +from .balances import DESCRIPTIONS as _BALANCES +from .commitments import DESCRIPTIONS as _COMMITMENTS +from .contracts import DESCRIPTIONS as _CONTRACTS +from .crowdloan import DESCRIPTIONS as _CROWDLOAN +from .drand import DESCRIPTIONS as _DRAND +from .ethereum import DESCRIPTIONS as _ETHEREUM +from .evm import DESCRIPTIONS as _EVM +from .grandpa import DESCRIPTIONS as _GRANDPA +from .limit_orders import DESCRIPTIONS as _LIMIT_ORDERS +from .mev_shield import DESCRIPTIONS as _MEV_SHIELD +from .multisig import DESCRIPTIONS as _MULTISIG +from .preimage import DESCRIPTIONS as _PREIMAGE +from .proxy import DESCRIPTIONS as _PROXY +from .safe_mode import DESCRIPTIONS as _SAFE_MODE +from .scheduler import DESCRIPTIONS as _SCHEDULER +from .subtensor import DESCRIPTIONS as _SUBTENSOR +from .sudo import DESCRIPTIONS as _SUDO +from .swap import DESCRIPTIONS as _SWAP +from .system import DESCRIPTIONS as _SYSTEM +from .utility import DESCRIPTIONS as _UTILITY + +DESCRIPTIONS: dict[str, str] = {} +for _part in ( + _ADMIN_UTILS, + _BALANCES, + _COMMITMENTS, + _CONTRACTS, + _CROWDLOAN, + _DRAND, + _EVM, + _ETHEREUM, + _GRANDPA, + _LIMIT_ORDERS, + _MEV_SHIELD, + _MULTISIG, + _PREIMAGE, + _PROXY, + _SAFE_MODE, + _SCHEDULER, + _SUBTENSOR, + _SUDO, + _SWAP, + _SYSTEM, + _UTILITY, +): + overlap = DESCRIPTIONS.keys() & _part.keys() + if overlap: + raise RuntimeError(f"duplicate error descriptions: {sorted(overlap)}") + DESCRIPTIONS.update(_part) + +__all__ = ["DESCRIPTIONS"] diff --git a/sdk/python/bittensor/error_descriptions/admin_utils.py b/sdk/python/bittensor/error_descriptions/admin_utils.py new file mode 100644 index 0000000000..8632aabdc5 --- /dev/null +++ b/sdk/python/bittensor/error_descriptions/admin_utils.py @@ -0,0 +1,65 @@ +"""Chain error descriptions declared (first) by the `AdminUtils` pallet.""" + +from __future__ import annotations + +DESCRIPTIONS: dict[str, str] = { + "BondsMovingAverageMaxReached": ( + "A subnet owner called `sudo_set_bonds_moving_average` with a value above 975000, the " + "cap for owner-set values; root is exempt. Lower the `bonds_moving_average` argument or " + "submit the call as root." + ), + "MaxAllowedUIdsLessThanCurrentUIds": ( + "`sudo_set_max_allowed_uids` was given a value below the number of neurons already " + "registered on the subnet. Compare the `max_allowed_uids` argument against " + "`SubnetworkN` for that netuid." + ), + "MaxAllowedUidsGreaterThanDefaultMaxAllowedUids": ( + "`sudo_set_max_allowed_uids` was given a value above the chain-wide ceiling. Compare " + "the `max_allowed_uids` argument against the `DefaultMaxAllowedUids` storage value." + ), + "MaxAllowedUidsLessThanMinAllowedUids": ( + "`sudo_set_max_allowed_uids` was given a value below the subnet's configured minimum. " + "Compare the `max_allowed_uids` argument against `MinAllowedUids` for that netuid." + ), + "MaxValidatorsLargerThanMaxUIds": ( + "`sudo_set_max_allowed_validators` was given a value exceeding the subnet's UID " + "capacity. Compare the `max_allowed_validators` argument against `MaxAllowedUids` for " + "that netuid." + ), + "MinAllowedUidsGreaterThanCurrentUids": ( + "`sudo_set_min_allowed_uids` was given a value not strictly below the number of neurons " + "currently registered on the subnet. Compare the `min_allowed_uids` argument against " + "`SubnetworkN` for that netuid." + ), + "MinAllowedUidsGreaterThanMaxAllowedUids": ( + "`sudo_set_min_allowed_uids` was given a value not strictly below the subnet's maximum " + "UID capacity. Compare the `min_allowed_uids` argument against `MaxAllowedUids` for " + "that netuid." + ), + "NegativeSigmoidSteepness": ( + "A non-root caller (subnet owner) passed a negative value to " + "`sudo_set_alpha_sigmoid_steepness`; negative steepness values are reserved for the " + "root origin. Use a non-negative `steepness` or submit the call as root." + ), + "NotPermittedOnRootSubnet": ( + "An admin-utils call that only applies to regular subnets (burn half-life, burn " + "increase multiplier, owner-cut flags, or the subnet emission toggle) was targeted at " + "the root network. Check that the `netuid` argument is not the root netuid 0." + ), + "POWRegistrationDisabled": ( + "`sudo_set_network_pow_registration_allowed` unconditionally fails because " + "proof-of-work registration is deprecated and its toggle can no longer be changed. " + "Nothing to check; the call is permanently disabled." + ), + "SubnetDoesNotExist": ( + "The admin-utils call targets a netuid with no registered subnet. Verify the `netuid` " + "argument against `NetworksAdded` (the set of existing subnets) before setting " + "hyperparameters." + ), + "ValueNotInBounds": ( + "An admin-utils argument fell outside its allowed range: `min_burn` must be below " + "`MinBurnUpperBound` and the subnet's max burn, `max_burn` above `MaxBurnLowerBound` " + "and the min burn, and `max_epochs_per_block` at least 1. Check the argument against " + "those bounds." + ), +} diff --git a/sdk/python/bittensor/error_descriptions/balances.py b/sdk/python/bittensor/error_descriptions/balances.py new file mode 100644 index 0000000000..a3a4736e49 --- /dev/null +++ b/sdk/python/bittensor/error_descriptions/balances.py @@ -0,0 +1,69 @@ +"""Chain error descriptions declared (first) by the `Balances` pallet.""" + +from __future__ import annotations + +DESCRIPTIONS: dict[str, str] = { + "DeadAccount": ( + "The beneficiary account does not exist and this operation is not allowed to create it. " + "Check `System.Account` for the destination: it must already hold at least the " + "existential deposit before the operation runs." + ), + "DeltaZero": ( + "The issuance adjustment was called with a delta of zero, which is meaningless. Check " + "the `delta` argument to `force_adjust_total_issuance` and pass a strictly positive " + "amount." + ), + "ExistentialDeposit": ( + "The amount is too small to create the destination account: its resulting balance would " + "sit below the existential deposit. Compare the transfer value plus the destination's " + "current free balance against the `ExistentialDeposit` constant." + ), + "ExistingVestingSchedule": ( + "A vesting schedule already exists for the target account and this call cannot add " + "another. Check the account's `Vesting` storage entry before attempting to set a new " + "vested transfer or schedule." + ), + "Expendability": ( + "The transfer or payment would drop the sender below the existential deposit and kill " + "the account while keep-alive semantics are required. Compare the sender's free balance " + "minus the amount and fees against `ExistentialDeposit`, or use `transfer_allow_death` " + "if reaping is acceptable." + ), + "InsufficientBalance": ( + "The caller's spendable balance is below what the operation needs, whether a plain " + "transfer, a crowdloan deposit or contribution, or a swap. Compare the account's free " + "balance in `System.Account` (net of holds, freezes, and fees) against the amount " + "being moved." + ), + "IssuanceDeactivated": ( + "Total issuance cannot be adjusted because issuance has already been deactivated. Check " + "the Balances `InactiveIssuance` state before calling `force_adjust_total_issuance` " + "again." + ), + "LiquidityRestrictions": ( + "The withdrawal is blocked by locks or freezes on the account even though the raw " + "balance looks sufficient. Check the account's `Locks` and `Freezes` entries and " + "compare the frozen amount against what would remain after the withdrawal." + ), + "TooManyFreezes": ( + "The account already has the maximum number of balance freezes, so a new freeze cannot " + "be added. Check the account's `Freezes` entry against the `MaxFreezes` constant; an " + "existing freeze must be thawed first." + ), + "TooManyHolds": ( + "The account already carries the maximum number of balance holds, one per hold reason " + "variant. Check the account's `Holds` entry; an existing hold must be released before a " + "new reason can place another." + ), + "TooManyReserves": ( + "The account already has the maximum number of named reserves. Check the account's " + "`Reserves` entry against the `MaxReserves` constant; a named reserve must be " + "unreserved before adding another." + ), + "VestingBalance": ( + "The account's balance is locked under a vesting schedule, leaving too little usable " + "balance to send the requested value. Check the account's `Vesting` schedules and its " + "lock in `Locks`, and compare the unvested (still locked) amount against what the " + "transfer needs." + ), +} diff --git a/sdk/python/bittensor/error_descriptions/commitments.py b/sdk/python/bittensor/error_descriptions/commitments.py new file mode 100644 index 0000000000..32d6f2824b --- /dev/null +++ b/sdk/python/bittensor/error_descriptions/commitments.py @@ -0,0 +1,26 @@ +"""Chain error descriptions declared (first) by the `Commitments` pallet.""" + +from __future__ import annotations + +DESCRIPTIONS: dict[str, str] = { + "AccountNotAllowedCommit": ( + "Raised by `set_commitment` when the runtime commit check fails: the subnet must exist " + "and the signing hotkey must be registered on it. Verify the `netuid` and that the " + "hotkey has a UID on that subnet." + ), + "SpaceLimitExceeded": ( + "The commitment would push the account's byte quota for the current epoch over the cap; " + "each `set_commitment` consumes at least 100 bytes. Check `UsedSpaceOf` for the netuid " + "and account against `MaxSpace`, or wait for the next epoch to reset usage." + ), + "TooManyFieldsInCommitmentInfo": ( + "The `CommitmentInfo` passed to `set_commitment` contains more entries in `fields` than " + "the pallet's `MaxFields` config allows. Count the fields in the `info` argument and " + "trim to the `MaxFields` limit." + ), + "UnexpectedUnreserveLeftover": ( + "While lowering a commitment deposit, `Currency::unreserve` failed to return the full " + "difference, leaving a leftover, which signals an internal inconsistency. Check the " + "account's reserved balance against the deposit recorded in `CommitmentOf`." + ), +} diff --git a/sdk/python/bittensor/error_descriptions/contracts.py b/sdk/python/bittensor/error_descriptions/contracts.py new file mode 100644 index 0000000000..e87ff1eba4 --- /dev/null +++ b/sdk/python/bittensor/error_descriptions/contracts.py @@ -0,0 +1,193 @@ +"""Chain error descriptions declared (first) by the `Contracts` pallet.""" + +from __future__ import annotations + +DESCRIPTIONS: dict[str, str] = { + "CannotAddSelfAsDelegateDependency": ( + "A contract called `lock_delegate_dependency` with its own code hash, which is not " + "permitted. Check the code hash argument passed to the delegate dependency API against " + "the contract's own code hash." + ), + "CodeInUse": ( + "`remove_code` was refused because at least one contract instance still references the " + "code hash. Check the code's reference count and terminate or `set_code` the contracts " + "using it before removal." + ), + "CodeInfoNotFound": ( + "No `CodeInfoOf` entry exists for the supplied code hash, so its owner and deposit " + "metadata cannot be read. Verify the code hash argument and that the code was uploaded " + "and not since removed." + ), + "CodeNotFound": ( + "No uploaded WASM binary exists under the supplied code hash. Verify the `code_hash` " + "argument used in instantiation, `set_code`, or a delegate call, and that the code was " + "uploaded via `upload_code` and not removed." + ), + "CodeRejected": ( + "The uploaded WASM failed validation, most often because it imports a host API the node " + "does not support, e.g. newer ink! against an older node. Rerun the node with " + "`-lruntime::contracts=debug` to see the detailed rejection reason." + ), + "CodeTooLarge": ( + "The code blob passed to `instantiate_with_code` or `upload_code` exceeds the maximum " + "code length in the pallet's schedule. Compare the WASM binary size against the " + "schedule's code length limit and shrink the contract." + ), + "ContractNotFound": ( + "No contract instance exists at the destination address; the account has no " + "`ContractInfoOf` entry. Verify the `dest` address and that the contract was " + "instantiated and has not been terminated." + ), + "ContractReverted": ( + "The contract ran to completion but returned with the REVERT flag set, rolling back its " + "state changes; only extrinsics surface this as an error. Dry-run the call via RPC and " + "decode the returned output data for the contract's error value." + ), + "ContractTrapped": ( + "The contract aborted with a WASM trap, e.g. a panic, unreachable instruction, or " + "memory violation, instead of returning normally. Dry-run the call with debug messages " + "enabled and check the input data against the contract's expectations." + ), + "DecodingFailed": ( + "Input bytes passed to a contract API host function could not be SCALE-decoded into the " + "expected type. Check the encoding of the call's input data or the argument bytes the " + "contract passes to the runtime API." + ), + "DelegateDependencyAlreadyExists": ( + "The contract called `lock_delegate_dependency` for a code hash it has already locked. " + "Check the contract's recorded delegate dependencies before adding, and unlock the old " + "entry first if replacing it." + ), + "DelegateDependencyNotFound": ( + "`unlock_delegate_dependency` was called for a code hash that is not among the " + "contract's locked delegate dependencies. Check the code hash argument against the " + "dependencies recorded in the contract's info." + ), + "DuplicateContract": ( + "Instantiation would create a contract at an address already occupied by an existing " + "contract. Check the derived contract address and vary the `salt` argument to obtain a " + "fresh address." + ), + "Indeterministic": ( + "Code flagged as non-deterministic (e.g. using floating point) was used where " + "determinism is enforced, such as on-chain instantiation or calls. Check the " + "determinism mode the code was uploaded with and rebuild the contract " + "deterministically." + ), + "InputForwarded": ( + "The contract forwarded its input to a callee via `seal_call` with the FORWARD_INPUT " + "flag and then tried to read or forward it again. Check the call flags used; use " + "CLONE_INPUT when the input is still needed afterwards." + ), + "InvalidCallFlags": ( + "The flags bitmask given to `seal_call` or `seal_delegate_call` contains an unknown or " + "disallowed combination; delegate calls accept only a restricted flag set. Check the " + "flags argument against the supported `CallFlags` bit values." + ), + "InvalidSchedule": ( + "The pallet's schedule is misconfigured, e.g. a zero weight or zero `ref_time_by_fuel` " + "for a basic operation, making gas conversion impossible. This is a runtime " + "configuration issue; inspect the `Schedule` constant rather than call arguments." + ), + "MaxCallDepthReached": ( + "A nested contract call would exceed the maximum call stack depth defined in the pallet " + "schedule. Inspect the cross-contract call chain for deep or unbounded recursion and " + "flatten it or raise the configured depth." + ), + "MaxDelegateDependenciesReached": ( + "`lock_delegate_dependency` failed because the contract already holds the maximum " + "number of delegate dependencies allowed by the runtime. Check the " + "`MaxDelegateDependencies` config value and unlock unused dependencies first." + ), + "MigrationInProgress": ( + "A multi-block storage migration of the contracts pallet is still running, so other " + "extrinsics of the pallet are rejected until it completes. Check the migration status " + "in storage and retry once done, or submit `migrate` calls to advance it." + ), + "NoChainExtension": ( + "The contract invoked `call_chain_extension` but this runtime registers no chain " + "extension; such code is normally rejected at upload. Check that the target chain " + "provides the chain extension the contract was built against." + ), + "NoMigrationPerformed": ( + "A `migrate` call ran but no migration step executed, either because no migration is " + "pending or the supplied `weight_limit` is too small for one step. Check the " + "in-progress migration status and increase the weight limit argument." + ), + "OutOfBounds": ( + "A pointer and length pair passed to a contract API host function references memory " + "outside the contract's sandbox. Check the buffer pointers and lengths the contract " + "passes to seal functions; this usually indicates a low-level contract bug." + ), + "OutOfGas": ( + "The contract exhausted the gas limit supplied for this execution before completing. " + "Increase the `gas_limit` argument on `call` or `instantiate`; dry-run the call via RPC " + "to estimate the required weight." + ), + "OutOfTransientStorage": ( + "A write would exceed the per-execution byte limit for transient storage. Check how " + "much data the contract places in transient storage during the call against the " + "runtime's transient storage limit." + ), + "OutputBufferTooSmall": ( + "The output buffer the contract supplied to an API call is smaller than the data the " + "runtime needs to write back. Check the output length pointer the contract passes and " + "enlarge the buffer; usually a contract-side bug." + ), + "RandomSubjectTooLong": ( + "The subject buffer given to the deprecated `seal_random` API exceeds the schedule's " + "`subject_len` limit. Shorten the randomness subject the contract passes or check the " + "schedule's limits section." + ), + "ReentranceDenied": ( + "A call tried to re-enter a contract already on the call stack without reentrancy being " + "allowed, or contract code called back into the contracts pallet through the runtime. " + "Check the callee address against the current call stack and the ALLOW_REENTRY call " + "flag." + ), + "StateChangeDenied": ( + "The contract invoked a state-modifying host function, such as a storage write, " + "transfer, or value-bearing call, while executing in read-only mode. Check whether the " + "enclosing call was made with the read-only flag or from a static context." + ), + "StorageDepositLimitExhausted": ( + "The execution created more storage than the caller's `storage_deposit_limit` allows to " + "be charged. Raise the `storage_deposit_limit` argument or reduce storage usage; a " + "dry-run reports the required `storage_deposit`." + ), + "StorageDepositNotEnoughFunds": ( + "The origin's free balance cannot cover the storage deposit limit specified or required " + "for this call. Check the caller's withdrawable balance against the " + "`storage_deposit_limit` argument and the dry-run's reported deposit." + ), + "TerminatedInConstructor": ( + "The contract called `seal_terminate` inside its constructor, self-destructing during " + "instantiation, which is forbidden. Inspect the constructor logic; termination is only " + "allowed in regular message calls." + ), + "TerminatedWhileReentrant": ( + "`seal_terminate` was called on a contract that appears more than once on the call " + "stack, so termination was refused. Check the call chain for reentrant calls into the " + "contract being terminated." + ), + "TooManyTopics": ( + "The number of topics passed to `seal_deposit_event` exceeds the schedule's " + "`event_topics` limit. Reduce the number of indexed topics in the contract's event " + "definition or compare against the schedule limit." + ), + "TransferFailed": ( + "A balance transfer performed during the contract call failed, most likely because the " + "sender lacks enough free balance. Check the transferring account's free balance " + "against the `value` being sent, accounting for the existential deposit." + ), + "ValueTooLarge": ( + "A value written to contract storage or emitted as event data exceeds the " + "`MaxValueSize` limit. Compare the size of the stored value or event payload against " + "the runtime's maximum value size constant." + ), + "XCMDecodeFailed": ( + "The bytes the contract passed to `xcm_execute` or `xcm_send` could not be decoded as a " + "versioned XCM message. Check the XCM encoding and version the contract produces " + "against what the runtime supports." + ), +} diff --git a/sdk/python/bittensor/error_descriptions/crowdloan.py b/sdk/python/bittensor/error_descriptions/crowdloan.py new file mode 100644 index 0000000000..bf55b03e08 --- /dev/null +++ b/sdk/python/bittensor/error_descriptions/crowdloan.py @@ -0,0 +1,133 @@ +"""Chain error descriptions declared (first) by the `Crowdloan` pallet.""" + +from __future__ import annotations + +DESCRIPTIONS: dict[str, str] = { + "AlreadyFinalized": ( + "The crowdloan's `finalized` flag is already true, so withdraw, finalize, refund, " + "dissolve, and the update calls are all rejected. Check the `finalized` field of the " + "`Crowdloans` entry for the given `crowdloan_id`." + ), + "AlreadyFinalizing": ( + "A `finalize` call was made while another finalization is still in progress, i.e. the " + "dispatched call from a previous finalize has not cleared. Check that the " + "`CurrentCrowdloanId` storage value is empty before retrying." + ), + "BlockDurationTooLong": ( + "The requested `end` block is more than `MaximumBlockDuration` blocks after the current " + "block. Compare `end` minus the current block number against the `MaximumBlockDuration` " + "pallet constant when calling `create` or `update_end`." + ), + "BlockDurationTooShort": ( + "The requested `end` block is fewer than `MinimumBlockDuration` blocks after the " + "current block. Compare `end` minus the current block number against the " + "`MinimumBlockDuration` pallet constant when calling `create` or `update_end`." + ), + "CallUnavailable": ( + "During `finalize` the crowdloan's stored call could not be fetched from preimage " + "storage, so nothing was dispatched. Check that the preimage referenced by the `call` " + "field of the `Crowdloans` entry still exists in the preimage pallet." + ), + "CannotEndInPast": ( + "The `end` block passed to `create` or `update_end` is not after the current block. " + "Compare the `end` argument against the current block number; it must be strictly " + "greater." + ), + "CapNotRaised": ( + "`finalize` was called before the crowdloan's `raised` amount equals its `cap`. Compare " + "the `raised` and `cap` fields of the `Crowdloans` entry; contribute the remainder or " + "lower the cap with `update_cap` before finalizing." + ), + "CapRaised": ( + "A contribution was attempted on a crowdloan whose `raised` amount has already reached " + "its `cap`, so no further contributions are accepted. Compare the `raised` and `cap` " + "fields of the `Crowdloans` entry for the `crowdloan_id`." + ), + "CapTooLow": ( + "On `create` the `cap` is not strictly greater than the initial `deposit`, or on " + "`update_cap` the new cap is below the amount already raised. Compare the cap argument " + "against the `deposit` or the `raised` field of the `Crowdloans` entry." + ), + "ContributionPeriodEnded": ( + "A contribution was made at or after the crowdloan's `end` block. Compare the `end` " + "field of the `Crowdloans` entry with the current block number; the creator can extend " + "it with `update_end` while the crowdloan is not finalized." + ), + "ContributionPeriodNotEnded": ( + "The operation requires the crowdloan's contribution period to be over, but the current " + "block is still before the crowdloan's `end` block. Compare the `end` field of the " + "`Crowdloans` entry for the `crowdloan_id` with the current block number." + ), + "ContributionTooLow": ( + "The `amount` passed to `contribute` is below the crowdloan's configured minimum " + "contribution. Check the `min_contribution` field of the `Crowdloans` entry for the " + "`crowdloan_id` and contribute at least that amount." + ), + "DepositCannotBeWithdrawn": ( + "The creator called `withdraw` but holds nothing above the initial deposit, which stays " + "locked until the crowdloan is dissolved. Compare the creator's `Contributions` entry " + "with the `deposit` field of the `Crowdloans` entry." + ), + "DepositTooLow": ( + "The `deposit` argument to `create` is below the pallet's required minimum. Check the " + "`MinimumDeposit` pallet constant and create the crowdloan with at least that initial " + "deposit." + ), + "InvalidCrowdloanId": ( + "No crowdloan exists in the `Crowdloans` storage map for the given `crowdloan_id`; it " + "was never created or has been dissolved. Check `Crowdloans` for the id and " + "`NextCrowdloanId` for the range of ids ever issued." + ), + "InvalidFinalizationConfig": ( + "Exactly one of `call` or `target_address` must be set, but both or neither were " + "provided to `create`, or the stored crowdloan holds an inconsistent pair at `finalize` " + "time. Check those two fields on the `create` arguments or the `Crowdloans` entry." + ), + "InvalidOrigin": ( + "The caller is not the crowdloan's creator, which is required for `finalize`, `refund`, " + "`dissolve`, and the update calls. Compare the signing account with the `creator` field " + "of the `Crowdloans` entry for the `crowdloan_id`." + ), + "MaxContributionReached": ( + "The contributor's cumulative contribution already equals the crowdloan's " + "per-contributor cap, so further contributions are rejected. Compare their " + "`Contributions` entry with the `MaxContributions` value for the `crowdloan_id`." + ), + "MaxContributorsReached": ( + "The crowdloan already has the maximum number of distinct contributors, so accounts " + "without an existing contribution are rejected. Compare the `contributors_count` field " + "of the `Crowdloans` entry with the `MaxContributors` pallet constant." + ), + "MaximumContributionTooLow": ( + "The `new_max_contribution` passed to `set_max_contribution` is below the crowdloan's " + "`min_contribution` or below the creator's existing contribution. Check both against " + "the `Crowdloans` entry and the creator's `Contributions` entry." + ), + "MinimumContributionTooHigh": ( + "The `new_min_contribution` passed to `update_min_contribution` exceeds the crowdloan's " + "configured per-contributor maximum. Compare it with the `MaxContributions` entry for " + "the `crowdloan_id`." + ), + "MinimumContributionTooLow": ( + "The minimum contribution given to `create` or `update_min_contribution` is below the " + "chain-wide floor. Check the `AbsoluteMinimumContribution` pallet constant and raise " + "the `min_contribution` argument to at least that value." + ), + "NoContribution": ( + "The account has no contribution recorded for this crowdloan, so `withdraw` has nothing " + "to pay out (or `dissolve` finds no creator contribution). Check the `Contributions` " + "double map under the `crowdloan_id` for the account in question." + ), + "NotReadyToDissolve": ( + "`dissolve` was called while outside contributions remain: the crowdloan's `raised` " + "amount still exceeds the creator's own contribution. Call `refund` until only the " + "creator's `Contributions` entry remains and equals `raised` in the `Crowdloans` " + "record." + ), + "Underflow": ( + "A checked subtraction underflowed in crowdloan accounting, e.g. `raised` exceeding " + "`cap` when computing remaining room, or a contributor count decrement, indicating " + "inconsistent state. Inspect the `Crowdloans` and `Contributions` entries for the " + "`crowdloan_id`." + ), +} diff --git a/sdk/python/bittensor/error_descriptions/drand.py b/sdk/python/bittensor/error_descriptions/drand.py new file mode 100644 index 0000000000..743f6d5fe0 --- /dev/null +++ b/sdk/python/bittensor/error_descriptions/drand.py @@ -0,0 +1,36 @@ +"""Chain error descriptions declared (first) by the `Drand` pallet.""" + +from __future__ import annotations + +DESCRIPTIONS: dict[str, str] = { + "DrandConnectionFailure": ( + "Declared for failures reaching the drand HTTP API, but pulse fetching happens in the " + "offchain worker, which logs errors instead of raising this. Check the node's offchain " + "worker logs and outbound connectivity to the drand endpoints." + ), + "InvalidRoundNumber": ( + "A submitted drand pulse has an unacceptable round: the first pulse ever stored must " + "have a round greater than zero, and each later pulse must be exactly `LastStoredRound` " + "plus one. Compare the pulse round against `LastStoredRound`." + ), + "NoneValue": ( + "Template leftover in the drand pallet meaning a storage value was read before ever " + "being set; no current code path raises it. If seen, inspect the drand pallet's storage " + "items for missing initialization." + ), + "PulseVerificationError": ( + "BLS signature verification of a submitted drand pulse against the stored beacon " + "configuration failed. Check the pulse's signature and round against the `BeaconConfig` " + "storage (drand quicknet public key)." + ), + "StorageOverflow": ( + "Template leftover in the drand pallet for a counter increment overflowing `u32::MAX`; " + "no current code path raises it. If seen, inspect the drand pallet's stored counters " + "for values near the u32 limit." + ), + "UnverifiedPulse": ( + "Declared for drand pulses that fail validity checks, but current code raises " + "`PulseVerificationError` for verification failures and silently skips unverified " + "pulses. If seen on an older runtime, check the pulse signature against `BeaconConfig`." + ), +} diff --git a/sdk/python/bittensor/error_descriptions/ethereum.py b/sdk/python/bittensor/error_descriptions/ethereum.py new file mode 100644 index 0000000000..7c3549096b --- /dev/null +++ b/sdk/python/bittensor/error_descriptions/ethereum.py @@ -0,0 +1,18 @@ +"""Chain error descriptions declared (first) by the `Ethereum` pallet.""" + +from __future__ import annotations + +DESCRIPTIONS: dict[str, str] = { + "InvalidSignature": ( + "Signature verification failed: the sender of an Ethereum or EVM transaction could not " + "be recovered from its signature, or a limit order's Sr25519 signature does not match " + "the order payload and signer. Check the signing key, chain id, and the exact payload " + "bytes that were signed." + ), + "PreLogExists": ( + "An `ethereum.transact` extrinsic was submitted in a block that already carries a " + "pre-log digest, meaning an Ethereum transaction is being injected by other means. " + "Check the block's digest for a pre-runtime Ethereum log; transact is not allowed " + "alongside it." + ), +} diff --git a/sdk/python/bittensor/error_descriptions/evm.py b/sdk/python/bittensor/error_descriptions/evm.py new file mode 100644 index 0000000000..269b692f74 --- /dev/null +++ b/sdk/python/bittensor/error_descriptions/evm.py @@ -0,0 +1,77 @@ +"""Chain error descriptions declared (first) by the `EVM` pallet.""" + +from __future__ import annotations + +DESCRIPTIONS: dict[str, str] = { + "BalanceLow": ( + "The sender's mapped account cannot cover the transaction's value plus maximum gas fee, " + "detected during validation or when withdrawing the fee. Check the account balance " + "against `value` plus `gas_limit` times the effective gas price." + ), + "CreateOriginNotAllowed": ( + "A CREATE, or a CALL that performs a nested CREATE, was attempted from an EVM address " + "not permitted to deploy contracts. Check whether the deploying address is in the " + "chain's allowed-deployers list." + ), + "FeeOverflow": ( + "Fee arithmetic overflowed, either multiplying the fee per gas by `gas_limit` or " + "converting the EVM fee into Substrate balance decimals. Check for absurdly large " + "`gas_limit` or fee-per-gas values in the transaction." + ), + "GasLimitTooHigh": ( + "The transaction's `gas_limit` exceeds the block gas limit configured for the EVM. " + "Compare the `gas_limit` argument against the chain's block gas limit and lower it." + ), + "GasLimitTooLow": ( + "The transaction's `gas_limit` is below the intrinsic gas required, or too small to " + "cover the weight and proof-size base cost. Raise the `gas_limit` argument, comparing " + "against an `eth_estimateGas` result." + ), + "GasPriceTooLow": ( + "The offered fee cannot satisfy the current base fee: `max_fee_per_gas` is below the " + "block base fee, the priority fee exceeds the max fee, or the fee inputs are " + "inconsistent. Check `max_fee_per_gas` and `max_priority_fee_per_gas` against the " + "chain's base fee." + ), + "InvalidChainId": ( + "The EIP-155 chain id encoded in the signed transaction does not match this chain's " + "configured chain id. Compare the transaction's chain id with the value returned by " + "`eth_chainId` and re-sign the transaction." + ), + "InvalidNonce": ( + "The transaction nonce does not match the sender's current account nonce, being either " + "too low (already used) or too high. Compare the transaction's `nonce` with the " + "sender's on-chain nonce from `eth_getTransactionCount`." + ), + "NotAllowed": ( + "Contract deployment was blocked because the source address is not in the " + "`WhitelistedCreators` list while the whitelist check is enabled. Check the deployer " + "address against `WhitelistedCreators` and the `DisableWhitelistCheck` storage value." + ), + "PaymentOverflow": ( + "Arithmetic overflowed while computing the total payment or refund for an EVM " + "transaction, such as refunding remaining gas at the effective gas price. Check for " + "extreme gas price or gas limit values in the transaction." + ), + "Reentrancy": ( + "EVM execution re-entered the pallet while another EVM execution was already in " + "progress on the same thread, e.g. a precompile or runtime call dispatching back into " + "the EVM. Inspect precompiles and runtime code that invoke the EVM from within an EVM " + "call." + ), + "TransactionMustComeFromEOA": ( + "Rejected per EIP-3607: the sender address has contract code deployed, and transactions " + "must originate from externally owned accounts. Check `eth_getCode` for the `source` " + "address and sign with a plain EOA key instead." + ), + "Undefined": ( + "Catch-all EVM validation error for cases without a dedicated variant, such as a " + "malformed EIP-7702 authorization list or an unknown validation failure. Inspect the " + "raw transaction for unsupported fields and check the node logs." + ), + "WithdrawFailed": ( + "Withdrawing the transaction fee from the sender's mapped account failed even though " + "the balance check passed, e.g. due to locks, holds, or existential deposit " + "constraints. Check the account's locks and its free versus withdrawable balance." + ), +} diff --git a/sdk/python/bittensor/error_descriptions/grandpa.py b/sdk/python/bittensor/error_descriptions/grandpa.py new file mode 100644 index 0000000000..5b2cacc406 --- /dev/null +++ b/sdk/python/bittensor/error_descriptions/grandpa.py @@ -0,0 +1,43 @@ +"""Chain error descriptions declared (first) by the `Grandpa` pallet.""" + +from __future__ import annotations + +DESCRIPTIONS: dict[str, str] = { + "ChangePending": ( + "A GRANDPA authority-set change has already been signalled and is still pending, so a " + "new change cannot be scheduled. Check the Grandpa `PendingChange` and `State` storage " + "and wait for the pending change to be applied first." + ), + "DuplicateOffenceReport": ( + "The equivocation proof is valid but this offence has already been reported and " + "recorded. Check whether an equivocation report for the same offender, session, and " + "round was previously submitted before reporting again." + ), + "InvalidEquivocationProof": ( + "The submitted GRANDPA equivocation proof does not demonstrate a real double-vote: the " + "two votes may be identical, from different rounds or set ids, or badly signed. Verify " + "the proof contains two distinct votes by the same authority in the same round and " + "authority set." + ), + "InvalidKeyOwnershipProof": ( + "The key ownership proof does not establish that the offending GRANDPA key belonged to " + "the claimed validator at that session. Regenerate the proof via the " + "`generate_key_ownership_proof` runtime API for the exact set id and authority id in " + "the report." + ), + "PauseFailed": ( + "A GRANDPA pause was signalled while the authority set is not live, i.e. it is already " + "paused or a pause is already pending. Check the Grandpa `State` storage before " + "signalling a pause." + ), + "ResumeFailed": ( + "A GRANDPA resume was signalled while the authority set is not paused, i.e. it is live " + "or already pending a resume. Check the Grandpa `State` storage before signalling a " + "resume." + ), + "TooSoon": ( + "A forced GRANDPA authority change was signalled too soon after the previous one. Check " + "the Grandpa `NextForced` storage and wait until the current block passes it before " + "signalling another forced change." + ), +} diff --git a/sdk/python/bittensor/error_descriptions/limit_orders.py b/sdk/python/bittensor/error_descriptions/limit_orders.py new file mode 100644 index 0000000000..d68283b884 --- /dev/null +++ b/sdk/python/bittensor/error_descriptions/limit_orders.py @@ -0,0 +1,95 @@ +"""Chain error descriptions declared (first) by the `LimitOrders` pallet.""" + +from __future__ import annotations + +DESCRIPTIONS: dict[str, str] = { + "ArithmeticOverflow": ( + "Converting a TAO amount to alpha during batched order execution overflowed the " + "fixed-point range, typically when the pool price is tiny relative to the batch's total " + "buy TAO. Check the subnet's current alpha price against the batch's aggregate buy " + "amounts." + ), + "ChainIdMismatch": ( + "The order payload's `chain_id` field differs from this chain's configured EVM chain " + "id, e.g. an order signed for testnet was submitted to mainnet. Compare the order's " + "`chain_id` with the runtime's `pallet_evm_chain_id` value and re-sign if needed." + ), + "DuplicateOrderInBatch": ( + "Two entries in one `execute_batched_orders` call hash to the same order id, meaning " + "the identical signed payload was included twice, which hard-fails the batch. " + "Deduplicate by the blake2-256 hash of each SCALE-encoded `VersionedOrder`." + ), + "IncorrectPartialFillAmount": ( + "The `partial_fill` amount is zero or exceeds the order's remaining unfilled amount, or " + "a full execution was submitted against an order already partially filled. Compare " + "`partial_fill` with `order.amount` minus the filled amount recorded in `Orders`." + ), + "LimitOrdersDisabled": ( + "Order execution was attempted while the pallet's global switch is off. Check the " + "`LimitOrdersEnabled` storage value; root must call `set_pallet_status` with true to " + "enable the pallet." + ), + "OrderAlreadyProcessed": ( + "The order id already has a terminal status: execution found it fulfilled, or " + "`cancel_order` found any existing status for it. Check the `Orders` storage map under " + "the blake2-256 hash of the SCALE-encoded `VersionedOrder`." + ), + "OrderCancelled": ( + "The order was previously cancelled via `cancel_order` and can never be executed. Check " + "the `Orders` storage entry for the order id; a `Cancelled` status is terminal, so the " + "signer must sign and submit a fresh order." + ), + "OrderExpired": ( + "The current chain time is past the order's `expiry` field, which is a unix timestamp " + "in milliseconds, so the order can no longer execute. Compare the `expiry` in the " + "signed order payload with the chain's current `Timestamp` value." + ), + "OrderNetUidMismatch": ( + "An order inside an `execute_batched_orders` call has a `netuid` field different from " + "the batch's `netuid` parameter, which hard-fails the entire batch. Check each order " + "payload's `netuid` against the batch argument and split mismatched orders out." + ), + "PalletHotkeyNotRegistered": ( + "Root tried to enable the pallet via `set_pallet_status` before its hotkey was " + "registered to the pallet's intermediary account. Check that the `PalletHotkey` " + "constant is registered for the pallet account, which genesis or the " + "`on_runtime_upgrade` migration performs." + ), + "PartialFillsNotEnabled": ( + "A `partial_fill` amount was supplied for an order whose signed payload has " + "`partial_fills_enabled` set to false. Check that field in the order payload; partial " + "execution requires the signer to have opted in when signing." + ), + "PriceConditionNotMet": ( + "The subnet's current alpha price does not satisfy the order's trigger: buys and " + "stop-losses require price at or below `limit_price`, take-profits at or above it. " + "Compare `current_alpha_price` for the order's `netuid`, scaled by 1e9, with the " + "`limit_price` field." + ), + "RelayerMissMatch": ( + "The order's `relayer` allowlist is set but the account that submitted the execution " + "transaction is not in it. Compare the extrinsic's signing account against the " + "`relayer` list in the signed order payload." + ), + "RelayerRequiredForPartialFill": ( + "A `partial_fill` was requested for an order whose `relayer` field is empty; partial " + "fills are only permitted on orders that restrict who may execute them. Check the order " + "payload and either set a relayer list or execute the full amount." + ), + "RootNetUidNotAllowed": ( + "The order or batch targets the root subnet, netuid 0, which the limit orders pallet " + "does not serve. Check the `netuid` field of the order payload or the `netuid` " + "parameter of the batch call and target a non-root subnet." + ), + "SwapReturnedZero": ( + "The netted pool swap in `execute_batched_orders` produced zero output for a non-zero " + "input, meaning the pool lacks liquidity or the derived price limit clamped the swap " + "entirely. Check the subnet pool's reserves and the batch's tightest slippage-derived " + "price limit." + ), + "ZeroShareInBatch": ( + "An order's pro-rata share of the batch output floored to zero, so the whole batch was " + "rejected rather than consuming that order's input for no payout. Check the order's " + "`amount` relative to the batch totals and retry it in a differently composed batch." + ), +} diff --git a/sdk/python/bittensor/error_descriptions/mev_shield.py b/sdk/python/bittensor/error_descriptions/mev_shield.py new file mode 100644 index 0000000000..8380ca0873 --- /dev/null +++ b/sdk/python/bittensor/error_descriptions/mev_shield.py @@ -0,0 +1,26 @@ +"""Chain error descriptions declared (first) by the `MevShield` pallet.""" + +from __future__ import annotations + +DESCRIPTIONS: dict[str, str] = { + "BadEncKeyLen": ( + "The `enc_key` passed to `announce_next_key` is not the exact ML-KEM-768 encapsulation " + "key length (1184 bytes). Check the byte length of the `enc_key` argument before " + "announcing." + ), + "TooManyPendingExtrinsics": ( + "`store_encrypted` was rejected because the shield pallet's queue of encrypted " + "extrinsics is already at capacity. Compare the `PendingExtrinsics` count against " + "`MaxPendingExtrinsicsLimit` and wait for queued items to be processed or expire." + ), + "Unreachable": ( + "`announce_next_key` could not identify the current block author via the `FindAuthors` " + "lookup, which should be impossible in a normally authored block. Check the block's " + "author digest and the shield pallet's authorship wiring." + ), + "WeightExceedsAbsoluteMax": ( + "`set_on_initialize_weight` or `set_max_extrinsic_weight` was given a value above the " + "shield pallet's hard cap of half the total block weight. Compare the `value` argument " + "against the `MAX_ON_INITIALIZE_WEIGHT` constant." + ), +} diff --git a/sdk/python/bittensor/error_descriptions/multisig.py b/sdk/python/bittensor/error_descriptions/multisig.py new file mode 100644 index 0000000000..7986265807 --- /dev/null +++ b/sdk/python/bittensor/error_descriptions/multisig.py @@ -0,0 +1,76 @@ +"""Chain error descriptions declared (first) by the `Multisig` pallet.""" + +from __future__ import annotations + +DESCRIPTIONS: dict[str, str] = { + "AlreadyApproved": ( + "The sender has already approved this multisig call, so a repeat approval is redundant. " + "Check the `Multisigs` entry for the call hash: the sender's account already appears in " + "its `approvals` list." + ), + "AlreadyStored": ( + "The call data supplied for storage is already stored on-chain for this multisig " + "operation. Check whether the call bytes were previously stored for this call hash " + "before submitting them again." + ), + "MaxWeightTooLow": ( + "The `max_weight` argument supplied with the final multisig approval is lower than the " + "actual weight of the call being dispatched. Compute the call's real dispatch weight " + "and pass a `max_weight` at least that large to `as_multi`." + ), + "MinimumThreshold": ( + "The multisig `threshold` argument was below 2, which these calls do not accept. Pass a " + "threshold of 2 or more, or use `as_multi_threshold_1` for the single-approval case." + ), + "NoApprovalsNeeded": ( + "The multisig call does not need any more approvals; it is only waiting for final " + "execution with the full call data. Instead of another `approve_as_multi`, submit " + "`as_multi` with the complete call to dispatch it." + ), + "NoTimepoint": ( + "No timepoint was supplied but this multisig operation is already underway, so the " + "approval cannot be matched to it. Read the operation's `when` field from the " + "`Multisigs` entry for the call hash and pass that height and index as " + "`maybe_timepoint`." + ), + "NotFound": ( + "The referenced item does not exist in storage: no multisig operation for that call " + "hash in `Multisigs`, no scheduled task at that slot or name in `Agenda`/`Lookup`, or " + "no matching proxy registration in `Proxies`. Verify the identifier against current " + "chain state." + ), + "NotOwner": ( + "Only the account that opened the multisig operation (its depositor) may cancel it or " + "adjust its deposit. Compare the sender against the `depositor` field stored in the " + "`Multisigs` entry for this call hash." + ), + "SenderInSignatories": ( + "The multisig sender was included in the `other_signatories` list, but that list must " + "contain only the remaining signatories. Remove the sender's own account from " + "`other_signatories` before resubmitting." + ), + "SignatoriesOutOfOrder": ( + "The `other_signatories` list is not sorted in strictly ascending account order, which " + "the multisig pallet requires for a canonical account derivation. Sort the list and " + "remove duplicates before resubmitting." + ), + "TooFewSignatories": ( + "The multisig signatory list is too short: `other_signatories` must contain at least " + "one account besides the sender. Check the length of `other_signatories` against the " + "threshold you are using." + ), + "TooManySignatories": ( + "The multisig signatory list exceeds the maximum allowed. Compare the length of " + "`other_signatories` plus the sender against the pallet's `MaxSignatories` constant." + ), + "UnexpectedTimepoint": ( + "A timepoint was supplied but no multisig operation is underway for this call hash; the " + "first approval must open the operation with no timepoint. Pass `maybe_timepoint: None` " + "on the opening call, or verify the call hash matches an existing `Multisigs` entry." + ), + "WrongTimepoint": ( + "The timepoint supplied does not match the one recorded when this multisig operation " + "was opened. Read the correct `when` height and index from the `Multisigs` entry for " + "the call hash and resubmit with that exact timepoint." + ), +} diff --git a/sdk/python/bittensor/error_descriptions/preimage.py b/sdk/python/bittensor/error_descriptions/preimage.py new file mode 100644 index 0000000000..385e534298 --- /dev/null +++ b/sdk/python/bittensor/error_descriptions/preimage.py @@ -0,0 +1,47 @@ +"""Chain error descriptions declared (first) by the `Preimage` pallet.""" + +from __future__ import annotations + +DESCRIPTIONS: dict[str, str] = { + "AlreadyNoted": ( + "The preimage for this hash has already been noted on-chain, so `note_preimage` has " + "nothing to add. Check `RequestStatusFor` (or legacy `StatusFor`) for the hash before " + "submitting the bytes again." + ), + "NotAuthorized": ( + "The caller is not permitted to manage this preimage; unnoting or unrequesting requires " + "the pallet's manager origin or the account that originally deposited it. Check which " + "origin noted or requested the preimage and use that origin or the configured " + "`ManagerOrigin`." + ), + "NotNoted": ( + "The preimage cannot be unnoted because no preimage was ever noted for this hash. Check " + "`RequestStatusFor` (or legacy `StatusFor`) for the hash to confirm what, if anything, " + "is stored." + ), + "NotRequested": ( + "The preimage request cannot be removed because there are no outstanding requests for " + "this hash. Check the request status for the hash in `RequestStatusFor` before calling " + "`unrequest_preimage`." + ), + "Requested": ( + "The preimage cannot be unnoted while there are still outstanding requests for it. " + "Check the request count in the hash's request status; all requests must be cleared " + "before the preimage can be removed." + ), + "TooBig": ( + "The preimage exceeds the maximum size the pallet will store on-chain (4 MiB). Check " + "the byte length of the preimage against the pallet's `MAX_SIZE` limit before noting " + "it." + ), + "TooFew": ( + "The bulk preimage upgrade was requested with zero hashes, so there is nothing to do. " + "Pass at least one hash to `ensure_updated`." + ), + "TooMany": ( + "A limit was exceeded: more preimage hashes than `MAX_HASH_UPGRADE_BULK_COUNT` were " + "passed to `ensure_updated`, or the account has hit its proxy or announcement cap. " + "Check the account's `Proxies` and `Announcements` entries against `MaxProxies` and " + "`MaxPending`." + ), +} diff --git a/sdk/python/bittensor/error_descriptions/proxy.py b/sdk/python/bittensor/error_descriptions/proxy.py new file mode 100644 index 0000000000..97490ababb --- /dev/null +++ b/sdk/python/bittensor/error_descriptions/proxy.py @@ -0,0 +1,48 @@ +"""Chain error descriptions declared (first) by the `Proxy` pallet.""" + +from __future__ import annotations + +DESCRIPTIONS: dict[str, str] = { + "AnnouncementDepositInvariantViolated": ( + "Internal invariant failure in `announce`: recomputing the announcement deposit " + "returned nothing after the pending announcements were updated. Inspect the caller's " + "`Announcements` entry and the announcement deposit constants; this indicates a pallet " + "bug rather than bad input." + ), + "Duplicate": ( + "This delegate is already registered as a proxy for the delegator with the same proxy " + "type and delay. Check the delegator's `Proxies` entry before calling `add_proxy` with " + "the same (delegate, proxy type, delay) tuple." + ), + "InvalidDerivedAccountId": ( + "Deriving the pure proxy account id from the provided entropy failed to decode into a " + "valid account. Check the spawner, `proxy_type`, and `index` arguments used with " + "`create_pure` (or the equivalent lookup when destroying one)." + ), + "NoPermission": ( + "The proxy pallet refused the action: the proxied call could escalate privileges, or " + "the caller lacks authority over the pure proxy (e.g. `kill_pure` by a non-spawner). " + "Check the proxy type's call filter and the original `create_pure` arguments." + ), + "NoSelfProxy": ( + "An account attempted to register itself as its own proxy, which is not allowed. Check " + "the `delegate` argument to `add_proxy` and ensure it differs from the calling " + "(delegator) account." + ), + "NotProxy": ( + "The sender is not registered as a proxy for the account it tried to act for. Check the " + "`Proxies` entry of the `real` account and confirm the sender appears there with a " + "proxy type and delay compatible with the call." + ), + "Unannounced": ( + "The proxied call was executed before its announcement matured, or no matching " + "announcement exists at all. Check the proxy's `Announcements` entry for the call hash " + "and the `delay` on the proxy registration; enough blocks must elapse between " + "`announce` and `proxy_announced`." + ), + "Unproxyable": ( + "The attempted call is not permitted by the registered proxy type's call filter. Check " + "which `proxy_type` the sender holds in the real account's `Proxies` entry and whether " + "that type's `InstanceFilter` allows this specific call." + ), +} diff --git a/sdk/python/bittensor/error_descriptions/safe_mode.py b/sdk/python/bittensor/error_descriptions/safe_mode.py new file mode 100644 index 0000000000..69657026b6 --- /dev/null +++ b/sdk/python/bittensor/error_descriptions/safe_mode.py @@ -0,0 +1,42 @@ +"""Chain error descriptions declared (first) by the `SafeMode` pallet.""" + +from __future__ import annotations + +DESCRIPTIONS: dict[str, str] = { + "AlreadyDeposited": ( + "The account calling safe-mode `enter` or `extend` already has a safe-mode deposit on " + "hold, so another cannot be placed. Check the account's `Deposits` entries and its " + "balance held under the `EnterOrExtend` hold reason." + ), + "CannotReleaseYet": ( + "`release_deposit` was called too early: the current block must exceed the deposit's " + "block plus `ReleaseDelay`, and safe-mode must be exited. Check the block key of the " + "entry in `Deposits` against the `ReleaseDelay` config." + ), + "CurrencyError": ( + "A balance hold, release, or burn inside pallet-safe-mode failed while managing an " + "enter/extend deposit. Check the account's free balance and existing holds under the " + "safe-mode `EnterOrExtend` hold reason." + ), + "Entered": ( + "Safe-mode is currently active, so `enter` or `force_enter` cannot activate it again " + "and `release_deposit` is blocked until it exits. Check `EnteredUntil` for the block at " + "which safe-mode disengages." + ), + "Exited": ( + "Safe-mode is not currently active, so `extend`, `force_extend`, or `force_exit` have " + "nothing to act on. Check that `EnteredUntil` contains a value before extending or " + "exiting." + ), + "NoDeposit": ( + "No safe-mode deposit exists for the given account and block combination, so nothing " + "can be released or slashed. Check the `Deposits` storage map for an entry under that " + "account and deposit block." + ), + "NotConfigured": ( + "The permissionless safe-mode operation is disabled because its config option is " + "`None`: `EnterDepositAmount` for `enter`, `ExtendDepositAmount` for `extend`, or " + "`ReleaseDelay` for `release_deposit`. Check the runtime config or use the root-only " + "force variants." + ), +} diff --git a/sdk/python/bittensor/error_descriptions/scheduler.py b/sdk/python/bittensor/error_descriptions/scheduler.py new file mode 100644 index 0000000000..2550d37086 --- /dev/null +++ b/sdk/python/bittensor/error_descriptions/scheduler.py @@ -0,0 +1,26 @@ +"""Chain error descriptions declared (first) by the `Scheduler` pallet.""" + +from __future__ import annotations + +DESCRIPTIONS: dict[str, str] = { + "FailedToSchedule": ( + "The scheduler could not place the call into the agenda, typically because the target " + "block's agenda is full or the schedule parameters are unusable. Check `Agenda` at the " + "target block against `MaxScheduledPerBlock` and pick a different block if it is " + "saturated." + ), + "Named": ( + "An unnamed scheduler function was used on a task that was scheduled with a name. Use " + "the named variants (`cancel_named`, `schedule_named`) with the task's id, which you " + "can find via the `Lookup` storage map." + ), + "RescheduleNoChange": ( + "The reschedule was rejected because the new dispatch time equals the task's currently " + "scheduled time. Check the task's existing slot in `Agenda` and pass a genuinely " + "different `when` block." + ), + "TargetBlockNumberInPast": ( + "The scheduler was given a dispatch block that is not in the future. Compare the `when` " + "argument against the current block number and choose a strictly later block." + ), +} diff --git a/sdk/python/bittensor/error_descriptions/subtensor.py b/sdk/python/bittensor/error_descriptions/subtensor.py new file mode 100644 index 0000000000..68c06ec128 --- /dev/null +++ b/sdk/python/bittensor/error_descriptions/subtensor.py @@ -0,0 +1,801 @@ +"""Chain error descriptions declared (first) by the `SubtensorModule` pallet.""" + +from __future__ import annotations + +DESCRIPTIONS: dict[str, str] = { + "AccountRejectsLockedAlpha": ( + "Locked alpha was being transferred to a coldkey whose `AccountFlags` do not have the " + "accept-locked-alpha bit set, e.g. during a lock transfer or coldkey swap of locks. " + "Check the destination coldkey's `AccountFlags` storage and have the recipient opt in " + "to receiving locked alpha before retrying." + ), + "ActiveLockExists": ( + "The destination coldkey already holds a lock with nonzero locked mass on that subnet, " + "so a new or transferred lock cannot be created there. Inspect the `Lock` storage for " + "the (coldkey, netuid, hotkey) triple and wait for the existing lock to unlock or " + "remove it first." + ), + "ActivityCutoffFactorMilliOutOfBounds": ( + "The `factor_milli` argument to set the activity-cutoff factor was outside the allowed " + "1000-50000 per-mille range (1 to 50 tempos). Adjust the argument to fall within those " + "bounds before resubmitting." + ), + "ActivityCutoffTooLow": ( + "An admin tried to set the subnet's activity cutoff below the chain-wide minimum. " + "Compare the requested value against the `MinActivityCutoff` storage item and current " + "`activity_cutoff` in `btcli sudo get --netuid `." + ), + "AddStakeBurnRateLimitExceeded": ( + "The add-stake-and-burn operation was submitted again before its per-key rate-limit " + "window elapsed. Wait some blocks and retry; no active raise site exists in current " + "code, so this mainly appears on older runtimes." + ), + "AdminActionProhibitedDuringWeightsWindow": ( + "An owner or admin hyperparameter change was attempted inside the protected freeze " + "window just before the subnet's epoch runs. Check `AdminFreezeWindow` and the blocks " + "remaining until the next epoch (subnet tempo), then retry after the epoch fires." + ), + "AllNetworksInImmunity": ( + "Creating a new subnet required pruning an existing one, but every candidate subnet is " + "still inside its network immunity period so none can be dissolved. Check " + "`NetworkImmunityPeriod` and each subnet's `NetworkRegisteredAt`, and retry once a " + "subnet leaves immunity." + ), + "AlphaHighTooLow": ( + "The `alpha_high` argument to set liquid-alpha values was below the minimum of roughly " + "0.025 (1638/65535 in u16 units). Raise `alpha_high` in the `sudo_set_alpha_values` " + "call; current values are in the `AlphaValues` storage per netuid." + ), + "AlphaLowOutOfRange": ( + "The `alpha_low` argument to set liquid-alpha values was below the ~0.025 minimum " + "(1638/65535) or greater than `alpha_high`. Choose alpha_low within that range and not " + "exceeding alpha_high; current settings are in the `AlphaValues` storage for the " + "netuid." + ), + "AmountTooLow": ( + "A stake, unstake, move or swap amount was zero or its TAO equivalent fell below the " + "minimum stake threshold after fees and slippage. Compare the amount against the " + "`DefaultMinStake` storage item and the subnet's alpha price before retrying with a " + "larger amount." + ), + "AnnouncedColdkeyHashDoesNotMatch": ( + "The `new_coldkey` passed to `coldkey_swap` hashes to a different value than the hash " + "committed in the earlier `announce_coldkey_swap`. Verify the announced hash in the " + "`ColdkeySwapAnnouncements` storage matches the BlakeTwo256 hash of the coldkey you are " + "swapping to." + ), + "AutoEpochAlreadyImminent": ( + "`trigger_epoch` was called when the next automatic epoch is closer than the " + "`AdminFreezeWindow`, so a manual trigger would have no effect. Check the subnet's " + "tempo and blocks until the next epoch, and simply wait for it to fire." + ), + "BalanceWithdrawalError": ( + "The requested TAO could not be withdrawn from the coldkey's free balance, typically " + "due to insufficient funds, the existential deposit, or frozen/reserved balance. Check " + "the coldkey's balance with `btcli wallet balance` and reduce the amount or top up." + ), + "BeneficiaryDoesNotOwnHotkey": ( + "When ending a subnet lease, the hotkey passed for the ownership handover is not owned " + "by the lease's beneficiary coldkey. Check the `Owner` storage for that hotkey and pass " + "a hotkey the beneficiary coldkey actually owns." + ), + "CallDisabled": ( + "The extrinsic has been switched off in the current runtime and cannot be dispatched. " + "There is no active raise site in current code; if seen, check release notes for " + "whether the call was re-enabled in a newer runtime version." + ), + "CanNotSetRootNetworkWeights": ( + "`set_weights` was called with netuid 0, the root network, where normal weight setting " + "is not allowed. Use a non-root `netuid` argument; root weights are handled by a " + "separate mechanism." + ), + "CannotAffordLockCost": ( + "The coldkey's free balance cannot cover the current dynamic subnet-creation lock cost. " + "Compare `btcli subnets create-cost` (or `btcli query subnet-registration-cost`) against " + "the coldkey balance from `btcli wallet balance` before registering a subnet." + ), + "CannotBurnOrRecycleOnRootSubnet": ( + "`recycle_alpha` or `burn_alpha` was called with netuid 0, and TAO on the root subnet " + "cannot be burned or recycled. Pass a non-root `netuid` argument for the subnet whose " + "alpha you want to recycle or burn." + ), + "CannotUseSystemAccount": ( + "The hotkey supplied for registration, hotkey swap, or subnet-owner-hotkey assignment " + "is a reserved subnet system account. Use a regular user-generated hotkey instead; " + "system accounts are derived per-subnet and rejected by `is_subnet_account_id`." + ), + "ChildParentInconsistency": ( + "A `set_children` or parent-delegation call would make the same hotkey appear as both a " + "child and a parent, or referenced a child missing from the proposed mapping. Inspect " + "the `ChildKeys` and `ParentKeys` storage for the hotkeys involved and remove the " + "overlap." + ), + "ColdKeyAlreadyAssociated": ( + "The destination coldkey of a coldkey swap already has staking hotkeys associated with " + "it, so it cannot receive the swapped identity. Check the `StakingHotkeys` storage for " + "the new coldkey and swap to a fresh, unused coldkey instead." + ), + "ColdkeySwapAlreadyDisputed": ( + "`dispute_coldkey_swap` was called for a coldkey whose pending swap announcement is " + "already under dispute. Check the `ColdkeySwapDisputes` storage for the coldkey; no " + "further dispute action is needed." + ), + "ColdkeySwapAnnounced": ( + "The coldkey has a pending swap announcement, so all but a small allow-list of " + "extrinsics are blocked until the swap completes or is cleared. Check the " + "`ColdkeySwapAnnouncements` storage for the coldkey and either finish the swap with " + "`coldkey_swap` or clear the announcement." + ), + "ColdkeySwapAnnouncementNotFound": ( + "`coldkey_swap`, `dispute_coldkey_swap`, or `clear_coldkey_swap_announcement` was " + "called for a coldkey with no pending announcement. Check the " + "`ColdkeySwapAnnouncements` storage; you must call `announce_coldkey_swap` first." + ), + "ColdkeySwapClearTooEarly": ( + "The swap announcement cannot be cleared until the reannouncement delay after the " + "announcement's execution block has passed. Compare the current block with the `when` " + "stored in `ColdkeySwapAnnouncements` plus `ColdkeySwapReannouncementDelay` and retry " + "later." + ), + "ColdkeySwapDisputed": ( + "All extrinsics from this coldkey are blocked because its pending coldkey swap is under " + "dispute. Check the `ColdkeySwapDisputes` storage for the coldkey; the dispute must be " + "resolved by root before the account can transact." + ), + "ColdkeySwapReannouncedTooEarly": ( + "`announce_coldkey_swap` was called again before the reannouncement delay after the " + "previous announcement's execution block elapsed. Compare the current block with the " + "stored announcement time plus `ColdkeySwapReannouncementDelay` and retry later." + ), + "ColdkeySwapTooEarly": ( + "`coldkey_swap` was executed before the announcement delay had elapsed since " + "`announce_coldkey_swap`. Check the execution block stored in " + "`ColdkeySwapAnnouncements` (announcement time plus `ColdkeySwapAnnouncementDelay`) and " + "wait until then." + ), + "CommitRevealDisabled": ( + "A weight commit or reveal was submitted on a subnet where commit-reveal is turned off. " + "Check the `commit_reveal_weights_enabled` hyperparameter for the netuid " + "(`btcli sudo get --netuid `); use plain `set_weights` instead when it is disabled." + ), + "CommitRevealEnabled": ( + "Plain `set_weights` was called on a subnet where commit-reveal is enabled, which " + "requires the commit/reveal flow instead. Check the `commit_reveal_weights_enabled` " + "hyperparameter for the netuid and switch to `commit_weights`/`reveal_weights`." + ), + "CommittingWeightsTooFast": ( + "The neuron committed weights again before the per-UID rate limit elapsed since its " + "last commit on that subnet. Compare blocks since the last commit against the " + "`weights_rate_limit` hyperparameter (`btcli sudo get --netuid `) and wait." + ), + "DelegateTakeTooHigh": ( + "The `take` argument exceeds the maximum delegate take allowed by the chain (18% by " + "default). Compare the requested value against the `MaxDelegateTake` storage item and " + "lower it." + ), + "DelegateTakeTooLow": ( + "The `take` argument was below the `MinDelegateTake` minimum, or " + "`increase_take`/`decrease_take` was not strictly increasing/decreasing relative to the " + "current take. Check the hotkey's current take in the `Delegates` storage and the " + "`MinDelegateTake` storage item." + ), + "DelegateTxRateLimitExceeded": ( + "The delegate changed its take again before the per-hotkey take-change rate limit " + "elapsed. Compare blocks since the hotkey's last take transaction against the " + "`TxDelegateTakeRateLimit` storage item and retry later." + ), + "Deprecated": ( + "The extrinsic has been removed and always fails, e.g. `schedule_swap_coldkey`, the " + "swap pallet's user-liquidity calls, or `sudo_set_total_issuance`. Migrate to the " + "replacement call noted in the deprecation (for coldkey swaps, `announce_coldkey_swap` " + "plus `coldkey_swap`)." + ), + "DisabledTemporarily": ( + "The operation has been temporarily switched off in the runtime, usually as a hotfix " + "measure. There is no active raise site in current code; if encountered, check the " + "runtime version and release notes for when the feature is re-enabled." + ), + "DuplicateChild": ( + "The children list passed to `set_children` contains the same child hotkey more than " + "once. Deduplicate the `children` argument; current relations are visible in the " + "`ChildKeys` storage for the parent hotkey and netuid." + ), + "DuplicateUids": ( + "The `uids` vector passed to `set_weights` (or a reveal) contains the same UID more " + "than once. Deduplicate the uids/values pairs before submitting; each target neuron may " + "appear only once per weight vector." + ), + "DynamicTempoBlockedByCommitReveal": ( + "`trigger_epoch` is refused while commit-reveal is enabled on the subnet, because an " + "out-of-band epoch would desync the CRv3 reveal window from the Drand schedule and drop " + "committed weights. Check the `commit_reveal_weights_enabled` hyperparameter; disable " + "it before manually triggering epochs." + ), + "EpochTriggerAlreadyPending": ( + "`trigger_epoch` was called while a previously triggered epoch is still queued for this " + "subnet. Check the `PendingEpochAt` storage for the netuid and wait for the pending " + "epoch to fire before triggering again." + ), + "EvmKeyAssociateRateLimitExceeded": ( + "`associate_evm_key` was called again before the per-UID rate limit since the last " + "association elapsed. Compare blocks since the association recorded in the " + "`AssociatedEvmAddress` storage against the `EvmKeyAssociateRateLimit` runtime constant " + "and retry later." + ), + "EvmKeyAssociationLimitExceeded": ( + "The EVM address is already associated with the maximum number of UIDs allowed on this " + "subnet. Inspect the `AssociatedUidsByEvmAddress` storage for the (netuid, evm_key) " + "pair and free a slot or use a different EVM address." + ), + "ExpectedBeneficiaryOrigin": ( + "A lease operation such as terminating a subnet lease was signed by an account other " + "than the lease's beneficiary coldkey. Check the beneficiary recorded in the " + "`SubnetLeases` storage for the lease id and sign with that coldkey." + ), + "ExpiredWeightCommit": ( + "The hash supplied to `reveal_weights` matches a commit whose reveal window has already " + "passed, so it can no longer be revealed. Check the `commit_reveal_period` " + "hyperparameter and reveal within the allowed epochs after committing; re-commit and " + "reveal on time." + ), + "FaucetDisabled": ( + "The `faucet` extrinsic was called on a runtime built without the pow-faucet feature, " + "i.e. any real network. The faucet only works on local test chains compiled with that " + "feature; use a funded wallet or testnet TAO instead." + ), + "FirstEmissionBlockNumberAlreadySet": ( + "`start_call` was issued for a subnet whose emissions have already been started. Check " + "the `FirstEmissionBlockNumber` storage for the netuid; a non-empty value means the " + "subnet is already emitting and no action is needed." + ), + "HotKeyAccountNotExists": ( + "The hotkey has no on-chain account, meaning it was never created through registration, " + "so staking or delegation operations cannot reference it. Check the `Owner` storage for " + "the hotkey or `btcli wallet overview`; register the hotkey on a subnet first." + ), + "HotKeyAlreadyDelegate": ( + "`become_delegate` was called for a hotkey that is already a delegate. Check the " + "`Delegates` storage for the hotkey; if it has a take entry it is already delegating " + "and no action is needed." + ), + "HotKeyAlreadyRegisteredInSubNet": ( + "A registration or hotkey swap targeted a hotkey that already holds a UID on the subnet " + "(or, for a swap without a netuid, on any subnet). Check the `Uids` storage for the " + "(netuid, hotkey) pair or `btcli wallet overview`; use a different hotkey or netuid." + ), + "HotKeyNotRegisteredInNetwork": ( + "The hotkey is not registered on the relevant subnet, raised by " + "`serve_axon`/`serve_prometheus` for the serving netuid or by identity calls requiring " + "registration on any subnet. Verify registration with `btcli query uid --netuid ` " + "(or `btcli query netuids-for-hotkey`) and register via `btcli subnets register` first." + ), + "HotKeyNotRegisteredInSubNet": ( + "The hotkey holds no UID on the given netuid, so weight setting, commits, or UID " + "lookups fail there. Check the `Uids` storage for the (netuid, hotkey) pair or " + "`btcli query uid --netuid `; confirm the netuid argument and register the hotkey if " + "needed." + ), + "HotKeySetTxRateLimitExceeded": ( + "The coldkey attempted a hotkey set/swap before `TxRateLimit` blocks had passed since " + "its last such transaction. Check the coldkey's last transaction block against the " + "`TxRateLimit` storage value and wait the remaining blocks." + ), + "HotKeySwapOnSubnetIntervalNotPassed": ( + "A hotkey swap on a subnet was attempted before `HotkeySwapOnSubnetInterval` blocks " + "passed since the coldkey's last swap on that netuid. Compare `LastHotkeySwapOnNetuid` " + "for the coldkey with the current block and retry after the interval." + ), + "IncorrectCommitRevealVersion": ( + "The `commit_reveal_version` argument does not match the chain's current commit-reveal " + "weights version. Query the `CommitRevealWeightsVersion` storage item and upgrade or " + "configure the client to commit with that version." + ), + "IncorrectWeightVersionKey": ( + "The `version_key` supplied with set_weights is older than the subnet's required " + "weights version. Compare it against the `WeightsVersionKey` hyperparameter " + "(`btcli sudo get --netuid `) and update the validator software or the key." + ), + "InputLengthsUnequal": ( + "A batch weights call passed vectors of different lengths, e.g. netuids vs commit " + "hashes, or uids vs values, salts and version_keys in batch reveal. Check that every " + "parallel vector argument in the batch extrinsic has the same length." + ), + "InsufficientAlphaBalance": ( + "A stake decrease asked to debit more alpha than the hotkey-coldkey pair holds on that " + "subnet. Compare the requested amount against the pair's current stake on the netuid " + "(`btcli stake list` or the `Alpha` storage entry)." + ), + "InsufficientLiquidity": ( + "The pool cannot absorb the operation: the swap simulation failed, reserves are smaller " + "than the payout, or the amount exceeds the pool's supported input. Check the subnet " + "pool reserves `SubnetTAO`, `SubnetAlphaIn` and `SubnetAlphaOut` against the amount." + ), + "InsufficientStakeForLock": ( + "The requested lock amount exceeds the coldkey's total alpha stake on that subnet " + "(existing locked mass included). Compare the amount against the coldkey's stake on the " + "netuid, e.g. via `btcli stake list`, and lock less or add stake first." + ), + "InsufficientTaoBalance": ( + "The coldkey's free TAO balance is below the amount a TAO-side operation needs: a " + "transfer between coldkeys, a burn or recycle, or a subnet-registration lock. Check " + "the coldkey's balance with `btcli wallet balance` against the amount being moved." + ), + "InvalidChild": ( + "The children or parents list includes the pivot hotkey itself (a self-loop), including " + "during a hotkey swap when the new hotkey is already a child or parent of the old one. " + "Check the `children` argument and `ChildKeys`/`ParentKeys` for the hotkeys involved." + ), + "InvalidChildkeyTake": ( + "The childkey take is outside the allowed range for the subnet: below the effective " + "minimum or above `MaxChildkeyTake`. Query `MinChildkeyTake` and `MaxChildkeyTake` and " + "pick a `take` value within those bounds." + ), + "InvalidDifficulty": ( + "The submitted proof-of-work hash does not meet the required difficulty (the faucet " + "uses a fixed 1,000,000; PoW registration uses the subnet's difficulty). Check the " + "`Difficulty` storage for the netuid and regenerate work against the current block." + ), + "InvalidIdentity": ( + "The submitted coldkey or subnet identity failed validation, typically a field " + "exceeding its allowed byte length or malformed data. Check each identity field's " + "length against the limits enforced by the chain before calling set_identity or " + "set_subnet_identity." + ), + "InvalidIpAddress": ( + "The `ip` argument to serve_axon or serve_prometheus is not a valid address for the " + "declared `ip_type` (prometheus additionally rejects the zero address). Verify the IP " + "encodes correctly as IPv4 or IPv6 and matches the `ip_type` passed." + ), + "InvalidIpType": ( + "The `ip_type` argument to serve_axon or serve_prometheus is not 4 or 6. Check the " + "value being sent by the miner or client; only IPv4 (4) and IPv6 (6) are accepted." + ), + "InvalidLeaseBeneficiary": ( + "The account registering a leased network is not the creator of the crowdloan currently " + "being finalized. Check the crowdloan's `creator` field in the crowdloan pallet storage " + "and submit the call from that coldkey." + ), + "InvalidNumRootClaim": ( + "The value passed to sudo_set_num_root_claims exceeds the compile-time maximum number " + "of root claims. Check the `new_value` argument against the chain's " + "`MAX_NUM_ROOT_CLAIMS` constant and pass a smaller number." + ), + "InvalidPort": ( + "The `port` argument to serve_axon or serve_prometheus is 0, which is rejected. Check " + "the miner or client axon configuration and serve on a non-zero port." + ), + "InvalidRecoveredPublicKey": ( + "The EVM key association signature recovered to a public key whose keccak hash does not " + "equal the supplied `evm_key`. Verify the signature was produced by the claimed EVM key " + "over the hotkey plus block hash message (EIP-191 format)." + ), + "InvalidRevealCommitHashNotMatch": ( + "The revealed uids, values, salt and version_key hash to a value that matches none of " + "the hotkey's pending non-expired commits. Check that the reveal parameters and salt " + "exactly match what was committed, and inspect `WeightCommits` for the hotkey and " + "netuid." + ), + "InvalidRevealRound": ( + "A timelocked weights commit specified a `reveal_round` older than the latest stored " + "DRAND round, so it could be decrypted immediately. Query the drand pallet's " + "`LastStoredRound` and commit with a future round number." + ), + "InvalidRootClaimThreshold": ( + "The value passed to set the root claim threshold exceeds the chain's maximum allowed " + "threshold. Check the `new_value` argument against the `MAX_ROOT_CLAIM_THRESHOLD` " + "constant and the current `RootClaimableThreshold` for the netuid." + ), + "InvalidSeal": ( + "The seal hash recomputed from the supplied `block_number`, `nonce` and key does not " + "equal the submitted `work`. Verify the PoW solver built the seal for the same key and " + "block it submits, and that the work bytes were not corrupted in transit." + ), + "InvalidSubnetNumber": ( + "A root-claim call passed an empty subnet set or more subnets than the per-call maximum " + "(claim_root, or KeepSubnets in set_root_claim_type). Check the `subnets` argument is " + "non-empty and within the `MAX_SUBNET_CLAIMS` limit." + ), + "InvalidValue": ( + "A generic out-of-range parameter on an admin or sudo call, e.g. mechanism counts, " + "emission splits summing away from 65535, max UIDs or take bounds. Check the specific " + "argument against the min/max storage items the extrinsic validates (e.g. " + "`MinAllowedUids`, `MaxMechanismCount`)." + ), + "InvalidVotingPowerEmaAlpha": ( + "The alpha passed to set the voting power EMA exceeds 10^18, which represents 1.0. " + "Check the `alpha` argument to sudo_set_voting_power_ema_alpha; it must be at most " + "10^18, and the current value is in `VotingPowerEmaAlpha` per netuid." + ), + "InvalidWorkBlock": ( + "The `block_number` in the proof-of-work submission is in the future or more than 3 " + "blocks old, so the work is stale. Compare the submitted block number with the current " + "chain height and regenerate the PoW against a fresh block." + ), + "LeaseCannotEndInThePast": ( + "The `end_block` supplied when registering a leased network is not after the current " + "block. Check the current chain height and pass an `end_block` in the future, or omit " + "it for a perpetual lease." + ), + "LeaseDoesNotExist": ( + "The `lease_id` argument does not correspond to any stored lease. Query the " + "`SubnetLeases` storage map to confirm the lease id and whether it was already " + "terminated." + ), + "LeaseHasNoEndBlock": ( + "The lease being terminated is perpetual (its `end_block` is None), so it can never be " + "ended this way. Check the lease's `end_block` field in `SubnetLeases` for the given " + "lease id." + ), + "LeaseHasNotEnded": ( + "The lease termination was attempted before the lease's `end_block` was reached. " + "Compare the current block height with the `end_block` stored in `SubnetLeases` for the " + "lease id and retry after it passes." + ), + "LeaseNetuidNotFound": ( + "After registering the leased network, no subnet owned by the lease's derived coldkey " + "could be found, so the netuid lookup failed. Inspect `SubnetOwner` entries for the " + "lease coldkey; this usually indicates the registration did not complete." + ), + "LiquidAlphaDisabled": ( + "Setting `alpha_low`/`alpha_high` was attempted while liquid alpha is disabled on the " + "subnet. Check the `LiquidAlphaOn` hyperparameter (`btcli sudo get --netuid `) and " + "have the subnet owner enable liquid alpha first." + ), + "LockHotkeyMismatch": ( + "The coldkey already has a conviction lock on this subnet bound to a different hotkey, " + "and locks for one coldkey per subnet must target a single hotkey. Check the existing " + "lock's hotkey in the lock storage for the coldkey and netuid, or move the lock first." + ), + "LockIdOverFlow": ( + "The global network-registration lock id counter reached its u32 maximum while queueing " + "a subnet registration, so no new lock could be created. Check the " + "`NetworkRegistrationLockId` storage value; this indicates lock id exhaustion, not a " + "balance problem." + ), + "MaxWeightExceeded": ( + "After normalization, one of the submitted weights exceeds the subnet's maximum weight " + "limit (self-weight is exempt). Check the `MaxWeightsLimit` hyperparameter " + "(`btcli sudo get --netuid `) and flatten the weight vector before setting." + ), + "MechanismDoesNotExist": ( + "The target subnet or its sub-mechanism does not exist: the netuid is unknown, the " + "mechanism index is at or above `MechanismCountCurrent`, or a non-dynamic `mechid` was " + "requested. Check the netuid with `btcli subnets list` and the mechanism count for that " + "subnet." + ), + "NeedWaitingMoreBlocksToStarCall": ( + "The subnet owner called start_call before enough blocks had passed since the subnet " + "was registered. Compare the current block with `NetworkRegisteredAt` for the netuid " + "plus the start-call delay and retry once the window opens." + ), + "NetworkDissolveAlreadyQueued": ( + "The subnet is already in the dissolve cleanup queue, so it cannot be queued for " + "dissolution again. Check the `DissolveCleanupQueue` storage value for the netuid " + "before submitting another dissolve request." + ), + "NetworkTxRateLimitExceeded": ( + "The coldkey attempted register_network again before the network registration rate " + "limit elapsed since its previous registration. Check the coldkey's last " + "register-network block against the `NetworkRateLimit` storage value and wait the " + "remaining blocks." + ), + "NeuronNoValidatorPermit": ( + "The hotkey tried to set weights on other neurons without holding a validator permit on " + "that subnet. Check `ValidatorPermit` for the neuron's uid (e.g. via the metagraph or " + "`btcli subnets metagraph`) and whether its stake ranks it as a validator." + ), + "NewColdKeyIsHotkey": ( + "The proposed new coldkey in a coldkey swap is already an existing hotkey account, " + "which is not allowed. Check the `Owner` storage for the candidate key to confirm it is " + "not registered as a hotkey, and pick a fresh coldkey." + ), + "NewHotKeyIsSameWithOld": ( + "swap_hotkey was called with `new_hotkey` equal to the current hotkey, so there is " + "nothing to swap. Check the extrinsic arguments and supply a different destination " + "hotkey." + ), + "NewHotKeyNotCleanForRootSwap": ( + "The destination hotkey has pending root claimable dividends, non-zero root stake, or " + "root-claimed history, so root accounting cannot merge safely. Check `RootClaimable` " + "and root-subnet stake for the new hotkey; claim or clear them, or use a fresh hotkey." + ), + "NoExistingLock": ( + "move_lock was called but no conviction lock exists for the signing coldkey on that " + "subnet. Check the lock storage for the coldkey and netuid, and create a lock before " + "attempting to move it to another hotkey." + ), + "NoNeuronIdAvailable": ( + "Registration could not obtain a uid: the subnet's `MaxAllowedUids` is 0, or the subnet " + "is full and every neuron is immune from pruning. Check `SubnetworkN` versus " + "`MaxAllowedUids` for the netuid and retry after immunity periods expire." + ), + "NoWeightsCommitFound": ( + "A weights reveal was submitted but no pending (non-expired) commit exists for the " + "hotkey and netuid, possibly because it already expired. Query the `WeightCommits` " + "storage map for the hotkey and check the commit hasn't passed the reveal window." + ), + "NonAssociatedColdKey": ( + "The signing coldkey does not own the hotkey it is trying to operate on (stake, swap, " + "serve, children or take changes). Check the `Owner` storage entry for the hotkey, e.g. " + "via `btcli wallet overview`, and sign with the coldkey that registered it." + ), + "NotEnoughAlphaOutToRecycle": ( + "A recycle or burn of alpha requested more than the subnet's outstanding alpha supply. " + "Compare the amount against the `SubnetAlphaOut` storage value for the netuid and " + "reduce the recycle amount." + ), + "NotEnoughBalanceToPaySwapColdKey": ( + "The coldkey's free TAO balance cannot cover the coldkey swap cost, which is recycled " + "when the swap executes. Check the balance with `btcli wallet balance` against the swap " + "cost and top up before scheduling the swap." + ), + "NotEnoughBalanceToPaySwapHotKey": ( + "The coldkey's free TAO balance is below the hotkey swap cost (a per-subnet cost " + "applies when swapping on a single netuid). Check `btcli wallet balance` against the " + "key swap cost and fund the coldkey before retrying." + ), + "NotEnoughBalanceToStake": ( + "The coldkey's free balance is less than the TAO required, either the stake amount in " + "add_stake or the burn cost of a registration. Check `btcli wallet balance` against the " + "amount or the current registration burn (`Burn` storage for the netuid)." + ), + "NotEnoughStake": ( + "The caller's hotkey holds less stake than the action requires; a generic " + "insufficient-stake failure on staking-related calls. Check the hotkey's stake on the " + "relevant subnet, e.g. `btcli stake list`, against the amount the extrinsic needs." + ), + "NotEnoughStakeToSetChildkeys": ( + "Raised by `set_children` when the parent hotkey's total stake is below " + "`StakeThreshold` and it is not the subnet owner hotkey. Compare the hotkey's total " + "stake (`btcli stake list`) against the `StakeThreshold` storage value." + ), + "NotEnoughStakeToSetWeights": ( + "Setting or committing weights failed because the hotkey's stake weight on the subnet " + "is below `StakeThreshold` (the weights-min-stake floor); the subnet owner hotkey is " + "exempt. Check the hotkey's stake on that netuid against `StakeThreshold`." + ), + "NotEnoughStakeToWithdraw": ( + "An unstake, stake move, swap, or transfer requested more alpha than the hotkey-coldkey " + "pair holds on that subnet. Compare the requested amount against the pair's current " + "stake on the netuid (`btcli stake list` or the `Alpha` storage)." + ), + "NotRootSubnet": ( + "A call that only operates on the root network, such as setting root network weights, " + "was given a non-root netuid. Check the netuid argument; root operations must target " + "netuid 0." + ), + "NotSubnetOwner": ( + "The signing coldkey is not the recorded owner of the subnet it tried to administer " + "(e.g. setting subnet identity or owner-only hyperparameters). Compare the caller " + "against the `SubnetOwner` storage entry for that netuid." + ), + "Overflow": ( + "A checked arithmetic operation overflowed, e.g. incrementing `NextSubnetLeaseId` when " + "registering a leased network, or adding to a crowdloan's `raised` amount or " + "contributor count. Internal guard; inspect the amounts involved as this should not " + "occur with realistic values." + ), + "ProportionOverflow": ( + "The child proportions passed to `set_children` sum to more than u64::MAX. Reduce the " + "per-child proportion values so their total fits in a u64; each proportion is a " + "fraction of u64::MAX." + ), + "RegistrationNotPermittedOnRootSubnet": ( + "A neuron registration or child-hotkey operation (`register`, `burned_register`, " + "`set_children`) was called with the root netuid, where these calls are invalid. Check " + "the netuid argument; use a regular subnet, or `root_register` for root membership." + ), + "RegistrationPriceLimitExceeded": ( + "`burned_register` with a price limit failed because the subnet's current registration " + "burn cost exceeds the supplied `limit_price`. Check the current burn via `Burn` " + "storage or `btcli subnets list` and raise the limit or wait for the cost to decay." + ), + "RevealPeriodTooLarge": ( + "`set_reveal_period` was given a commit-reveal period above the compiled-in maximum " + "number of epochs. Lower the `reveal_period` argument; the current setting is readable " + "from `RevealPeriodEpochs` for the netuid." + ), + "RevealPeriodTooSmall": ( + "`set_reveal_period` was given a commit-reveal period below the compiled-in minimum " + "number of epochs. Raise the `reveal_period` argument; the current setting is readable " + "from `RevealPeriodEpochs` for the netuid." + ), + "RevealTooEarly": ( + "A weight reveal was submitted before the commit's reveal window: the current epoch " + "must equal the commit epoch plus the reveal period. Check the commit in " + "`WeightCommits` and the subnet's `RevealPeriodEpochs`, then wait for the reveal epoch." + ), + "RootNetworkDoesNotExist": ( + "Root registration or root stake claiming found no root network in chain state, which " + "only happens on misconfigured or freshly bootstrapped chains. Verify netuid 0 exists " + "in `NetworksAdded`." + ), + "SameAutoStakeHotkeyAlreadySet": ( + "The coldkey tried to set its auto-stake destination on a subnet to the hotkey that is " + "already configured. Read `AutoStakeDestination` for the coldkey and netuid before " + "calling; only a different hotkey is accepted." + ), + "SameNetuid": ( + "A stake swap or move where coldkey, hotkey, and subnet are all unchanged, so " + "`origin_netuid` equals `destination_netuid` with nothing to transition. Check the call " + "arguments; at least the subnet or one of the keys must differ." + ), + "ServingRateLimitExceeded": ( + "`serve_axon` or `serve_prometheus` was called again before enough blocks passed since " + "the neuron's last serving update. Check the axon's last update block in `Axons` " + "against the `ServingRateLimit` (serving_rate_limit hyperparameter) and wait." + ), + "SettingWeightsTooFast": ( + "The neuron set weights again before `WeightsSetRateLimit` blocks elapsed since its " + "last weight update on that subnet. Check the weights_rate_limit hyperparameter and the " + "neuron's `LastUpdate` entry, then wait the remaining blocks." + ), + "SlippageTooHigh": ( + "A stake, unstake, or move with a price limit would execute at a worse rate than the " + "limit allows and `allow_partial` was false. Compare the `limit_price` argument with " + "the subnet's current alpha price (`btcli subnets price`) or permit partial execution." + ), + "StakeTooLowForRoot": ( + "`root_register` when the root network is full and the hotkey's stake on netuid 0 does " + "not exceed the lowest-staked current root member. Compare your hotkey's root stake " + "against existing root validators (`btcli subnets metagraph 0` or " + "`btcli query neurons --netuid 0`)." + ), + "StakeUnavailable": ( + "An unstake would dip into stake that is still locked: the requested alpha exceeds the " + "coldkey's unlocked balance on that subnet. Check the `Lock` entry for the coldkey and " + "netuid; only total stake minus the decaying locked mass can be unstaked." + ), + "StakingRateLimitExceeded": ( + "Staking operations (add_stake, remove_stake, and similar) were submitted faster than " + "the per-block staking rate limit allows for the hotkey-coldkey pair. Space the " + "transactions out and retry in a later block." + ), + "StartCallNotReady": ( + "`start_call` was made before `StartCallDelay` blocks elapsed since the subnet was " + "registered. Compare the current block against `NetworkRegisteredAt` for the netuid " + "plus `StartCallDelay`, and wait for the remainder." + ), + "SubNetRegistrationDisabled": ( + "Neuron registration is switched off: either the subnet's `NetworkRegistrationAllowed` " + "flag is false, or network creation has not opened yet (`NetworkRegistrationStartBlock` " + "is in the future). Check the network_registration_allowed hyperparameter for the " + "netuid." + ), + "SubnetBuybackRateLimitExceeded": ( + "A subnet buyback operation (staking TAO and immediately burning the acquired alpha, " + "e.g. via `add_stake_burn`) was repeated within its rate-limit window. Wait for the " + "window to pass before retrying the buyback." + ), + "SubnetLimitReached": ( + "`register_network` failed because the subnet count is at the network limit and no " + "existing subnet is eligible to be pruned. Check the number of registered subnets " + "(`btcli subnets list`) against the subnet limit and retry once a subnet becomes " + "prunable." + ), + "SubnetNotExists": ( + "The netuid passed to the call does not correspond to a registered subnet. Verify the " + "netuid argument against `NetworksAdded` or `btcli subnets list`; the subnet may also " + "have been dissolved." + ), + "SubtokenDisabled": ( + "The subnet's alpha token is not yet enabled, so staking, swapping, and trading on it " + "are blocked; `SubtokenEnabled` is false until the owner makes the `start_call` after " + "registration. Check `SubtokenEnabled` for the netuid involved." + ), + "SymbolAlreadyInUse": ( + "The token symbol requested for the subnet is already assigned to another subnet. Scan " + "`TokenSymbol` across netuids and pick a symbol that is not taken." + ), + "SymbolDoesNotExist": ( + "The requested token symbol is not in the chain's predefined symbol table, so it cannot " + "be assigned to a subnet. Check the symbol argument against the chain's built-in " + "`SYMBOLS` list and choose a valid entry." + ), + "TempoOutOfBounds": ( + "The subnet owner gave `sudo_set_tempo` a tempo outside the allowed MIN_TEMPO to " + "MAX_TEMPO range (360-50,400 blocks). Check the tempo argument against those chain " + "constants and pick a value inside the bounds; only root may set a tempo outside them." + ), + "TooManyChildren": ( + "`set_children` was called with more than 5 child hotkeys for a parent on the subnet. " + "Trim the children list to at most 5 entries." + ), + "TooManyRegistrationsThisBlock": ( + "Registrations in the current block already reached the subnet's per-block cap " + "(`MaxRegistrationsPerBlock`, the max_regs_per_block hyperparameter); root registration " + "enforces the same cap on netuid 0. Retry in the next block." + ), + "TooManyRegistrationsThisInterval": ( + "Registrations in the current interval reached the cap of three times " + "`TargetRegistrationsPerInterval` for the subnet. Compare `RegistrationsThisInterval` " + "against that hyperparameter and wait for the next interval to start." + ), + "TooManyUIDsPerMechanism": ( + "Setting max UIDs or mechanism count would make max_uids times mechanism_count exceed " + "the chain default of 256 UIDs per subnet. Check `MaxAllowedUids` and the subnet's " + "mechanism count so their product stays within the limit." + ), + "TooManyUnrevealedCommits": ( + "`commit_weights` (or a CRv3 commit) failed because the hotkey already has 10 " + "unrevealed commits queued on the subnet. Inspect `WeightCommits` for the hotkey and " + "reveal or let old commits expire before committing again." + ), + "TransactorAccountShouldBeHotKey": ( + "The extrinsic must be signed by the hotkey itself, but a different account (typically " + "the coldkey) was the origin. Check which key signs the transaction; calls like axon " + "serving expect the hotkey as origin." + ), + "TransferDisallowed": ( + "A stake transfer or cross-subnet move was attempted while the origin or destination " + "subnet has stake transfers switched off. Check the `TransferToggle` storage for both " + "netuids involved; the subnet owner must enable transfers first." + ), + "TrimmingWouldExceedMaxImmunePercentage": ( + "Trimming the subnet's UIDs cannot proceed because immune neurons would make up at " + "least the maximum immune share (80%) of the reduced slot count. Check neurons still in " + "`ImmunityPeriod` and retry after their immunity lapses or with a higher max UID " + "target." + ), + "TxChildkeyTakeRateLimitExceeded": ( + "`set_childkey_take` was called again for the hotkey on this subnet before its " + "rate-limit window elapsed. Check the block of the last childkey-take change against " + "`TxChildkeyTakeRateLimit` and wait out the remainder." + ), + "TxRateLimitExceeded": ( + "An owner or admin transaction (e.g. `set_children`, tempo or hyperparameter updates, " + "setting the owner hotkey) was repeated within its rate-limit window for that key and " + "subnet. Check when the same transaction type last succeeded and wait for the limit to " + "pass." + ), + "UidMapCouldNotBeCleared": ( + "During a UID reshuffle or trim, clearing the subnet's `Uids` map left residual entries " + "(the storage clear returned a cursor). Internal state inconsistency rather than a " + "caller error; inspect the `Uids` storage for the netuid and report it." + ), + "UidVecContainInvalidOne": ( + "The weight submission includes a UID that is not registered on the subnet, i.e. at " + "least one entry is not below `SubnetworkN`. Check the `uids` argument against the " + "subnet's neuron count in the metagraph (`btcli subnets metagraph`)." + ), + "UidsLengthExceedUidsInSubNet": ( + "The weight submission contains more UID entries than there are neurons registered on " + "the subnet. Compare the length of the `uids` argument against `SubnetworkN` for the " + "netuid and trim the vector." + ), + "UnableToRecoverPublicKey": ( + "While associating an EVM key, the secp256k1 public key recovered from the supplied " + "signature could not be parsed. Check that the signature was produced by signing the " + "expected EIP-191 message (hotkey plus block hash) with the EVM private key." + ), + "UnlockAmountTooHigh": ( + "An unlock requested more alpha than remains locked for the coldkey on that subnet. " + "Check the lock's remaining decaying locked mass in the `Lock` storage entry and " + "request at most that amount." + ), + "VotingPowerTrackingNotEnabled": ( + "Disabling voting power tracking was requested on a subnet where tracking is not " + "currently active. Check the `VotingPowerTrackingEnabled` storage flag for the netuid " + "before calling the disable extrinsic." + ), + "WaitingForDissolvedSubnetCleanup": ( + "The operation is blocked while a dissolved subnet's storage is still being torn down " + "in the background. Check the `DissolveCleanupQueue` and retry after the on-idle " + "cleanup for that netuid completes." + ), + "WeightVecLengthIsLow": ( + "The weight submission has fewer entries than the subnet's minimum (setting only a " + "self-weight is the one exception). Compare the vector length against the " + "`MinAllowedWeights` (min_allowed_weights hyperparameter) for the netuid." + ), + "WeightVecNotEqualSize": ( + "The `uids` and `values` vectors passed to a weight-setting call have different " + "lengths, so they cannot be paired. Check the call arguments; both vectors must have " + "exactly one value per UID." + ), + "ZeroBalanceAfterWithdrawn": ( + "Withdrawing TAO from the coldkey (e.g. paying a registration burn or adding stake) " + "would leave the account at zero, below what keeps it alive. Check the coldkey's free " + "balance and leave at least the existential deposit after the amount withdrawn." + ), +} diff --git a/sdk/python/bittensor/error_descriptions/sudo.py b/sdk/python/bittensor/error_descriptions/sudo.py new file mode 100644 index 0000000000..1d2422a2b0 --- /dev/null +++ b/sdk/python/bittensor/error_descriptions/sudo.py @@ -0,0 +1,10 @@ +"""Chain error descriptions declared (first) by the `Sudo` pallet.""" + +from __future__ import annotations + +DESCRIPTIONS: dict[str, str] = { + "RequireSudo": ( + "The call requires the sudo key but was signed by a different account. Compare the " + "sender against the account stored in the Sudo pallet's `Key` storage item." + ), +} diff --git a/sdk/python/bittensor/error_descriptions/swap.py b/sdk/python/bittensor/error_descriptions/swap.py new file mode 100644 index 0000000000..cf2efcf7d0 --- /dev/null +++ b/sdk/python/bittensor/error_descriptions/swap.py @@ -0,0 +1,46 @@ +"""Chain error descriptions declared (first) by the `Swap` pallet.""" + +from __future__ import annotations + +DESCRIPTIONS: dict[str, str] = { + "FeeRateTooHigh": ( + "`set_fee_rate` was called with a rate above the swap pallet's `MaxFeeRate` config " + "constant. Compare the `rate` argument (u16-scaled fraction) against `MaxFeeRate` " + "before submitting." + ), + "InsufficientInputAmount": ( + "Declared for swap inputs too small to execute, but no current code path raises it " + "since the user-liquidity code was removed. If seen on an older runtime, check that the " + "swap input amount is nonzero and large enough to produce output." + ), + "InvalidLiquidityValue": ( + "Legacy error from the removed V3 user-liquidity code, raised when an added or removed " + "liquidity amount was below the pallet's `MinimumLiquidity`. Not raised on current " + "runtimes; on older ones compare the `liquidity` argument to `MinimumLiquidity`." + ), + "InvalidTickRange": ( + "Legacy error from the removed V3 user-liquidity code, raised when `tick_low` was not " + "below `tick_high` or a tick failed to convert to a sqrt price. Not raised on current " + "runtimes; check the tick range arguments on older ones." + ), + "PriceLimitExceeded": ( + "The `limit_price` given to a swap is not beyond the current pool price in the trade's " + "direction, so the swap would immediately breach it. Compare the limit price argument " + "against the subnet's current alpha price before submitting." + ), + "ReservesOutOfBalance": ( + "Swap balancer initialization failed because the subnet's TAO and alpha reserves " + "produce an invalid ratio, for example both reserves are zero when an initial price is " + "supplied. Inspect the subnet's TAO and alpha reserves and the `SwapBalancer` entry." + ), + "ReservesTooLow": ( + "The output-side reserve is below the swap pallet's `MinimumReserve`, or a swap step " + "produced zero output for a nonzero input. Check the subnet's TAO and alpha reserves " + "against `MinimumReserve` and reduce the trade size." + ), + "SwapInputTooLarge": ( + "The swap's net input after fees exceeds 1000 times the input-side reserve, the " + "pallet's hard per-trade cap. Compare the input amount against the subnet's input-side " + "reserve (TAO or alpha) and split the trade if needed." + ), +} diff --git a/sdk/python/bittensor/error_descriptions/system.py b/sdk/python/bittensor/error_descriptions/system.py new file mode 100644 index 0000000000..15b34dec59 --- /dev/null +++ b/sdk/python/bittensor/error_descriptions/system.py @@ -0,0 +1,52 @@ +"""Chain error descriptions declared (first) by the `System` pallet.""" + +from __future__ import annotations + +DESCRIPTIONS: dict[str, str] = { + "CallFiltered": ( + "The runtime's origin call filter (e.g. `BaseCallFilter` or a restricted origin) " + "rejected this call before dispatch. Check whether the specific call is permitted for " + "the origin you used, including any proxy or safe-mode filtering in effect." + ), + "FailedToExtractRuntimeVersion": ( + "The new runtime code passed to `set_code` did not yield a readable version: calling " + "`Core_version` or decoding `RuntimeVersion` failed. Check that the submitted blob is a " + "valid, complete runtime wasm and not truncated or compressed incorrectly." + ), + "InvalidSpecName": ( + "The new runtime's `spec_name` does not match the current runtime, so `set_code` " + "refuses the upgrade. Check the `RuntimeVersion` embedded in the new wasm and ensure " + "the spec name is identical to the chain's current one." + ), + "MultiBlockMigrationsOngoing": ( + "Runtime code replacement is blocked while a multi-block migration is still executing. " + "Wait for the ongoing migrations to complete (check the multi-block migrations cursor) " + "before retrying `set_code` or the authorized upgrade." + ), + "NonDefaultComposite": ( + "The account cannot be killed because its composite account data is not in the default " + "state. Check the account's `System.Account` entry; all balance and data fields must be " + "default before the account can be removed this way." + ), + "NonZeroRefCount": ( + "The account cannot be purged because other pallets still reference it. Check the " + "`consumers`, `providers`, and `sufficients` counters in the account's `System.Account` " + "record; all references must be released first." + ), + "NothingAuthorized": ( + "No code upgrade has been authorized, so `apply_authorized_upgrade` has nothing to " + "apply. Check the `AuthorizedUpgrade` storage item in System; an authorization must be " + "recorded before applying the new code." + ), + "SpecVersionNeedsToIncrease": ( + "The new runtime's `spec_version` is not greater than the current one, so the upgrade " + "is rejected. Check the `RuntimeVersion` in the new wasm and bump `spec_version` above " + "the version currently on chain." + ), + "Unauthorized": ( + "In System, the code passed to `apply_authorized_upgrade` does not hash to the value in " + "`AuthorizedUpgrade`. In LimitOrders, the caller is not the order's signer, who alone " + "may cancel it. Check the code hash against the authorization, or the sender against " + "the order's `signer`." + ), +} diff --git a/sdk/python/bittensor/error_descriptions/utility.py b/sdk/python/bittensor/error_descriptions/utility.py new file mode 100644 index 0000000000..2f51dcdec0 --- /dev/null +++ b/sdk/python/bittensor/error_descriptions/utility.py @@ -0,0 +1,16 @@ +"""Chain error descriptions declared (first) by the `Utility` pallet.""" + +from __future__ import annotations + +DESCRIPTIONS: dict[str, str] = { + "InvalidDerivedAccount": ( + "Deriving the sub-account for `as_derivative` failed to decode into a valid account id " + "from the (caller, index) entropy. Check the `index` argument and the caller account " + "used for derivation." + ), + "TooManyCalls": ( + "The batch submitted to the utility pallet contains more calls than the batched-calls " + "limit allows. Check the length of the `calls` vector and split the work across " + "multiple smaller `batch`, `batch_all`, or `force_batch` submissions." + ), +} diff --git a/sdk/python/bittensor/error_map.py b/sdk/python/bittensor/error_map.py index 8ed72cb1fa..19d089d3a1 100644 --- a/sdk/python/bittensor/error_map.py +++ b/sdk/python/bittensor/error_map.py @@ -65,7 +65,7 @@ class ErrorCode(str, Enum): # ── Balances ──────────────────────────────────────────────────────── "VestingBalance": _C.INSUFFICIENT_BALANCE, "LiquidityRestrictions": _C.INSUFFICIENT_BALANCE, - "InsufficientBalance": _C.INSUFFICIENT_BALANCE, # also SubtensorModule, Crowdloan, Swap + "InsufficientBalance": _C.INSUFFICIENT_BALANCE, # also Crowdloan, Swap "ExistentialDeposit": _C.INSUFFICIENT_BALANCE, "Expendability": _C.INSUFFICIENT_BALANCE, "ExistingVestingSchedule": _C.ALREADY_EXISTS, @@ -186,6 +186,8 @@ class ErrorCode(str, Enum): "SubtokenDisabled": _C.SUBTOKEN_DISABLED, # also Swap "HotKeySwapOnSubnetIntervalNotPassed": _C.RATE_LIMITED, "SameNetuid": _C.INVALID_ARGUMENT, + "InsufficientTaoBalance": _C.INSUFFICIENT_BALANCE, + "InsufficientAlphaBalance": _C.INSUFFICIENT_BALANCE, "InvalidLeaseBeneficiary": _C.INVALID_ARGUMENT, "LeaseCannotEndInThePast": _C.INVALID_ARGUMENT, "LeaseNetuidNotFound": _C.NOT_FOUND, diff --git a/sdk/python/bittensor/extension/client.py b/sdk/python/bittensor/extension/client.py index 2bed2d1eef..fc6d9f2c53 100644 --- a/sdk/python/bittensor/extension/client.py +++ b/sdk/python/bittensor/extension/client.py @@ -122,3 +122,7 @@ async def sign_bytes(self, address: str, data_hex: str) -> dict[str, Any]: if not isinstance(result, dict): raise BridgeError("bridge returned an invalid bytes signature") return result + + async def report_transaction_result(self, success: bool) -> None: + """Tell the bridge page that the submitted transaction has finished.""" + await self.request("transaction.result", {"success": success}) diff --git a/sdk/python/bittensor/extension/signer.py b/sdk/python/bittensor/extension/signer.py index 223a212d28..395d5c2584 100644 --- a/sdk/python/bittensor/extension/signer.py +++ b/sdk/python/bittensor/extension/signer.py @@ -93,6 +93,10 @@ def account_name(self) -> str: async def close(self) -> None: await self._bridge.close() + async def report_transaction_result(self, success: bool) -> None: + """Update the bridge page after the chain submission completes.""" + await self._bridge.report_transaction_result(success) + async def sign_extrinsic_payload(self, payload: dict[str, Any]) -> dict[str, Any]: body = dict(payload) body["address"] = self.ss58_address diff --git a/sdk/python/bittensor/extension/static/bridge.html b/sdk/python/bittensor/extension/static/bridge.html index 8315399091..e394f7f4f2 100644 --- a/sdk/python/bittensor/extension/static/bridge.html +++ b/sdk/python/bittensor/extension/static/bridge.html @@ -14,69 +14,356 @@ Bittensor Extension Bridge - -

Bittensor Extension Bridge

-

Starting…

-

- Keep this tab open while using btcli --signer extension. - Authorize Talisman, Polkadot.js, or another compatible wallet when prompted. -

-
    + +
    + +
    +

    Keep this page open

    +

    Switch back to btcli.

    +
    +