# For agents (/docs/agents) The SDK and CLI are built to be driven by agents. Every operation is discoverable at runtime with a JSON schema, every mutation can be previewed before it spends anything, every failure returns a machine-readable code with a remediation hint, and a `Policy` can hard-bound what a session is allowed to do. Nothing here requires parsing human prose. ## Searching these docs [#searching-these-docs] Start with one fetch: [`/llms.txt`](/llms.txt). Every docs link in it is already raw markdown (`content.md`), so you can open a page without rewriting URLs or scraping HTML. ```bash # Curated index (start here) curl -s https://www.bittensor.com/llms.txt # Full docs corpus (for rg, not for stuffing into context) curl -s https://www.bittensor.com/llms-full.txt | rg -n '' # One page (same URLs as the links inside llms.txt) curl -s https://www.bittensor.com/llms.mdx/docs/quickstart/content.md # Chain source: search by symbol, or fetch a known path curl -s 'https://www.bittensor.com/code/search.json?q=do_move_stake' curl -s https://www.bittensor.com/code/raw/pallets/subtensor/src/staking/move_stake.rs # Heavy exploration: clone the repo (docs live in docs/) git clone --depth 1 https://github.com/RaoFoundation/subtensor ``` Prefer that index plus the JSON catalogs below over dumping [`/llms-full.txt`](/llms-full.txt) into context. ## Discover the operations [#discover-the-operations] ```bash btcli tools # JSON: every transaction op + summary, description, signer, input schema ``` ```python import bittensor as bt bt.intents.list_tools() # same catalog, as Python dicts bt.reads.list_reads() # every read: name, params, docs, category ``` The same catalogs are published statically by these docs: [`/catalog/intents.json`](/catalog/intents.json), [`/catalog/reads.json`](/catalog/reads.json), [`/catalog/errors.json`](/catalog/errors.json). Each entry includes `docs_url` (HTML), `markdown_url` (raw markdown to fetch), and often `sources` / `hits` with a `raw_url` into `/code`. Errors live under `.codes` (semantic) and `.chain_errors` (exact pallet names); see `.note`. ## Execute by name [#execute-by-name] An agent never needs to import intent classes — build and execute by op name with a plain dict, validated against the schema: ```python async with bt.Subtensor() as client: result = await client.execute_tool( "transfer", {"dest_ss58": "5F...", "amount_tao": 1.0}, wallet ) ``` On the CLI, every op in the catalog is `btcli tx ` (underscores become dashes) and every read is `btcli query `. ## Preview before you spend [#preview-before-you-spend] `plan` (SDK) and `--dry-run` (CLI) run the full pipeline — fee estimation, predicted effects, warnings, policy check — without submitting: ```python plan = await client.plan(intent, wallet) plan.fee # estimated fee plan.effects # list[str]: what this will do plan.warnings # non-fatal cautions (e.g. dust amounts) plan.ok # would policy allow it? ``` ## Bound the blast radius with Policy [#bound-the-blast-radius-with-policy] Attach a `Policy` to the client and every execution — by class, by name, or raw — passes through one choke point: ```python policy = bt.Policy(max_spend_tao=5.0, allowed_netuids=[1, 2]) async with bt.Subtensor("finney", policy=policy) as client: ... # anything exceeding the caps raises PolicyError at execute time ``` Operations whose cost cannot be bounded ahead of time (e.g. subnet registration, whose price floats) are blocked by a spend cap until it is raised — the safe default. Raw calls are refused unless the policy sets `allow_raw_calls=True`. ## Branch on failures, don't parse them [#branch-on-failures-dont-parse-them] Every write returns an `ExtrinsicResult`; failures carry a semantic [`ErrorCode`](/docs/errors) and a remediation hint: ```python result = await client.execute(intent, wallet) if not result.success: match result.error.code: case bt.ErrorCode.RATE_LIMITED: ... # wait and retry case bt.ErrorCode.INSUFFICIENT_BALANCE: ... # reduce amount case _: log(result.error.remediation) # actionable next step, always present ``` ## CLI rules for non-interactive use [#cli-rules-for-non-interactive-use] * `--json` on any command produces machine-readable output. * `--yes` skips confirmation prompts. Without it, a non-interactive session is **declined, not blocked** — the CLI never hangs waiting for input it can't get. * `--dry-run` previews any `tx` command. * Configuration comes from flags, `BT_*` environment variables, or `btcli config` — highest to lowest precedence. ## The escape hatch [#the-escape-hatch] Anything on chain that no intent wraps (deprecated, root/admin-only, or off-chain-signed calls) is still reachable: ```python call = bt.calls.Commitments.set_commitment(netuid=1, info={...}) await client.submit_call(call, wallet, signer="hotkey") ``` And generic accessors cover any storage item, constant, or runtime API in the chain metadata: ```python tempo = await client.query(bt.storage.SubtensorModule.Tempo, [1]) ed = await client.constant(bt.constants.Balances.ExistentialDeposit) ``` # The CLI (/docs/cli) The `btcli` command is the SDK with a shell in front: every read in the catalog is `btcli query `, every transaction is `btcli tx `, and both are generated from the same registries as the Python API, so they cannot diverge from it. ## Configuration [#configuration] Set connection and identity once, override per-command as needed: ```bash btcli config set network finney btcli config set wallet my_coldkey btcli config get ``` Precedence, highest first: **CLI flag > environment variable > config file > built-in default**. | Option | Env | Meaning | | ----------------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------ | | `--network`, `-n` | `BT_NETWORK` | `finney` / `test` / `local`, or a `ws://` endpoint | | `--wallet`, `-w` | `BT_WALLET` | coldkey wallet name | | `--wallet-hotkey`, `-H` | `BT_WALLET_HOTKEY` | hotkey name within the wallet | | `--wallet-path` | `BT_WALLET_PATH` | wallet directory | | `--json` | | machine-readable JSON output | | `--yes`, `-y` | | skip confirmation prompts | | `--dry-run` | | preview a mutation without submitting | | `--quiet`, `-q` | | suppress informational output | | `--signer` | | `wallet` (default) or `extension` — sign in a [browser extension](/docs/guides/extension-signing) like Talisman or Polkadot.js | ## Command groups [#command-groups] * `btcli query ` — every [read](/docs/query); `query --help` lists them grouped by topic. * `btcli tx ` — every [transaction](/docs/tx); `tx --help` lists them grouped by pallet. All support `--dry-run`, `--yes`, and `--proxy-for`. * Familiar workflows also have hand-written groups that wrap the same machinery: `wallet`, `stake`, `subnets`, `weights`, `axon`, `proxy`, `multisig`, `crowd`, `lock`, `timelock`, `config`. * The v9 shorthands still work as hidden aliases: `w`, `st`, `s`, `su`, `c`, `wt`, `cr`, `d` (plus variants like `wallets`, `subnet`, `conf`), and every hyphenated command also accepts its snake\_case spelling (`wallet new_hotkey`, `tx add_stake`). `-h` works as `--help` everywhere. * `btcli evm` — the [EVM layer](/docs/guides/evm): keys, funding, precompiles, and contract deployment. * `btcli tools` — the JSON catalog of every transaction op with schemas, for agents. ## Interactive by default, safe when it can't be [#interactive-by-default-safe-when-it-cant-be] Run a `tx` command with required options missing and the CLI prompts for them, then shows the plan and asks for confirmation. In a non-interactive session (no TTY) without `--yes`, the command is **declined rather than left hanging** — automation never blocks on a prompt it can't answer. ## Address arguments [#address-arguments] Any address option (`--dest`, `--hotkey`, `--coldkey`, ...) accepts a raw ss58 address, an address-book or proxy-book name, or a local key name (`HOTKEY`, `WALLET/HOTKEY`, or a wallet name for coldkey options). `--hotkey` / `--coldkey` fall back to the configured wallet when omitted; destination options never default. ## Shell completion [#shell-completion] Completion scripts for bash, zsh, and fish ship in `completions/` and install into the standard system locations. If your shell doesn't auto-load them (e.g. a venv install), source the script from your rc file, or run `btcli --install-completion`. # Bittensor Documentation (/docs) Bittensor is an open network where independent **subnets** produce digital commodities — compute, inference, storage, prediction — and the chain pays participants in its token, **TAO**, in proportion to the value they contribute. **Miners** produce the commodity, **validators** score the miners, **subnet creators** define the incentive mechanism, and **stakers** back validators with TAO. These docs cover the `bittensor` Python SDK and CLI: one install gives you a library (`import bittensor`) and a command line (`btcli`) that together can perform every user-facing operation on the chain. Coming from the v10 SDK or the standalone `bittensor-cli`? The API changed substantially in v11 — see [Migrating from v9/v10](/docs/migration) for exact old-to-new mappings. ## Where to go [#where-to-go] ## Install [#install] Requires Python 3.10–3.13. Using [uv](https://docs.astral.sh/uv/): ```bash uv venv && source .venv/bin/activate uv pip install bittensor ``` This installs both the `btcli` command and the `bittensor` Python package. ## Machine-readable everything [#machine-readable-everything] If you are an agent (or building one), you never need to scrape these pages. See [For agents → Searching these docs](/docs/agents#searching-these-docs) for the curl/grep recipes. * [`/llms.txt`](/llms.txt) — curated index; every docs link in it is already raw markdown (`content.md`). * [`/llms-full.txt`](/llms-full.txt) — entire docs corpus in one file (for `rg`, not for stuffing into context). * Every page as raw markdown: `/llms.mdx/docs//content.md` (also linked from each page’s copy-markdown control). * [`/catalog/intents.json`](/catalog/intents.json) — every transaction with its JSON schema, signer, `markdown_url`, and summary. * [`/catalog/reads.json`](/catalog/reads.json) — every query with parameters, `markdown_url`, and docs. * [`/catalog/errors.json`](/catalog/errors.json) — semantic codes under `.codes`, chain names under `.chain_errors`; each has remediation + `markdown_url`. * Chain Rust source: [`/code/search.json?q=…`](/code/search.json?q=do_move_stake) to search, [`/code/index.json`](/code/index.json) to list, `/code/raw/` to fetch. * The same catalogs, live from the tool itself: `btcli tools` on the CLI, `bt.intents.list_tools()` in Python. # Migrating from v9/v10 (/docs/migration) Bittensor 11 replaces two packages — the `bittensor` SDK (v10.x) and the separate `bittensor-cli` (v9.x, the old `btcli`) — with one package that contains both. The API is not a rename of the old one: the `Subtensor` class and its \~165 methods are gone, replaced by a small client with typed reads and a catalog of **intents** executed through one `plan`/`execute` pipeline. This page maps every commonly scripted old call and CLI command to its v11 equivalent. **If you are an LLM migrating a user's scripts**, follow this procedure: 1. Read this entire page — the tables are exact, not illustrative. 2. Apply the [SDK mappings](#reading-chain-state) and [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), [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: `bt.intents.list_tools()` and `bt.reads.list_reads()` in Python, `btcli tools` and `btcli query --help` on the CLI, or the generated references under [Transactions](/docs/tx) and [Queries](/docs/query). 5. Preview every rewritten mutation with `client.plan(...)` or `--dry-run` before submitting anything. ## Packages and install [#packages-and-install] | | Old | New | | ------ | -------------------------------------- | ---------------------------- | | SDK | `bittensor` 10.x | `bittensor` 11.x | | CLI | `bittensor-cli` 9.x (separate package) | included in `bittensor` | | Wallet | `bittensor_wallet` (separate package) | included: `bittensor.wallet` | | Python | 3.10+ | 3.10–3.13 | ```bash uv pip install bittensor # library + btcli, one package ``` The `[cli]` and `[evm]` extras still parse (`bittensor[cli]` installs fine) but are empty — everything is in the base install now. Remove `bittensor-cli` and `bittensor_wallet` from your requirements; both are superseded. Wallets on disk are **keyfile-compatible** — nothing to convert (see [Wallets](#wallets)). ### Upgrading an existing environment [#upgrading-an-existing-environment] Order matters in an environment that already has the old stack, because `bittensor-cli` 9.x and `bittensor` 11.x both own the `btcli` command: ```bash pip uninstall -y bittensor-cli bittensor-wallet pip install -U bittensor ``` If you upgraded first and uninstalled `bittensor-cli` after, pip deletes the `btcli` script (it is still listed in the old package's file manifest). Nothing is lost — reinstate it with: ```bash pip install --force-reinstall --no-deps bittensor ``` While a stale `bittensor-cli` is still installed, the v11 `btcli` prints a warning with this fix on every run. Two version pitfalls that fail *silently*: * **Python 3.14**: v10 allowed it, v11 supports 3.10–3.13. On a 3.14 interpreter `pip install -U bittensor` quietly keeps 10.x (pip picks the newest release whose `requires-python` matches). If `btcli --version` still says 9.x/10.x after upgrading, check `python --version` first. * **Pinned requirements**: anything pinning `bittensor<11` or `bittensor~=10` keeps working against v10 untouched — the new package changes nothing until you move the pin. Platform support: wheels ship for Linux (x86\_64, aarch64) and macOS (Apple Silicon and Intel), Python 3.10–3.13 — the same coverage as the old stack. Windows is unsupported natively (as before); use WSL. On any other platform pip falls back to a source build, which requires a Rust toolchain. ## The model change in one example [#the-model-change-in-one-example] Old (v10): ```python import bittensor as bt subtensor = bt.Subtensor(network="finney") wallet = bt.Wallet(name="my_coldkey", hotkey="my_hotkey") balance = subtensor.get_balance("5F...") response = subtensor.add_stake( wallet, netuid=1, hotkey_ss58="5F...validator", amount=bt.tao(10), ) if not response.success: print(response.message) ``` New (v11): ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") sub = bt.Subtensor() # defaults to finney; no close() needed balance = sub.balances.get("5F...") result = sub.execute( bt.AddStake(hotkey_ss58="5F...validator", netuid=1, amount_tao=10), wallet, ) if not result.success: print(result.error.code, result.error.remediation) ``` The three structural changes: 1. **One client, no method zoo.** `bt.Subtensor` keeps its name but is a new class: one entry point that is blocking when used directly and async when awaited (`async with bt.Subtensor() as client:`) — `AsyncSubtensor` is gone. The \~165 v10 methods are replaced by typed reads on category namespaces (`sub.subnets`, `sub.staking`, `sub.delegation`, `sub.prices`, ... — with autocomplete and signature help), dispatchable by name too (`sub.read("uid", hotkey_ss58=..., netuid=...)`), and anything else is reachable through generic accessors (`sub.query(bt.storage...)`). See [The client](/docs/concepts/client). 2. **Transactions are intents.** Every mutation is a small dataclass (`bt.Transfer`, `bt.AddStake`, `bt.SetWeights`, ...) executed through `client.plan(intent, wallet)` / `client.execute(intent, wallet)`. See [The transaction model](/docs/concepts/transactions). 3. **Results are data.** `ExtrinsicResponse` becomes `ExtrinsicResult` with a classified `error.code` and a `remediation` hint instead of prose. ## Connecting [#connecting] | v10 | v11 | | ------------------------------------------------------------- | ------------------------------------------------------------------ | | `bt.Subtensor(network="finney")` | `bt.Subtensor()` (blocking, finney is the default) | | `bt.AsyncSubtensor(...)` + `await bt.initialize()` | `async with bt.Subtensor() as client:` | | `get_async_subtensor(network=...)` | `await bt.Subtensor(...)` | | `Subtensor(config=config)` | gone — pass arguments directly | | `Subtensor(..., mock=True)` / `MockSubtensor` | gone — test against a [local node](/docs/guides/local-development) | | `fallback_endpoints=`, `archive_endpoints=`, `retry_forever=` | same names on `Subtensor` | | `subtensor.close()` | optional — automatic on garbage collection / process exit | Network names `finney`, `test`, `archive`, `local` resolve to the same endpoints as before, and any `ws://` / `wss://` URL is accepted directly. The extra v9-CLI aliases (`dev`, `rao`, `latent-lite`) are gone — pass the URL. Every v10 read took `block=`. In v11, namespace methods still accept `block=`, and for multi-read consistency pin a snapshot: ```python snap = await client.at(block) # Snapshot: same read surface, one block bal = await snap.balances.get("5F...") ``` ## Environment variables and configuration [#environment-variables-and-configuration] | v10 SDK / v9 CLI | v11 | | ------------------------------------------------- | -------------------------------------------------------------------------------- | | `BT_SUBTENSOR_NETWORK` | `BT_NETWORK` | | `BT_SUBTENSOR_CHAIN_ENDPOINT` | pass a `ws://` URL as the network; `BT_CHAIN_ENDPOINT` overrides `local` | | `BT_WALLET_NAME` | `BT_WALLET` | | `BT_WALLET_HOTKEY` | `BT_WALLET_HOTKEY` (unchanged) | | `BT_WALLET_PATH` | `BT_WALLET_PATH` (unchanged) | | — | `BT_WALLET_PASSWORD`, `BT_WALLET_PASSWORD_FILE` (non-interactive coldkey unlock) | | `BT_NO_PARSE_CLI_ARGS` | gone — the SDK never parses CLI args | | `BT_LOGGING_*` | gone — standard `logging` (see below) | | `BT_AXON_*`, `BT_PRIORITY_*`, `BT_MEV_PROTECTION` | gone | | `BTCLI_CONFIG_PATH` (`~/.bittensor/config.yml`) | `BTCLI_CONFIG` (`~/.bittensor/btcli.json`) | The old argparse machinery — `bt.Config`, `Subtensor.add_args`, `Wallet.add_args`, `--subtensor.network`, `--wallet.name` flags parsed inside the SDK — is gone entirely. Scripts own their own argument parsing; the SDK reads nothing from `sys.argv`. `bt.logging` is gone. The SDK logs under the `bittensor.*` namespace via the standard library and configures no handlers: ```python import logging logging.getLogger("bittensor").setLevel(logging.DEBUG) ``` ## Reading chain state [#reading-chain-state] General rules: every named read below is a typed method on its namespace (`client.staking.stake_for_coldkey(...)`) and equally dispatchable by name (`client.read("stake_for_coldkey", ...)`); both take the same `..._ss58` parameter names the old methods used. Results that used to be raw u16/u64 integers come back normalized (weights and bonds as 0..1 floats, balances as `Balance`). Anything without a named read is reachable through the generic accessors — `client.query(bt.storage.Pallet.Item, [params])`, `client.query_map(...)`, `client.constant(bt.constants...)`, `client.runtime(bt.runtime_api...)` — which cover the entire runtime surface, replacing `query_subtensor`, `query_module`, `query_map`, `query_map_subtensor`, `query_constant`, `query_runtime_api`, and `state_call`. ### Balances and stake [#balances-and-stake] | v10 `Subtensor` method | v11 | | ----------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | | `get_balance(address)` | `await client.balances.get(address)` | | `get_balances(*addresses)` | `await client.balances.get_many([...])` | | `get_existential_deposit()` | `await client.balances.existential_deposit()` | | `get_stake(coldkey_ss58, hotkey_ss58, netuid)` | `await client.staking.get(coldkey_ss58, hotkey_ss58, netuid)` | | `get_stake_info_for_coldkey(coldkey_ss58)` | `await client.staking.stake_for_coldkey(coldkey_ss58=...)` → `list[StakePosition]` | | `get_stake_info_for_coldkeys([...])` | `client.staking.stake_for_coldkeys(coldkey_ss58s=[...])` | | `get_stake_for_coldkey_and_hotkey(...)` | `client.staking.stake(...)` per netuid, or filter `stake_for_coldkey` | | `get_staking_hotkeys(coldkey_ss58)` | `client.staking.staking_hotkeys(coldkey_ss58=...)` | | `get_auto_stakes(coldkey_ss58)` | `client.staking.auto_stake_all(coldkey_ss58=...)` | | valuing stake in TAO (manual sum) | `client.staking.stake_value_for_coldkey(coldkey_ss58=...)` — never sum alpha yourself ([Money](/docs/concepts/money)) | | `sim_swap(...)`, `get_stake_add_fee(...)` | `client.prices.quote_stake(netuid=..., amount_tao=...)` | | `get_unstake_fee(...)`, `get_stake_movement_fee(...)` | `client.prices.quote_unstake(netuid=..., amount_alpha=...)` | | `get_transfer_fee(...)`, `get_extrinsic_fee(...)` | `(await client.plan(intent, wallet)).fee` | | `get_stake_lock` / `get_coldkey_lock` | `client.locks.coldkey_lock(coldkey_ss58=..., netuid=...)` | | `get_stake_locks(...)` | `client.locks.locks_for_coldkey(coldkey_ss58=...)` | | `get_hotkey_conviction(hotkey_ss58, netuid)` | `client.locks.hotkey_conviction(...)` | | `get_most_convicted_hotkey_on_subnet(netuid)` | `client.locks.most_convicted_hotkey(netuid=...)` | ### Subnets and hyperparameters [#subnets-and-hyperparameters] | v10 | v11 | | ---------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | | `all_subnets()` / `get_all_subnets_info()` | `await client.subnets.all()` → `list[SubnetInfo]` | | `subnet(netuid)` / `get_subnet_info(netuid)` | `await client.subnets.info(netuid)` | | `get_all_subnets_netuid()` / `get_total_subnets()` | derive from `client.subnets.all()` | | `subnet_exists(netuid)` | `client.subnets.subnet(netuid=...)` and check | | `get_subnet_burn_cost()` | `client.subnets.subnet_registration_cost()` | | `recycle(netuid)` (registration price) | `await client.subnets.burn(netuid)` | | `get_subnet_price(netuid)` / `get_subnet_prices()` | `client.prices.alpha_price(netuid=...)` / `client.prices.alpha_prices()` | | `get_subnet_hyperparameters(netuid)` | `client.subnets.subnet_hyperparameters(netuid=...)` | | `difficulty` / `immunity_period` / `min_allowed_weights` / `max_weight_limit` / `weights_rate_limit` | same names on `client.hyperparameters`: `client.hyperparameters.immunity_period(netuid=...)` etc. | | `commit_reveal_enabled(netuid)` | `client.subnets.commit_reveal_enabled(netuid)` | | `get_subnet_reveal_period_epochs(netuid)` | `client.hyperparameters.reveal_period(netuid=...)` | | `tempo(netuid)` / `subnetwork_n(netuid)` | fields on `client.subnets.info(netuid)` / the metagraph | | `blocks_since_last_step` / `blocks_until_next_epoch` / `get_next_epoch_start_block` | same names on `client.epochs` (`client.epochs.blocks_until_next_epoch(netuid=...)`) | | `blocks_since_last_update(netuid, uid)` | `client.epochs.blocks_since_last_update(netuid=..., uid=...)` | | `is_subnet_active(netuid)` | `client.epochs.epoch_status(netuid=...)` / `client.subnets.subnet_start_schedule(netuid=...)` | | `get_mechanism_count` / `get_mechanism_emission_split` | `client.subnets.mechanism_count(...)` / `client.subnets.mechanism_emission_split(...)` | | `weights(netuid, mechid)` | `client.weights.weights(netuid=..., mechid=0)` — now `dict[uid, dict[uid, float]]` normalized 0..1, **not** u16 tuples | | `bonds(netuid, mechid)` | `client.weights.bonds(netuid=..., mechid=0)` — same shape change | | `get_timelocked_weight_commits(...)` | `client.weights.timelocked_weight_commits(netuid=..., mechid=0)` | ### Neurons, registration, delegates [#neurons-registration-delegates] | v10 | v11 | | ----------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | | `neurons(netuid)` / `neurons_lite(netuid)` | `await client.neurons.all(netuid, lite=...)` | | `neuron_for_uid(uid, netuid)` | `(await client.subnets.metagraph(netuid)).neuron(uid)` | | `get_uid_for_hotkey_on_subnet(hotkey_ss58, netuid)` | `client.neurons.uid(hotkey_ss58=..., netuid=...)` | | `is_hotkey_registered` / `..._on_subnet` / `..._any` | `client.neurons.uid(...)` / `client.neurons.netuids_for_hotkey(...)` | | `get_netuids_for_hotkey(hotkey_ss58)` | `client.neurons.netuids_for_hotkey(hotkey_ss58=...)` | | `get_hotkey_owner(hotkey_ss58)` / `does_hotkey_exist` | `client.neurons.hotkey_owner(hotkey_ss58=...)` | | `get_owned_hotkeys(coldkey_ss58)` | `client.neurons.owned_hotkeys(coldkey_ss58=...)` | | `get_delegates()` / `get_delegate_by_hotkey` / `is_hotkey_delegate` / `get_delegate_take` / `get_delegated` | same names on `client.delegation`: `delegates`, `delegate`, `is_delegate`, `delegate_take`, `delegated` | | `get_delegate_identities()` | `client.identity.hotkey_identities(hotkey_ss58s=[...])` | | `get_children` / `get_children_pending` / `get_parents` | `client.delegation.children` / `pending_children` / `parents` | | `query_identity(coldkey_ss58)` | `client.identity.identity(coldkey_ss58=...)` | | `get_commitment` / `get_commitment_metadata` | `client.identity.commitment(netuid=..., hotkey_ss58=...)` | | `get_all_commitments(netuid)` | `await client.subnets.commitments(netuid)` | | `get_revealed_commitment*` | `client.identity.revealed_commitment(netuid=..., hotkey_ss58=...)` | | `get_neuron_certificate(...)` | no named read — generic storage query | ### Chain, blocks, and the rest [#chain-blocks-and-the-rest] | v10 | v11 | | -------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | | `subtensor.block` / `get_current_block()` | `sub.block` (blocking) / `await client.block()` | | `get_block_info(block)` / `get_block_hash` | `await client.block_info(block)` | | `get_timestamp(block)` | `await client.timestamp(block)` | | `wait_for_block(block)` | `await client.wait_for_block(block)` (plus `wait_for_timestamp`, `wait_for_epoch`) | | `tx_rate_limit()` / `is_fast_blocks()` | `client.chain.tx_rate_limit()` / `client.is_fast_blocks()` | | `last_drand_round()` | `bt.timelock.current_round()` | | `get_proxies_for_real_account(ss58)` | `client.balances.proxies(coldkey_ss58=...)` | | `get_coldkey_swap_announcement(ss58)` | `client.balances.coldkey_swap_announcement(coldkey_ss58=...)` | | `get_crowdloans()` / `get_crowdloan_by_id` / `get_crowdloan_contributions` | `client.leasing.crowdloans()` / `crowdloan(...)` / `crowdloan_contributors(...)` | | `get_mev_shield_next_key()` | `client.chain.mev_shield_next_key()` | | `get_root_claim_type(ss58)` | `client.staking.root_claim_type(coldkey_ss58=...)` | | other `get_root_claim*` reads | no named read — generic storage query | | `get_liquidity_list(...)` | removed (feature retired on chain) | ## The metagraph [#the-metagraph] `subtensor.metagraph(netuid)` returned an object of parallel NumPy arrays (`S`, `W`, `B`, `I`, `uids`, `hotkeys`, `axons`, ...) with `sync()`, `save()`, `load()`. The v11 metagraph is a plain typed object with per-neuron records and no matrices: ```python mg = await client.subnets.metagraph(netuid=1) for n in mg: # MetagraphNeuron, ordered by uid n.uid, n.hotkey, n.coldkey n.incentive, n.dividends, n.rank, n.trust, n.consensus # 0..1 floats n.emission, n.alpha_stake, n.tao_stake, n.total_stake # Balance n.axon # "ip:port" or None mg.hotkeys, mg.coldkeys, mg.validators mg.neuron(5), mg.by_hotkey("5F...") ``` | Old metagraph usage | v11 | | ----------------------------------- | ------------------------------------------------------ | | `mg.S` (stake array) | `[n.total_stake for n in mg]` | | `mg.I`, `mg.D`, `mg.C`, ... arrays | per-neuron fields (`n.incentive`, `n.dividends`, ...) | | `mg.W` (weight matrix) | `await client.weights.weights(netuid=...)` | | `mg.B` (bond matrix) | `await client.weights.bonds(netuid=...)` | | `mg.axons` (`AxonInfo` list) | `n.axon` (`"ip:port"` string or `None`) | | `mg.sync(block=...)` | refetch: `client.subnets.metagraph(netuid, block=...)` | | `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(bt.runtime_api.SubnetInfoRuntimeApi.get_metagraph, [netuid]) ``` ## Transactions [#transactions] Every old transaction method maps to an intent class executed with `client.execute(intent, wallet)`. Trailing kwargs move as follows: `wait_for_inclusion` / `wait_for_finalization` / `period` are kwargs of `execute`; `raise_error=True` becomes `result.raise_for_failure()`; `mev_protection` becomes `mev_shield` (on by default for stake-trading intents). The `safe_staking` / `rate_tolerance` / `allow_partial_stake` flags become the explicit `*Limit` intent variants with a `limit_price_rao` (see [slippage](/docs/concepts/money#slippage) for converting a tolerance to a limit price). | v10 method | v11 intent | | --------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | | `transfer(wallet, destination_ss58, amount)` | `bt.Transfer(dest_ss58=..., amount_tao=...)` | | `transfer(..., transfer_all=True)` | `bt.TransferAll(dest_ss58=...)` | | `add_stake(wallet, netuid, hotkey_ss58, amount)` | `bt.AddStake(hotkey_ss58=..., netuid=..., amount_tao=...)` | | `add_stake(..., safe_staking=True, rate_tolerance=...)` | `bt.AddStakeLimit(..., limit_price_rao=..., allow_partial=...)` | | `unstake(wallet, netuid, hotkey_ss58, amount)` | `bt.RemoveStake(hotkey_ss58=..., netuid=..., amount_alpha=...)` (accepts `"all"`) | | `unstake(..., safe_unstaking=True)` | `bt.RemoveStakeLimit(...)` | | `unstake_all(wallet, netuid, hotkey_ss58)` | `bt.UnstakeAll(hotkey_ss58=...)` / `bt.UnstakeAllAlpha(...)` | | `add_stake_multiple` / `unstake_multiple` | `bt.Batch(intents=[...])` | | `move_stake(...)` | `bt.MoveStake(origin_hotkey_ss58, origin_netuid, dest_hotkey_ss58, dest_netuid, amount_alpha)` | | `swap_stake(...)` | `bt.SwapStake(hotkey_ss58, origin_netuid, dest_netuid, amount_alpha)` | | `transfer_stake(...)` | `bt.TransferStake(dest_coldkey_ss58, hotkey_ss58, origin_netuid, dest_netuid, amount_alpha)` | | `set_auto_stake(...)` | `bt.SetAutoStake(netuid=..., hotkey_ss58=...)` | | `lock_stake` / `move_lock` / `set_perpetual_lock` | `bt.LockStake` / `bt.MoveLock` / `bt.SetPerpetualLock` | | `burned_register(wallet, netuid)` / `register(...)` | `bt.BurnedRegister(netuid=..., hotkey_ss58=None)` | | `register_subnet(wallet)` | `bt.RegisterSubnet()` | | `root_register(wallet)` | `bt.RootRegister()` | | `start_call(wallet, netuid)` | `bt.StartCall(netuid=...)` | | `serve_axon(netuid, axon: Axon)` | `bt.ServeAxon(netuid=..., ip=..., port=...)` — no `Axon` object; TLS via `bt.ServeAxonTls` | | `set_weights(wallet, netuid, uids, weights, mechid)` | `bt.SetWeights(netuid=..., weights={uid: w} or uids=[...], weights=[...], mechid=0)` | | `commit_weights` / `reveal_weights` | `bt.CommitWeights` / `bt.RevealWeights` (reveal only for legacy salt commits) | | `set_delegate_take(wallet, hotkey_ss58, take)` | `bt.SetTake(take=...)` — **unit change, see below** | | `set_children(wallet, netuid, hotkey_ss58, children)` | `bt.SetChildren(netuid=..., children=[...])` — **unit change, see below** | | `set_subnet_identity(...)` | `bt.SetSubnetIdentity(netuid=..., subnet_name=..., ...)` (flattened fields) | | identity via extrinsics | `bt.SetIdentity(name=..., url=..., ...)` | | `claim_root(wallet, netuids)` | `bt.ClaimRoot(subnets=[...])` | | `set_root_claim_type(wallet, type)` | `bt.SetRootClaimType(claim_type="Swap"/"Keep"/"KeepSubnets")` | | `add_proxy` / `remove_proxy` / `remove_proxies` / `create_pure_proxy` / `kill_pure_proxy` | same-name intents (`bt.AddProxy(delegate_ss58=..., proxy_type=..., delay=...)`, ...) | | `proxy(wallet, real_ss58, type, call)` | `client.execute(intent, wallet, proxy_for=real_ss58)` | | `announce_coldkey_swap` / `swap_coldkey_announced` / `dispute_coldkey_swap` / `clear_coldkey_swap_announcement` | same-name intents | | crowdloan methods | same-name intents (`bt.CreateCrowdloan`, `bt.ContributeCrowdloan`, ...) | | `set_commitment(wallet, netuid, data)` | no intent — raw call: `client.submit_call(bt.calls.Commitments.set_commitment(...), wallet)` | | hyperparameters (was CLI-only `sudo set`) | `bt.SetHyperparameter(netuid=..., name=..., value=...)` (includes `collateral_lock_share` / `collateral_drain_ratio`) | | *(new)* miner collateral top-up / floor | `bt.AddCollateral(netuid=..., amount_alpha=...)` / `bt.SetMinCollateral(netuid=..., min_alpha=...)`; reads via `client.collateral.*` | | `add_liquidity` / `modify_liquidity` / `remove_liquidity` | removed (feature retired on chain) | | `mev_submit_encrypted(wallet, call)` | `client.submit_shielded(intent, wallet)`, or the default shielding on stake intents | | `compose_call(...)` + `sign_and_send_extrinsic(...)` | `client.submit_call(bt.calls.Pallet.function(...), wallet, signer="coldkey"/"hotkey")` | | `bittensor.core.extrinsics.*` functions | gone — intents or `submit_call` | Dynamic dispatch (the old pattern of calling methods by name) becomes `bt.intents.build(op, args_dict)` or `client.execute_tool(op, args_dict, wallet)` — see [For agents](/docs/agents). New in v11, worth adopting during migration: `client.plan(intent, wallet)` previews fee, effects, and warnings without submitting, and a `bt.Policy(max_spend_tao=..., allowed_netuids=[...])` on the client hard-bounds what any execution may do. ## Weights, children, and take [#weights-children-and-take] Three call families changed **units**, not just names: * **Weights** are easier: pass floats (any scale — only proportions matter) or a `{uid: weight}` dict to `bt.SetWeights`; clipping, normalization, and u16 quantization happen internally (`bittensor.intents.normalize` is the canonical implementation). `bittensor.utils.weight_utils` (`process_weights_for_netuid`, `convert_weights_and_uids_for_emit`, ...) is gone and unnecessary. `set_weights` also auto-selects plain vs timelocked commit-reveal submission; drop any manual commit/reveal scheduling unless you used salt-based commits. `version_key` now defaults to 0 instead of the SDK version number — pass the subnet's required key explicitly if it enforces one. * **Take** still accepts the float 0–1 form (`take=0.18`); a plain integer is the raw u16 proportion (`take=11796`), following the hyperparameter value rules (a decimal point marks the human form). `set_delegate_take`'s auto-direction behavior is preserved by `bt.SetTake`; `bt.IncreaseTake` / `bt.DecreaseTake` pin the direction. * **Children proportions** likewise accept floats 0–1 (`children=[[0.5, "5F...child"]]`); a plain integer is the raw u64 share of `u64::MAX`. ## Money and Balance [#money-and-balance] `Balance` survives but is stricter — the biggest silent-behavior change in v11 ([Money](/docs/concepts/money)): * Every `Balance` carries a `netuid` (0 = TAO). Arithmetic or comparison across units raises `bt.UnitMismatchError` (was `BalanceUnitMismatchError`); comparing with a bare `float` raises `TypeError`. * `.tao` on an alpha balance **raises** instead of returning a number. Use `.amount` for the value in its own currency, or the `stake-value-for-coldkey` read to value alpha in TAO. * `Balance.from_tao(amount)` no longer takes a netuid; `Balance.from_alpha(amount, netuid=...)` is the alpha constructor. Shorthands: `bt.tao(1.5)`, `bt.alpha(2.5, 42)`, `bt.rao(1_500_000_000)`. * Intent amount fields are unit-named (`amount_tao`, `amount_alpha`, `limit_price_rao`) and accept `int | float | str | Decimal | Balance` — the strict "must be a Balance object" rule from v10 is relaxed because the field name now carries the unit. Fields that support it accept `"all"`. ## Wallets [#wallets] `bittensor_wallet` is replaced by `bittensor.wallet` with the **same on-disk format** — same paths (`~/.bittensor/wallets//`), same keyfile encryption (NaCl / legacy), same SS58 format. Existing wallets just work. ```python from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") # same constructor wallet.coldkey, wallet.hotkey, wallet.coldkeypub # same properties wallet.create_new_coldkey(...), wallet.regenerate_coldkey(...) ``` Differences: `bt.Wallet(config=...)` and `Wallet.add_args` are gone (pass name/hotkey/path directly); `bt.Keypair` / `bt.Keyfile` are no longer top-level exports (`bittensor.keyfiles.Keyfile` exists; keypairs come from the wallet). Convenience helpers live in `bt.wallets` (`create`, `regen_coldkey`, `list_wallets`, `sign_message`, `verify_message`, ...). Non-interactive unlock: `BT_WALLET_PASSWORD` or `BT_WALLET_PASSWORD_FILE` ([wallets](/docs/concepts/wallets#non-interactive-unlock)). ## Axon, Dendrite, and Synapse are gone [#axon-dendrite-and-synapse-are-gone] v11 contains **no miner/validator networking stack**: `bt.Axon`, `bt.Dendrite`, `bt.Synapse`, `bt.StreamingSynapse`, `bt.SubnetsAPI`, and `bt.Tensor` do not exist. What the old stack actually provided splits into three layers, and each has a v11 answer: | Old stack | v11 | | ------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------- | | hotkey signing + verification (`dendrite.preprocess_synapse_for_request`, `axon.default_verify`) | `bt.http_auth.sign` / `bt.http_auth.verify` — [Signed requests](/docs/guides/signed-requests) | | `Axon` (FastAPI server, lifecycle, middleware) | your own server; FastAPI recipe in the guide | | `Dendrite` (aiohttp client, fan-out) | your own client; httpx recipe in the guide | | `Synapse` subclasses (shared request schema) | your own request/response models — signing covers raw bytes, so any schema works | | `blacklist_fn` | metagraph caller policy (registered / validator permit / min stake) — snippet in the guide | | `priority_fn`, threadpool | your framework's rate limiting, keyed by the verified caller hotkey | | `StreamingSynapse` | SSE / websockets in your framework | | `Tensor` | serialize with your ML library's own tools | The chain-side operations remain — publish your endpoint with `bt.ServeAxon` (see [Mining](/docs/guides/mining)), set weights with `bt.SetWeights` (see [Validating](/docs/guides/validating)). The identity layer — hotkey-signed HTTP requests, replacing the old `bt_header_*` scheme with a cleaner cross-language wire format (btauth/1) — ships in the SDK as pure `sign`/`verify` functions with no server or client attached. A miner that must keep accepting old-SDK validators during a transition can also verify the legacy v10 header format (recipe in the guide). ## Results and errors [#results-and-errors] | v10 | v11 | | --------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | | `ExtrinsicResponse.success` / `.message` | `ExtrinsicResult.success` / `.message` | | `.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` (`bt.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` / `bt.UnitMismatchError` | Prefer `.code` over matching `.name` or message strings. SubtensorModule renamed `InsufficientBalance` → [`InsufficientTaoBalance`](/code/pallets/subtensor/src/macros/errors.rs#L218) (same error index) and added [`InsufficientAlphaBalance`](/code/pallets/subtensor/src/macros/errors.rs#L334); 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: match result.error.code: case bt.ErrorCode.RATE_LIMITED: ... # wait and retry case bt.ErrorCode.INSUFFICIENT_BALANCE: ... case _: log(result.error.remediation) ``` ## Chain and runtime changes [#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 [#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`](/code/pallets/subtensor/src/coinbase/block_step.rs#L69-L79) 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 [#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 [#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`](/code/pallets/subtensor/src/macros/errors.rs#L192) 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. * **Miner collateral.** `add_collateral` takes **alpha** (`amount_alpha` / `AlphaBalance`), not TAO — free stake covers the lock first; only a shortfall is bought with TAO under `limit_price`. Bonded `swap_hotkey` / `swap_hotkey_v2` with `keep_stake=true` fails with [`KeepStakeBlockedByCollateral`](/docs/errors/chain/KeepStakeBlockedByCollateral); use `keep_stake=false` so the bond migrates (lineage maps track the rename). Coldkey swap migrates `MinerCollateral` with the stake. * **Key lineage (runtime 437).** Hotkey swaps write per-subnet `HotkeySuccessor` / `HotkeyRoot`; coldkey swaps write global `ColdkeySuccessor` / `ColdkeyRoot`. Prefer `hotkey_root` / `coldkey_root` (and `same_*_lineage`) for bans and owner attribution; `*_lineage_tip` is best-effort. See [The V437 Upgrade](/releases/v436-upgrade). ### Registration, hyperparameters, and randomness [#registration-hyperparameters-and-randomness] * **Queued subnet registration.** When capacity is full or cleanup is pending, `register_network` may emit [`NetworkRegistrationQueued`](/code/pallets/subtensor/src/macros/events.rs#L681) without creating the subnet. `client.execute(RegisterSubnet(...))` and `btcli subnets create` wait for the matching [`NetworkAdded`](/code/pallets/subtensor/src/macros/events.rs#L13) by default and return the assigned `netuid`; raw-call clients must do the same 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 `bt.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 [#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) [#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 [#cli-migration] One binary, still called `btcli`, now installed with `bittensor`. The structure: every read is `btcli query `, every transaction is `btcli tx ` (intent op names with dashes), and familiar groups (`wallet`, `stake`, `subnets`, `sudo`, `weights`, `axon`, `proxy`, ...) wrap the same machinery. The v9 group shorthands (`w`, `st`, `s`, `su`, `c`, `wt`, `cr`, `d`) and snake\_case command spellings (`wallet new_hotkey`) still work as hidden aliases. Full details: [The CLI](/docs/cli). ### Global flags [#global-flags] | v9 | v11 | | ------------------------------------------------------------------------------ | ---------------------------------------- | | `--wallet.name` / `--wallet-name` / `--name` | `--wallet` / `-w` | | `--wallet.hotkey` / `--hotkey` / `-H` | `--wallet-hotkey` / `-H` | | `--wallet.path` / `-p` | `--wallet-path` | | `--network` / `--subtensor.network` / `--chain` / `--subtensor.chain_endpoint` | `--network` / `-n` (name or `ws://` URL) | | `--no-prompt` / `-y` / `--yes` | `--yes` / `-y` | | `--json-output` / `--json-out` | `--json` | | `--quiet` | `--quiet` / `-q` | | `--verbose` | `-v` / `-vv` / `-vvv` | | `--mev-protection` / `--no-mev-protection` | `--mev-shield` / `--no-mev-shield` | | `--proxy NAME` | `--proxy-for NAME` | | — | `--dry-run` (preview any `tx` command) | Config file moves from `~/.bittensor/config.yml` to `~/.bittensor/btcli.json` and keys are renamed: `btcli config set network finney`, `btcli config set wallet my_coldkey` (was `wallet_name`). The old `use_cache`, `rate_tolerance`, `safe_staking`, `allow_partial_stake`, and `dashboard_path` keys have no equivalent. Non-interactive behavior changed for the better: without a TTY and without `--yes`, a mutation is **declined with exit code 1** instead of hanging; missing required options exit 2. ### Command mapping [#command-mapping] | v9 command | v11 command | | ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | | `wallet list` / `create` / `new-coldkey` / `new-hotkey` / `regen-*` | same names under `wallet` | | `wallet balance [--all]` | `wallet balance [--all]` or `btcli query balance --coldkey NAME --json` | | `wallet transfer --dest --amount` | `wallet transfer --dest --amount` or `btcli tx transfer --dest --amount-tao` | | `wallet overview` / `inspect` / `sign` / `verify` / `encrypt` / `decrypt` | same names | | `wallet swap-hotkey DEST` | `wallet swap-hotkey --new-hotkey ...` / `tx swap-hotkey` | | `wallet swap-coldkey announce/execute/...` | `wallet announce-coldkey-swap`, `wallet swap-coldkey`, `wallet swap-check`, `tx dispute-coldkey-swap`, `tx clear-coldkey-swap-announcement` | | `wallet set-identity` / `get-identity` / `associate-hotkey` | same names | | `wallet history` | `wallet history` (works again, mainnet indexer) | | `stake add --amount` | `stake add` / `tx add-stake --amount-tao` | | `stake remove --amount` | `stake remove` / `tx remove-stake --amount-alpha` (accepts `all`) | | `stake remove --all` | `tx unstake-all` / `tx unstake-all-alpha` | | `stake list` / `move` / `transfer` / `swap` | same names (single hotkey per call; `--include-hotkeys`/`--exclude-hotkeys`/`--all-hotkeys` batching is gone — loop in your script) | | `stake auto` / `set-auto` / `set-claim` / `process-claim` | same names | | `stake child get/set/revoke/take` | same names (`child set` takes `--children` as JSON pairs) | | `sudo set --param NAME --value V` | `sudo set --name NAME --value V` / `tx set-hyperparameter` | | `sudo get` / `subnets hyperparameters` | `sudo get` / `subnets hyperparameters NETUID` / `query subnet-hyperparameters` | | `sudo set-take` / `get-take` | same names (`--take` still a float on the CLI) | | `sudo trim` / `stake-burn` | same names | | `sudo senate` / `proposals` / `senate-vote` | **removed** — the senate is gone from the runtime ([Governance](/docs/guides/governance)) | | `subnets list` / `show` / `create` / `register` / `burn-cost` / `price` | same names (`show`/`burn-cost` take netuid positionally) | | `subnets metagraph` | `subnets metagraph NETUID` / `query metagraph --netuid N --json` | | `subnets register --netuid 0` | `tx root-register` | | `subnets start` / `check-start` | `sudo start` / `sudo check-start` | | `subnets set-identity` / `get-identity` / `set-symbol` | `sudo set-identity` / `sudo get-identity` / `sudo set-symbol` | | `subnets mechanisms ...` | `sudo mechanisms ...` | | `weights commit` / `reveal` | `weights commit` / `reveal` — and **`weights set` now exists** (auto commit-reveal); prefer it | | `axon set` / `reset` | same names / `tx serve-axon`, `tx reset-axon` | | `proxy create/add/remove/kill/execute` | same names | | `config add-proxy` / `proxies` / `remove-proxy` | `proxy book add` / `list` / `remove` | | `crowd create/contribute/withdraw/finalize/update/refund/dissolve` | `tx create-crowdloan`, `tx contribute-crowdloan`, ... (`crowd` keeps the reads: `list`, `info`, `contributors`) | | `liquidity add/list/modify/remove` | **removed** (chain feature retired) | | `view dashboard` | **removed** | | `utils convert` / `latency` | 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 | Two new commands help automated migration directly: `btcli tools` dumps the full transaction catalog with JSON schemas, and `btcli explain ` explains any error code. ## Migration checklist [#migration-checklist] 1. Replace `bittensor` + `bittensor-cli` + `bittensor_wallet` with a single pinned `bittensor>=11` (the CLI is included). In live environments, uninstall the old packages *before* upgrading ([why](#upgrading-an-existing-environment)). 2. Keep `bt.Subtensor(...)` — the name survives, blocking as before (async when awaited; `bt.AsyncSubtensor` is gone) — but every method on it changed; move `block=`-pinned reads onto `sub.at(block)`. 3. Rewrite reads using the tables above; anything unmapped goes through `client.query(bt.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 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. 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 `logging`), and `weight_utils` preprocessing. 8. Update env vars (`BT_SUBTENSOR_NETWORK` → `BT_NETWORK`, `BT_WALLET_NAME` → `BT_WALLET`) and shell scripts per the CLI tables (`--wallet.name` → `-w`, `--json-output` → `--json`, `--no-prompt` → `--yes`). 9. If the script served an axon or queried miners with a dendrite, rebuild that layer on your own HTTP stack with `bt.http_auth` for the hotkey signing ([Signed requests](/docs/guides/signed-requests)); only its chain calls move to this SDK's client. 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; let the SDK wait for `NetworkAdded` on queued registrations (or do so in raw-call clients); 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. # Quickstart (/docs/quickstart) ## 1. Install [#1-install] Requires Python 3.10–3.13: ```bash uv venv && source .venv/bin/activate uv pip install bittensor ``` One install gives you both the `bittensor` Python library and the `btcli` command. In anything unattended, pin the exact version and upgrade only to announced releases — see [supply-chain risk](/docs/concepts/wallets#supply-chain-risk). ## 2. Configure once [#2-configure-once] ```bash btcli config set network finney # or `test` for testnet, `local` for a dev node btcli config set wallet my_coldkey btcli config get # show the whole config ``` Precedence, highest first: **CLI flag > environment variable > config file > built-in default**. So `btcli -n test query tx-rate-limit` overrides the configured network for that one call. The environment variables are `BT_NETWORK`, `BT_WALLET`, `BT_WALLET_HOTKEY`, `BT_WALLET_PATH`. ## 3. Create or import a wallet [#3-create-or-import-a-wallet] ```bash btcli wallet create -w my_coldkey # new coldkey + hotkey btcli wallet regen-coldkey -w my_coldkey # import from mnemonic (prompted securely) btcli wallet list ``` A wallet has two keys: the **coldkey** holds funds and signs financial operations; the **hotkey** identifies you on subnets and signs operational calls (weights, serving). See [Wallets and keys](/docs/concepts/wallets). ## 4. Read chain state [#4-read-chain-state] Reads are free and unsigned. On the CLI (add `--json` for machine output): ```bash btcli wallet balance my_coldkey btcli subnets list btcli query metagraph --netuid 1 btcli query --help # all 70+ reads, grouped by topic ``` In Python: ```python import bittensor as bt sub = bt.Subtensor() # defaults to finney; connects lazily bal = sub.balances.get("5F...coldkey") subnets = sub.subnets.all() mg = sub.subnets.metagraph(netuid=1) ``` No `close()` needed — the connection is cleaned up automatically. The same class is the async client when awaited: ```python import asyncio import bittensor as bt async def main(): async with bt.Subtensor() as client: bal = await client.balances.get("5F...coldkey") asyncio.run(main()) ``` ## 5. Submit a transaction [#5-submit-a-transaction] Every mutation supports `--dry-run`: it shows the fee, the predicted effects, and any policy verdict without submitting anything. ```bash btcli tx transfer --dest 5F...dest --amount-tao 1.5 --dry-run btcli tx transfer --dest 5F...dest --amount-tao 1.5 -w my_coldkey ``` In Python the same two-step shape is `plan` then `execute`: ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.Transfer(dest_ss58="5F...", amount_tao=1.5) sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, warnings — nothing submitted result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` ## Next [#next] * [The transaction model](/docs/concepts/transactions) — intents, plan/execute, Policy. * [Money](/docs/concepts/money) — TAO, alpha, and why the SDK refuses to mix them. * [For agents](/docs/agents) — discover and drive every operation programmatically. * [Staking](/docs/guides/staking), [Mining](/docs/guides/mining), [Validating](/docs/guides/validating), [EVM](/docs/guides/evm). # The SDK (/docs/sdk) Everything in the SDK hangs off five primitives. Know these and the rest of the docs is reference. | Primitive | What it is | Easy constructor | | -------------------------------------------------- | ------------------------------------------------ | ------------------------------------------------ | | [`Subtensor`](#subtensor--the-connection) | the chain connection, blocking or async | `bt.Subtensor()` | | [`Wallet`](#wallet--the-keys) | your keys: coldkey (money) + hotkey (operations) | `bt.Wallet("my_coldkey", "my_hotkey")` | | [`Balance`](#balance--the-money) | unit-tagged money: TAO or a subnet's alpha | `bt.tao(1.5)`, `bt.alpha(2.5, 42)` | | [intents](#intents--the-mutations) | one mutation, as a dataclass | `bt.Transfer(dest_ss58="5F...", amount_tao=1.5)` | | [`ExtrinsicResult`](#extrinsicresult--the-outcome) | the outcome, as data | returned by `sub.execute(intent, wallet)` | And one optional guardrail: [`Policy`](#policy--the-guardrail) bounds what any execution may do. ## Subtensor — the connection [#subtensor--the-connection] ```python import bittensor as bt sub = bt.Subtensor() # mainnet (finney); "test" | "local" | "ws://..." too ``` One class for both worlds: used directly it is a blocking client — connects lazily on the first call, cleaned up automatically, no `close()` — and awaited it is the async client: ```python sub.block # blocking client = await bt.Subtensor() # async, same surface awaited async with bt.Subtensor() as client: ... # scoped async ``` Every read in the catalog is a typed method on a category namespace, also dispatchable by name; anything else on chain is reachable through generic accessors: ```python sub.balances.get("5F...") # typed namespace sub.subnets.metagraph(netuid=1) # a whole subnet as one object sub.collateral.collateral_policy(netuid=1) # lock share + drain ratio sub.read("delegate_take", hotkey_ss58="5F...") # same catalog, by name sub.query(bt.storage.SubtensorModule.Tempo, [1]) # anything in the runtime ``` One page per read under [Queries](/docs/query); the full tour is [The client](/docs/concepts/client). ## Wallet — the keys [#wallet--the-keys] ```python wallet = bt.Wallet("my_coldkey", "my_hotkey") # ~/.bittensor/wallets// wallet = bt.Wallet("my_coldkey/my_hotkey") # same thing, one string wallet = bt.Wallet() # name and hotkey both "default" ``` The **coldkey** holds funds and signs financial operations; the **hotkey** identifies you on subnets and signs operational calls (weights, serving). The SDK picks the right signer per operation — you just pass the wallet. Anywhere a wallet is expected, the name string alone also works: `sub.execute(intent, "my_coldkey/my_hotkey")`. And anywhere an ss58 *address* is expected (`dest_ss58`, `hotkey_ss58`, `coldkey_ss58`, ...), a `Wallet` or keypair works too — the parameter takes the matching key's address: ```python sub.balances.get(wallet) # instead of wallet.coldkeypub.ss58_address bt.AddStake(hotkey_ss58=wallet, netuid=1, amount_tao=10) # the wallet's hotkey ``` Encrypted coldkeys prompt for the password; set `BT_WALLET_PASSWORD` for unattended use. Creation and recovery live in `bt.wallets` (`create`, `regen_coldkey`, ...) or `btcli wallet`. See [Wallets and keys](/docs/concepts/wallets). ## Balance — the money [#balance--the-money] ```python bt.tao(1.5) # 1.5 TAO bt.alpha(2.5, 42) # 2.5 alpha on subnet 42 bt.rao(1_500_000_000) # raw chain units (1 TAO = 1e9 rao) ``` Every `Balance` is tagged with its unit — TAO or one subnet's alpha — and refuses to mix them: adding alpha to TAO, or comparing across subnets, raises `bt.UnitMismatchError` instead of silently producing a wrong number. Read the amount back with `.tao` or `.alpha` (each raises on the other's unit). Intent amount fields are unit-named (`amount_tao`, `amount_alpha`) and also accept plain numbers and strings — `amount_tao=1.5` means 1.5 TAO. Why the strictness: [Money](/docs/concepts/money). ## Intents — the mutations [#intents--the-mutations] Every state-changing operation is a small dataclass, exported at the top level — `bt.Transfer`, `bt.AddStake`, `bt.SetWeights`, `bt.BurnedRegister`, ... ([one page each](/docs/tx)). Preview with `plan`, submit with `execute`: ```python intent = bt.AddStake(hotkey_ss58="5F...validator", netuid=1, amount_tao=10) # Miner collateral (spec >= 435): bt.AddCollateral / bt.SetMinCollateral plan = sub.plan(intent, wallet) # fee, effects, warnings — nothing submitted result = sub.execute(intent, wallet) # sign and submit ``` Intents are plain data — build them by name from a dict too (`sub.execute_tool("add_stake", {...}, wallet)`), which is how [agents](/docs/agents) and the CLI drive the same catalog. Semantics: [The transaction model](/docs/concepts/transactions). The most common operations also have one-call helpers that bundle construct-connect-execute, e.g.: ```python bt.set_weights(1, {0: 0.1, 1: 0.7, 2: 0.2}, wallet="my_coldkey", hotkey="my_hotkey") ``` ## ExtrinsicResult — the outcome [#extrinsicresult--the-outcome] `execute` returns data, not an exception: ```python result = sub.execute(intent, wallet) if not result.success: print(result.error.code) # bt.ErrorCode.RATE_LIMITED, INSUFFICIENT_BALANCE, ... print(result.error.remediation) # what to do about it result.raise_for_failure() # or opt into raising ``` `result.error.code` is a stable, machine-readable `bt.ErrorCode` — branch on it, never on message strings. Success carries `block_hash`, `extrinsic_id`, `events`, and `explorer_url`. The full taxonomy: [Errors](/docs/errors). ## Policy — the guardrail [#policy--the-guardrail] ```python policy = bt.Policy(max_spend_tao=5.0, allowed_netuids=[1, 2]) sub = bt.Subtensor(policy=policy) ``` A `Policy` on the connection hard-bounds every mutation that passes through it: `max_spend_tao`, `max_fee_tao`, `allowed_netuids` (raw calls are refused outright unless `allow_raw_calls=True`). Violations raise `PolicyError` at execute time and show up in `plan`. Designed for handing a key to an agent or a script you don't fully trust. ## All five together [#all-five-together] ```python import bittensor as bt sub = bt.Subtensor() # connection wallet = bt.Wallet("my_coldkey/my_hotkey") # keys balance = sub.balances.get(wallet) print(balance) # a Balance, e.g. τ12.5 intent = bt.Transfer(dest_ss58="5F...", amount_tao=1.5) # mutation result = sub.execute(intent, wallet) # outcome if not result.success: print(result.error.code, result.error.remediation) ``` # Advanced submission (/docs/concepts/advanced) These modes compose with any intent — they change *how* a transaction is signed and submitted, not *what* it does. ## Proxy: keep the coldkey offline [#proxy-keep-the-coldkey-offline] A registered proxy signs on behalf of the real account, so the coldkey holding the funds never has to be on the machine doing the work: ```python await client.execute(intent, delegate_wallet, proxy_for="5F...real_coldkey") ``` On the CLI, `--proxy-for ` works on every `tx` command (and `--force-proxy-type` requires a specific proxy type). Manage delegations with [`add-proxy`](/docs/tx/add-proxy) / [`remove-proxy`](/docs/tx/remove-proxy), create standalone proxy accounts with [`create-pure-proxy`](/docs/tx/create-pure-proxy), and inspect with the [`proxies`](/docs/query/proxies) read. ### Proxy types [#proxy-types] Each grant is scoped by `proxy_type` — the runtime filters every proxied call against the type's allowlist ([`proxy_type_filter`](/code/runtime/src/proxy_filters/mod.rs#L108-L129)). Prefer the narrowest type that covers the job: * **Any** — everything the account can do, including transfers. Avoid. * **NonTransfer** — everything *except* balance transfers, stake transfers, and coldkey swaps. Notably it can manage the account's other proxies. * **Transfer** — balance transfers and stake transfers (`transfer_stake` / `transfer_stake_and_hotkey`) only. **SmallTransfer** is the same but caps each transfer below 0.5 TAO (0.5 alpha for stake transfers). * **Staking** — stake operations only: add / remove / move / swap / unstake-all and their limit variants, plus [`set_root_claim_type`](/code/pallets/subtensor/src/macros/dispatches.rs#L1937-L1951). The allowlist does **not** include `Utility.batch_all`, so batching staking calls through a Staking proxy fails with `CallFiltered` — use a NonTransfer proxy for batched staking. * **Registration** — neuron registration calls ([`burned_register`](/code/pallets/subtensor/src/macros/dispatches.rs#L826-L832), [`register`](/code/pallets/subtensor/src/macros/dispatches.rs#L804-L814), [`register_limit`](/code/pallets/subtensor/src/macros/dispatches.rs#L2242-L2249)). * **ChildKeys** — [`set_children`](/code/pallets/subtensor/src/macros/dispatches.rs#L1093-L1101) and [`set_childkey_take`](/code/pallets/subtensor/src/macros/dispatches.rs#L935-L945). * **RootClaim** — [`claim_root`](/code/pallets/subtensor/src/macros/dispatches.rs#L1910-L1926) only. * **Owner** — subnet-owner admin calls; **NonCritical** — everything except registrations, coldkey swaps, and subnet dissolution; **SubnetLeaseBeneficiary** — the lease-scoped subset of owner calls. ### Deposits and limits [#deposits-and-limits] Proxy bookkeeping is paid with **reserved** deposits, not fees: adding a proxy reserves a base of 0.06 TAO plus 0.033 TAO per delegation from the granting account, returned on removal. An account can have at most 20 proxies. Announcements (below) reserve 0.036 TAO base plus 0.068 TAO each, at most 75 pending. ### Delayed proxies: announce, monitor, execute [#delayed-proxies-announce-monitor-execute] A delegation granted with `delay` > 0 cannot act directly. The delegate first announces the *hash* of the intended call (blake2-256 of the SCALE-encoded call), waits out the delay, then executes with [`execute-proxy-announced`](/docs/tx/execute-proxy-announced) — which must rebuild the call with byte-identical parameters, since anything else hashes differently and matches no announcement. During the delay the real account can veto the pending call. Two operational notes. A delay only protects you if you check pending announcements (the `Proxy.Announcements` storage map, via `client.query`) on a schedule *shorter* than the delay — otherwise it is only forensic. And a zero-delay proxy executes with no veto window at all: treat a leaked zero-delay key as immediately spent, and rotate it. The SDK wraps the execute side as an intent; announcing and vetoing have no intent and are reachable as raw calls: ```python # delegate announces a future call await client.submit_call( bt.calls.Proxy.announce(real="5F...real", call_hash="0x..."), delegate_wallet ) # real account vetoes it during the delay await client.submit_call( bt.calls.Proxy.reject_announcement(delegate="5F...delegate", call_hash="0x..."), real_wallet, ) ``` ## Batch: all-or-nothing [#batch-all-or-nothing] Several intents in one atomic extrinsic — either every one lands or none do: ```python await client.execute(bt.Batch(intents=[ {"op": "transfer", "dest_ss58": "5F...", "amount_tao": 1.0}, {"op": "add_stake", "hotkey_ss58": "5F...", "netuid": 1, "amount_tao": 2.0}, ]), wallet) ``` `Batch` wraps `Utility.batch_all`, the atomic variant. The chain also has best-effort `Utility.batch` (stops at the first failure, earlier calls stand) and `Utility.force_batch` (skips failures and continues) — no intent wraps them, but both are reachable as raw calls (below). A batch pays a transaction fee if any inner call is fee-bearing. ## MEV-shielded submission [#mev-shielded-submission] Encrypts the call so the mempool cannot read (and front-run) it; the chain decrypts and applies it validator-side. A mainnet feature: ```python await client.submit_shielded(bt.Transfer(dest_ss58="5F...", amount_tao=1.0), wallet) ``` Under the hood, the intent's call is signed as a complete inner extrinsic, encrypted to the chain's rotating ML-KEM-768 key ([`mev-shield-next-key`](/docs/query/mev-shield-next-key)) with XChaCha20-Poly1305 for the payload, and carried inside `MevShield.submit_encrypted`. The same account signs both, so the outer wrapper takes the account's next nonce and the inner call the one after — the SDK pins both automatically. Both are signed with a short 8-block era; the chain rejects shielded wrappers signed with anything longer, so a stuck submission evicts from the pool within a handful of blocks. The block author decrypts the wrapper and includes the inner extrinsic in the block it builds, where it executes — and reports success or failure — like any normally submitted extrinsic. The wrapper's acceptance is marked by the pallet's [`EncryptedSubmitted`](/code/pallets/shield/src/lib.rs#L206) event, and a successful submission carries `inner_extrinsic_hash` in the result data so the inner extrinsic can be located on chain. Shielding is for coldkey-signed operations. The chain accepts a shielded wrapper from any signed origin, but the SDK and CLI refuse to shield hotkey-signed extrinsics as policy: the wrapper pays a fee, and funding a hotkey with TAO to cover it defeats the key-separation model. Hotkey calls (weights, serving) are fee-free and gain nothing from shielding anyway. ## Multisig [#multisig] `client.multisig(signatories, threshold)` manages k-of-n accounts; the [`multisig-*`](/docs/tx/multisig-execute) intents approve, execute, and cancel multisig calls, and the [`multisig`](/docs/query/multisig) read inspects pending ones. The CLI's `btcli multisig` group wraps the full flow, printing the co-signer commands after each approval. The first approver of a pending call reserves a deposit — 0.132 TAO base plus 0.032 TAO per unit of threshold — returned when the multisig executes or is cancelled. A multisig can have at most 100 signatories. ## Limit orders [#limit-orders] The chain has a `LimitOrders` pallet: resting orders (limit buy, take-profit, stop-loss) signed off-chain with a limit price and expiry, settled against the subnet's pool by an executor submitting them in batches — each order can restrict which relayer accounts may execute it, and a root-set global toggle enables the pallet. [`execute_batched_orders`](/code/pallets/limit-orders/src/lib.rs#L469-L481) nets a batch's buys against its sells at the current price, so only the residual amount touches the pool in a single swap — offset-side orders fill internally with no price impact. No intent wraps the pallet; the calls are reachable as raw calls under `bt.calls.LimitOrders.*` ([`execute_orders`](/code/pallets/limit-orders/src/lib.rs#L419-L443), `execute_batched_orders`, [`cancel_order`](/code/pallets/limit-orders/src/lib.rs#L490-L511), [`set_pallet_status`](/code/pallets/limit-orders/src/lib.rs#L520-L538)). ## Raw calls: the escape hatch [#raw-calls-the-escape-hatch] Every chain call in the runtime metadata is available under `bt.calls` — even those no intent wraps (deprecated, root/admin-only, or off-chain-signed calls, each recorded with a reason): ```python call = bt.calls.Commitments.set_commitment(netuid=1, info={...}) await client.submit_call(call, wallet, signer="hotkey") ``` Raw calls bypass intent preview, so an active `Policy` refuses them unless it sets `allow_raw_calls=True`. # The client (/docs/concepts/client) `bt.Subtensor` is the entry point to everything: reads, transactions, and chain metadata. One class serves both worlds — used directly it is a blocking client; awaited, it is the async client. The mode is declared by syntax, never guessed from the environment. ```python import bittensor as bt sub = bt.Subtensor() # defaults to finney; connects lazily print(sub.block) # blocking; no close() needed client = await bt.Subtensor() # the same class, awaited: the async client print(await client.block()) with bt.Subtensor("test") as client: ... # scoped, blocking async with bt.Subtensor("test") as client: ... # scoped, async ``` Networks: `finney` (mainnet, the default), `test` (the public testnet), `local` (a dev node at `ws://127.0.0.1:9944`), and any `ws://` / `wss://` endpoint directly. Blocking mode never needs `close()`: the connection opens on the first call and is torn down automatically when the object is garbage collected or the process exits. Async mode follows normal asyncio hygiene — prefer `async with`, or `await client.close()` for deterministic teardown. The examples below show the async form; every one works identically in blocking mode with the `await`s dropped. ## Three levels of reading [#three-levels-of-reading] **Typed namespaces** — one per read category (`balances`, `subnets`, `neurons`, `staking`, `delegation`, `prices`, `epochs`, `weights`, `locks`, `identity`, `leasing`, `hyperparameters`, `collateral`, `chain`). Every read in the catalog is a method on its namespace, with autocomplete, signature help, and typed returns; a few curated helpers (`subnets.all()`, `balances.get()`) add renamed or richer variants on top: ```python await client.balances.get("5F...") # Balance await client.subnets.all() # list[SubnetInfo] await client.subnets.subnet_registration_cost() # Balance await client.delegation.delegate_take(hotkey_ss58="5F...") await client.neurons.all(netuid=1) # list[Neuron] await client.collateral.miner_collateral(netuid=1, hotkey_ss58="5F...") ``` Every method takes an optional `block=` to pin the read to one block. **Named reads** — the same catalog dispatched by name, the form agents and the CLI's `query` group use. One page each under [Queries](/docs/query): ```python take = await client.read("delegate_take", hotkey_ss58="5F...") client.reads() # the machine-readable catalog ``` **Generic accessors** — anything in the chain's runtime metadata, via generated descriptors. This is the escape hatch when no read wraps what you need: ```python tempo = await client.query(bt.storage.SubtensorModule.Tempo, [1]) pairs = await client.query_map(bt.storage.SubtensorModule.Tempo) ed = await client.constant(bt.constants.Balances.ExistentialDeposit) info = await client.runtime(bt.runtime_api.NeuronInfoRuntimeApi.get_neurons_lite, [1]) ``` The `bt.storage`, `bt.constants`, `bt.runtime_api`, and `bt.calls` modules are generated from chain metadata and cover the entire runtime surface. ## Typed results: the metagraph [#typed-results-the-metagraph] The richest typed result is the metagraph — a whole subnet as one object. `client.subnets.metagraph(netuid)` returns a `Metagraph` (the [`metagraph`](/docs/query/metagraph) named read returns the underlying raw runtime record instead): ```python mg = await client.subnets.metagraph(netuid=1) for n in mg: # iterates neurons, ordered by uid; len(mg) works print(n.uid, n.hotkey, n.incentive) mg.validators # neurons holding a validator permit mg.neuron(5) # by uid (KeyError if unknown) mg.by_hotkey("5F...") # by hotkey, or None mg.hotkeys, mg.coldkeys # parallel address lists ``` Each `MetagraphNeuron` carries the identity columns (`uid`, `hotkey`, `coldkey`), status (`active`, `validator_permit`, `last_update`, `block_at_registration`), the 0..1-normalized scores (`rank`, `trust`, `consensus`, `incentive`, `dividends`, `pruning_score`), balances (`emission`, `alpha_stake`, `tao_stake`, `total_stake`), and `axon` (the served `ip:port` or None), `identity`, and `commitment`. Subnet-level fields (`tempo`, `price`, `owner_hotkey`, ...) live on the `Metagraph` itself, and `mg.raw` keeps the untouched runtime record. `commitment` is the neuron's entry in the Commitments pallet, timelock-aware: ```python c = mg.neuron(5).commitment # or mg.commitments[5]; None if none if c is not None: c.status # "plain" | "sealed" | "revealed" c.value # visible content, or None while sealed c.reveals_at # UTC datetime a sealed payload opens (else None) ``` `client.subnets.commitments(netuid)` fetches just the commitments (keyed by hotkey, much cheaper than the full metagraph); the named reads are [`commitment`](/docs/query/commitment), [`commitments`](/docs/query/commitments), and [`revealed-commitment`](/docs/query/revealed-commitment). Writing one has no dedicated intent: the chain call is `Commitments.set_commitment`, reachable via the raw-call escape hatch ([Advanced submission](/docs/concepts/advanced)). ## Blocks, time, and waiting [#blocks-time-and-waiting] ```python await client.block() # current block number await client.block_info(123) # header + extrinsics + timestamp async for header in await client.blocks(): ... # subscribe to new blocks await client.wait_for_block(1_000_000) await client.wait_for_timestamp("2026-08-01T00:00:00Z") await client.wait_for_epoch(netuid=1) # next epoch boundary on a subnet ``` ## Pinned snapshots [#pinned-snapshots] `client.at(block)` returns a `Snapshot`: the same read surface — all the typed namespaces and named reads — pinned to one block, so a multi-read computation sees one consistent state instead of racing the chain. Writes through a snapshot are rejected — it is a view, not a signer. ```python snap = await client.at(await client.block()) bal = await snap.balances.get("5F...") subnets = await snap.subnets.all() ``` ## Writing [#writing] `plan`, `execute`, `execute_tool`, `submit_shielded`, and `submit_call` are covered in [The transaction model](/docs/concepts/transactions) and [Advanced submission](/docs/concepts/advanced). ## Logging [#logging] The SDK logs under the `bittensor.*` namespace and never configures handlers — it is silent unless your application opts in: ```python import logging logging.getLogger("bittensor").setLevel(logging.DEBUG) ``` # Emissions (/docs/concepts/emissions) Emissions run in two stages. Every block, the chain mints TAO, splits it across subnets, and injects liquidity into their pools (the **coinbase**). Every tempo, each subnet distributes its accumulated alpha to owner, miners, validators, and stakers via **Yuma Consensus** (the **epoch**). This page covers the numbers; [The network](/docs/concepts/network) covers the roles. ## TAO emission and halvings [#tao-emission-and-halvings] The base emission is **1 TAO per block** (one block every 12 seconds), decaying toward a hard cap of 21 million TAO — minted by [`get_block_emission`](/code/pallets/subtensor/src/coinbase/block_emission.rs#L21-L29) as part of the per-block [`block_step`](/code/pallets/subtensor/src/coinbase/block_step.rs#L7-L36). Halvings are triggered by **total-issuance thresholds**, not block counts: emission halves each time issuance crosses the midpoint of the remaining supply (10.5M, 15.75M, ...) ([`get_block_emission_for_issuance`](/code/pallets/subtensor/src/coinbase/block_emission.rs#L38-L81)). Because recycled TAO (registration burns) is subtracted from total issuance and can be re-emitted, recycling pushes halvings out. Transaction fees are not recycled — they are paid to the block author (see [fees](/docs/concepts/transactions#fees)). The first halving occurred in December 2025: current emission is 0.5 TAO per block, roughly 3,600 TAO per day. A TAO halving also halves every pool's injection: each subnet's `tao_in` is a share of the halved block emission, and `alpha_in` tracks it (`tao_in / price`), so alpha injection halves too — slowing every subnet's alpha issuance and stretching alpha-halving timelines. Supply accounting in brief: max supply is 21M for TAO and for each subnet's alpha; **total issuance** counts what has been emitted and not recycled (the halving yardstick), while circulating supply is smaller — issuance includes pool reserves and staked positions. Burned tokens stay counted in issuance forever; recycled amounts are re-emittable ([recycled vs burned](/docs/concepts/money#recycled-vs-burned)). ## Alpha emission [#alpha-emission] Subnet tokens date from the **dTAO** upgrade (February 2025, first dTAO block 4,920,351), which converted all existing stake to root TAO stake at the switch. Each subnet's alpha token has its own 21M cap and follows the same halving curve, applied to that subnet's alpha issuance and starting from the subnet's launch. Per block, a subnet mints alpha in two places: * **`alpha_out`** — up to 1 alpha (at the subnet's current halving rate) destined for participants, accumulated for distribution at the next epoch. * **`alpha_in`** — alpha injected into the pool alongside the subnet's TAO emission, normally `tao_in / price` so the injection is price-neutral. So a young subnet mints up to 2 alpha per block in total. The injection is capped at `root_proportion × alpha_emission`, where `root_proportion = (root_tao × tao_weight) / (root_tao × tao_weight + alpha_issuance)`. As a subnet ages its alpha issuance grows, the cap falls, and the TAO that can no longer be injected as liquidity is instead swapped for alpha on the subnet's own pool — buying pressure that transitions mature subnets from liquidity injection to chain buybacks. Alpha bought this way accumulates as protocol-owned alpha. ## Subnet emission shares [#subnet-emission-shares] Each block's TAO emission is divided across subnets in proportion to their **EMA price**, weighted by a miner-burn penalty ([`get_shares`](/code/pallets/subtensor/src/coinbase/subnet_emissions.rs#L354-L389); this price-based formula shipped in June 2026): ``` share_i = p_i × (1 − b_i) / Σ_j p_j × (1 − b_j) ``` where `p_i` is the subnet's moving price (`SubnetMovingPrice`) and `b_i` is the proportion of the last tempo's miner incentive that was withheld because it was directed to subnet-owner hotkeys (counted whether the withheld alpha was recycled or burned). If the combined weight is zero across all subnets, the chain falls back to unweighted price shares so emission is never stranded. Emission-disabled subnets get zero share, redistributed proportionally to enabled ones — and with `tao_in` zeroed, `alpha_in` (`tao_in / price`) is zero too, so pool injection stops entirely while `alpha_out` keeps accruing for participants. The EMA uses an age-dependent smoothing factor: ``` ema_alpha = base_alpha × blocks_since_start / (blocks_since_start + halving_blocks) ``` with `halving_blocks` defaulting to 201,600 (\~4 weeks). New subnets start near zero — their moving price adapts extremely slowly, which blunts launch pumps, coordinated buys, and flash attacks on emission shares. The spot price feeding the EMA is capped at 1.0. There is no zero-emission floor: a subnet with a low EMA price still receives a small non-zero share. ## Pools and price [#pools-and-price] Pools are Balancer-style weighted pools, and the spot alpha price is `(w_alpha / w_tao) × (TAO reserve / alpha reserve)` (read it with [`alpha-price`](/docs/query/alpha-price)). The two weights start at 0.5/0.5 — where the price reduces to the plain reserve ratio and the math to constant-product — and are bounded to \[0.01, 0.99]. Per-block liquidity injections shift the weights instead of the price, so emission does not move the market, but it does nudge the weights slightly off 0.5/0.5 — so the price is the weighted ratio, not exactly `TAO / alpha`. Pool liquidity is protocol-owned by default: the chain has a user-liquidity feature (per-subnet `user_liquidity_enabled` toggle, off by default) but with it off there are no user LP positions or LP tokens. ## The per-tempo split [#the-per-tempo-split] Per-block `alpha_out` is divided as it accrues ([`emit_to_subnets`](/code/pallets/subtensor/src/coinbase/run_coinbase.rs#L265-L354)): * **18%** to the subnet owner ([`SubnetOwnerCut`](/code/pallets/subtensor/src/lib.rs#L1780), 11796/65535). * **41%** to miners — 50% of the remainder. * **41%** to validators and their stakers — the other 50%. A [`root_proportion`](/code/pallets/subtensor/src/coinbase/block_step.rs#L69-L79) share of the validator half (same formula as the injection cap) is reserved for **root TAO stakers** and accumulated as claimable root dividends — but only in blocks where the sum of all subnets' EMA prices exceeds 1.0; otherwise that alpha is recycled. If an epoch ends with zero total miner incentive, the miner half of that tempo's pending alpha is paid to validators instead of being withheld. Within a validator's dividends, each staker is paid according to the validator's stake mix: the TAO-staker portion is `τ × w / (α + τ × w)` and the alpha-staker portion is `α / (α + τ × w)`, where `w` is the global TAO weight (currently 0.18 on mainnet, governance-set). The validator's [take](/docs/tx/set-take) is deducted before delegators are paid. ## Epochs [#epochs] Distribution happens at epoch boundaries. Each subnet's epoch fires once `tempo` blocks have passed since its last epoch; the default tempo is 360 blocks (\~72 minutes), owner-settable between 360 and 50,400 (\~7 days), and some older subnets carry smaller legacy values. At most 2 subnet epochs run per block ([`MaxEpochsPerBlock`](/code/pallets/subtensor/src/lib.rs#L2181)) — extras are deferred one block ([`drain_pending`](/code/pallets/subtensor/src/coinbase/run_coinbase.rs#L379-L463)) — and a subnet owner can trigger an early epoch manually. An epoch that hits inconsistent chain state is skipped (the schedule still advances); its pending emission keeps accruing and is drained by the next successful epoch. Emission is settled per epoch, at epoch end: the accumulated alpha is paid to the hotkeys holding each UID when the epoch fires, not continuously per block. Deregistration follows the same rule — a neuron pruned mid-tempo gets nothing for the partial tempo, since the payout lands on whoever holds the UID at the epoch (its last payout was the last epoch that fired while it was registered). The [`metagraph`](/docs/query/metagraph)'s per-neuron emission field reflects this settlement: it is denominated in rao (of the subnet's alpha) and holds each UID's combined payout from the subnet's most recent epoch — a per-tempo amount, not a per-block rate. [`epoch-status`](/docs/query/epoch-status) and [`blocks-until-next-epoch`](/docs/query/blocks-until-next-epoch) expose the timing per subnet. ## Yuma Consensus [#yuma-consensus] At each epoch the chain resolves the validator-weight matrix (set via [`set-weights`](/docs/tx/set-weights)) into per-neuron emission shares. **Stake weight.** Each neuron's stake weight is `alpha_stake + tao_stake × tao_weight` — alpha staked on the subnet plus root-subnet TAO stake scaled by the global TAO weight (currently 0.18 on mainnet, governance-set). Validators below the chain's stake threshold (currently 1,000 tokens' worth) are zeroed, and neurons whose last weight update is older than the activity cutoff (default 5,000 blocks) are inactive and excluded from consensus. **Permits.** Validator permits are recalculated every epoch as the top-K neurons by stake weight ([`is_topk_nonzero`](/code/pallets/subtensor/src/epoch/math.rs#L227-L241)), with K = `MaxAllowedValidators` (default 128). Only permitted neurons can set weights over others; bonds are kept while a permit is held and cleared when it is lost. **Weight filtering.** Self-weights are removed (except the subnet owner's), as are weights from non-permitted validators and weights set before the target neuron's latest registration. Each validator's surviving weights are row-normalized, so influence is independent of absolute weight values. **Consensus and clipping.** For each miner, consensus is the stake-weighted median ([`weighted_median_col`](/code/pallets/subtensor/src/epoch/math.rs#L1053-L1118)): the highest weight level supported by at least `kappa` of active stake, with kappa defaulting to 32767/65535 ≈ 0.5. Weights above consensus are clipped down to it. **Rank and incentive.** A miner's rank is the stake-weighted sum of clipped weights, `r_j = Σ_i s_i × w̄_ij`; normalized ranks become **incentive**, each miner's share of the miner emission. **Validator trust** is the sum of a validator's clipped weights. The per-miner **trust** metric (the ratio of clipped to unclipped rank) is deprecated — the chain no longer computes it, and the metagraph returns empty trust and rank vectors. **Bonds and dividends.** Bond weights interpolate between raw and clipped weights by the bonds-penalty hyperparameter (default: fully clipped). Instant bonds `ΔB = W ∘ S` (weights times stake, column-normalized) are smoothed by an EMA ([`compute_ema_bonds_normal`](/code/pallets/subtensor/src/epoch/run_epoch.rs#L1297-L1320)): ``` B(t) = alpha × ΔB + (1 − alpha) × B(t−1) ``` where `alpha = 1 − bonds_moving_average / 1,000,000` (default 900,000, so alpha = 0.1). Validator dividends are bonds times miner incentive, `d_i = Σ_j B_ij × I_j` — validators who recognize good miners early build bonds and earn more when consensus catches up. **Yuma3** (a per-subnet toggle, [`Yuma3On`](/code/pallets/subtensor/src/lib.rs#L2204)) switches to fixed-point bond computation ([`compute_bonds`](/code/pallets/subtensor/src/epoch/run_epoch.rs#L1333-L1360)) with per-pair scaling, and computes dividends differently: the row-sum of bonds × incentive, scaled by each validator's active stake, then renormalized. With **liquid alpha** enabled, the EMA rate becomes dynamic per validator–miner pair ([`compute_liquid_alpha_values`](/code/pallets/subtensor/src/epoch/run_epoch.rs#L1415-L1455)), moving between `alpha_low` (default 0.7) and `alpha_high` (default 0.9) via a sigmoid on the distance from consensus (steepness default 1000). Liquid alpha only takes effect when `yuma3_enabled` is also on — the classic bond path ignores the toggle entirely. Both toggles and their parameters are owner-set [hyperparameters](/docs/query/subnet-hyperparameters). If no valid weights exist, emission falls back to stake proportions, so a subnet without consensus still pays its stakers. The epoch writes consensus, incentive, dividends, validator trust, bonds, and permits back to chain state — [`metagraph`](/docs/query/metagraph) returns all of it in one read, and combined emission drives pruning (deregistration) order. # Money (/docs/concepts/money) Amounts on Bittensor come in two kinds of currency: **TAO**, the chain token, and **alpha**, of which every subnet has its own. They trade against each other in per-subnet pools, so an alpha amount is meaningless without knowing which subnet it belongs to — and 1 alpha on subnet 1 is not worth 1 alpha on subnet 2. ## The Balance type [#the-balance-type] `Balance` is unit-tagged: every instance carries the netuid whose currency it denominates (netuid 0 is TAO). Arithmetic between different units raises `UnitMismatchError` instead of silently mixing currencies. ```python import bittensor as bt bt.Balance.from_tao("10000000.123456789") # exact TAO (str/Decimal for big amounts) bt.Balance.from_alpha(2.5, netuid=42) # subnet-42 alpha; prints with the subnet's # on-chain token symbol (α₄₂ before connect) bt.tao(1.5); bt.alpha(2.5, 42); bt.rao(1_500_000_000) # shorthands ``` Readback is unit-named too: `.tao` on a TAO balance, `.alpha` on an alpha balance — and `.tao` on an alpha balance raises rather than pretending the units are interchangeable. The base unit is **rao**: 1 TAO = 10⁹ rao. ## Amount parameters [#amount-parameters] Intent fields are named by unit so there is no ambiguity about what a number means: `amount_tao` is TAO, `amount_alpha` is the subnet's alpha (the origin subnet for cross-subnet moves), `*_rao` is rao. Money fields that support it accept the string `"all"` — the concrete amount is resolved from chain state at build time (e.g. [`transfer`](/docs/tx/transfer), [`remove-stake`](/docs/tx/remove-stake)). ## Valuing stake [#valuing-stake] Alpha is never summed across subnets or silently treated as TAO. To value a coldkey's stake in TAO, use the [`stake-value-for-coldkey`](/docs/query/stake-value-for-coldkey) read: a spot price mark (`alpha × price`, excluding slippage and fees), pinned to one block. For what you would actually receive by exiting a position, simulate the swap with [`quote-unstake`](/docs/query/quote-unstake), which includes fees and slippage. ## Slippage [#slippage] 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 **price impact**; **slippage** is the further movement from other transactions landing before yours. The quotes account for the first, and the limit variants protect against both. The `*-limit` variants ([`add-stake-limit`](/docs/tx/add-stake-limit), [`remove-stake-limit`](/docs/tx/remove-stake-limit)) bound the execution price and fail instead of filling badly. To turn a tolerance into a `limit_price`: when staking, ceiling = spot × (1 + tolerance); when unstaking, floor = spot × (1 − tolerance). At a spot price of 2.0 TAO ([`alpha-price`](/docs/query/alpha-price)), a 0.5% tolerance means 2.01 staking and 1.99 unstaking. The same economics decide what is worth [MEV-shielding](/docs/concepts/advanced#mev-shielded-submission). Root-subnet (netuid 0) staking is not a pool swap — it converts 1:1 with no swap fee and no price impact, so there is nothing for a front-runner to extract and shielding it buys nothing. Small swaps (well under \~1 TAO) are equally unattractive targets; the danger case is a large swap with a loose or absent limit price. ## Swap fees [#swap-fees] Every swap against a pool pays a fee of amount × FeeRate / 65535, where [`FeeRate`](/code/pallets/swap/src/pallet/mod.rs#L96) is per-subnet and governance-settable (default 33 ≈ 0.05%). It is taken from the input side: TAO when staking, alpha when unstaking — and paid to the block author (an alpha-side fee is converted to TAO first). Moving stake between hotkeys within the same subnet is not a swap and pays no swap fee; a cross-subnet move runs two swaps but is charged the fee only once. The [`quote-stake`](/docs/query/quote-stake) / [`quote-unstake`](/docs/query/quote-unstake) reads include it. This is distinct from the per-extrinsic transaction fee — see [the transaction model](/docs/concepts/transactions). ## Minimums [#minimums] Two floors to know about: * **Existential deposit** — 500 rao. An account whose balance drops below it is reaped: deactivated, with the remaining dust destroyed. Live value: [`existential-deposit`](/docs/query/existential-deposit). * **Minimum stake** — staking operations below a chain-configured minimum ([`DefaultMinStake`](/code/pallets/subtensor/src/lib.rs#L1047), enforced in [`stake_utils.rs`](/code/pallets/subtensor/src/staking/stake_utils.rs#L1089-L1102)) (default 2,000,000 rao = 0.002 TAO, plus the swap fee on top) fail with [`AmountTooLow`](/code/pallets/subtensor/src/macros/errors.rs#L188); unstakes are held to the same floor, both on the amount withdrawn and on any dust position left behind. ## Supply [#supply] Every token is hard-capped at **21 million** — TAO and each subnet's alpha alike. **Total issuance** is the chain's counter of what has been emitted and not recycled; it is what halving thresholds are measured against ([emissions](/docs/concepts/emissions)). Circulating supply is smaller: issuance includes tokens locked in pool reserves and staked positions. ## Recycled vs burned [#recycled-vs-burned] Both remove tokens from circulation, but not equivalently. **Recycled** tokens are subtracted from the chain's issuance counter, so the emission schedule can re-issue them later — neuron registration costs are recycled this way. **Burned** tokens stay counted in total issuance and are simply gone forever; nothing re-emits them. The distinction matters when reasoning about supply: recycling slows emission's approach to the cap, burning permanently retires supply. Transaction fees are neither — they are paid to the block author, not destroyed ([fees](/docs/concepts/transactions#fees)). # The network (/docs/concepts/network) Bittensor is a blockchain (the **subtensor** chain) that coordinates and pays for useful work. The work itself happens off-chain, inside **subnets**; the chain records who participates, measures agreement about who is doing good work, and emits the network's token, **TAO**, accordingly. The chain produces one block every 12 seconds. Subtensor is a Substrate-based chain. Each block contains **extrinsics** — the submitted calls — and **events**; some events are emitted by the chain's own per-block hooks (emissions, epoch boundaries) rather than by any extrinsic. ## Networks and endpoints [#networks-and-endpoints] The SDK's network names resolve to public endpoints: * `finney` — mainnet, `wss://entrypoint-finney.opentensor.ai:443` * `test` — public testnet, `wss://test.finney.opentensor.ai:443` * `devnet` — the development chain, `wss://dev.chain.opentensor.ai:443` * `local` — a dev node at `ws://127.0.0.1:9944` Mainnet also has a public **lite** node at `wss://lite.chain.opentensor.ai:443` and an **archive** node at `wss://archive.chain.opentensor.ai:443`. A lite node keeps only recent state; to query state older than roughly 300 blocks (block-pinned reads, historical snapshots) you need the archive endpoint or your own archive node. Any `ws://` / `wss://` endpoint can be passed to [`Subtensor`](/docs/concepts/client) client directly. For heavy workloads, [run your own node](/docs/guides/running-a-node). ## Subnets [#subnets] A subnet is an independent incentive market, identified by an integer **netuid**. Each subnet has: * an **owner** — the coldkey that registered it, who defines the incentive mechanism and sets its [hyperparameters](/docs/tx/set-hyperparameter); * **miners** — hotkeys registered on the subnet that produce whatever the subnet's mechanism demands (inference, storage, predictions, ...); * **validators** — hotkeys with enough stake behind them to hold a validator permit; they score miners by [setting weights](/docs/tx/set-weights); * a liquidity pool pairing TAO with the subnet's own token, **alpha**. Netuid 0 is the **root network**: it has no miners, no alpha token, and no validation work — it is the pool where TAO is staked directly. Root TAO stake counts toward validator stake weight on every subnet (see below) and earns a share of each subnet's dividends. Registration there is stake-based rather than burn-based. By convention, root (TAO) is priced at 1.0 wherever subnet prices are summed. Subnet slots are limited: a subnet holds at most `max_allowed_uids` neurons (default 256, and no lower than 64). Registering on a full subnet ([`burned-register`](/docs/tx/burned-register)) evicts the lowest-ranked non-immune neuron ([`get_neuron_to_prune`](/code/pallets/subtensor/src/subnets/registration.rs#L280-L356)); a freshly registered neuron ([`append_neuron`](/code/pallets/subtensor/src/subnets/uids.rs#L117-L145)) is protected for an immunity period, then competes like everyone else. A neuron's **stake weight** on a subnet is `alpha_stake + tao_stake × tao_weight` — its alpha staked there, plus its root TAO stake scaled by the global TAO weight (currently 0.18 on mainnet, governance-set). Validator permits go to the top neurons by stake weight, up to the subnet's `max_allowed_validators` (default 128), recalculated every epoch. ## TAO and alpha [#tao-and-alpha] TAO is the chain's native token (1 TAO = 10⁹ rao). Each subnet also has its own token, alpha, traded against TAO in the subnet's pool. Staking TAO into a subnet swaps it to that subnet's alpha at the pool price; unstaking swaps back. Alpha on one subnet is not worth alpha on another — the SDK's [`Balance` type](/docs/concepts/money) enforces this at the type level. Spot prices come from the [`alpha-price`](/docs/query/alpha-price) read, and [`quote-stake`](/docs/query/quote-stake) / [`quote-unstake`](/docs/query/quote-unstake) simulate a swap including fees and slippage before you commit. ## Emissions and epochs [#emissions-and-epochs] The chain continuously emits TAO to subnets, and within each subnet to its participants, in proportion to measured value. Each subnet runs on a cycle (its **tempo**): validators submit weights over miners during the epoch, and at the epoch boundary Yuma Consensus combines those weights — weighted by validator stake — into ranks, trust, and incentive, which set each participant's share of the emission. Delegated stake earns a share of its validator's dividends, minus the validator's [take](/docs/tx/set-take). The numbers behind all of this — halving schedules, the emission-share formula, the 18/41/41 per-tempo split, and the consensus math — are on the [Emissions](/docs/concepts/emissions) page. The [`epoch-status`](/docs/query/epoch-status) and [`blocks-until-next-epoch`](/docs/query/blocks-until-next-epoch) reads expose the timing; [`metagraph`](/docs/query/metagraph) returns the full per-neuron state of a subnet in one call. ## Who signs what [#who-signs-what] Every account is an **ss58 address** backed by a keypair. Operations divide by signer: * **coldkey** — owns funds: transfers, staking, registration, subnet ownership. * **hotkey** — operates on subnets: weights, serving an axon. The [wallets page](/docs/concepts/wallets) covers how the two keys relate and how to manage them. # Staking and pools (/docs/concepts/staking-pools) 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 [#the-pool] Each subnet's pool is a **weighted balancer pool** (a generalization of the constant-product AMM): two reserves, TAO ([`SubnetTAO`](/code/pallets/subtensor/src/lib.rs#L1377)) and alpha ([`SubnetAlphaIn`](/code/pallets/subtensor/src/lib.rs#L1419)), plus a pair of weights $w_1 + w_2 = 1$. The spot price ([`calculate_price`](/code/pallets/swap/src/pallet/balancer.rs#L220-L228)) 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`](/code/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 [#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`](/code/pallets/swap/src/pallet/mod.rs#L96)/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`](/code/pallets/subtensor/src/lib.rs#L1424)). 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 [#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 [#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( bt.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 [#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 [#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`](/code/pallets/swap/src/pallet/mod.rs#L151)). ## Reading the pool [#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. # The transaction model (/docs/concepts/transactions) Every state change goes through one shape, whether you drive it from Python, the CLI, or an agent tool call: 1. Describe **what** you want as an **intent** — a small serializable dataclass. 2. **`plan`** it — fee, predicted effects, warnings, policy verdict. Nothing is submitted. 3. **`execute`** it — sign and submit through a single policy-gated choke point, and get a typed result back. ## Intents [#intents] An intent's fields are JSON-native, so it round-trips to and from a dict without custom encoders. Each intent knows its chain call, its signer (coldkey or hotkey), and its JSON schema. All 70+ of them are cataloged under [Transactions](/docs/tx), one page each. ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.AddStake(hotkey_ss58="5F...validator", netuid=1, amount_tao=10) ``` Or build one by op name from a plain dict — how agents and tools do it: ```python intent = bt.intents.build("add_stake", {"hotkey_ss58": "5F...", "netuid": 1, "amount_tao": 10}) ``` ## Plan: preview everything [#plan-preview-everything] ```python async with bt.Subtensor() as client: plan = await client.plan(intent, wallet) plan.fee # estimated fee (Balance) plan.effects # ["stake 10 TAO to 5F... on subnet 1", ...] plan.warnings # non-fatal cautions plan.violations # policy violations that would block execute plan.ok # no violations? ``` On the CLI, `--dry-run` on any `tx` command prints the same plan and submits nothing. ## Execute: one choke point [#execute-one-choke-point] ```python result = await client.execute(intent, wallet) result.success # bool result.block_hash # where it landed ``` `execute` re-plans, enforces the policy, signs with the correct key (the intent declares whether it needs the coldkey or hotkey), submits, and decodes the receipt. `client.execute_tool(op, args, wallet)` is the by-name variant. ## Policy: hard guardrails [#policy-hard-guardrails] A `Policy` bounds what any execution may do. Violations raise `PolicyError` at execute time and appear in every plan: ```python policy = bt.Policy( max_fee_tao=0.1, max_spend_tao=5.0, allowed_netuids=[1, 2], allow_raw_calls=False, # default: raw submit_call is refused ) async with bt.Subtensor("finney", policy=policy) as client: ... ``` Intents whose spend cannot be bounded ahead of time (subnet registration, `transfer_all`, ...) report an infinite spend, so a spend cap blocks them until deliberately raised. ## Typed results and errors [#typed-results-and-errors] Failures come back as data, not prose. The `error` on a failed `ExtrinsicResult` carries the exact chain error name, a semantic [`ErrorCode`](/docs/errors) to branch on, and a remediation hint: ```python result = await client.execute(bt.BurnedRegister(netuid=999), wallet) if not result.success: print(result.error.name) # "SubnetNotExists" print(result.error.code) # ErrorCode.SUBNET_NOT_EXISTS print(result.error.remediation) # what to try next ``` Every chain error name is deliberately classified — a CI gate keeps the mapping complete in both directions — so a failure never degrades to "unknown" without a reason. ### Pool-level rejections [#pool-level-rejections] Besides dispatch errors, a transaction can be rejected before it enters the pool — the node's pre-checks fail it with `Custom error: N` instead of a named error, surfaced over RPC as error 1010 `Invalid Transaction`. The codes: | Code | Name | Meaning | | ---- | -------------------------------- | -------------------------------------------------- | | 0 | ColdkeyInSwapSchedule | Coldkey has an announced swap pending (deprecated) | | 1 | StakeAmountTooLow | Amount below the minimum stake | | 2 | BalanceTooLow | Not enough free balance | | 3 | SubnetNotExists | Target subnet does not exist | | 4 | HotkeyAccountDoesntExist | Hotkey account not found | | 5 | NotEnoughStakeToWithdraw | Unstake exceeds the stake held | | 6 | RateLimitExceeded | Weight-setting or transaction rate limit hit | | 7 | InsufficientLiquidity | Pool cannot absorb the trade | | 8 | SlippageTooHigh | Execution price outside the limit | | 9 | TransferDisallowed | Transfers disabled for this operation | | 10 | HotKeyNotRegisteredInNetwork | Hotkey not registered anywhere | | 11 | InvalidIpAddress | Axon/prometheus IP invalid | | 12 | ServingRateLimitExceeded | Axon/prometheus serving rate limit hit | | 13 | InvalidPort | Axon/prometheus port invalid | | 14 | ZeroMaxAmount | Computed max amount is zero | | 15 | InvalidRevealRound | Wrong reveal round for timelocked weights | | 16 | CommitNotFound | No matching weight commit | | 17 | CommitBlockNotInRevealRange | Reveal outside its window | | 18 | InputLengthsUnequal | Paired input vectors differ in length | | 19 | UidNotFound | Hotkey not registered on this subnet | | 20 | EvmKeyAssociateRateLimitExceeded | EVM key association rate limit hit | | 21 | ColdkeySwapDisputed | Coldkey swap under dispute | | 22 | InvalidRealAccount | Proxy real account invalid | | 23 | FailedShieldedTxParsing | Shielded payload failed to decode | | 24 | InvalidShieldedTxPubKeyHash | Shielded payload key hash mismatch | | 25 | NonAssociatedColdKey | Coldkey does not own this hotkey | | 26 | DelegateTakeTooLow | Take below the allowed minimum | | 27 | DelegateTakeTooHigh | Take above the allowed maximum | | 255 | BadRequest | Catch-all for anything else | Code 6 covers weight-setting and general transaction rate limits; axon serving has its own code 12. ## Fees [#fees] A fee-bearing extrinsic pays two components in TAO from the signer's free balance: a **weight fee**, linear in the call's dispatch weight, and a **length fee** of 1 rao per byte of the encoded extrinsic. Both are withdrawn up front, before the call runs — insufficient balance rejects the transaction outright, and a failed call does not refund them. The fee is paid to the block author, not recycled or burned (dropped in the edge case of a block with no author). `plan.fee` (and the `--dry-run` output) is this number, estimated from the chain before anything is signed. The validator hot path is free: [`set-weights`](/docs/tx/set-weights), [`commit-weights`](/docs/tx/commit-weights), [`reveal-weights`](/docs/tx/reveal-weights) and their batch and timelocked variants pay no transaction fee, as do [`serve-axon`](/docs/tx/serve-axon) and take changes. For a small set of unstake-side calls (`remove_stake` and friends), a signer with no TAO can have the fee taken in alpha instead, converted at pool price; every other call simply requires TAO. Staking, unstaking, and stake moves additionally pay a pool swap fee on the amount transacted — a property of the swap, not the extrinsic; see [swap fees](/docs/concepts/money#swap-fees). ## Rate limits [#rate-limits] Chain rate limits are block-based cooldowns armed only by **successful** operations — a failed call can be retried immediately. Notable defaults (governance-settable): * Delegate-take changes ([`increase-take`](/docs/tx/increase-take) / [`decrease-take`](/docs/tx/decrease-take)): once per 216,000 blocks (\~30 days). Childkey-take changes ([`set-childkey-take`](/docs/tx/set-childkey-take)) have the same limit. * [`set-children`](/docs/tx/set-children): once per 150 blocks (\~30 minutes) per subnet. * [`swap-hotkey`](/docs/tx/swap-hotkey): a global cooldown (live value: [`tx-rate-limit`](/docs/query/tx-rate-limit)) plus a per-subnet interval of 7,200 blocks (\~1 day). ## Mortality [#mortality] Extrinsics are signed with a mortal era: valid for 128 blocks (\~25 minutes) by default, after which an unincluded transaction lapses instead of lingering in the pool indefinitely. `execute` and `submit_call` take a `period=` argument to change it (`period=None` signs an immortal transaction); MEV-shielded inner extrinsics use a shorter 8-block era — the chain rejects shielded wrappers signed with anything longer. ## The CLI is the same machinery [#the-cli-is-the-same-machinery] Every intent is `btcli tx `; the command's options are generated from the intent's fields, so the CLI and SDK can't diverge. Interactive sessions prompt for missing required options and confirm before submitting; `--yes` skips confirmation, and a non-interactive session without `--yes` is declined rather than left hanging. # Wallets and keys (/docs/concepts/wallets) A Bittensor wallet is a pair of keys stored on disk (default `~/.bittensor/wallets//`): * The **coldkey** holds funds and signs financial operations: transfers, staking, registration, subnet ownership. It is encrypted at rest with a password. Keep it offline where possible — [proxies](/docs/concepts/advanced) exist precisely so day-to-day operations don't need it. * The **hotkey** is your operational identity on subnets: it gets the UID when you register, signs weights and axon serving, and is safe to keep on a running machine. One wallet can hold many hotkeys. ## Key material [#key-material] Coldkeys and hotkeys are **sr25519** keypairs, addressed with SS58 network prefix 42. Each key is generated from a mnemonic (12 words by default), and that mnemonic is the only recovery path: the wallet password merely decrypts the keyfile on one machine — it cannot regenerate a lost key, and no password is needed to regenerate the key from the mnemonic. Whoever holds the mnemonic owns the funds. On disk, each wallet directory contains: * `coldkey` — the coldkey secret, password-encrypted (NaCl); * `coldkeypub.txt` — the coldkey's public key and ss58 address, unencrypted (no secret material); * `hotkeys/` — one file per hotkey, **unencrypted**: the SDK's create and regen helpers always write hotkeys in plaintext, private key and mnemonic included. Treat every hotkey file as readable by anything that can read the disk. ## Create, import, inspect [#create-import-inspect] ```bash btcli wallet create -w my_coldkey # new coldkey + hotkey (prints mnemonics once — save them) btcli wallet new-hotkey -w my_coldkey -H my_hotkey btcli wallet regen-coldkey -w my_coldkey # recover from mnemonic (prompted securely) btcli wallet regen-hotkey -w my_coldkey btcli wallet regen-coldkeypub -w watch_only --ss58 5F... --public-key 0x... # watch-only, no secrets btcli wallet list # wallets on disk, multisigs, address book btcli wallet show # public keys + crypto schemes btcli wallet balance my_coldkey # free TAO + stake marked to TAO ``` The same flows exist in Python under `bt.wallets` (`create`, `regen_coldkey`, `regen_hotkey`, `list_wallets`, `sign_message`, `verify_message`, ...). Key material is handled by the SDK's native wallet module (`bittensor.wallet`, backed by the in-repo `bittensor-core` extension); this package adds thin conveniences. ```python from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") ``` ## Back up the seed phrase [#back-up-the-seed-phrase] A seed phrase fails in two ways: **loss** (the funds are gone permanently — nobody, including the Rao Foundation, can recover them) and **leak** (anyone who saw it can drain the wallet — respond by rotating keys, see below). Backups defend against loss; handling discipline defends against leak. * Never type a seed phrase into messaging apps, email, cloud documents, or any networked application. Assume keyloggers, screen capture, and cameras exist when generating or restoring keys. * The "hex-encoded seed" or "private key hex" is the seed phrase in another encoding — anyone asking for it is asking for the seed phrase. Nobody legitimate asks for either. "Fix your RPC settings" DMs are phishing, and unsolicited DMs offering support are scams; keep help requests in public channels. * Keep redundant physical backups in separate locations so one fire, flood, or theft cannot destroy every copy: paper in a tamper-evident envelope inside a safe or deposit box; a stamped metal plate survives what paper doesn't. * A digital backup should live on an encrypted offline drive (GPG or VeraCrypt, strong unique passphrase) that is only ever connected to a trusted, offline machine. * A hardware wallet is a signing device, not a backup: it will not export the seed phrase, and too many wrong PINs factory-reset it. Use it in addition to seed-phrase backups, never instead of them. ## Security model [#security-model] Match each key to the least-trusted machine that needs it: 1. **Public keys only** — balances and state are readable without any secret: import just the address with `btcli wallet regen-coldkeypub` and use any [query](/docs/query) from an everyday machine. 2. **Hotkey machines** — mining and validation servers run subnet code and ML dependencies, which is untrusted code sitting next to your hotkey. Create hotkeys on a trusted workstation and provision them to servers through a secrets manager (Vault, AWS/GCP secret stores) or ephemeral CI injection; never commit key files to a repository. A leaked hotkey can't move TAO, but it can submit garbage weights and burn the reputation of its UID. 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 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](/docs/guides/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 [#supply-chain-risk] Pin exact package versions in anything unattended, and upgrade only to releases announced on official channels. This is not hypothetical: in July 2024 a compromised PyPI release of the legacy `bittensor` package (6.12.2) exfiltrated coldkey material from machines that installed it, and the chain ran in safe mode from July 2 to July 12, 2024 while the damage was contained. For high-value machines, installing from the source repository at a signed tag (`git tag -v`) beats trusting a package index. ## Signing and verification [#signing-and-verification] ```bash btcli wallet sign --message "hello" -w my_coldkey # coldkey signature btcli wallet sign --message "hello" --use-hotkey -w my_coldkey btcli wallet verify --message "hello" --signature 0x... --ss58 5F... ``` Every transaction declares which key signs it — the `signer` column on each [transaction page](/docs/tx). Staking and transfers are coldkey-signed; weights and serving are hotkey-signed. ## How address arguments resolve [#how-address-arguments-resolve] Any address option in the CLI (`--dest`, `--hotkey`, `--coldkey`, ...) accepts three forms: * a raw **ss58 address**; * a **local name** — hotkey options take `HOTKEY` or `WALLET/HOTKEY`, coldkey options take a wallet name (its coldkey is used), and address-book or proxy-book names resolve too; * **omitted** — `--hotkey` / `--coldkey` fall back to the configured wallet's own keys. Destination-style options never default. ```bash btcli query hotkey-owner --hotkey my_coldkey/my_hotkey btcli wallet balance my_coldkey ``` ## Address hygiene [#address-hygiene] Transfers are irreversible, and **address poisoning** exploits that: attackers grind vanity addresses matching the first and last characters of addresses you transact with, then send you a dust transfer so the lookalike appears in your history — waiting for you to copy the wrong entry. Defenses: * read the whole address, not just both ends; * know the shape: every Bittensor ss58 address starts with "5" (SS58 prefix 42\) — anything else isn't a Bittensor address; * pay from a saved address book (`btcli wallet list` shows yours), never from transaction history; * treat unexpected dust transfers as hostile; * send a small test transaction before any large transfer. Always send TAO to a **coldkey** address. Transferring to a hotkey address is technically possible but can strand the funds. And there is no undo anywhere in this system: no one — the Rao Foundation included — can reverse a theft or recover lost keys. ## Key rotation and recovery [#key-rotation-and-recovery] **Hotkey swap.** [`swap-hotkey`](/docs/tx/swap-hotkey) replaces a hotkey with a new one, moving its registrations and delegated stake. Swapping across all subnets recycles 0.1 TAO from the coldkey; swapping on a single subnet recycles 0.001 TAO and is limited to once per subnet per 7,200 blocks (\~1 day). **Coldkey swap** is the response to a leaked coldkey — but only worth it if the coldkey has registrations or owns a subnet. A plain holder or staker should simply transfer their TAO and stake to a fresh coldkey instead. The swap is a two-step, delayed process: 1. [`announce-coldkey-swap`](/docs/tx/announce-coldkey-swap) declares the destination and starts a 36,000-block (\~5-day) waiting period. The first announcement costs 0.1 TAO, which must be in the coldkey at announcement time (reannouncing later is free). During the 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 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 announcement can be reannounced (overwriting it and restarting the clock) or cleared only after a further 7,200 blocks (\~1 day). The delay exists so the real owner can catch a thief's announcement: [`dispute-coldkey-swap`](/docs/tx/dispute-coldkey-swap) freezes the key until the dispute is manually resolved. ## Non-interactive unlock [#non-interactive-unlock] For automation, the coldkey password resolves in this order (first match wins): an explicit `password=` argument in Python; the `BT_WALLET_PASSWORD` environment variable; the per-wallet variable named after the keyfile path (`BT_PW__...`, from the SDK); a password file via `--wallet-password-file` or `BT_WALLET_PASSWORD_FILE`; the macOS Keychain (`btcli wallet keychain`); then an interactive prompt. A browser-extension signer is also available so the coldkey never touches the machine running the CLI — see [signing with a browser extension](/docs/guides/extension-signing). # already_exists (/docs/errors/already-exists) 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 [#remediation] The object or state already exists; treat the goal as met or pick a different target ## Chain errors [#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). # disabled (/docs/errors/disabled) 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 [#remediation] This call or feature is switched off on this network ## Chain errors [#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). # expired (/docs/errors/expired) 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 [#remediation] The window has closed; restart the flow with fresh state ## Chain errors [#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). # Errors (/docs/errors) 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) | 35 | 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) | 137 | Check the argument values against the operation schema | | [`policy_violation`](/docs/errors/policy-violation) | 1 | The action exceeds a configured safety policy | | [`internal`](/docs/errors/internal) | 19 | A chain-side invariant failed; nothing to fix client-side — report it if it persists | | [`unknown`](/docs/errors/unknown) | — | Inspect the message for details | # insufficient_balance (/docs/errors/insufficient-balance) 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 [#remediation] Fund the signing account or reduce the amount; check with `btcli wallet balance` ## Chain errors [#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 or same-subnet stake transfer would dip into stake that is still reserved: the requested alpha exceeds the coldkey's free balance on that subnet after subtracting conviction `Lock` and any miner registration collateral locked against hotkeys the coldkey owns. Check `Lock` and `MinerCollateral` for the netuid; only total stake minus those reservations can move. | | [`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). # insufficient_liquidity (/docs/errors/insufficient-liquidity) 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 [#remediation] The pool cannot absorb this trade; reduce the amount or split it into smaller steps ## Chain errors [#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). # internal (/docs/errors/internal) 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 [#remediation] A chain-side invariant failed; nothing to fix client-side — report it if it persists ## Chain errors [#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. | | [`ColdkeyCollateralIncomplete`](/docs/errors/chain/ColdkeyCollateralIncomplete) | A coldkey swap could not fully migrate miner collateral: after migrating every indexed collateral hotkey, the old coldkey's ColdkeyMinerCollateral aggregate was still non-zero. This is a fail-closed invariant — retry after investigating orphaned MinerCollateral rows for that coldkey, or contact runtime maintainers. | | [`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). # invalid_argument (/docs/errors/invalid-argument) 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 [#remediation] Check the argument values against the operation schema ## Chain errors [#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. | | [`CollateralDrainRatioOutOfBounds`](/docs/errors/chain/CollateralDrainRatioOutOfBounds) | `sudo_set_collateral_drain_ratio` was given a value that is zero, negative, or above `MaxCollateralDrainRatio` (10). The drain ratio must be strictly positive — with no other exit path for miner collateral, a ratio of zero would make locked collateral permanently unrecoverable. Pass a `drain_ratio` in the range (0, 10]. | | [`CollateralLockShareTooHigh`](/docs/errors/chain/CollateralLockShareTooHigh) | `sudo_set_collateral_lock_share` was given a value above `MaxCollateralLockShare` (62258, \~95% of u16::MAX). The burned share of the registration price must stay strictly positive so re-registration always pays a nonzero, floating burn. Lower the `lock_share` argument to at most 62258. | | [`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). # limit_exceeded (/docs/errors/limit-exceeded) 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 [#remediation] A chain-side capacity limit was hit; reduce the size or count of the request ## Chain errors [#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. | | [`ColdkeyCollateralPositionsFull`](/docs/errors/chain/ColdkeyCollateralPositionsFull) | This coldkey already has the maximum number of distinct hotkeys with miner collateral on the subnet. Drain or consolidate existing bonds before adding another collateral position. | | [`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). # not_authorized (/docs/errors/not-authorized) 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 [#remediation] Sign with the key or origin that owns the target object, then retry ## Chain errors [#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). # not_found (/docs/errors/not-found) 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 [#remediation] The referenced object is not on-chain; check the identifier ## Chain errors [#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). # not_registered (/docs/errors/not-registered) 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 [#remediation] Register the hotkey on this subnet first with `btcli subnets register` ## Chain errors [#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). # policy_violation (/docs/errors/policy-violation) 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 [#remediation] The action exceeds a configured safety policy ## Chain errors [#chain-errors] The exact chain error names (from the extrinsic receipt) that classify to `policy_violation`; the description says what triggered the failure and where to check. Each name has its own page: | Chain error | Description | | --------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [`KeepStakeBlockedByCollateral`](/docs/errors/chain/KeepStakeBlockedByCollateral) | A hotkey swap with keep\_stake=true was refused because the old hotkey still has standing miner registration collateral. keep\_stake leaves stake on the old key while the UID moves, which would strand the bond. Retry with keep\_stake=false so collateral migrates with the UID; on-chain hotkey lineage maps track the rename for blacklist continuity. Or drain the bond through earned emission first. | The same explanation is available in the terminal: `btcli explain policy_violation` (or `btcli explain ` for one exact chain error). # rate_limited (/docs/errors/rate-limited) 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 [#remediation] Wait for the rate-limit window to pass, then retry ## Chain errors [#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). # subnet_not_exists (/docs/errors/subnet-not-exists) 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 [#remediation] Use an existing netuid; `btcli subnets list` shows valid ones ## Chain errors [#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). # subtoken_disabled (/docs/errors/subtoken-disabled) 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 [#remediation] The subnet is not active yet; wait for start\_call ## Chain errors [#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). # too_early (/docs/errors/too-early) 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 [#remediation] The required window has not opened yet; wait some blocks and retry ## Chain errors [#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). # unit_mismatch (/docs/errors/unit-mismatch) 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 [#remediation] Match the Balance netuid to the operation's currency ## Chain errors [#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). # unknown (/docs/errors/unknown) 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 [#remediation] Inspect the message for details ## Chain errors [#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). # Conviction locks (/docs/guides/conviction) Conviction is **time-weighted commitment**: when you [`lock-stake`](/docs/tx/lock-stake) alpha on a subnet, the locked amount accrues **conviction** toward the lock's target hotkey. Conviction is not spendable stake — it is a score the chain uses to recognize long-horizon alignment, including the automatic **subnet ownership** transfer when enough aggregate conviction exists. This guide walks through a fictional **Subnet 7 ("Atlas")** with three stakers. The numbers are round teaching examples, not live chain state — always read real values with [`subnet-convictions`](/docs/query/subnet-convictions) before acting. ## What locking does [#what-locking-does] A lock is a **floor on unstaking**, not a separate bucket: * Your coldkey's **total** staked alpha on the subnet must stay at or above the locked mass. Anything above remains freely unstakable. * Locked alpha **keeps earning** validator dividends and emissions — locking changes liquidity, not rewards. * Conviction is credited to the **hotkey you choose** at lock time (often your validator). Stake and conviction hotkeys can differ, but repeat [`lock-stake`](/docs/tx/lock-stake) calls must target the same hotkey. One lock per coldkey per subnet. Top-ups add mass; conviction continues from its current value. ## Example subnet: Atlas (netuid 7) [#example-subnet-atlas-netuid-7] | Field | Value | Meaning | | ------------------- | ---------------- | ------------------------------------------------------ | | `SubnetAlphaOut` | 8,000,000 α | Outstanding alpha on the subnet | | Ownership threshold | 800,000 α | 10% of alpha out — aggregate conviction gate | | Subnet age | > 1 year | Required before ownership can transfer | | Alice (owner) | 250,000 α locked | Perpetual lock on **owner hotkey** — conviction = mass | | Bob (validator) | 600,000 α locked | Perpetual lock toward his validator hotkey | | Carol (staker) | 200,000 α locked | Decaying lock (default) toward a validator | Drag **elapsed time** forward: Bob's perpetual conviction climbs toward 600k α. Carol's decaying conviction **rises then falls** as her locked mass frees. When **total conviction** crosses 800k α and the subnet is old enough, the **highest-conviction hotkey** becomes the new owner. ## Perpetual vs decaying [#perpetual-vs-decaying] New locks are **decaying** by default. Opt into **perpetual** with [`set-perpetual-lock`](/docs/tx/set-perpetual-lock). | Mode | Locked mass | Conviction | | ------------- | --------------------------------- | --------------------------------------------- | | **Perpetual** | Fixed | Approaches mass: `c = m − (m − c₀)·e^(−Δt/τ)` | | **Decaying** | Exponential decay on `UnlockRate` | Integral of decaying mass — peaks, then falls | On mainnet today [`MaturityRate`](/code/pallets/subtensor/src/lib.rs#L1654) is **311,622 blocks** (\~43 days) and [`UnlockRate`](/code/pallets/subtensor/src/lib.rs#L1658) is **934,866 blocks** (\~130 days). Both are governance-set storage values — read them live before planning, do not hardcode them. Switching to **decaying** mode emits a public on-chain event — an advance exit signal to other stakers. Locks on the **subnet owner hotkey** mature instantly: conviction always equals locked mass. ## Subnet ownership via conviction [#subnet-ownership-via-conviction] After each epoch, the chain runs [`change_subnet_owner_if_needed`](/code/pallets/subtensor/src/staking/lock.rs#L1160-L1377) when **all** of these hold: 1. Subnet age ≥ **ONE\_YEAR** (2,629,800 blocks from registration). 2. **Total aggregate conviction** ≥ 10% of [`SubnetAlphaOut`](/code/pallets/subtensor/src/lib.rs#L1424). 3. A hotkey with the highest rolled aggregate conviction resolves to a real coldkey owner (not the default account). The winning hotkey becomes [`SubnetOwnerHotkey`](/code/pallets/subtensor/src/lib.rs#L1988); its coldkey becomes [`SubnetOwner`](/code/pallets/subtensor/src/lib.rs#L1983). If the leader already owns the subnet, nothing changes. This is **not** the same as the per-hotkey projection in [`subnet-convictions`](/docs/query/subnet-convictions) — that API estimates when *one* hotkey might reach 10% of alpha out; ownership requires **aggregate** conviction across all lockers. ## Lock a position on a real subnet [#lock-a-position-on-a-real-subnet] Replace netuid `7` with your target subnet: ```bash # Inspect existing locks and conviction leaderboard btcli query subnet-convictions --netuid 7 --json btcli query hotkey-conviction --hotkey --netuid 7 --json btcli query coldkey-lock --coldkey --netuid 7 --json # Lock 1,000 alpha toward a validator hotkey (dry-run first) 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 -w my_coldkey ``` The same flow in Python — lock, opt into perpetual, then read conviction back: ```python await client.execute(bt.LockStake(netuid=7, hotkey_ss58="5F...", amount_alpha=1000), wallet) await client.execute(bt.SetPerpetualLock(netuid=7, enabled=True), wallet) lock = await client.locks.coldkey_lock(coldkey_ss58="5C...", netuid=7) conviction = await client.locks.hotkey_conviction(hotkey_ss58="5F...", netuid=7) ``` ## Rules worth remembering [#rules-worth-remembering] * [`move-lock`](/docs/tx/move-lock) to another hotkey **owned by a different coldkey** resets conviction to zero; same-owner moves preserve it. * Subnet **deregistration** deletes lock records — conviction is lost even if stake is paid out through the pool. * A **coldkey swap** fails if the destination has active locked mass on any subnet. For staking mechanics beyond locks, see the [Staking guide](/docs/guides/staking). # Signing with a browser extension (/docs/guides/extension-signing) Every transaction command can hand signing off to a wallet browser extension instead of a local keyfile. Pass `--signer extension` and the CLI asks the extension to sign; the private key never leaves the browser, so nothing on the machine running `btcli` — no keyfile, no password, no mnemonic — can move your funds. Any extension that speaks the standard Polkadot dapp interface works: [Talisman](https://talisman.xyz), [Polkadot.js extension](https://polkadot.js.org/extension/), SubWallet, and friends. The account must be **sr25519** or **ed25519** — Ethereum-style (ecdsa) accounts are refused, since the chain can't verify their signatures on Substrate extrinsics. ## Setup [#setup] 1. Install Talisman or the Polkadot.js extension in your browser. 2. Create or import an account in the extension (any Substrate account works; Bittensor uses SS58 prefix 42, so addresses render starting with "5"). 3. There is no step 3 — the first `--signer extension` command walks you through authorization. ## Sign a transaction [#sign-a-transaction] Add `--signer extension` to any [transaction](/docs/tx): ```bash btcli tx transfer --dest 5F... --amount-tao 1 --signer extension ``` What happens: 1. The CLI starts a local **bridge** (a small daemon on `127.0.0.1:39295`) and opens its page in your browser. 2. The extension pops up asking to let "Bittensor SDK" access your accounts — approve it. This authorization is remembered by the extension, but the popup appears on the first run. 3. Back in the terminal, pick the signing account from the list of accounts the extension exposes — first run only; afterwards the choice is reused silently. 4. The transaction summary is printed and the extension pops up with the same transaction to review. The popup **is** the confirmation — there is no separate terminal prompt to bounce back for. Approve it there, and the CLI submits the signed extrinsic and reports the result (`--dry-run` still previews without signing anything). Keep the bridge tab open while the command runs. Each command starts a fresh bridge session, so a stale tab from an earlier run cannot sign anything. ## Choosing the account [#choosing-the-account] With several accounts authorized, the CLI prompts with a numbered picker once and remembers your choice (as `signer_address` in the config); later commands reuse it without asking, going straight to the extension popup. To pick a different account: ```bash btcli tx transfer ... --signer extension --signer-address 5F... # pin one account btcli tx transfer ... --signer extension --extension-source talisman # filter by extension ``` Non-interactive sessions (scripts, `--json`) must pin the account with `--signer-address` (or `BT_SIGNER_ADDRESS`) — with several accounts and no terminal to ask on, the command fails rather than guessing. The picked account is the signing key for whatever role the intent declares: for coldkey-signed intents (staking, transfers) pick the account holding your coldkey; for hotkey-signed intents (weights, serving) pick the account holding the hotkey. ## Options [#options] | Option | Env | Config key | Meaning | | --------------------- | ---------------------- | ------------------- | ----------------------------------------------------------------------- | | `--signer` | | | `wallet` (default) or `extension` | | `--signer-address` | `BT_SIGNER_ADDRESS` | `signer_address` | ss58 of the extension account to sign with | | `--extension-source` | | | only offer accounts from one extension (`talisman`, `polkadot-js`, ...) | | `--extension-browser` | `BT_EXTENSION_BROWSER` | `extension_browser` | browser for the bridge page (`firefox`, `chrome`, or an app name) | | `--extension-bridge` | `BT_EXTENSION_BRIDGE` | | bridge URL, if not the default local one | Persist the browser choice with `btcli config set extension_browser firefox` if your default browser isn't the one holding the extension. ## The bridge [#the-bridge] The bridge is what connects two worlds that can't talk directly: a wallet extension only exposes itself to web pages, and the CLI is not a web page. So the CLI serves one — a local page that connects to the extensions in your browser — and talks to it over a WebSocket. Signing requests flow CLI → bridge → extension, and only signatures flow back; key material never crosses. It is safe by construction: the bridge binds to localhost only, Python clients must present a token written to `~/.bittensor/extension_bridge.token` (readable only by your user), and the page URL is scoped to one session so a leftover tab from an earlier run cannot reconnect. The bridge page shows every connected extension (name, version, account count), and each has a toggle: switch an extension off to hide its accounts from btcli entirely — useful when several wallets are installed and only one should ever sign. The choice is remembered across sessions, and during signing the page names the extension the popup belongs to ("Approve in talisman"). `--extension-source` does the same filtering from the CLI side for one command. The bridge starts automatically with `--signer extension`; the `btcli extension` group manages it when needed: ```bash btcli extension accounts # list accounts the extensions expose (starts the bridge) btcli extension stop # stop the background bridge daemon btcli extension bridge # run the bridge in the foreground (rarely needed) ``` ## Limitations [#limitations] * **No MEV shielding.** Shielding wraps the signed call in a second encrypted extrinsic, which this flow can't produce. Stake-trading commands (which shield by default) print a notice and submit unshielded; an explicitly requested `--mev-shield` fails instead. * **Substrate accounts only** — sr25519 or ed25519, not ecdsa/Ethereum. * `btcli wallet sign` (raw message signing) still uses local keys. ## Python [#python] The same signer is available to the SDK. An `ExtensionSigner` stands in for the wallet in `plan` and `execute`: ```python import bittensor as bt from bittensor.extension import connect_extension_signer, ensure_bridge url = await ensure_bridge() # starts the bridge, opens the browser flow signer = await connect_extension_signer(url) # prompts for the account on the terminal async with bt.Subtensor() as client: intent = bt.Transfer(dest_ss58="5F...", amount_tao=1.0) result = await client.execute(intent, signer) # extension popup asks to sign await signer.close() ``` `connect_extension_signer` takes `address=`, `source=`, and `name=` filters to select the account programmatically instead of prompting. For signers this bridge doesn't cover (air-gapped devices, custom HSMs), `client.prepare_call` returns the exact payload bytes to sign plus the Polkadot-JS `SignerPayloadJSON`, and `client.submit_signature` submits the externally produced signature. # Signing with a Ledger (/docs/guides/ledger) Every transaction command can sign on a Ledger device instead of a local keyfile. Pass `--ledger` and the CLI ships the transaction to the device over USB; you review the decoded call on the Ledger's own screen and approve it there. The private key never exists on the computer, and there is no blind-signing mode — the device refuses anything it cannot display. This works through the [Polkadot generic app](https://support.ledger.com/article/360016289919-zd) and [RFC-0078 merkleized metadata](https://polkadot-fellows.github.io/RFCs/approved/0078-merkleized-metadata.html): alongside each transaction, the CLI sends a compact cryptographic proof of the runtime types the transaction touches. The device checks the proof against a metadata digest that is *also signed into the transaction* (the `CheckMetadataHash` extension), and the chain verifies the same digest on its side. If the proof, the device, and the chain disagree about what the transaction means, the signature is rejected — nothing can show you "transfer 1 TAO" while signing something else. ## Setup [#setup] 1. Install the **Polkadot** app (the "generic" app, not a legacy chain-specific one) on the device through Ledger Live. Version 100.0.5 or later. 2. Connect the device, unlock it, and open the Polkadot app. 3. There is no step 3 — the account lives on the device. The device derives the standard Polkadot path `m/44'/354'/account'/0'/index'` with ed25519 keys — the same addresses Ledger Live, Nova, Talisman, and SubWallet show for the device, rendered with Bittensor's SS58 prefix. ## Sign a transaction [#sign-a-transaction] Add `--ledger` to any [transaction](/docs/tx): ```bash btcli tx transfer --dest 5F... --amount-tao 1 --ledger ``` What happens: 1. The CLI connects to the device and prints the Ledger account's address. 2. The transaction is prepared exactly as usual (same fees, same preview, same confirmation prompt). 3. The device shows the decoded transaction — pallet, call, destination, amount — on its screen. Review and approve with the buttons. 4. The CLI submits the signed extrinsic and reports inclusion as usual. Different accounts on the same device are selected with the derivation path options: ```bash btcli tx transfer --dest 5F... --amount-tao 1 --ledger --ledger-account 1 ``` `--ledger` is shorthand for `--signer ledger`; both forms work anywhere `--signer extension` does. ## From the SDK [#from-the-sdk] `LedgerSigner` satisfies the SDK's `Signer` protocol and plugs into every signing path a wallet does: ```python import bittensor signer = bittensor.LedgerSigner() # account 0, index 0 print(signer.ss58_address) # the on-device account print(signer.confirm_address()) # re-derive with on-screen display async with bittensor.Subtensor() as client: result = await client.execute( bittensor.Transfer(dest_ss58="5F...", amount_tao=bittensor.tao(1)), signer, ) ``` The signer implements the transport's clear-signing capabilities: it computes the RFC-0078 metadata digest for the exact runtime the payload targets (`metadata_digest`), and signs prepared extrinsics with the type proof shipped to the device (`sign_unsigned_extrinsic`). Raw `sign(bytes)` calls are refused by design — the generic app does not blind-sign. ## Platform notes [#platform-notes] * **macOS**: works out of the box with the published wheels. * **Linux**: works out of the box with the published wheels too. The device itself must be accessible from userspace, which usually means installing the standard udev rules for Ledger devices ([ledger-udev-rules](https://github.com/LedgerHQ/udev-rules)) or running as a user in the appropriate group. * **Windows**: the SDK is WSL-only (no native wheels are published). Under WSL 2 the Linux wheel applies; attach the device to the WSL VM with [usbipd-win](https://github.com/dorssel/usbipd-win) and set up the udev rules as on Linux. ## Troubleshooting [#troubleshooting] * **"no Ledger device found"** — check the cable, unlock the device, and open the Polkadot app. On Linux, check the udev rules. * **"instruction not supported"** — a different app is open on the device; switch to the Polkadot app. * **"the request was rejected on the device"** — the transaction was declined on-screen (or timed out waiting). * **The device shows "Blind signing must be enabled"** — you are in a legacy chain-specific app; install and open the *Polkadot* (generic) app instead. No blind-signing toggle is needed with the generic app. # Local development (/docs/guides/local-development) A local chain is a private, isolated subtensor: you get sudo, a pre-funded dev account, and (by default) 0.25-second blocks — the fastest way to test transactions, subnet mechanics, or SDK code without spending real TAO. ## Start a localnet with Docker [#start-a-localnet-with-docker] ```bash docker run --rm --name local_chain \ -p 9944:9944 -p 9945:9945 \ ghcr.io/raofoundation/subtensor-localnet:devnet ``` Append `False` to the command to run real 12-second blocks instead of the default fast-blocks mode (0.25 s). Choose the image tag for the code you need to test: | Tag | Source | | ----------------- | -------------------------------------------------------------- | | `devnet` | Exact commit deployed to devnet (the `devnet` mirror branch) | | `testnet` | Exact commit deployed to testnet (the `testnet` mirror branch) | | `main` / `latest` | Current `main` branch candidate | | `sha-` | Immutable commit build for reproducible CI | | `v` | Immutable mainnet release build | The three branch tags are deliberately independent because a candidate moves through the networks in stages. The release train updates `devnet` or `testnet` only after verifying the new runtime on that live network; those mirror pushes publish the corresponding image tags. Re-pull a moving tag before testing changes that landed since your last run. To keep chain state, mount `/tmp` and pass `--no-purge` when starting a new container from that volume: ```bash docker volume create subtensor-localnet-data docker run --rm --name local_chain \ -p 9944:9944 -p 9945:9945 \ -v subtensor-localnet-data:/tmp \ ghcr.io/raofoundation/subtensor-localnet:devnet True --no-purge ``` A fresh localnet ships with subnet 0 (root) and subnet 1 already created. ## Or build from source [#or-build-from-source] For custom runtime flags or chain modifications, build the node yourself: ```bash git clone https://github.com/RaoFoundation/subtensor.git cd subtensor ./scripts/init.sh # rust toolchain update + wasm target ./scripts/localnet.sh # build, purge state, launch in fast-blocks mode ``` `localnet.sh` accepts `False` (12-second blocks), `--no-purge` (keep existing chain state), and `--build-only` (compile and generate the chainspec without starting the node). On macOS, two prerequisites before building: Apple Silicon needs Rosetta (`softwareupdate --install-rosetta`), and OpenSSL must be installed via Homebrew. `localnet.sh` builds with the `pow-faucet` cargo feature (the `FEATURES` line in the script), which enables the [`faucet`](/code/pallets/subtensor/src/macros/dispatches.rs#L1028-L1039) extrinsic — disabled on mainnet — so you can mint test TAO to any coldkey with a small proof-of-work. The grant is 1,000 TAO per call, a hardcoded constant in the registration pallet ([`pallets/subtensor/src/subnets/registration.rs`](/code/pallets/subtensor/src/subnets/registration.rs)) you can edit for more. ## Connect [#connect] The network name `local` resolves to `ws://127.0.0.1:9944`. A quick connectivity check that also tells you which block mode the chain runs ([`is-fast-blocks`](/docs/query/is-fast-blocks)): ```bash btcli query is-fast-blocks --network local ``` ```python import bittensor as bt async with bt.Subtensor("local") as client: print(await client.block()) ``` Set `BT_CHAIN_ENDPOINT` to point `local` at a different endpoint — a source-built localnet typically listens on `ws://127.0.0.1:9945` rather than 9944\. Any `ws://` URL also works directly as the `--network` value. ## The Alice dev account [#the-alice-dev-account] Every localnet pre-funds the well-known dev account **Alice** (`5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY`) with 1,000,000 TAO. Her key is derived from the standard Substrate dev URI `//Alice`, which corresponds to a publicly known seed. Materialize her as a local wallet with that seed: ```bash btcli wallet regen-coldkey -w alice --no-password \ --seed 0xe5be9a5092b81bca64be81d212e7f2f9eba183bb7a90954f7b76361f6edb5c0a ``` (The seed is public — it works only because localnet genesis funds this address. Never reuse dev keys outside a local chain.) In Python, any SDK call that takes a wallet also accepts a raw keypair, so you can skip wallet files entirely: ```python from bittensor.keyfiles import Keypair import bittensor as bt alice = Keypair.create_from_uri("//Alice") async with bt.Subtensor("local") as client: result = await client.execute( bt.Transfer(dest_ss58="5F...", amount_tao=1000), alice ) ``` ## A typical development loop [#a-typical-development-loop] Create a wallet per role, fund each from Alice, then walk the subnet lifecycle: ```bash btcli wallet create -w owner -H default --no-password btcli wallet create -w validator -H default --no-password btcli wallet create -w miner -H default --no-password btcli tx transfer --dest owner --amount-tao 2000 -w alice --network local -y btcli tx transfer --dest validator --amount-tao 100 -w alice --network local -y btcli tx transfer --dest miner --amount-tao 100 -w alice --network local -y btcli tx register-subnet -w owner --network local # creates netuid 2 btcli tx start-call --netuid 2 -w owner --network local # activate emissions btcli tx burned-register --netuid 2 -w validator --network local btcli tx burned-register --netuid 2 -w miner --network local btcli query metagraph --netuid 2 --network local --json ``` See [`register-subnet`](/docs/tx/register-subnet), [`start-call`](/docs/tx/start-call), [`burned-register`](/docs/tx/burned-register), and [`metagraph`](/docs/query/metagraph) for the details of each step. **Fast-blocks caveat:** anything the chain measures in blocks — rate limits, immunity periods, activation delays, epoch tempo — elapses 48× faster than mainnet wall-clock time. If you are testing timing behavior, run the localnet with `False` (12-second blocks) instead. ## Serving real traffic: run your own node [#serving-real-traffic-run-your-own-node] Public OTF endpoints are rate-limited (roughly 1 request per second per IP). For serious validator, miner, or indexer workloads against mainnet or testnet, run your own node — see [Running a node](/docs/guides/running-a-node) for lite vs archive, Docker and source setups, and operations. # Multisig wallets (/docs/guides/multisig) 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 [#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 [#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 [#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 [#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 bt async with bt.Subtensor("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 [#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 [#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 [#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 [#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 [#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 [#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 [#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 [#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` [#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-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 bt from bittensor.wallet import Wallet async with bt.Subtensor("test") as client: ms = await client.multisig( ["5Grw...alice", "5FHn...bob", "5DAA...carol"], threshold=2, ) call = bt.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 = bt.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) ``` `bt.MultisigApprove`, `bt.MultisigCancel`, and `bt.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 [#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 [#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. # Proxy accounts (/docs/guides/proxies) 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 [#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 ([`proxy_type_filter`](/code/runtime/src/proxy_filters/mod.rs#L108-L129)) before dispatching it *as the real account*. Bookkeeping is paid with reserved deposits (returned on removal), not fees. ### Proxy types [#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 stake transfers (`transfer_stake` / `transfer_stake_and_hotkey`) 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`](/code/pallets/subtensor/src/macros/dispatches.rs#L1937-L1951). Its allowlist does *not* include [`Utility.batch_all`](/code/pallets/utility/src/lib.rs#L314-L362), so batched staking through a Staking proxy fails with `CallFiltered` — use NonTransfer for batched staking. * **Registration** — neuron registration ([`burned_register`](/code/pallets/subtensor/src/macros/dispatches.rs#L826-L832), [`register`](/code/pallets/subtensor/src/macros/dispatches.rs#L804-L814), [`register_limit`](/code/pallets/subtensor/src/macros/dispatches.rs#L2242-L2249)). * **ChildKeys** — [`set_children`](/code/pallets/subtensor/src/macros/dispatches.rs#L1093-L1101) and [`set_childkey_take`](/code/pallets/subtensor/src/macros/dispatches.rs#L935-L945); **RootClaim** — [`claim_root`](/code/pallets/subtensor/src/macros/dispatches.rs#L1910-L1926) 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 [#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 [#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 [#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 bt async with bt.Subtensor("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 [#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 bt.Subtensor("test") as client: intent = bt.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`](/code/pallets/proxy/src/lib.rs#L785) / `CallFiltered` — the runtime filter, not the CLI, is what enforces the scope. ## Step 4 — delayed proxies: announce, wait, execute, veto [#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 bt.Subtensor("test") as client: call = bt.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( bt.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 `bt.ExecuteProxyAnnounced` in Python). Executing before the delay elapses fails with [`Unannounced`](/code/pallets/proxy/src/lib.rs#L791). **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 bt.Subtensor("test") as client: for delegate, value in await client.query_map(bt.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 [#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 [#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 [#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 [#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 [#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 [#troubleshooting] * [`NotProxy`](/code/pallets/proxy/src/lib.rs#L783) — 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`](/code/pallets/proxy/src/lib.rs#L787) — a delegation with the same (delegate, type, delay) already exists. * [`TooMany`](/code/pallets/proxy/src/lib.rs#L779) — over the 20-proxy or 75-announcement cap; remove unused entries. # Running a node (/docs/guides/running-a-node) Public endpoints are rate-limited (roughly 1 request per second per IP) and shared with everyone else. For serious validator, miner, or indexer workloads you want your own node: a private, low-latency endpoint whose limits you control. This guide covers running one against **mainnet (finney)** or **testnet** — for a private development chain, see [Local development](/docs/guides/local-development). ## Lite or archive [#lite-or-archive] Decide this first; it determines disk and sync time. * **Lite node** — warp-syncs to the head of the chain in minutes and keeps only recent state (roughly the last 300 blocks). \~128 GB of disk. This is what validators, miners, and most applications need. * **Archive node** — full-syncs every block from genesis and prunes nothing. At least \~3.5 TB of disk and growing; the initial sync takes days. You need one only to query state older than the recent window — block-pinned reads, historical snapshots, indexers. If you only occasionally need history, the public archive endpoint (`wss://archive.chain.opentensor.ai:443`) may be enough — see [The network](/docs/concepts/network) for all public endpoints. ## Requirements [#requirements] * x86\_64 Linux or arm64 (both are published Docker platforms; the binary also builds on Apple Silicon macOS). * 4+ CPU cores and 16 GB RAM as a floor; the repo's compose file allocates 4 CPUs and a 40 GB memory limit per node. Fast NVMe storage matters more than CPU. * Disk as above: \~128 GB lite, \~3.5 TB+ archive. Three ports are involved: | Port | What | Exposure | | ----- | ---------------------- | ------------------------------------------------------------------------ | | 30333 | p2p socket | Must accept inbound connections from the internet. | | 9944 | RPC (WebSocket + HTTP) | Keep firewalled to localhost unless you are deliberately serving others. | | 9933 | legacy RPC | Unused; modern nodes serve everything on 9944. | ## Run with Docker [#run-with-docker] Release images are published to `ghcr.io/raofoundation/subtensor` for linux/amd64 and linux/arm64 on every runtime release; `:latest` tracks the newest release. The repo's [`docker-compose.yml`](https://github.com/RaoFoundation/subtensor/blob/main/docker-compose.yml) defines one service per node flavor — `mainnet-lite`, `mainnet-archive`, `testnet-lite`, `testnet-archive` — each with a named volume for chain state: ```bash git clone https://github.com/RaoFoundation/subtensor.git cd subtensor docker compose up -d mainnet-lite ``` Follow the sync: ```bash docker compose logs -f mainnet-lite ``` A lite node warp-syncs in minutes; you will see `Warp sync is complete` followed by imported blocks at the chain head. Chain state lives in the service's named volume (`mainnet-lite-volume`), so `docker compose down` and `up` resume where they left off; add `--volumes` to `down` only if you want to resync from scratch. Swap the service name for `mainnet-archive`, `testnet-lite`, or `testnet-archive` for the other flavors. To build the image yourself instead of pulling, uncomment the `build:` block in the compose file — it builds the `subtensor` target of the repo [`Dockerfile`](https://github.com/RaoFoundation/subtensor/blob/main/Dockerfile). The container starts as root only to fix ownership of the data directory, then drops to the unprivileged `subtensor` user (UID 10001) via `gosu` — see `scripts/docker_entrypoint.sh`. ## Run from source [#run-from-source] Build the production binary — the same profile and features the release images ship: ```bash git clone https://github.com/RaoFoundation/subtensor.git cd subtensor ./scripts/init.sh # rust toolchain + wasm target cargo build -p node-subtensor --profile production --features metadata-hash ``` (Build prerequisites per OS are in [Rust setup](/docs/internals/rust-setup).) Then run it with the same flags the compose services use. Mainnet lite: ```bash ./target/production/node-subtensor \ --base-path /var/lib/subtensor \ --chain ./chainspecs/raw_spec_finney.json \ --sync warp \ --database paritydb \ --db-cache 4096 \ --trie-cache-size 2048 \ --rpc-cors all \ --no-mdns \ --bootnodes /dns/bootnode.finney.chain.opentensor.ai/tcp/30333/ws/p2p/12D3KooWRwbMb85RWnT8DSXSYMWQtuDwh4LJzndoRrTDotTR5gDC ``` For an **archive node**, replace `--sync warp --database paritydb --db-cache 4096 --trie-cache-size 2048` with `--pruning archive`. For **testnet**, use `--chain ./chainspecs/raw_spec_testfinney.json` and the testnet bootnode `/dns/bootnode.test.finney.opentensor.ai/tcp/30333/p2p/12D3KooWPM4mLcKJGtyVtkggqdG84zWrd7Rij6PGQDoijh1X86Vr`. Two flags the compose file passes that you should add deliberately, not by default: * `--rpc-external` — listen for RPC on all interfaces instead of localhost. The containers need it (localhost inside a container is unreachable from outside); on a host it exposes your RPC port to whatever your firewall allows. * `--rpc-cors all` — required if browsers will talk to the node; harmless otherwise. `node-subtensor --help` lists every flag; useful ones include `--name` (the node's telemetry/network name), `--rpc-max-connections`, `--rpc-rate-limit`, and `--log =`. ### As a systemd service [#as-a-systemd-service] ```ini [Unit] Description=subtensor node After=network.target [Service] User=subtensor ExecStart=/usr/local/bin/node-subtensor \ --base-path /var/lib/subtensor \ --chain /opt/subtensor/chainspecs/raw_spec_finney.json \ --sync warp --database paritydb \ --db-cache 4096 --trie-cache-size 2048 \ --rpc-cors all --no-mdns \ --bootnodes /dns/bootnode.finney.chain.opentensor.ai/tcp/30333/ws/p2p/12D3KooWRwbMb85RWnT8DSXSYMWQtuDwh4LJzndoRrTDotTR5gDC Restart=on-failure LimitNOFILE=65536 [Install] WantedBy=multi-user.target ``` Copy the binary and the `chainspecs/` directory to the paths above, create the `subtensor` user and data directory, then `systemctl enable --now subtensor`. ## Verify and use it [#verify-and-use-it] Port 9944 serves both WebSocket and HTTP JSON-RPC. Check health directly: ```bash curl -s -H 'Content-Type: application/json' \ -d '{"id":1,"jsonrpc":"2.0","method":"system_health","params":[]}' \ http://127.0.0.1:9944 ``` `"isSyncing":false` with a healthy peer count means you are at the chain head. Point the SDK and CLI at your node by passing the endpoint as the network: ```bash btcli query block-info --block 0 --network ws://127.0.0.1:9944 ``` ```python import bittensor as bt async with bt.Subtensor("ws://127.0.0.1:9944") as client: print(await client.block()) ``` Or set `BT_CHAIN_ENDPOINT=ws://127.0.0.1:9944` once and keep using named networks. The node also exposes Prometheus metrics on port 9615 (localhost-only unless you pass `--prometheus-external`). ## Upgrades [#upgrades] **Runtime** upgrades are on-chain wasm: your node applies them automatically at the upgrade block, no restart needed. The **node binary** still needs occasional updates for host-side changes (networking, RPC, database). New binaries ship with each release on the [release train](/docs/internals/release-process): ```bash # Docker docker compose pull && docker compose up -d mainnet-lite # Source git pull && cargo build -p node-subtensor --profile production --features metadata-hash ``` Chain state in the volume or `--base-path` survives the swap. Run an outdated binary long enough and it will eventually fail to follow the chain — track releases on the [repo](https://github.com/RaoFoundation/subtensor/releases). # Signed requests (/docs/guides/signed-requests) Validators and miners talk to each other over plain HTTP; the chain only stores where to find each other (see [Mining](/docs/guides/mining)). What those requests need is **identity**: proof that a request came from a specific hotkey, was addressed to yours, covers exactly the bytes received, and is recent. `bittensor.http_auth` provides that as two pure functions — `sign` produces headers, `verify` checks them — with no HTTP server or client attached. Bring your own framework; recipes for FastAPI and httpx are below. ```python import bittensor as bt # client (e.g. a validator querying a miner) headers = bt.http_auth.sign( wallet, method="POST", path="/generate", body=body_bytes, receiver_ss58=miner_hotkey, ) # server (e.g. the miner) caller = bt.http_auth.verify( request_headers, body_bytes, method="POST", path="/generate", self_hotkey_ss58=my_hotkey, ) caller.hotkey_ss58 # the authenticated sender — score it, rate-limit it, gate it ``` `verify` raises an `AuthError` subclass on failure — `MalformedAuth`, `WrongReceiver`, `StaleRequest`, `ReplayedRequest`, or `BadSignature` — with messages safe to return to the unauthenticated caller. ## The wire format (btauth/1) [#the-wire-format-btauth1] This section is normative; everything needed to implement the protocol in any language is here and in the [test vectors](#golden-test-vectors). A signed request carries these headers (names are case-insensitive, values exact): | Header | Value | | ----------------------- | -------------------------------------------- | | `X-Bittensor-Version` | `1` | | `X-Bittensor-Hotkey` | sender hotkey, ss58 | | `X-Bittensor-Receiver` | receiver hotkey, ss58 (optional — see below) | | `X-Bittensor-Nonce` | unix time in **nanoseconds**, decimal string | | `X-Bittensor-Crypto` | `ed25519` — omitted entirely for sr25519 | | `X-Bittensor-Signature` | `0x` + lowercase hex signature | The signature is over the UTF-8 bytes of exactly eight lines joined by `\n` (no trailing newline): ``` btauth/1 ``` The request target is the **percent-encoded (wire) form** on both sides — `/a%20b?q=1`, never the decoded `/a b`. Server frameworks usually hand you the decoded path, so rebuild the target from the raw/encoded value (see the FastAPI recipe below). Signing the method, path, and body hash means a captured signature cannot be replayed against a different route or payload; signing the receiver means a malicious miner cannot proxy a validator's request to another miner. sr25519 signatures use the substrate signing context (`b"substrate"` in schnorrkel) — the same as chain extrinsics; ed25519 is plain RFC 8032. Signatures must be over the exact payload bytes, never a wrapped or re-encoded form. Verification, in order: 1. **Shape** — all required headers present and parseable, version is `1`, the scheme (if present) is known. 2. **Receiver** — the `X-Bittensor-Receiver` value must equal the server's hotkey. Servers reject unbound requests by default (`require_receiver=False` opts out). 3. **Freshness** — the nonce must be within `max_age` seconds in the past (default 10) and `allowed_skew` seconds in the future (default 2). 4. **Signature** — rebuild the payload from the *received* method, path, and body (always recompute the body hash), decode the sender's ss58, and verify under the declared scheme **only**. Never fall back to trying the other scheme: sr25519 and ed25519 public keys are both 32-byte curve25519-family points, and try-both verification opens an algorithm-confusion surface. The scheme inside the signed payload closes it. 5. **Replay** — the `(hotkey, nonce)` pair must not have been accepted before within the window. The store is consulted only after the signature verifies, so unauthenticated traffic cannot poison it. The default is an in-process `InMemoryNonceStore`; a multi-process server should supply a `NonceStore` backed by shared storage (`SET NX EX` in Redis is exactly this shape). Clients sign a fresh nonce for every attempt, including retries of an identical body. ## Serving: FastAPI [#serving-fastapi] The one rule when wiring `verify` into any framework: hash the **raw received bytes**, before anything parses or re-serializes the body. A server that re-encodes parsed JSON (key order, whitespace) will compute a different hash and reject valid requests. ```python import bittensor as bt from fastapi import Depends, FastAPI, HTTPException, Request MY_HOTKEY = bt.Wallet(name="my_coldkey", hotkey="my_hotkey").hotkey.ss58_address app = FastAPI() async def authenticated(request: Request) -> bt.http_auth.Caller: body = await request.body() # raw bytes, cached by Starlette # Rebuild the request target in its wire (percent-encoded) form. ASGI's # `path` is percent-decoded — using it would break signatures on any # encoded path — so take `raw_path` from the scope instead. target = request.scope["raw_path"].decode() if request.scope["query_string"]: target += "?" + request.scope["query_string"].decode() try: return bt.http_auth.verify( request.headers, body, method=request.method, path=target, self_hotkey_ss58=MY_HOTKEY, ) except bt.http_auth.AuthError as e: raise HTTPException(status_code=401, detail=str(e)) @app.post("/generate") async def generate(request: Request, caller: bt.http_auth.Caller = Depends(authenticated)): payload = await request.json() # parse only after verification ... ``` Authentication says *who*; whether they may call you is your policy. The old Axon `blacklist_fn` was almost always "is this hotkey a validator on my subnet with real stake" — that's a metagraph lookup: ```python mg = await client.subnets.metagraph(netuid) # refresh periodically def allowed(caller: bt.http_auth.Caller) -> bool: n = mg.by_hotkey(caller.hotkey_ss58) return n is not None and n.validator_permit and n.tao_stake >= bt.tao(1000) ``` Return 403 for a verified-but-unwelcome caller, and use `caller.hotkey_ss58` as the key for any rate limiting or prioritization. ## Querying: httpx [#querying-httpx] Discovery comes from the metagraph — each neuron's served endpoint is `n.axon` (`"ip:port"` or `None`, published via [`serve-axon`](/docs/tx/serve-axon)). An `httpx.Auth` signs each request as it is sent (both sides use the percent-encoded request target, matching the FastAPI recipe above). Application-level retries through `auth_flow` get a fresh nonce; note that transport-level connection retries resend the already-signed request and will be rejected as replays — retry at the application level: ```python import bittensor as bt import httpx class HotkeyAuth(httpx.Auth): requires_request_body = True def __init__(self, wallet, receiver_ss58: str): self.wallet, self.receiver = wallet, receiver_ss58 def auth_flow(self, request): request.headers.update(bt.http_auth.sign( self.wallet, method=request.method, path=request.url.raw_path.decode(), # path + query, as sent body=request.content, receiver_ss58=self.receiver, )) yield request async def query_miner(wallet, miner: bt.MetagraphNeuron, body: bytes): async with httpx.AsyncClient(base_url=f"http://{miner.axon}") as http: r = await http.post( "/generate", content=body, auth=HotkeyAuth(wallet, receiver_ss58=miner.hotkey), ) r.raise_for_status() return r.json() ``` Fan-out to many miners is `asyncio.gather` over `query_miner` calls; there is nothing protocol-specific about it. ## Composing with timelock (drand) [#composing-with-timelock-drand] `sign` covers raw bytes without caring what they are, so [timelock](/docs/guides/timelock)-encrypted bodies compose with no protocol changes. Two patterns subnets use: * **Synchronized challenges.** A validator seals a challenge with `timelock.encrypt(challenge, reveal_in="30s")` and fans it out signed. Every miner holds the ciphertext early; none can open it before the beacon round — no head start for better network placement, and challenge batches can be distributed ahead of time. * **Copy-proof responses.** A miner returns its answer timelocked to a short reveal. The validator provably received it (the response is in hand) but cannot read it until the round opens — so a colluding validator cannot leak one miner's answer to another before scoring. This is the chain's commit-reveal-weights mechanism applied to the data plane, on the same beacon. The header names `X-Bittensor-Drand-Round` and `X-Bittensor-Drand-Randomness` are **reserved** for a future optional extension that anchors freshness to the drand beacon instead of the sender's clock (proving a request was signed *after* a beacon round existed, which a timestamp cannot). They are not part of btauth/1; do not use them for other purposes. ## Cross-language implementations [#cross-language-implementations] The format is deliberately buildable from any language's standard substrate tooling: * **sha256** is in every standard library (this is why it was chosen over blake2b). * **ss58 decode** and **sr25519 verify** ship in every Polkadot-ecosystem toolkit: `sp-core`/`schnorrkel` (Rust), `@polkadot/util-crypto` + `@polkadot/wasm-crypto` (JS/TS), `go-schnorrkel` (Go), `bittensor.sp_core` (this SDK). sr25519 verification **must** use the substrate signing context (`b"substrate"`); most libraries default to it, but a mismatch produces silent verification failures. * **ed25519** is plain RFC 8032 — Go stdlib, PyNaCl, noble-ed25519, ring. * Sign the exact payload bytes. (One asymmetry to know about: this SDK's verifier, via `sp_core`, additionally tolerates signatures over the ``-wrapped payload for Polkadot-JS `signRaw` compatibility. Other implementations are not required to accept the wrapped form — and must never produce it.) * **JavaScript**: the nanosecond nonce exceeds `Number.MAX_SAFE_INTEGER`. Treat it as a string or `BigInt` end to end; a `parseInt` round-trip corrupts the payload you reconstruct. * Everywhere: hash the raw received bytes before any body parsing, and build the payload byte-for-byte as specified — no trailing newline, lowercase hex, uppercase method. ### Golden test vectors [#golden-test-vectors] Fixtures: sender `//Alice`, receiver `//Bob` (sr25519: `5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty`), nonce `1752076800000000000`, body `{"prompt": "hello"}` (19 bytes, no trailing newline), request `POST /generate?stream=false`. `sha256(body)` = `341c57448e531310fbbe83f44cea2a5e838bd9e8a6b82b269f01d0dbbc23c3cc`. **sr25519** — `//Alice` is `5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY`. The exact signed payload: ``` btauth/1 sr25519 POST /generate?stream=false 341c57448e531310fbbe83f44cea2a5e838bd9e8a6b82b269f01d0dbbc23c3cc 1752076800000000000 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY 5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty ``` sr25519 signatures are randomized, so no fixed signature bytes exist: a conforming implementation signs this payload and verifies it against Alice's public key (and verifies any signature produced by another implementation). **ed25519** — deterministic, so the signature bytes are pinned. `//Alice` with ed25519 derivation is `5FA9nQDVg267DEd8m1ZypXLBnvN7SFxYwV7ndqSYGiN9TTpu`; the payload is identical except line 2 is `ed25519` and line 7 is that address. The signature must be exactly: ``` 0x1a6b631988bf1f7ea093ab9d5acf61f05c316d0b0e428b24b487b75a5f6ee0e1a56a26918fdf871b6cf09437e6588023a4ce423cf622621b2a7feee17612f704 ``` The Python `bt.http_auth.build_payload(...)` function produces these payload bytes directly, for pinning in tests. ## Accepting legacy (v10 SDK) callers [#accepting-legacy-v10-sdk-callers] During a subnet's transition, a miner on this SDK may still receive requests from validators on the v10 `Dendrite`. Its scheme signed the sr25519 message `f"{nonce}.{sender_hotkey}.{receiver_hotkey}.{uuid}.{body_hash}"` from the `bt_header_dendrite_*` headers, where `body_hash` is sha3-256 over the concatenated sha3-256 hashes of `str()` of the synapse's `required_hash_fields` — schema-dependent, so it cannot be verified generically. If you must accept it, recompute `body_hash` with your synapse's field rules and check: ```python from bittensor import sp_core def verify_v10(headers: dict, body_hash: str) -> str: h = {k.lower(): v for k, v in headers.items()} sender = h["bt_header_dendrite_hotkey"] message = ( f"{h['bt_header_dendrite_nonce']}.{sender}." f"{h['bt_header_axon_hotkey']}.{h['bt_header_dendrite_uuid']}.{body_hash}" ) sig = bytes.fromhex(h["bt_header_dendrite_signature"].removeprefix("0x")) if not sp_core.verify(message.encode(), sig, sender): raise PermissionError("bad v10 signature") return sender ``` Note what this legacy scheme does not cover — method, path, receiver-bound replay, unlisted fields — which is exactly why btauth/1 replaced it. Treat it as a bridge, enforce your own nonce freshness on top, and remove it once your validators have migrated. # Staking (/docs/guides/staking) 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. For how the pools themselves work — price impact, fees, MEV — see [staking and pools](/docs/concepts/staking-pools). ## Pick a validator [#pick-a-validator] ```bash btcli query delegates --json # all delegate hotkeys btcli query delegate --hotkey 5F... # one delegate's details btcli query delegate-take --hotkey 5F... # the fraction the delegate keeps ``` The **take** is the fraction of staking emissions the validator keeps before distributing the rest to its nominators. The chain default is 18% (stored as 11796/65535); a delegate can lower it any time with [`decrease-take`](/docs/tx/decrease-take), but raising it ([`increase-take`](/docs/tx/increase-take) / [`set-take`](/docs/tx/set-take)) is rate-limited to once per 216,000 blocks (\~30 days). Read the current value with [`delegate-take`](/docs/query/delegate-take). ## Stake and unstake [#stake-and-unstake] Quote first — staking is a swap, so large amounts incur slippage: ```bash btcli query quote-stake --netuid 1 --amount-tao 100 --json btcli tx add-stake --hotkey 5F... --netuid 1 --amount-tao 100 --dry-run btcli tx add-stake --hotkey 5F... --netuid 1 --amount-tao 100 -w my_coldkey ``` ```python intent = bt.AddStake(hotkey_ss58="5F...", netuid=1, amount_tao=100) await client.execute(intent, wallet) ``` Every stake operation must move at least the chain minimum — by default 2,000,000 rao (0.002 TAO), stored in [`MinStake`](/code/pallets/subtensor/src/lib.rs#L1047). The chain can also sweep nominations that fall below a nominator minimum (a governance-set fraction of the minimum stake; currently zero, so the sweep is effectively disabled). Exit with [`remove-stake`](/docs/tx/remove-stake), or exit everything at once with [`unstake-all`](/docs/tx/unstake-all). Quote the exit the same way you quote the entry: ```python quote = await client.prices.quote_unstake(netuid=1, amount_alpha=50) # TAO out, fee, slippage await client.execute(bt.RemoveStake(hotkey_ss58="5F...", netuid=1, amount_alpha="all"), wallet) ``` `"all"` resolves to the full position at build time — only the remove-stake variants accept it. To bound the execution price instead of accepting whatever the pool gives, use [`add-stake-limit`](/docs/tx/add-stake-limit) / [`remove-stake-limit`](/docs/tx/remove-stake-limit). Compute the limit from the spot price and your tolerance (see [how money moves](/docs/concepts/money) for the pool mechanics): ```python spot = await client.prices.alpha_price(netuid=1) intent = bt.AddStakeLimit( hotkey_ss58="5F...", netuid=1, amount_tao=100, limit_price_rao=int(spot["price_rao"] * 1.02), # accept at most 2% above spot allow_partial=False, ) await client.execute(intent, wallet) ``` There is no unbonding period: plain unstaking settles immediately, with the TAO spendable at once. The only time constraints in the staking system are conviction locks (opt-in, below) and the rate limits on child and take changes. ## Move positions without exiting [#move-positions-without-exiting] * [`move-stake`](/docs/tx/move-stake) — different hotkey (and optionally subnet). * [`swap-stake`](/docs/tx/swap-stake) — same hotkey, different subnet. * [`transfer-stake`](/docs/tx/transfer-stake) — different coldkey (gives the position away). Pass `--dest-hotkey` to land it on a different hotkey in the same call. ```python intent = bt.MoveStake(origin_hotkey_ss58="5F...", origin_netuid=1, dest_hotkey_ss58="5G...", dest_netuid=1, amount_alpha=25) await client.execute(intent, wallet) ``` Same-subnet moves just change which validator backs the stake; cross-subnet moves swap through both pools and can incur slippage on each leg. ## Inspect what you have [#inspect-what-you-have] ```bash btcli stake show --hotkey 5F... --netuid 1 btcli query stake-for-coldkey --coldkey my_coldkey --json # all positions btcli query stake-value-for-coldkey --coldkey my_coldkey --json # marked to TAO at spot ``` Spot valuation is `alpha × price`, excluding slippage and fees — use [`quote-unstake`](/docs/query/quote-unstake) for what you would actually receive. ## Conviction locks [#conviction-locks] [`lock-stake`](/docs/tx/lock-stake) commits alpha on a subnet as **locked mass** — a floor on unstaking that accrues **conviction** toward a target hotkey over time. Locked alpha keeps earning rewards; locking changes liquidity, not emissions. For a full walkthrough with interactive charts on a worked example subnet, see the [Conviction locks guide](/docs/guides/conviction). Quick reference: * One lock per coldkey per subnet; top-ups add mass, conviction continues. * **Perpetual** vs **decaying** modes — opt in with [`set-perpetual-lock`](/docs/tx/set-perpetual-lock). * The two timescales are governance-set storage values, not fixed constants. On mainnet today [`MaturityRate`](/code/pallets/subtensor/src/lib.rs#L1654) is 311,622 blocks (\~43 days) and [`UnlockRate`](/code/pallets/subtensor/src/lib.rs#L1658) is 934,866 blocks (\~130 days) — read them live before planning around either. * Aggregate conviction can trigger **subnet ownership** after one year when total conviction ≥ 10% of [`SubnetAlphaOut`](/code/pallets/subtensor/src/lib.rs#L1424). * Query with [`subnet-convictions`](/docs/query/subnet-convictions), [`hotkey-conviction`](/docs/query/hotkey-conviction), [`coldkey-lock`](/docs/query/coldkey-lock). ## Root stake and dividends [#root-stake-and-dividends] Stake on the root network earns dividends from across subnets. Each subnet's root dividends are split between TAO and alpha according to the root proportion of stake, and the alpha part accrues per subnet until claimed. Unlike subnet staking, root staking and unstaking is not a pool swap: no swap fee, no slippage, no MEV exposure — you move TAO in and out at face value. See [how money moves](/docs/concepts/money) for the swap mechanics that root stake skips. How the alpha pays out is your choice, set with [`set-root-claim-type`](/docs/tx/set-root-claim-type): **Swap** (the chain default) sells the alpha dividends into TAO on your root position, **Keep** restakes them as alpha on the subnet, and a per-subnet map mixes the two. Read your setting with [`root-claim-type`](/docs/query/root-claim-type). You normally don't need to claim at all: every block the chain picks a handful of staking coldkeys pseudo-randomly and processes any of their accrued dividends that exceed the per-subnet claim threshold (default 500,000 rao; settable by the subnet owner or root). Each account comes up automatically every few days, depending on how many staking coldkeys exist. Manual [`claim-root`](/docs/tx/claim-root) is optional — it just claims sooner and takes at most 5 subnets per call. Unclaimed dividends keep accruing — there is no deadline. For small positions the extrinsic fee of a manual claim can exceed the dividends it collects — let the automatic sweep handle small amounts. ## Child hotkeys [#child-hotkeys] A validator can delegate a fraction of its stake weight to **child hotkeys** per subnet with [`set-children`](/docs/tx/set-children). Two reasons to do this: confine a hotkey compromise to a single subnet (one child per subnet instead of one hotkey exposed everywhere), or lend stake weight to another operator without moving stake. The chain enforces: * At most **5 children per netuid**; a child can have multiple parents. Proportions cannot be zero, and any proportion not assigned to children stays with the parent. * Setting or revoking children is rate-limited to once per 150 blocks (\~30 minutes) per hotkey per subnet, and changes only take effect after a cooldown (default 7,200 blocks, \~24 hours) — see [`pending-children`](/docs/query/pending-children). * The child's take ([`set-childkey-take`](/docs/tx/set-childkey-take)) ranges 0–18% and defaults to 0; increases are rate-limited to once per 216,000 blocks (\~30 days). * The parent must hold a minimum total stake (the chain's [`StakeThreshold`](/code/pallets/subtensor/src/lib.rs#L2489), set by governance — read it live rather than assuming a value). Dividends settle back along the same links: a child hotkey's dividends are split among its parents in proportion to the stake weight each contributed (alpha plus TAO × [`TaoWeight`](/code/pallets/subtensor/src/lib.rs#L1197)), after deducting the childkey take. The deducted take is added to the child validator's own dividends — and since those distribute to the child's nominators like any validator dividends, most of the take flows onward to that validator's stakers rather than its operator. Because stake weight flows without infrastructure, a coldkey can act as a stakeless "validator": hold stake on its own hotkey and childkey-delegate the weight to operating validators, running no machines at all. Inspect relationships with [`children`](/docs/query/children) and [`parents`](/docs/query/parents). ## Automation [#automation] [`set-auto-stake`](/docs/tx/set-auto-stake) makes future rewards on a subnet stake themselves to a hotkey of your choice instead of accumulating unstaked. ## Security [#security] Two habits for meaningful position sizes: * **Bound the price and shield the submission.** These protect against different things: limit variants ([`add-stake-limit`](/docs/tx/add-stake-limit)) cap slippage however it arises, while [MEV-shielded submission](/docs/concepts/advanced#mev-shielded-submission) hides the transaction from front-runners until inclusion. Use both. * **Use delayed proxies, and monitor them.** A leaked zero-delay `Staking` proxy cannot transfer your balance, but it can still drain value by forcing repeated high-slippage stake/unstake round trips that counterparties profit from. Give staking proxies an announcement delay and watch for announcements you didn't make — see [proxies](/docs/concepts/advanced#proxy-keep-the-coldkey-offline). # Running a subnet (/docs/guides/subnets) A subnet owner defines an incentive mechanism: what miners must produce and how validators must score it. The chain side — creating the subnet, tuning its hyperparameters, publishing its identity — is all reachable through the SDK. The mechanism itself is code you write and run off-chain. ## Create [#create] [`register-subnet`](/docs/tx/register-subnet) creates a new subnet owned by your coldkey, with your wallet's hotkey as the subnet-owner hotkey: ```bash btcli query subnet-registration-cost --json # check the lock cost FIRST btcli tx register-subnet --dry-run -w my_coldkey btcli tx register-subnet -w my_coldkey ``` The cost **doubles** each time anyone registers a subnet, then decays linearly back over the lock-reduction interval (100,800 blocks, \~2 weeks), floored at [`NetworkMinLockCost`](/code/pallets/subtensor/src/lib.rs#L1765) — 1,000 TAO by default. It is only known at execution time, so a `Policy` spend cap blocks this call until raised; read the live price with [`subnet-registration-cost`](/docs/query/subnet-registration-cost). Subnet registrations are also chain-rate-limited to one per 7,200 blocks (\~1 day) network-wide. The lock is not a refundable deposit. The **entire** lock is transferred into the new subnet's pool as its initial TAO reserve (floored at `NetworkMinLockCost`); the matching alpha reserve is sized so the starting price equals the median alpha price across subnets. Nothing is recycled at registration. You get it back only by owning a subnet that earns. Identity can be set atomically at registration: the chain's [`register_network_with_identity`](/code/pallets/subtensor/src/macros/dispatches.rs#L1198-L1204) extrinsic takes an optional identity record. `register-subnet` wraps plain [`register_network`](/code/pallets/subtensor/src/macros/dispatches.rs#L1016-L1018), so the identity variant 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 [#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 [#activate] Once the chain's activation delay has passed, flip the subnet on with [`start-call`](/docs/tx/start-call) — owner-only, once per subnet: ```bash btcli tx start-call --netuid 42 -w my_coldkey ``` Check the schedule with [`subnet-start-schedule`](/docs/query/subnet-start-schedule). ### Turning a subnet on: active vs emission-enabled [#turning-a-subnet-on-active-vs-emission-enabled] "On" is two independent flags, held by two different parties: * **Want the subnet live — staking, alpha trading, epochs?** That is the owner's one-shot [`start-call`](/docs/tx/start-call) (btcli's `sudo start`), tracked by [`subnet_is_active`](/docs/hyperparameters/subnet-is-active). * **Want the subnet to receive its TAO emission share — pool injections and chain buys?** That is root's switch, [`subnet_emission_enabled`](/docs/hyperparameters/subnet-emission-enabled), flipped with `btcli tx set-subnet-emission-enabled`. New subnets register with it off, and owners cannot set it. A subnet can be fully active with emissions off: trading and epochs run, but no TAO flows into its pool until root flips the switch. Verify both flags: ```bash btcli sudo get --netuid 42 --name subnet_is_active btcli query subnet-emission-enabled --netuid 42 ``` ## The subnet economy [#the-subnet-economy] Each subnet is an automated market maker with two reserves: TAO in and alpha in. The alpha price is the pool's weighted reserve ratio — exactly TAO-in / alpha-in at the default 0.5/0.5 pool weights — and moves on every stake and unstake; emission injections shift the pool weights instead of the price. The subnet's share of network TAO emissions follows the exponential moving average of that price — stakers vote with their TAO — so a subnet's income is set by market demand for its token, not by governance. See [emissions](/docs/concepts/emissions) for the full mechanics. Each tempo the subnet's alpha emission splits three ways: 18% to the owner, \~41% to miners (incentive), \~41% to validators and their stakers (dividends). The owner cut can be auto-locked into a conviction position via the `owner_cut_auto_lock_enabled` hyperparameter instead of arriving as free stake; conviction locking and its unlock schedule are covered in the [staking guide](/docs/guides/staking). Three owner habits worth adopting. Decide your `registration_allowed` policy before activation — owners pause registration during mechanism upgrades so participants can update without eviction risk, and the toggle is root-gated here, so plan ahead. Run a validator on the owner hotkey: the owner cut and validation rewards land on the same key, and the 18% cut can be staked behind it. Do **not** mine on the owner hotkey — miner emission directed at owner hotkeys is burned or recycled, never paid. ## Tune hyperparameters [#tune-hyperparameters] Owner-settable hyperparameters go through [`set-hyperparameter`](/docs/tx/set-hyperparameter) (the equivalent of btcli's `sudo set`); read them back with [`subnet-hyperparameters`](/docs/query/subnet-hyperparameters): ```bash btcli query subnet-hyperparameters --netuid 42 --json btcli tx set-hyperparameter --netuid 42 --name commit_reveal_weights_enabled --value 1 -w my_coldkey ``` The important owner-settable parameters, with chain defaults: | Parameter | Default | What it does | | ------------------------------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `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). | | `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. | | `liquid_alpha_enabled` | false | Per-weight-pair bond smoothing between `alpha_low` and `alpha_high`. Only takes effect with `yuma3_enabled` on. | | `alpha_values` \* | 0.7 / 0.9 | The `alpha_low` / `alpha_high` bounds used by liquid alpha. | | `yuma3_enabled` | false | Runs Yuma Consensus v3 instead of v2. | | `bonds_moving_avg` | 900,000 | Bond EMA window; larger is smoother and slower, smaller is faster and more volatile. | | `transfers_enabled` | true | Allows stake transfers on the subnet. | | `recycle_or_burn` \* | burn | Whether miner emission directed at burn UIDs is destroyed or recycled back into the pool. | | `owner_cut_auto_lock_enabled` | false | Auto-locks the owner cut into conviction instead of paying it as free stake. | | `immune_owner_uids_limit` \* | 1 | How many owner hotkeys are permanently immune from pruning and trimming (`ImmuneOwnerUidsLimit`). Owner-raisable to a maximum of 10. | Parameters marked \* have their own extrinsics on chain rather than a name in `set-hyperparameter`; if the SDK doesn't expose one, use the [raw-call escape hatch](/docs/concepts/advanced). Chain reads also return `rho`, `difficulty`, `adjustment_interval`, `adjustment_alpha`, and `target_regs_per_interval` — legacy registration and consensus parameters that still exist as storage but no longer drive neuron admission; the burn price decay/bump mechanism replaced the old PoW and adjustment-interval machinery. (`max_registrations_per_block` is still enforced.) Two chain-side constraints on owner changes: * **Rate limit** — each hyperparameter can be changed roughly once per two tempos (default [`OwnerHyperparamRateLimit`](/code/pallets/subtensor/src/lib.rs#L1160) = 2), tracked independently per parameter, so you can change several different parameters in one tempo but not the same one twice. * **Freeze window** — owner admin calls are rejected during the last \~10 blocks of each tempo ([`AdminFreezeWindow`](/code/pallets/subtensor/src/lib.rs#L1155)), so parameters can't change while an epoch is being computed. Root-only parameters — `kappa` (default 32,767 ≈ 0.5), `max_allowed_validators` (128), `weights_set_rate_limit` (100 blocks between weight submissions), tempo outside the owner bounds, `registration_allowed`, and others — need root privileges and the [raw-call escape hatch](/docs/concepts/advanced). ## Trim UIDs [#trim-uids] [`trim-subnet`](/docs/tx/trim-subnet) lowers `max_allowed_uids` and evicts the excess neurons. Rules: * Rate-limited to one trim per 216,000 blocks (\~30 days). * Victims are the lowest-emission UIDs; temporally immune UIDs (within `immunity_period`) and owner-immune UIDs are skipped, and immune UIDs may not exceed 80% of the new maximum. * You cannot trim below 64 UIDs. * Surviving UIDs are renumbered consecutively from 0 — a miner's UID can change even if it survives. * A trimmed UID's on-chain state (hotkey mapping, registration block, weights, bonds, axon info) is permanently deleted. ## Mechanisms [#mechanisms] A subnet can run multiple scoring mechanisms, each with its own weights. [`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 and separate bond pools. Miners keep one UID across all mechanisms — no separate registration — and their total emission is the sum across mechanisms. * The same applies to validators: emission is computed per mechanism from that mechanism's own consensus, then combined according to the emission split. * Mechanisms were formerly called "subsubnets"; the old name survives in some APIs and code comments. * `max_allowed_uids` × mechanism count must stay ≤ 256; reduce `max_allowed_uids` first if raising the count would exceed it. * 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 changes are rate-limited to once per 7,200 blocks (\~24 hours). ## Deregistration [#deregistration] There is no inactivity-based deregistration; pruning is price-based. When the subnet limit (128) is reached, the next [`register-subnet`](/docs/tx/register-subnet) prunes the non-immune subnet with the lowest EMA alpha price (ties go to the oldest) and reuses its netuid. New subnets are immune for 1,296,000 blocks (\~6 months) from registration; if every subnet is immune the new registration fails instead. Price-ranked deregistration has history: it existed pre-dTAO (ranked by emission then), was removed at dTAO's launch in February 2025, and returned price-ranked in September 2025. Deregistering a subnet deregisters all of its neurons with it — every UID's on-chain state is deleted along with the subnet. On dissolution the subnet's TAO reserve is distributed pro-rata over all alpha — credited directly to each holder's coldkey balance, not restaked. Two caveats: protocol-held alpha (the pool's alpha-in plus alpha bought back by the chain) counts in the denominator and its share returns to the chain, diluting holder payouts; and conviction locks are deleted before liquidation — locked stake is paid out like any other, but accumulated conviction is not compensated. Owners of subnets registered since dTAO get no lock refund. The payout can also cut the other way: liquidation pays the pool's TAO-per-alpha rate, so if that exceeds the market price at dissolution, holders receive more than market value. ## Identity and token [#identity-and-token] * [`set-subnet-identity`](/docs/tx/set-subnet-identity) — the subnet's public profile (name, links, contact, logo). Public and permanent; each call overwrites the whole record. * [`update-symbol`](/docs/tx/update-symbol) — the subnet token's ticker, chosen from a fixed on-chain list of available symbols. The most common identity mistake: `logo_url` must point at a raw image file (e.g. a raw\.githubusercontent.com URL), not an HTML page that displays the image. ## Financing: leases and crowdloans [#financing-leases-and-crowdloans] A subnet can be financed collectively: create a crowdloan ([`create-crowdloan`](/docs/tx/create-crowdloan), contributions via [`contribute-crowdloan`](/docs/tx/contribute-crowdloan)) and register the network under a lease that shares emissions with contributors ([`register-leased-network`](/docs/tx/register-leased-network), ended with [`terminate-lease`](/docs/tx/terminate-lease)). Inspect with the [`crowdloans`](/docs/query/crowdloans) and [`leases`](/docs/query/leases) reads. Economics worth knowing before committing: * The lease's `emissions_share` is a percentage **of the owner cut** (18%), not of total emissions: a 50% share pays contributors 9% of the subnet's emission. * Contributor dividends are swapped from alpha to TAO and paid automatically during coinbase, on a fixed distribution interval (100 blocks), pro-rata by contribution. If pool liquidity is too thin to swap, the alpha accumulates for a later interval. * At lease creation, whatever remains of the raised cap after paying the registration cost is refunded to contributors pro-rata (rounding remainder to the beneficiary) — so set the cap above the projected lock cost, not wildly above it. * Crowdloans must run between 50,400 and 432,000 blocks (7–60 days). Contributions are clipped to the remaining cap rather than rejected. Refunds after a failed raise process at most 50 contributors per [`refund-crowdloan`](/docs/tx/refund-crowdloan) call, so large campaigns need several. ## Bootstrapping validation [#bootstrapping-validation] A brand-new subnet has no validators, and the owner gets no special treatment: setting weights requires meeting the same stake threshold as any validator (see [validating](/docs/guides/validating)). The common tactic is to ask an established root validator to parent your validator hotkey as a childkey ([`set-children`](/docs/tx/set-children)), lending it stake weight for consensus participation until the subnet attracts its own stake. # Timelock (/docs/guides/timelock) Timelock encryption binds data to a future round of the drand **quicknet** randomness beacon. Once that round's signature is published — every 3 seconds, worldwide, no keys or accounts involved — anyone can decrypt. Before then, nobody can, including the author. This is the same mechanism the chain uses for commit-reveal weights, exposed directly so you can timelock anything. The reveal moment is wall-clock time, not chain blocks: a round happens every 3 seconds regardless of block speed, so `reveal_in="1h"` means one hour on any network, including fast-blocks local chains. Encryption is pure local cryptography — no network, no keys, nothing to keep secret afterwards except the plaintext. Only decryption fetches the round signature from the drand HTTP API. ## Python [#python] ```python from bittensor import timelock sealed = timelock.encrypt("the answer is 42", reveal_in="1h") sealed.reveal_at # datetime of the reveal, UTC sealed.remaining # timedelta until then; zero once passed sealed.revealed # False until the round is published sealed.hex() # portable ciphertext — publish it anywhere ``` The hex is all anyone needs — the reveal round is embedded in the ciphertext, so there is no key or metadata to carry alongside it. Decrypt from the hex, a `Timelocked`, or raw bytes: ```python timelock.decrypt(sealed_hex) # raises TimelockNotReady if early timelock.decrypt(sealed_hex, wait=True) # sleeps until the reveal, then opens sealed.decrypt(wait=True) # same, as a method ``` `TimelockNotReady` carries `.reveal_round`, `.reveal_at`, and `.remaining`, so callers can schedule a retry instead of polling. A `timeout=` caps the total wait in seconds, and `timelock.decrypt_async` does the same on the event loop. Give the reveal moment exactly one way: ```python timelock.encrypt(data, reveal_in="15m") # duration: "30s", "1h30m", "2d", seconds, timedelta timelock.encrypt(data, reveal_at="2026-07-08T12:00Z") # absolute: datetime or ISO-8601, no offset = UTC timelock.encrypt(data, reveal_round=30212846) # explicit drand round ``` Round arithmetic is exact local math from the quicknet genesis (3 seconds per round), available directly: ```python timelock.current_round() # latest published round, computed locally timelock.round_at("2026-07-08T12:00Z") # first round at or after a moment timelock.reveal_time(30212846) # UTC datetime a round is published ``` ## CLI [#cli] ```bash btcli timelock encrypt "the answer is 42" --in 1h # prints ciphertext hex btcli timelock encrypt --file plan.pdf --in 2d --out plan.sealed btcli timelock encrypt "..." --at 2026-07-08T12:00Z # or --round 30212846 ``` Hex goes to stdout (pipeable); the reveal note goes to stderr. `--out` writes raw ciphertext bytes to a file instead of printing hex. ```bash btcli timelock decrypt # fails with the reveal time if early btcli timelock decrypt plan.sealed --wait --timeout 600 btcli timelock show # when it unlocks, without decrypting ``` `decrypt` and `show` take the hex or a file path as the argument (or `--file`); files may hold raw bytes or hex text. `decrypt --out` writes the plaintext bytes to a file — useful for binary payloads. ## Use cases [#use-cases] * **Sealed challenge answers** — a subnet mechanism publishes the answer timelocked, so miners provably cannot have seen it early. * **Embargoed announcements** — publish now, readable at the embargo time, with no trusted party holding the key. * **Sealed-bid auctions** — all bids open simultaneously at the deadline. * **Delayed configuration** — ship a config that activates itself at a known moment. ## Relationship to weight commit-reveal [#relationship-to-weight-commit-reveal] Validator weights use this machinery automatically: the chain timelocks committed weights and reveals them on schedule, with no action needed from you — see [`commit-weights`](/docs/tx/commit-weights) and the [validating guide](/docs/guides/validating). This page is for timelocking your own data. ## Mechanics [#mechanics] * The ciphertext carries its reveal round in a trailer, so no separate bookkeeping is needed — parsing the hex recovers everything. * Round arithmetic never touches the network: rounds map to timestamps by exact local math from the quicknet genesis. * After the reveal time passes, the beacon or HTTP relay may lag by a few seconds; decryption retries within a short grace window before giving up. # Validating (/docs/guides/validating) A validator is a hotkey that scores miners. It needs enough stake behind it to hold a validator permit on the subnet, and it earns dividends (shared with its nominators) for setting weights that agree with consensus. ## Get in position [#get-in-position] Register the hotkey on the subnet like any neuron ([`burned-register`](/docs/tx/burned-register)), then accumulate stake — your own ([`add-stake`](/docs/tx/add-stake)) and delegated. Whether you hold a validator permit is visible in the [`metagraph`](/docs/query/metagraph). To attract delegation, publish who you are with [`set-identity`](/docs/tx/set-identity) and set your [take](/docs/tx/set-take) — the fraction of staking emissions you keep before distributing the rest to nominators. Take increases are rate-limited and capped by the chain; [`set-take`](/docs/tx/set-take) dispatches whichever of [`increase_take`](/code/pallets/subtensor/src/macros/dispatches.rs#L533-L539)/[`decrease_take`](/code/pallets/subtensor/src/macros/dispatches.rs#L500-L506) reaches your target. Validators are also the gateway to a subnet: external applications can only query miners through a validator's registered hotkey, which is why validators build API businesses on top of their position. Stake cuts both ways here — miners prioritize queries from higher-stake validators, and on some subnets miners ignore weak validators entirely. The chain minimum is far below the practical bar. Meeting the stake threshold and landing a top-K permit gets you consensus participation, not competitive returns — validating seriously typically needs five figures of TAO-equivalent stake (the community rule of thumb is \~20k+). And because root-validator returns are additive across every subnet the validator serves, serious validators register broadly rather than on one subnet. ## Permits [#permits] Every epoch, the chain recomputes validator permits from the current stake distribution: the top **K** neurons by stake weight get one, where K is the [`MaxAllowedValidators`](/code/pallets/subtensor/src/lib.rs#L2072) hyperparameter (default 128; owner/root settable — read the live value via [`subnet-hyperparameters`](/docs/query/subnet-hyperparameters)). A chain-wide minimum stake weight ([`StakeThreshold`](/code/pallets/subtensor/src/lib.rs#L2489)) also applies: stake below it counts as zero, and it doubles as the minimum stake to set weights at all. The runtime default is zero, but mainnet runs a non-zero threshold (around 1,000 TAO-equivalent at the time of writing). Stake weight is not raw TAO. It is the alpha staked to your hotkey on that subnet, plus your TAO stake discounted by the global [`TaoWeight`](/code/pallets/subtensor/src/lib.rs#L1197) factor (governance-set; currently 0.18 on mainnet, against a runtime default of \~0.053) — so alpha counts far more than TAO. The subnet owner's UID is exempt from both gates: it always receives a validator permit, and its stake is not zeroed by the threshold. The permit gates four things: setting weights on other neurons (any neuron can self-weight), participation in Yuma Consensus, retaining bonds, and counting toward active stake. Losing the permit — falling out of the top K or below the threshold — does **not** deregister you, but it stops your dividends and **deletes your bonds**, so you rebuild from zero when you regain it. With no emissions, a permitless validator eventually drifts to the bottom of the pruning order like any idle neuron. One more clock to respect: the activity cutoff. It scales with the tempo — cutoff = `activity_cutoff_factor` × tempo / 1000 blocks, with the factor defaulting to 13,889 (≈5,000 blocks at the default tempo of 360). A validator that hasn't submitted weights within that window is treated as inactive — its stake is masked out of consensus until it submits again. ## Set weights [#set-weights] [`set-weights`](/docs/tx/set-weights) is the one entry point. It conforms your weights to the subnet's hyperparameters (max-weight clip, u16 quantization, minimum weight count) and submits via whichever path the subnet runs — plain weights when commit-reveal is off, or a timelock-encrypted commit (auto-revealed by the chain) when it is on. The simplest form is one call: ```python import bittensor as bt bt.set_weights(1, {0: 0.1, 1: 0.7, 2: 0.2}, wallet="my_coldkey", hotkey="my_hotkey") ``` Blocking, and everything handled: it connects on first use (the connection is reused across calls, so a scoring loop redials nothing), conforms the weights, picks the plaintext or commit-reveal path, preflights registration and the rate limit, signs with the hotkey, retries transient pool rejections, and **raises** on failure so a loop can't fail silently. For plan previews, policies, proxies, or async code, use the intent through a client. `SetWeights` takes the scores in either shape — a `{uid: weight}` mapping (preferred) or parallel `uids`/`weights` lists: ```python intent = bt.SetWeights(netuid=1, weights={0: 0.1, 1: 0.7, 2: 0.2}) intent = bt.SetWeights( # same submission, parallel lists netuid=1, uids=[0, 1, 2], weights=[0.1, 0.7, 0.2], ) result = await client.execute(intent, wallet) # signed by the hotkey ``` Values are **relative**, not absolute — only the proportions matter. Before submission they are clipped to the subnet's max-weight limit, normalized, and quantized to u16 (zeros dropped); the canonical implementation is `normalize` in `bittensor.intents` if you want to reproduce exactly what hits the chain. On subnets running multiple [mechanisms](/docs/guides/subnets#mechanisms), pass `mechid=` to score one mechanism (default 0 — for most subnets the only one): ```python intent = bt.SetWeights(netuid=1, mechid=1, weights={0: 0.4, 3: 0.6}) ``` On the CLI, the dedicated `btcli weights` group and the generated `btcli tx set-weights` submit the same operation — the group just adds convenience (comma-separated lists, weight-specific help): ```bash btcli weights set --netuid 1 --uids 0,1,2 --weights 0.5,0.3,0.2 -w my_coldkey -H my_hotkey btcli tx set-weights --netuid 1 --uids 0,1,2 --weights 0.5,0.3,0.2 -w my_coldkey -H my_hotkey ``` `btcli weights set` takes `--netuid`, `--uids`, `--weights` (comma-separated, parallel), and optionally `--mechid` and `--version-key`, plus the usual global wallet/network flags. Before signing, the SDK preflights the checks the chain would reject on — a rate-limited or unregistered submission fails fast with the same error the chain would return, and the rate-limit error says how many blocks to wait. Chain-side rules to know: * Submissions are rate-limited per UID by [`WeightsSetRateLimit`](/code/pallets/subtensor/src/lib.rs#L2097) (default 100 blocks between sets) — read it with [`weights-rate-limit`](/docs/query/weights-rate-limit). * Every submission carries a **weights version key**. If the subnet's stored version key is higher than yours, the call fails with [`IncorrectWeightVersionKey`](/code/pallets/subtensor/src/macros/errors.rs#L83). Subnet owners bump the key to force validators onto new subnet code — if you hit this error, update your subnet software. * Setting weights requires the same minimum stake (`StakeThreshold`) that gates permits; below it the call fails with [`NotEnoughStakeToSetWeights`](/code/pallets/subtensor/src/macros/errors.rs#L35). Timing reads: [`weights-rate-limit`](/docs/query/weights-rate-limit), [`blocks-since-last-update`](/docs/query/blocks-since-last-update), [`epoch-status`](/docs/query/epoch-status), [`reveal-period`](/docs/query/reveal-period). Reading back what the chain holds: [`client.weights.weights(netuid=1)`](/docs/query/weights) returns every validator's row as normalized fractions, and [`client.weights.bonds(netuid=1)`](/docs/query/bonds) the bond matrix your dividends flow through. ## Commit-reveal [#commit-reveal] Commit-reveal exists to defeat **weight copying**. Weights are public after consensus runs, and a lazy validator can skip evaluating miners entirely and submit the stake-weighted median of everyone else's revealed weights. Because Yuma Consensus rewards agreement with consensus, such copiers historically earned *better* vtrust and dividends per TAO than the honest validators they copied — while contributing nothing. With commit-reveal on, weights are **timelock-encrypted** when submitted: the ciphertext can only be decrypted (by anyone, including you) once a designated [Drand](https://drand.love) randomness round arrives. The reveal round is computed from the subnet's `commit_reveal_period`, measured in tempos (default 1), and the chain decrypts and applies the weights automatically at reveal time — there is no manual reveal step, no reveal-uptime requirement, and no selective-reveal exploit (earlier commit-reveal versions had manual reveals, which copiers abused by revealing only when it helped their vtrust). Copiers therefore only ever see weights that are at least one concealment period stale; if miner rankings moved in the meantime, copying them lands away from consensus and wrecks the copier's vtrust. Two caveats: * Commit-reveal only defends a subnet whose rankings actually **change** over the concealment window. If miner performance is static, stale weights are still accurate and copying still pays. * Subnet owners must keep `immunity_period` (in blocks) greater than `commit_reveal_period × tempo`, or new miners can be pruned before their first scores are ever revealed and counted. * Scores you see are stale by the reveal period. A miner you started weighting only surfaces in revealed weights after the reveal period elapses, and a crashed miner's score collapse is delayed the same way — don't read on-chain weights as a live view of the subnet. Commit-reveal is a per-subnet toggle — check it with [`commit-reveal-enabled`](/docs/query/commit-reveal-enabled) and the window with [`reveal-period`](/docs/query/reveal-period). `set-weights` picks the right path automatically. **Forcing the commit path.** `bt.CommitWeights` / [`btcli weights commit`](/docs/tx/commit-weights) always submits a timelocked commit, even on subnets that would accept plain weights — same inputs and preflight as `SetWeights`, just the path pinned. The counterpart `reveal` (`bt.RevealWeights` / [`btcli weights reveal`](/docs/tx/reveal-weights)) exists only for the **legacy salt-based** commit flow: it must reproduce the exact committed uids, weights, salt, and version key, and is never needed for timelocked commits, which the chain reveals on its own. ## Scoring and collateral [#scoring-and-collateral] On subnets that opt into [miner collateral](/docs/guides/mining/collateral), validators enforce floors off chain: read [`collateral_locked`](/docs/query/metagraph) / [`subnet_collateral`](/docs/query/subnet-collateral) and simply stop scoring under-collateralized or misbehaving hotkeys. There is no on-chain slash — zero incentive stops the drain and strands the lock. ## Bonds and liquid alpha [#bonds-and-liquid-alpha] Dividends flow through **bonds**: each validator accumulates bond value toward the miners it weights, as an exponential moving average, and is paid in proportion to bonds × miner incentive. The EMA is the incentive to evaluate honestly and early — a validator that identifies a good miner before consensus catches up builds bond value cheaply and earns more when the miner rises. With the **liquid alpha** hyperparameter enabled, the EMA rate is no longer one constant: it becomes dynamic per validator–miner pair, moving between the subnet's `alpha_low` and `alpha_high` bounds (defaults 0.7 and 0.9) based on how the validator's weight relates to consensus. It only takes effect on subnets that also have `yuma3_enabled` on — the classic bond path ignores it. See [emissions](/docs/concepts/emissions) for the full mechanics. ## Childkeys: delegate stake weight [#childkeys-delegate-stake-weight] [`set-children`](/docs/tx/set-children) lets a parent hotkey delegate a fraction of its stake weight to other hotkeys on one subnet — commonly used to point stake at a separate validating key without moving the stake itself. The call replaces the full child set (empty list revokes all), is rate-limited, and takes effect after a chain-defined cooldown. Inspect with [`children`](/docs/query/children) / [`parents`](/docs/query/parents) / [`pending-children`](/docs/query/pending-children). ## Root and governance [#root-and-governance] Register on the root network with [`root-register`](/docs/tx/root-register) — placement is stake-based, and joining a full root network evicts the member with the least stake. The legacy on-chain senate-vote extrinsic — and the collective and membership pallets behind it — have been removed from the runtime, so top root members no longer form a voting senate. Privileged operations (runtime upgrades, protocol changes) are dispatched through sudo, held by a multisig of Rao Foundation keys. Root stake earns cross-subnet dividends; see [Staking](/docs/guides/staking#root-stake-and-dividends) for claiming. ## Operational hygiene [#operational-hygiene] Validating is not supported on Windows (wallet operations work under WSL 2). Keep the coldkey off the validator box: weights are hotkey-signed, and anything coldkey-signed can go through a [proxy](/docs/concepts/advanced). Publish your endpoint with [`serve-axon`](/docs/tx/serve-axon) if your subnet's protocol requires validators to be reachable. # Signing with Polkadot Vault (/docs/guides/vault) Every transaction command can sign on a [Polkadot Vault](https://vault.novasama.io/) phone instead of a local keyfile. Pass `--signer vault` and the CLI shows the transaction as a QR code; you scan it with the Vault app, review the decoded call on the phone's screen, approve, and show the phone's signature QR back to your webcam. The private key lives on a device that never goes online — no keyfile, no password, and no mnemonic ever exists on the machine running `btcli`. Like [Ledger signing](/docs/guides/ledger), this works through [RFC-0078 merkleized metadata](https://polkadot-fellows.github.io/RFCs/approved/0078-merkleized-metadata.html): the transaction QR embeds a compact cryptographic proof of the runtime types the transaction touches, so the phone can decode and display exactly what you are signing with nothing pre-loaded. The proof's digest is *also signed into the transaction* (the `CheckMetadataHash` extension) and verified by the chain, so the phone, the CLI, and the chain must all agree about what the transaction means — nothing can show you "transfer 1 TAO" while signing something else. There are no metadata updates to scan, ever, including after runtime upgrades. ## Setup [#setup] You do this once. ### 1. Dedicate a phone [#1-dedicate-a-phone] Polkadot Vault is designed for a phone that stays offline forever: an old handset works. Install the app ([iOS](https://apps.apple.com/us/app/polkadot-vault/id1450849813), [Android](https://play.google.com/store/apps/details?id=io.parity.signer) — version 7.5 or later, which understands proof-carrying transactions), then put the phone in airplane mode and leave it there. The app nags you until you do. ### 2. Create a key set [#2-create-a-key-set] In Vault, create a new key set and write the recovery phrase down on paper. This phrase is the coldkey — anyone with it has your funds, and the phone never displays it again outside the backup screen. ### 3. Add the Bittensor network [#3-add-the-bittensor-network] Vault ships knowing only Polkadot, Kusama, and Westend. Teach it Bittensor by scanning this **chain specs** QR with Vault's scanner and approving it: Bittensor chain specs QR for Polkadot Vault It tells the phone the network's name, address format, and token (signed by the Opentensor Foundation's verifier key, so Vault shows it as verified), and it is the only setup scan the phone ever needs — transaction QRs carry their own metadata proof, so there are no metadata updates, ever. ### 4. Derive a Bittensor key [#4-derive-a-bittensor-key] In the key set, add a derived key for the Bittensor network. Tapping the key shows its address as a QR code — that address is your new coldkey account. ## Import the address into btcli [#import-the-address-into-btcli] The machine running `btcli` only ever needs the *public* address. Scan it straight off the phone into your [address book](/docs/cli#addresses): ```bash btcli addresses add my-vault --vault ``` A local page opens with your webcam; open the key in Vault so its address QR is on screen, and hold the phone up to the camera. The address is validated and saved under the name `my-vault`. (No camera? Skip `--vault` and paste the ss58 instead: `btcli addresses add my-vault 5F...`.) ## Fund the account [#fund-the-account] Every signed transaction pays a fee, so a completely empty account cannot submit anything — send a little TAO to the new address first. A fraction of a TAO covers many transactions (a transfer's fee is on the order of 0.0002 TAO). ## Sign a transaction [#sign-a-transaction] Add `--signer vault` to any [transaction](/docs/tx). For a first end-to-end test that moves nothing anywhere, transfer a small amount from the vault account *to itself* — the only cost is the fee: ```bash btcli wallet transfer --dest my-vault --amount 0.001 --signer vault --signer-address my-vault ``` What happens: 1. The transaction is prepared exactly as usual — same fee estimate, same preview, same confirmation prompt (`--dry-run` previews without signing). 2. A local page opens showing the transaction QR (animated when it doesn't fit in one code) next to a live webcam panel, with a summary of what is being signed. 3. Scan the QR with Vault. The phone decodes the call — pallet, method, destination, amount, fee context — verifies the embedded metadata proof, and asks for approval. 4. Approve on the phone. Vault displays a signature QR. 5. Hold the phone up to the webcam. The page captures the signature, the CLI verifies it against the transaction before submitting (a stale or mismatched scan is rejected on the spot), submits, and reports inclusion as usual. The page flips to its success state. The transaction is mortal: the signature must come back within the era window (about 25 minutes at the default period), or the CLI times out and the command must be re-run. For routine use, pin the account once and stop typing it: ```bash btcli config set signer_address ``` `--signer-address` accepts a raw ss58 or an address-book name; when omitted, it falls back to the configured wallet's coldkeypub — so a pubkey-only wallet whose coldkeypub matches the Vault key makes `-w my-wallet --signer vault` work with no address flag at all. ## Options [#options] | Option | Env | Config key | Meaning | | --------------------- | ---------------------- | ------------------- | --------------------------------------------------------------------------------- | | `--signer vault` | | | sign via Polkadot Vault QR round-trip | | `--signer-address` | `BT_SIGNER_ADDRESS` | `signer_address` | ss58 or address-book name of the Vault account (default: the wallet's coldkeypub) | | `--extension-browser` | `BT_EXTENSION_BROWSER` | `extension_browser` | browser for the signing page (`firefox`, `chrome`, or an app name) | The signing page binds to `127.0.0.1` on an ephemeral port and its URL is scoped to one session — a leftover tab from an earlier run cannot see or sign anything. Only the public address and the signature ever cross the camera gap. ## MEV-shielded transactions [#mev-shielded-transactions] Stake-trading commands submit [MEV-shielded](/docs/tx) by default — the signed transaction is encrypted so the mempool can't front-run it — and that stays true with the vault signer. The catch is speed: the shield requires the transaction to be signed with an 8-block era, so **you have about 96 seconds** from the first QR appearing to complete the flow, and a shielded submission means **two scans back to back** (the shielded transaction, then the encrypted carrier extrinsic that delivers it). The CLI sets you up to make the window: it opens the signing page and waits for your webcam *before* starting the clock, tells you to have Vault's scanner ready, labels each QR ("scan 1 of 2", "scan 2 of 2"), and shows a live countdown on the page. If the window closes before both scans finish, nothing is lost — the signature expires harmlessly and the CLI reports it; re-run the command and scan faster. With the phone unlocked and in hand, two scans in 90 seconds is comfortable; fumbling for the phone after confirming is what misses it. To skip the shield (and the time pressure) on a stake command, pass `--no-mev-shield` — the transaction then submits like any other, visible in the mempool. ## Limitations [#limitations] * **Extrinsics only.** Raw message signing (`btcli wallet sign`) is refused — Vault only signs what it can decode and display. * **One camera hop each way.** The webcam needs to see the phone's screen; laptop cameras work fine in normal lighting. ## Python [#python] The same signer is available to the SDK. A `VaultSigner` stands in for the wallet in `plan` and `execute`: ```python import bittensor as bt signer = bt.VaultSigner("5F...") # the Vault account's ss58 async with bt.Subtensor() as client: intent = bt.Transfer(dest_ss58="5F...", amount_tao=0.001) result = await client.execute(intent, signer) # opens the QR page, waits for the scan ``` The signer implements the transport's clear-signing capabilities: it computes the RFC-0078 metadata digest for the exact runtime the payload targets (`metadata_digest`) and frames prepared extrinsics as proof-carrying Vault QRs (`sign_unsigned_extrinsic`). Raw `sign(bytes)` calls are refused by design. Every returned signature is verified against the signing payload before the SDK accepts it. ## Troubleshooting [#troubleshooting] * **Vault says "unknown network"** — the chain specs were never added; scan the chain-specs QR in [setup step 3](#3-add-the-bittensor-network) once. * **Vault shows an error instead of the decoded call** — the app is too old to understand proof-carrying payloads; update Polkadot Vault (7.5+). * **"camera access failed" on the signing page** — allow camera access for the page in the browser and reload the tab; the session survives a reload. * **"the scanned signature does not verify"** — the camera caught a QR from an earlier transaction, or a different key signed; re-scan the signature Vault is showing *for this* transaction. * **"timed out waiting for the Vault signature scan"** — the era expired while the transaction waited; re-run the command and complete the scan within the window. * **Vault shows only a hash** — the call is too large to display and Vault falls back to blind-signing it; the CLI warns when this happens. # activity_cutoff_factor (/docs/hyperparameters/activity-cutoff-factor) `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 [#how-it-works] [`get_activity_cutoff_blocks`](/code/pallets/subtensor/src/utils/misc.rs#L638-L646) (`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 [#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`](/code/pallets/admin-utils/src/lib.rs#L664-L678) (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`](/code/pallets/admin-utils/src/lib.rs#L621-L653), whose stored value the epoch ignores. ## Related [#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) # activity_cutoff (/docs/hyperparameters/activity-cutoff) `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 [#how-it-works] At each epoch ([`pallets/subtensor/src/epoch/run_epoch.rs`](/code/pallets/subtensor/src/epoch/run_epoch.rs#L183-L198)), 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`](/code/pallets/subtensor/src/utils/misc.rs#L638-L646) (`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 [#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`](/code/pallets/admin-utils/src/lib.rs#L621-L653)) still exists on chain, but the value it writes is ignored by the epoch and it will be removed; don't use it. ## Related [#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) # adjustment_alpha (/docs/hyperparameters/adjustment-alpha) 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 [#how-it-works] The value lives in [`AdjustmentAlpha`](/code/pallets/subtensor/src/lib.rs#L2117) storage as a u64 fraction where `u64::MAX` encodes 1.0. The mainnet default is **0** ([`SubtensorInitialAdjustmentAlpha`](/code/runtime/src/lib.rs#L798) 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](/code/pallets/subtensor/src/subnets/registration.rs#L473-L515) 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`](/code/pallets/admin-utils/src/lib.rs#L428-L452) 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 [#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 [#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 # adjustment_interval (/docs/hyperparameters/adjustment-interval) 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 [#how-it-works] The parameter lives in [`AdjustmentInterval`](/code/pallets/subtensor/src/lib.rs#L2077) storage (u16 block count, default 100 blocks ≈ 20 minutes, from [`SubtensorInitialAdjustmentInterval`](/code/runtime/src/lib.rs#L797) 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](/code/pallets/subtensor/src/subnets/registration.rs#L473-L515) 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`](/code/pallets/admin-utils/src/lib.rs#L402-L418) 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 [#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 [#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) # alpha_high (/docs/hyperparameters/alpha-high) `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 [#how-it-works] Each epoch, [`alpha_sigmoid`](/code/pallets/subtensor/src/epoch/run_epoch.rs#L1538-L1565) (`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`](/code/pallets/subtensor/src/epoch/run_epoch.rs#L1579-L1609): liquid alpha must already be enabled ([`LiquidAlphaDisabled`](/code/pallets/subtensor/src/macros/errors.rs#L134)), and `alpha_high` must be at least 1638 (`u16::MAX / 40` ≈ 0.025), else [`AlphaHighTooLow`](/code/pallets/subtensor/src/macros/errors.rs#L136). `alpha_low` may not exceed it. ## Reading and setting [#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`](/code/pallets/admin-utils/src/lib.rs#L1293-L1316) call. The CLI reads the current pair and resubmits it with only `alpha_high` changed, so the other side is preserved. ## Related [#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) # alpha_low (/docs/hyperparameters/alpha-low) `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 [#how-it-works] Each epoch, [`alpha_sigmoid`](/code/pallets/subtensor/src/epoch/run_epoch.rs#L1538-L1565) (`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`](/code/pallets/subtensor/src/epoch/run_epoch.rs#L1579-L1609): liquid alpha must already be enabled ([`LiquidAlphaDisabled`](/code/pallets/subtensor/src/macros/errors.rs#L134)), `alpha_low` must be at least 1638 (`u16::MAX / 40` ≈ 0.025) and at most `alpha_high`, else [`AlphaLowOutOfRange`](/code/pallets/subtensor/src/macros/errors.rs#L138). ## Reading and setting [#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`](/code/pallets/admin-utils/src/lib.rs#L1293-L1316) call. The CLI reads the current pair and resubmits it with only `alpha_low` changed, so the other side is preserved. ## Related [#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) # alpha_sigmoid_steepness (/docs/hyperparameters/alpha-sigmoid-steepness) `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 [#how-it-works] For each validator–miner pair, [`alpha_sigmoid`](/code/pallets/subtensor/src/epoch/run_epoch.rs#L1538-L1565) (`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`](/code/pallets/admin-utils/src/lib.rs#L1623-L1655) in `pallets/admin-utils/src/lib.rs` reserves that to root: a subnet owner passing a negative value gets [`NegativeSigmoidSteepness`](/code/pallets/admin-utils/src/lib.rs#L135). ## Reading and setting [#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 [#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) # bonds_moving_avg (/docs/hyperparameters/bonds-moving-avg) `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 [#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`](/code/pallets/subtensor/src/epoch/run_epoch.rs#L1297-L1320) 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`](/code/pallets/subtensor/src/epoch/run_epoch.rs#L1567-L1577)). With liquid alpha on, the per-pair sigmoid rate replaces it entirely. Subnet owners can set at most 975,000 ([`BondsMovingAverageMaxReached`](/code/pallets/admin-utils/src/lib.rs#L133) in `pallets/admin-utils/src/lib.rs`); only root can go higher. ## Reading and setting [#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 [#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) # bonds_penalty (/docs/hyperparameters/bonds-penalty) `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 [#how-it-works] Before the bonds EMA runs, the epoch [interpolates](/code/pallets/subtensor/src/epoch/run_epoch.rs#L358-L368) 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 [#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 [#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) # bonds_reset_enabled (/docs/hyperparameters/bonds-reset-enabled) `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 [#how-it-works] When a hotkey commits metadata (the commitments pallet's [`OnMetadataCommitment`](/code/pallets/commitments/src/lib.rs#L367) hook, wired up as [`ResetBondsOnCommit`](/code/runtime/src/lib.rs#L728-L741) in `runtime/src/lib.rs`), the chain calls [`do_reset_bonds`](/code/pallets/subtensor/src/epoch/run_epoch.rs#L1611-L1643) 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`](/code/pallets/admin-utils/src/lib.rs#L93) event ([`sudo_set_bonds_reset_enabled`](/code/pallets/admin-utils/src/lib.rs#L1702-L1723) in `pallets/admin-utils/src/lib.rs`). ## Reading and setting [#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 [#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) # burn_half_life (/docs/hyperparameters/burn-half-life) 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 [#how-it-works] Every block, [`update_registration_prices_for_networks`](/code/pallets/subtensor/src/subnets/registration.rs#L473-L515) (in `pallets/subtensor/src/subnets/registration.rs`, called from [`block_step`](/code/pallets/subtensor/src/coinbase/block_step.rs#L7-L36)) 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`](/code/pallets/subtensor/src/utils/misc.rs#L1043-L1067) 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`](/code/pallets/subtensor/src/lib.rs#L2726) 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`](/code/pallets/admin-utils/src/lib.rs#L2128-L2164) 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 [#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 [#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 # burn_increase_mult (/docs/hyperparameters/burn-increase-mult) 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 [#how-it-works] After each successful registration, [`bump_registration_price_after_registration`](/code/pallets/subtensor/src/subnets/registration.rs#L517-L541) (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`](/code/pallets/subtensor/src/lib.rs#L2731) 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`](/code/pallets/admin-utils/src/lib.rs#L2176-L2214) 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 [#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 [#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 # collateral_drain_ratio (/docs/hyperparameters/collateral-drain-ratio) `collateral_drain_ratio` (call it `k`) sets how fast a hotkey earns back the collateral locked at registration when the subnet uses a nonzero [`collateral_lock_share`](/docs/hyperparameters/collateral-lock-share). Operators care because `lock / k` is the total emission (miner incentive and validator dividends) they must earn before their collateral is fully withdrawable; owners tune it to control how long hotkeys stay collateralized — the security parameter for subnets whose scoring can be gamed on short horizons. ## How it works [#how-it-works] Each tempo, when hotkey emission is credited ([`distribute_dividends_and_incentives`](/code/pallets/subtensor/src/coinbase/run_coinbase.rs#L669-L766) in `pallets/subtensor/src/coinbase/run_coinbase.rs`), [`settle_miner_collateral`](/code/pallets/subtensor/src/subnets/collateral.rs#L193-L229) releases ``` released = min(locked − min_locked, k × emission) ``` from the hotkey's lock back to withdrawable stake, where `min_locked` is the optional self-maintained floor (zero unless set via `set_min_collateral`; while the lock is under the floor, emission is captured into the lock instead). The release is keyed to the registered hotkey even when miner incentive is redirected by auto-stake. At `k = 1` (the default) collateral releases one-for-one with earned emission; at `k = 0.5` a hotkey must earn two alpha per alpha released; at `k = 2` half the lock's worth of emission releases all of it. The subnet value is stored in [`CollateralDrainRatio`](/code/pallets/subtensor/src/lib.rs#L2795-L2801) as a `U64F64` fixed-point number, but each miner's effective ratio is **snapshot into their collateral entry at registration** ([`MinerCollateralState`](/code/pallets/subtensor/src/lib.rs#L364-L383)): owner changes apply to future registrations only and can never slow (or speed) the drain of standing collateral. The owner-set extrinsic ([`sudo_set_collateral_drain_ratio`](/code/pallets/admin-utils/src/lib.rs#L2311-L2354) in `pallets/admin-utils/src/lib.rs`) requires `0 < k ≤ 10` ([`MaxCollateralDrainRatio`](/code/pallets/subtensor/src/lib.rs#L446-L450)); out-of-range values fail with [`CollateralDrainRatioOutOfBounds`](/docs/errors/chain/CollateralDrainRatioOutOfBounds). Zero is excluded by design: with no exit path for collateral, `k = 0` would make the lock permanently unrecoverable — a burn with extra steps. The deterrence math, briefly: an adversary who plans to farm emissions and abandon the hotkey must collect roughly `T / (1 + k)` (T = registration price) before validators stop scoring them, just to break even. Lower `k` raises that bar; see the [worked example](/docs/guides/mining/collateral#a-subnet-deters-tail-risk-farming). ## Reading and setting [#reading-and-setting] ``` btcli sudo get --netuid N --name collateral_drain_ratio ``` Fixed-point decimal; owner or root, rate-limited, outside the weights window: ``` btcli sudo set --netuid N --name collateral_drain_ratio --value 0.5 ``` Common settings: 1.0 (default, earn-back at par), 0.2–0.5 (slow release for long-horizon or gameable-score subnets), 2.0 (fast release where the entry gate matters more than ongoing lockup). ## Related [#related] [`collateral_lock_share`](/docs/hyperparameters/collateral-lock-share), [`burn_half_life`](/docs/hyperparameters/burn-half-life), [Registration collateral guide](/docs/guides/mining/collateral) # collateral_lock_share (/docs/hyperparameters/collateral-lock-share) `collateral_lock_share` (call it `p`) splits the subnet's floating registration price: the `(1 − p)` share is burned as in a classic burned registration, and the `p` share is staked to the registering hotkey and locked as [miner collateral](/docs/guides/mining/collateral). Miners care because it decides how much of the entry price they can earn back by mining; owners tune it to price post-registration accountability — sybils, UID squatters, and miners that validators blacklist never release the lock. ## How it works [#how-it-works] At registration ([`do_register`](/code/pallets/subtensor/src/subnets/registration.rs#L66-L127) in `pallets/subtensor/src/subnets/registration.rs`), with floating price `T`: ``` burned = (1 − p) × T top-up = max(0, p × T − value of standing collateral) ``` The burned share is recycled exactly like a pre-collateral registration; the top-up is staked to the hotkey via the normal staking path and locked ([`pay_registration`](/code/pallets/subtensor/src/subnets/collateral.rs)). Standing collateral from a previous registration of the same hotkey is valued at the subnet's moving-average price and credited, so a pruned miner re-registers for roughly the burned share alone. Locked collateral is excluded from what the coldkey can unstake and is released only against earned emission (miner incentive and validator dividends), at [`collateral_drain_ratio`](/docs/hyperparameters/collateral-drain-ratio) alpha per alpha earned. The value is stored in [`CollateralLockShare`](/code/pallets/subtensor/src/lib.rs#L2786-L2793) as a `u16` normalized so `u16::MAX` (65535) = 100%. The chain default is 0 — collateral disabled, whole price burned, identical to pre-collateral behavior. The owner-set extrinsic ([`sudo_set_collateral_lock_share`](/code/pallets/admin-utils/src/lib.rs#L2267-L2309) in `pallets/admin-utils/src/lib.rs`) caps the value at [`MaxCollateralLockShare`](/code/pallets/subtensor/src/lib.rs#L431-L437) (62258, ≈95%): the burned share must stay strictly positive so re-registration always pays a nonzero, floating burn. Values above the cap fail with [`CollateralLockShareTooHigh`](/docs/errors/chain/CollateralLockShareTooHigh). Changes are **prospective only**: each miner's lock is created at registration time, so raising `p` affects future registrants (and returning miners' top-ups), never standing collateral. ## Reading and setting [#reading-and-setting] ``` btcli sudo get --netuid N --name collateral_lock_share ``` Stored u16-normalized (65535 = 100%); `btcli` accepts either the 0..1 fraction or the raw integer. Owner or root, rate-limited, outside the weights window: ``` btcli sudo set --netuid N --name collateral_lock_share --value 0.75 # or the raw integer 49151 ``` Common settings: 0 (pure burn, default), 0.75 (standard bond), 0.83–0.9 (subnets defending against short-horizon score gaming — pair with a low drain ratio). `btcli` accepts the 0..1 fraction or the raw u16 integer. ## Related [#related] [`collateral_drain_ratio`](/docs/hyperparameters/collateral-drain-ratio), [`min_burn`](/docs/hyperparameters/min-burn), [`burn_increase_mult`](/docs/hyperparameters/burn-increase-mult), [Registration collateral guide](/docs/guides/mining/collateral) # commit_reveal_period (/docs/hyperparameters/commit-reveal-period) `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 [#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`](/code/pallets/subtensor/src/subnets/weights.rs#L1174-L1179) (`pallets/subtensor/src/subnets/weights.rs`) requires `current_epoch == commit_epoch + commit_reveal_period`. Revealing earlier fails with [`RevealTooEarly`](/code/pallets/subtensor/src/macros/errors.rs#L182); once the current epoch moves past that target, [`is_commit_expired`](/code/pallets/subtensor/src/subnets/weights.rs#L1206-L1211) drops the commit and a late reveal fails with [`ExpiredWeightCommit`](/code/pallets/subtensor/src/macros/errors.rs#L180). A hotkey may hold at most 10 unrevealed commits. The value is bounded on-chain: [`set_reveal_period`](/code/pallets/subtensor/src/subnets/weights.rs#L1213-L1228) 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 [#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 [#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 # commit_reveal_weights_enabled (/docs/hyperparameters/commit-reveal-weights-enabled) `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 [#how-it-works] The flag flips the accepted submission path. With it **on**, the plain [`set_weights`](/code/pallets/subtensor/src/macros/dispatches.rs#L70-L82) extrinsic is rejected outright with [`CommitRevealEnabled`](/code/pallets/subtensor/src/macros/errors.rs#L130) (`pallets/subtensor/src/macros/dispatches.rs`); validators must instead commit — either a salted hash (revealed later by a matching [`reveal_weights`](/code/pallets/subtensor/src/macros/dispatches.rs#L281-L290) 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`](/code/pallets/subtensor/src/macros/errors.rs#L132) 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 [#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 [#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 # difficulty (/docs/hyperparameters/difficulty) `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 [#how-it-works] A PoW registration submits a seal built from a recent block hash, the hotkey, and a nonce ([`create_seal_hash`](/code/pallets/subtensor/src/subnets/registration.rs#L426-L446) in `pallets/subtensor/src/subnets/registration.rs`). The check is [`hash_meets_difficulty`](/code/pallets/subtensor/src/subnets/registration.rs#L362-L372): 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`](/code/pallets/subtensor/src/macros/dispatches.rs#L804-L814) 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`](/code/pallets/subtensor/src/subnets/registration.rs#L38-L132)). No controller adjusts `difficulty` anymore either — the per-block price update ([`update_registration_prices_for_networks`](/code/pallets/subtensor/src/subnets/registration.rs#L473-L515)) 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`](/code/pallets/subtensor/src/subnets/registration.rs#L165-L214), 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 [#reading-and-setting] ``` btcli sudo get --netuid N --name difficulty ``` Root/governance only: [`sudo_set_difficulty`](/code/pallets/admin-utils/src/lib.rs#L827-L841) 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 [#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) # immunity_period (/docs/hyperparameters/immunity-period) `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 [#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`](/code/pallets/subtensor/src/subnets/registration.rs#L280-L356) (`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`](/code/pallets/subtensor/src/macros/errors.rs#L119). 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 [#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 [#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) # Hyperparameters (/docs/hyperparameters) 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; the source column links the on-chain storage declaration. | Hyperparameter | Unit | Owner-settable | What it controls | Source | | -------------------------------------------------------------------------------------------- | ------------------------------- | -------------- | ------------------------------------- | ---------------------------------------------------------------------------- | | [`rho`](/docs/hyperparameters/rho) | integer | yes | trust curve steepness | [`Rho`](/code/pallets/subtensor/src/lib.rs#L2124) | | [`kappa`](/docs/hyperparameters/kappa) | fraction (u16, 65535 = 1.0) | root only | consensus majority-stake threshold | [`Kappa`](/code/pallets/subtensor/src/lib.rs#L2133) | | [`immunity_period`](/docs/hyperparameters/immunity-period) | blocks (12s) | yes | prune-immunity window for new neurons | [`ImmunityPeriod`](/code/pallets/subtensor/src/lib.rs#L2162) | | [`min_allowed_weights`](/docs/hyperparameters/min-allowed-weights) | integer | yes | minimum weights per submission | [`MinAllowedWeights`](/code/pallets/subtensor/src/lib.rs#L2188) | | [`max_weights_limit`](/docs/hyperparameters/max-weights-limit) | fraction (u16, 65535 = 1.0) | root only | cap on a single miner's weight | [`MaxWeightsLimit`](/code/pallets/subtensor/src/lib.rs#L2178) | | [`tempo`](/docs/hyperparameters/tempo) | blocks (12s) | yes | blocks per consensus epoch | [`Tempo`](/code/pallets/subtensor/src/lib.rs#L1952) | | [`min_difficulty`](/docs/hyperparameters/min-difficulty) | PoW difficulty (u64) | root only | PoW registration difficulty floor | [`MinDifficulty`](/code/pallets/subtensor/src/lib.rs#L2266) | | [`max_difficulty`](/docs/hyperparameters/max-difficulty) | PoW difficulty (u64) | yes | PoW registration difficulty ceiling | [`MaxDifficulty`](/code/pallets/subtensor/src/lib.rs#L2271) | | [`difficulty`](/docs/hyperparameters/difficulty) | PoW difficulty (u64) | root only | current PoW registration difficulty | [`Difficulty`](/code/pallets/subtensor/src/lib.rs#L2252) | | [`weights_version`](/docs/hyperparameters/weights-version) | integer | yes | minimum version key for set\_weights | [`WeightsVersionKey`](/code/pallets/subtensor/src/lib.rs#L2183) | | [`weights_rate_limit`](/docs/hyperparameters/weights-rate-limit) | blocks (12s) | root only | wait between weight submissions | [`WeightsSetRateLimit`](/code/pallets/subtensor/src/lib.rs#L2218) | | [`adjustment_interval`](/docs/hyperparameters/adjustment-interval) | blocks (12s) | root only | difficulty/burn adjustment cadence | [`AdjustmentInterval`](/code/pallets/subtensor/src/lib.rs#L2198) | | [`activity_cutoff`](/docs/hyperparameters/activity-cutoff) | blocks (12s) | root only | no-weights window before inactive | [`ActivityCutoff`](/code/pallets/subtensor/src/lib.rs#L2168) | | [`activity_cutoff_factor`](/docs/hyperparameters/activity-cutoff-factor) | integer | yes | activity cutoff, per-mille of tempo | [`ActivityCutoffFactorMilli`](/code/pallets/subtensor/src/lib.rs#L1995) | | [`registration_allowed`](/docs/hyperparameters/registration-allowed) | flag | root only | new neuron registrations allowed | [`NetworkRegistrationAllowed`](/code/pallets/subtensor/src/lib.rs#L2033) | | [`network_pow_registration_allowed`](/docs/hyperparameters/network-pow-registration-allowed) | flag | yes | PoW registration toggle | [`NetworkPowRegistrationAllowed`](/code/pallets/subtensor/src/lib.rs#L2038) | | [`target_regs_per_interval`](/docs/hyperparameters/target-regs-per-interval) | integer | root only | registration-rate controller target | [`TargetRegistrationsPerInterval`](/code/pallets/subtensor/src/lib.rs#L2233) | | [`min_burn`](/docs/hyperparameters/min-burn) | TAO amount in rao | yes | burned-registration cost floor | [`MinBurn`](/code/pallets/subtensor/src/lib.rs#L2256) | | [`max_burn`](/docs/hyperparameters/max-burn) | TAO amount in rao | yes | burned-registration cost ceiling | [`MaxBurn`](/code/pallets/subtensor/src/lib.rs#L2261) | | [`bonds_moving_avg`](/docs/hyperparameters/bonds-moving-avg) | fraction (1,000,000 = 1.0) | yes | bonds EMA smoothing factor | [`BondsMovingAverage`](/code/pallets/subtensor/src/lib.rs#L2203) | | [`max_regs_per_block`](/docs/hyperparameters/max-regs-per-block) | integer | root only | per-block registration cap | [`MaxRegistrationsPerBlock`](/code/pallets/subtensor/src/lib.rs#L1868) | | [`serving_rate_limit`](/docs/hyperparameters/serving-rate-limit) | blocks (12s) | yes | cooldown between axon serve calls | [`ServingRateLimit`](/code/pallets/subtensor/src/lib.rs#L2119) | | [`max_validators`](/docs/hyperparameters/max-validators) | integer | root only | top-stake validator permit cap | [`MaxAllowedValidators`](/code/pallets/subtensor/src/lib.rs#L2193) | | [`adjustment_alpha`](/docs/hyperparameters/adjustment-alpha) | fraction (u64, u64::MAX = 1.0) | yes | difficulty/burn adjust smoothing | [`AdjustmentAlpha`](/code/pallets/subtensor/src/lib.rs#L2238) | | [`commit_reveal_period`](/docs/hyperparameters/commit-reveal-period) | epochs (tempos) | yes | weight commit-to-reveal delay | [`RevealPeriodEpochs`](/code/pallets/subtensor/src/lib.rs#L2680) | | [`commit_reveal_weights_enabled`](/docs/hyperparameters/commit-reveal-weights-enabled) | flag | yes | commit-reveal weights toggle | [`CommitRevealWeightsEnabled`](/code/pallets/subtensor/src/lib.rs#L2243) | | [`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) | [`LiquidAlphaOn`](/code/pallets/subtensor/src/lib.rs#L2320) | | [`bonds_penalty`](/docs/hyperparameters/bonds-penalty) | fraction (u16, 65535 = 1.0) | yes | penalty on out-of-consensus bonds | [`BondsPenalty`](/code/pallets/subtensor/src/lib.rs#L2208) | | [`alpha_sigmoid_steepness`](/docs/hyperparameters/alpha-sigmoid-steepness) | integer | yes | liquid-alpha sigmoid steepness | [`AlphaSigmoidSteepness`](/code/pallets/subtensor/src/lib.rs#L2128) | | [`min_childkey_take`](/docs/hyperparameters/min-childkey-take) | fraction (u16, 65535 = 1.0) | yes | floor for childkey take | [`MinChildkeyTakePerSubnet`](/code/pallets/subtensor/src/lib.rs#L1342) | | [`owner_immune_neuron_limit`](/docs/hyperparameters/owner-immune-neuron-limit) | integer | yes | owner-designated prune-immune UIDs | [`ImmuneOwnerUidsLimit`](/code/pallets/subtensor/src/lib.rs#L2419) | | [`max_allowed_uids`](/docs/hyperparameters/max-allowed-uids) | integer | yes | neuron slot capacity before pruning | [`MaxAllowedUids`](/code/pallets/subtensor/src/lib.rs#L2157) | | [`burn_increase_mult`](/docs/hyperparameters/burn-increase-mult) | multiplier (U64F64 bits / 2^64) | yes | burn cost bump per registration | [`BurnIncreaseMult`](/code/pallets/subtensor/src/lib.rs#L2852) | | [`burn_half_life`](/docs/hyperparameters/burn-half-life) | blocks (12s) | yes | burn cost decay half-life | [`BurnHalfLife`](/code/pallets/subtensor/src/lib.rs#L2847) | | [`collateral_lock_share`](/docs/hyperparameters/collateral-lock-share) | fraction (u16, 65535 = 1.0) | yes | registration price share locked | [`CollateralLockShare`](/code/pallets/subtensor/src/lib.rs#L2861) | | [`collateral_drain_ratio`](/docs/hyperparameters/collateral-drain-ratio) | multiplier (U64F64 bits / 2^64) | yes | collateral released per α earned | [`CollateralDrainRatio`](/code/pallets/subtensor/src/lib.rs#L2870) | | [`yuma3_enabled`](/docs/hyperparameters/yuma3-enabled) | flag | yes | yuma3 consensus variant toggle | [`Yuma3On`](/code/pallets/subtensor/src/lib.rs#L2325) | | [`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) | — | | [`subnet_emission_enabled`](/docs/hyperparameters/subnet-emission-enabled) | flag | root only | root switch for TAO emission share | [`SubnetEmissionEnabled`](/code/pallets/subtensor/src/lib.rs#L1515) | | [`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 | [`BondsResetOn`](/code/pallets/subtensor/src/lib.rs#L2213) | | [`transfers_enabled`](/docs/hyperparameters/transfers-enabled) | flag | yes | stake transfers between coldkeys | [`TransferToggle`](/code/pallets/subtensor/src/lib.rs#L1936) | | [`owner_cut_enabled`](/docs/hyperparameters/owner-cut-enabled) | flag | yes | owner emission cut toggle | [`OwnerCutEnabled`](/code/pallets/subtensor/src/lib.rs#L1911) | | [`owner_cut_auto_lock_enabled`](/docs/hyperparameters/owner-cut-auto-lock-enabled) | flag | yes | auto-lock the owner's emission cut | [`OwnerCutAutoLockEnabled`](/code/pallets/subtensor/src/lib.rs#L1758) | # kappa (/docs/hyperparameters/kappa) `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 [#how-it-works] Each epoch, the chain computes a per-miner consensus score as a stake-weighted median ([`weighted_median_col`](/code/pallets/subtensor/src/epoch/math.rs#L1053-L1118) 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`](/code/pallets/subtensor/src/epoch/math.rs#L905-L918)), 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 [#reading-and-setting] Inspect with: ``` btcli sudo get --netuid N --name kappa ``` Not settable by the subnet owner — root/governance only ([`sudo_set_kappa`](/code/pallets/admin-utils/src/lib.rs#L575-L584) in the AdminUtils pallet requires the root origin). When set, the raw value is a u16: 32767 ≈ 0.5. ## Related [#related] [`rho`](/docs/hyperparameters/rho) · [`tempo`](/docs/hyperparameters/tempo) · [`activity_cutoff`](/docs/hyperparameters/activity-cutoff) · [`max_validators`](/docs/hyperparameters/max-validators) # liquid_alpha_enabled (/docs/hyperparameters/liquid-alpha-enabled) `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 [#how-it-works] In the epoch's [`compute_bonds`](/code/pallets/subtensor/src/epoch/run_epoch.rs#L1333-L1360) (`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`](/code/pallets/subtensor/src/epoch/run_epoch.rs#L1415-L1455): 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`](/code/pallets/subtensor/src/epoch/run_epoch.rs#L1567-L1577)). 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`](/code/pallets/subtensor/src/macros/errors.rs#L134) error otherwise). ## Reading and setting [#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 [#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) # max_allowed_uids (/docs/hyperparameters/max-allowed-uids) `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 [#how-it-works] While the neuron count is below `max_allowed_uids`, a registration simply appends a new UID ([`append_neuron`](/code/pallets/subtensor/src/subnets/uids.rs#L117-L145)). Once full, [`get_neuron_to_prune`](/code/pallets/subtensor/src/subnets/registration.rs#L280-L356) selects the lowest-emission prunable neuron and [`replace_neuron`](/code/pallets/subtensor/src/subnets/uids.rs#L62-L114) 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`](/code/pallets/admin-utils/src/lib.rs#L131) 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`](/code/pallets/subtensor/src/subnets/uids.rs#L147-L379) (`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 [#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 [#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) # max_burn (/docs/hyperparameters/max-burn) 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 [#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`](/code/pallets/subtensor/src/subnets/registration.rs#L517-L541)). 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`](/code/pallets/subtensor/src/subnets/registration.rs#L473-L515)), pulling it back down from the ceiling toward [`min_burn`](/docs/hyperparameters/min-burn). The value lives in [`MaxBurn`](/code/pallets/subtensor/src/lib.rs#L2140) storage as a rao amount (1 TAO = 1e9 rao); the chain default is 100 TAO. The owner-set extrinsic ([`sudo_set_max_burn`](/code/pallets/admin-utils/src/lib.rs#L788-L820) in `pallets/admin-utils/src/lib.rs`) requires the new value to be **above 0.1 TAO** ([`MaxBurnLowerBound`](/code/runtime/src/lib.rs#L821)) 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 [#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 [#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 # max_difficulty (/docs/hyperparameters/max-difficulty) `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 [#how-it-works] Storage is [`MaxDifficulty`](/code/pallets/subtensor/src/lib.rs#L2150) in `pallets/subtensor/src/lib.rs`, accessed via [`get_max_difficulty`](/code/pallets/subtensor/src/utils/misc.rs#L500-L502) / [`set_max_difficulty`](/code/pallets/subtensor/src/utils/misc.rs#L503-L506) (`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`](/code/runtime/src/lib.rs#L814) 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 [#reading-and-setting] ``` btcli sudo get --netuid N --name max_difficulty ``` Owner-settable (the AdminUtils extrinsic [`sudo_set_max_difficulty`](/code/pallets/admin-utils/src/lib.rs#L308-L334) 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 [#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) # max_regs_per_block (/docs/hyperparameters/max-regs-per-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`](/code/runtime/src/lib.rs#L802) in `runtime/src/lib.rs`). ## How it works [#how-it-works] Each successful registration increments the `RegistrationsThisBlock` counter ([`do_register`](/code/pallets/subtensor/src/subnets/registration.rs#L38-L132), step 11, in `pallets/subtensor/src/subnets/registration.rs`), and the counter is reset to zero every block by [`update_registration_prices_for_networks`](/code/pallets/subtensor/src/subnets/registration.rs#L473-L515) (same file), which runs in `on_initialize`. The cap is checked against that counter in two places: * Root registration: [`do_root_register`](/code/pallets/subtensor/src/coinbase/root.rs#L77-L188) (`pallets/subtensor/src/coinbase/root.rs`) fails with [`TooManyRegistrationsThisBlock`](/code/pallets/subtensor/src/macros/errors.rs#L61) 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`](/code/pallets/subtensor/src/lib.rs#L2802-L2823) (`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 [#reading-and-setting] ``` btcli sudo get --netuid N --name max_regs_per_block ``` Root/governance only: [`sudo_set_max_registrations_per_block`](/code/pallets/admin-utils/src/lib.rs#L950-L970) 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 [#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) # max_validators (/docs/hyperparameters/max-validators) `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 [#how-it-works] Every epoch recomputes permits from scratch ([`pallets/subtensor/src/epoch/run_epoch.rs`](/code/pallets/subtensor/src/epoch/run_epoch.rs#L276-L282)): [`is_topk_nonzero(&stake, k)`](/code/pallets/subtensor/src/epoch/math.rs#L227-L241) 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 [#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`](/code/pallets/admin-utils/src/lib.rs#L848-L873) in AdminUtils requires the root origin and enforces the `max_allowed_uids` ceiling). The value is a plain integer. ## Related [#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) # max_weights_limit (/docs/hyperparameters/max-weights-limit) `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 [#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`](/code/pallets/subtensor/src/epoch/math.rs#L123-L129)), then [`max_weight_limited`](/code/pallets/subtensor/src/subnets/weights.rs#L1131-L1145) (`pallets/subtensor/src/subnets/weights.rs`) sum-normalizes them and rejects the whole submission with [`MaxWeightExceeded`](/code/pallets/subtensor/src/macros/errors.rs#L77) if the largest share exceeds `max_weights_limit / 65535` ([`check_vec_max_limited`](/code/pallets/subtensor/src/epoch/math.rs#L132-L142) in `epoch/math.rs`). A single self-weight is exempt. In the current runtime the enforced limit is pinned: [`get_max_weight_limit`](/code/pallets/subtensor/src/utils/misc.rs#L557-L559) (`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`](/code/pallets/subtensor/src/lib.rs#L2057) 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 [#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 [#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 # min_allowed_weights (/docs/hyperparameters/min-allowed-weights) `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 [#how-it-works] [`set_weights`](/code/pallets/subtensor/src/macros/dispatches.rs#L70-L82) validates the submission length in [`check_length`](/code/pallets/subtensor/src/subnets/weights.rs#L1093-L1115) (`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`](/code/pallets/subtensor/src/macros/errors.rs#L58). 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 [#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 [#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 # min_burn (/docs/hyperparameters/min-burn) 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 [#how-it-works] Every block, [`update_registration_prices_for_networks`](/code/pallets/subtensor/src/subnets/registration.rs#L473-L515) (in `pallets/subtensor/src/subnets/registration.rs`, run from [`block_step`](/code/pallets/subtensor/src/coinbase/block_step.rs#L7-L36)) 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`](/code/pallets/subtensor/src/lib.rs#L2135) 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`](/code/pallets/admin-utils/src/lib.rs#L749-L781) in `pallets/admin-utils/src/lib.rs`) enforces two bounds: the new value must be **below 1 TAO** ([`MinBurnUpperBound`](/code/runtime/src/lib.rs#L820)) 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 [#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 [#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) # min_childkey_take (/docs/hyperparameters/min-childkey-take) 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 [#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`](/code/pallets/subtensor/src/macros/errors.rs#L166). 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 [#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 [#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 # min_difficulty (/docs/hyperparameters/min-difficulty) `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 [#how-it-works] Storage is [`MinDifficulty`](/code/pallets/subtensor/src/lib.rs#L2145) in `pallets/subtensor/src/lib.rs`, read and written by [`get_min_difficulty`](/code/pallets/subtensor/src/utils/misc.rs#L492-L494) / [`set_min_difficulty`](/code/pallets/subtensor/src/utils/misc.rs#L495-L498) (`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`](/code/pallets/subtensor/src/migrations/migrate_set_min_difficulty.rs#L9-L53) (`pallets/subtensor/src/migrations/migrate_set_min_difficulty.rs`) set the floor to 10,000,000 on every subnet, matching the runtime default ([`SubtensorInitialMinDifficulty`](/code/runtime/src/lib.rs#L813) 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 [#reading-and-setting] ``` btcli sudo get --netuid N --name min_difficulty ``` Root/governance only: [`sudo_set_min_difficulty`](/code/pallets/admin-utils/src/lib.rs#L284-L301) 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 [#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) # network_pow_registration_allowed (/docs/hyperparameters/network-pow-registration-allowed) `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 [#how-it-works] Storage is [`NetworkPowRegistrationAllowed`](/code/pallets/subtensor/src/lib.rs#L1917) 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`](/code/pallets/subtensor/src/migrations/migrate_clear_deprecated_registration_maps.rs#L5-L64) (`pallets/subtensor/src/migrations/`) cleared every stored entry, so all subnets read the storage default. * The AdminUtils setter [`sudo_set_network_pow_registration_allowed`](/code/pallets/admin-utils/src/lib.rs#L709-L715) (`pallets/admin-utils/src/lib.rs`, call index 20) no longer writes anything — it unconditionally returns the [`POWRegistrationDisabled`](/code/pallets/admin-utils/src/lib.rs#L151) error. Independently, the PoW [`register`](/code/pallets/subtensor/src/macros/dispatches.rs#L804-L814) 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 [#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 [#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) # owner_cut_auto_lock_enabled (/docs/hyperparameters/owner-cut-auto-lock-enabled) 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 [#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 [#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 [#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 # owner_cut_enabled (/docs/hyperparameters/owner-cut-enabled) 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 [#how-it-works] The cut percentage itself is a **global, root-set** value ([`SubnetOwnerCut`](/code/pallets/subtensor/src/lib.rs#L1780), 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](/code/pallets/subtensor/src/coinbase/run_coinbase.rs#L302-L312), 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 [#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 [#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 # owner_immune_neuron_limit (/docs/hyperparameters/owner-immune-neuron-limit) `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 [#how-it-works] [`get_immune_owner_tuples`](/code/pallets/subtensor/src/subnets/registration.rs#L237-L277) (`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`](/code/pallets/subtensor/src/subnets/registration.rs#L280-L356) and [`trim_to_max_allowed_uids`](/code/pallets/subtensor/src/subnets/uids.rs#L147-L379) 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`](/code/pallets/subtensor/src/utils/misc.rs#L955-L964) in `pallets/subtensor/src/utils/misc.rs` rejects anything outside [`MinImmuneOwnerUidsLimit`](/code/pallets/subtensor/src/lib.rs#L2292)..[`MaxImmuneOwnerUidsLimit`](/code/pallets/subtensor/src/lib.rs#L2286) with [`InvalidValue`](/code/pallets/subtensor/src/macros/errors.rs#L252)). The default is 1: just the owner's primary hotkey. ## Reading and setting [#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 [#related] [`immunity_period`](/docs/hyperparameters/immunity-period), [`max_allowed_uids`](/docs/hyperparameters/max-allowed-uids), [`serving_rate_limit`](/docs/hyperparameters/serving-rate-limit) # registration_allowed (/docs/hyperparameters/registration-allowed) `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 [#how-it-works] Storage is [`NetworkRegistrationAllowed`](/code/pallets/subtensor/src/lib.rs#L1912) 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`](/code/pallets/subtensor/src/subnets/registration.rs#L38-L132) (`pallets/subtensor/src/subnets/registration.rs`): the extrinsic fails with [`SubNetRegistrationDisabled`](/code/pallets/subtensor/src/macros/errors.rs#L97) if [`get_network_registration_allowed`](/code/pallets/subtensor/src/utils/misc.rs#L661-L663) returns false. Both [`burned_register`](/code/pallets/subtensor/src/macros/dispatches.rs#L826-L832) and the legacy PoW [`register`](/code/pallets/subtensor/src/macros/dispatches.rs#L804-L814) extrinsic land in `do_register`, so the flag closes every registration path at once. The validity helper [`checked_allowed_register`](/code/pallets/subtensor/src/lib.rs#L2802-L2823) (`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 [#reading-and-setting] ``` btcli sudo get --netuid N --name registration_allowed ``` Root/governance only: [`sudo_set_network_registration_allowed`](/code/pallets/admin-utils/src/lib.rs#L685-L699) in the AdminUtils pallet requires the root origin (a boolean). It is not settable by the subnet owner via `btcli sudo set`. ## Related [#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) # rho (/docs/hyperparameters/rho) `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 [#how-it-works] The formula lives in [`sigmoid_safe`](/code/pallets/subtensor/src/epoch/math.rs#L198-L207) (`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`](/code/pallets/subtensor/src/epoch/run_epoch.rs#L340-L343)) 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`](/code/pallets/subtensor/src/epoch/run_epoch.rs#L1111-L1113)) 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 [#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 [#related] [`kappa`](/docs/hyperparameters/kappa) · [`tempo`](/docs/hyperparameters/tempo) · [`activity_cutoff`](/docs/hyperparameters/activity-cutoff) · [`max_validators`](/docs/hyperparameters/max-validators) # serving_rate_limit (/docs/hyperparameters/serving-rate-limit) `serving_rate_limit` is the minimum number of blocks between consecutive [`serve_axon`](/code/pallets/subtensor/src/macros/dispatches.rs#L650-L673) 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 [#how-it-works] When a neuron posts its axon endpoint, [`do_serve_axon`](/code/pallets/subtensor/src/subnets/serving.rs#L42-L104) (`pallets/subtensor/src/subnets/serving.rs`) checks [`axon_passes_rate_limit`](/code/pallets/subtensor/src/subnets/serving.rs#L165-L173): 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`](/code/pallets/subtensor/src/macros/errors.rs#L85). The same limit applies independently to [`serve_prometheus`](/code/pallets/subtensor/src/macros/dispatches.rs#L758-L767) via [`prometheus_passes_rate_limit`](/code/pallets/subtensor/src/subnets/serving.rs#L175-L183). 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 [#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 [#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) # subnet_emission_enabled (/docs/hyperparameters/subnet-emission-enabled) `subnet_emission_enabled` controls whether the subnet receives its share of network TAO emission into the pool. While it is false the chain injects no `tao_in` or `alpha_in` into the subnet's reserves and makes no `excess_tao` chain buys — the pool side of emission is zeroed. Everything else keeps running: `alpha_out` still pays miners and validators each epoch, the owner cut and root proportion are untouched, and pending emissions are preserved. The flag decides whether the subnet's market demand translates into actual TAO flowing into its pool. ## How it works [#how-it-works] Storage is [`SubnetEmissionEnabled`](/code/pallets/subtensor/src/lib.rs#L1394) in `pallets/subtensor/src/lib.rs`. New subnets register with it **false** ([`do_register_network`](/code/pallets/subtensor/src/subnets/subnet.rs#L142-L268) in `pallets/subtensor/src/subnets/subnet.rs`), so a freshly created subnet earns no TAO emission share until root turns it on. Each coinbase run [skips the pool-side terms for disabled subnets](/code/pallets/subtensor/src/coinbase/subnet_emissions.rs#L48-L70) and clears any stale per-block TAO-side emission storage. This is not the same flag as [`subnet_is_active`](/docs/hyperparameters/subnet-is-active). That one is the owner's one-shot [`start-call`](/docs/tx/start-call): it opens staking, alpha trading, and epochs. This one is root's emission valve. The two are independent — a subnet can be fully active, with trading and epochs running, while its TAO emission share stays off. Bringing a new subnet fully online takes both: the owner starts it, and root enables its emission. ## Reading and setting [#reading-and-setting] Anyone can check the flag: ```bash btcli query subnet-emission-enabled --netuid 89 --json ``` Root only — there is no `btcli sudo set` name for it and the subnet owner cannot flip it. The chain sudo key uses [`set-subnet-emission-enabled`](/docs/tx/set-subnet-emission-enabled), which takes multiple netuids and batches them atomically, wrapping the call in `Sudo.sudo`: ```bash btcli tx set-subnet-emission-enabled --netuids 89,127 --enabled -w sudo_key btcli tx set-subnet-emission-enabled --netuids 89 --no-enabled -w sudo_key ``` The [raw-call escape hatch](/docs/concepts/advanced) still works for a single netuid: ```bash btcli call AdminUtils.sudo_set_subnet_emission_enabled --args '{"netuid": 89, "enabled": true}' --sudo ``` ## Related [#related] [`subnet_is_active`](/docs/hyperparameters/subnet-is-active) · [`registration_allowed`](/docs/hyperparameters/registration-allowed) · [`set-subnet-emission-enabled`](/docs/tx/set-subnet-emission-enabled) · [`subnet_emission_enabled`](/docs/query/subnet-emission-enabled) · [Emissions](/docs/concepts/emissions) # subnet_is_active (/docs/hyperparameters/subnet-is-active) `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 [#how-it-works] The value is the [`SubtokenEnabled`](/code/pallets/subtensor/src/lib.rs#L2214) storage map in `pallets/subtensor/src/lib.rs` — "is subtoken trading enabled" — which defaults to **false** for a newly registered subnet. [`do_start_call`](/code/pallets/subtensor/src/subnets/subnet.rs#L531-L556) (`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`](/code/pallets/subtensor/src/lib.rs#L2826-L2832) 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`](/code/pallets/subtensor/src/macros/errors.rs#L212). * **No emissions.** [`get_subnets_to_emit_to`](/code/pallets/subtensor/src/coinbase/subnet_emissions.rs#L21-L32) (`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`](/code/pallets/subtensor/src/macros/dispatches.rs#L1093-L1101) skips the pending-cooldown schedule before start and applies at once ([`schedule_or_apply_ck`](/code/pallets/subtensor/src/staking/set_children.rs#L551-L585) in `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`](/code/pallets/admin-utils/src/lib.rs#L1779-L1792) (AdminUtils), but in normal operation it is written exactly once, by `start-call`. ## Reading and setting [#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 [#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) # target_regs_per_interval (/docs/hyperparameters/target-regs-per-interval) 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 [#how-it-works] The value lives in [`TargetRegistrationsPerInterval`](/code/pallets/subtensor/src/lib.rs#L2112) storage (u16, default **2**, so the cap is 6). Two places enforce the 3× cap: * [`do_root_register`](/code/pallets/subtensor/src/coinbase/root.rs#L77-L188) (`pallets/subtensor/src/coinbase/root.rs`) rejects a root registration with [`TooManyRegistrationsThisInterval`](/code/pallets/subtensor/src/macros/errors.rs#L99) 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`](/code/pallets/subtensor/src/lib.rs#L2802-L2823) (`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 [#reading-and-setting] ```bash btcli sudo get --netuid 12 --name target_regs_per_interval ``` Root/governance only — [`sudo_set_target_registrations_per_interval`](/code/pallets/admin-utils/src/lib.rs#L722-L742) 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 [#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 # tempo (/docs/hyperparameters/tempo) `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 [#how-it-works] Each subnet's epoch fires once `tempo` blocks have passed since its last epoch: [`should_run_epoch`](/code/pallets/subtensor/src/coinbase/run_coinbase.rs#L1117-L1132) (`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](/code/pallets/subtensor/src/coinbase/run_coinbase.rs#L388-L411), 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`](/code/pallets/subtensor/src/utils/misc.rs#L121-L125) 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 [#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`](/code/pallets/admin-utils/src/lib.rs#L1020-L1024) (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 [#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) # transfers_enabled (/docs/hyperparameters/transfers-enabled) 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 [#how-it-works] The flag lives in [`TransferToggle`](/code/pallets/subtensor/src/lib.rs#L1815) 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`](/code/pallets/subtensor/src/macros/errors.rs#L194) 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 [#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 [#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 # user_liquidity_enabled (/docs/hyperparameters/user-liquidity-enabled) `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 [#how-it-works] Under swap v3, `EnabledUserLiquidity` in the Swap pallet controlled per-subnet access to user LP calls ([`add_liquidity`](/code/pallets/swap/src/pallet/mod.rs#L226-L235), [`remove_liquidity`](/code/pallets/swap/src/pallet/mod.rs#L240-L247), [`modify_position`](/code/pallets/swap/src/pallet/mod.rs#L253-L261)). The balancer migration ([`pallets/swap/src/pallet/migrations/migrate_swapv3_to_balancer.rs`](/code/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`](/code/pallets/swap/src/pallet/mod.rs#L215-L221) 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`](/code/pallets/subtensor/src/rpc_info/subnet_info.rs#L468-L631) (`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 [#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 [#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 # weights_rate_limit (/docs/hyperparameters/weights-rate-limit) `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 [#how-it-works] The chain tracks each UID's last submission block (`LastUpdate`). [`check_rate_limit`](/code/pallets/subtensor/src/subnets/weights.rs#L1023-L1043) (`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`](/code/pallets/subtensor/src/macros/errors.rs#L81). Which call is limited depends on the commit-reveal setting. With commit-reveal off, the limit applies to [`set_weights`](/code/pallets/subtensor/src/macros/dispatches.rs#L70-L82) itself. With it on, the limit applies to the **commit** (failing with [`CommittingWeightsTooFast`](/code/pallets/subtensor/src/macros/errors.rs#L186)), 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 [#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 [#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 # weights_version (/docs/hyperparameters/weights-version) `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 [#how-it-works] Every [`set_weights`](/code/pallets/subtensor/src/macros/dispatches.rs#L70-L82) call carries a `version_key` argument. The chain compares it in [`check_version_key`](/code/pallets/subtensor/src/subnets/weights.rs#L1013-L1019) (`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`](/code/pallets/subtensor/src/macros/errors.rs#L83). 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 [#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 [#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 # yuma_version (/docs/hyperparameters/yuma-version) `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 [#how-it-works] [`get_subnet_hyperparams_v3`](/code/pallets/subtensor/src/rpc_info/subnet_info.rs#L468-L631) (`pallets/subtensor/src/rpc_info/subnet_info.rs`) computes it inline: 3 when [`Yuma3On`](/code/pallets/subtensor/src/lib.rs#L2204) is set for the subnet, 2 otherwise. No `YumaVersion` storage exists; the epoch itself [branches on `Yuma3On` directly](/code/pallets/subtensor/src/epoch/run_epoch.rs#L372) (`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 [#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 [#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) # yuma3_enabled (/docs/hyperparameters/yuma3-enabled) `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 [#how-it-works] The epoch function [branches on `Yuma3On`](/code/pallets/subtensor/src/epoch/run_epoch.rs#L372) (`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`](/code/pallets/subtensor/src/epoch/run_epoch.rs#L1297-L1320)), and dividends are `d_i = Σ_j B_ij × I_j`. * **Yuma3 path (on).** Bonds are read as fixed proportions ([`get_bonds_fixed_proportion`](/code/pallets/subtensor/src/epoch/run_epoch.rs#L1230-L1238)) without column normalization, the EMA runs through [`compute_bonds`](/code/pallets/subtensor/src/epoch/run_epoch.rs#L1333-L1360) — 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 [#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 [#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) # Benchmarks and weights (/docs/internals/benchmarks-and-weights) Every extrinsic in the runtime has a **weight** — a measure of the computational resources it consumes. Weights are used to calculate transaction fees and to prevent blocks from being overloaded. Weights are defined in `weights.rs` files inside each pallet and are generated by running benchmarks on reference hardware. ## Tools [#tools] | Tool | Purpose | | -------------------------- | ------------------------------------------------------------------- | | `scripts/benchmark_all.sh` | Generate `weights.rs` for one or all pallets (runs real benchmarks) | | `weight-compare` | Compare two `weights.rs` files and report drift (used by CI) | `weight-compare` lives in `support/weight-tools/` and has no heavy dependencies (no runtime build required). ## Adding a new pallet [#adding-a-new-pallet] 1. Write your benchmarks in `pallets//src/benchmarking.rs` using `#[benchmarks]` and `#[benchmark]` macros. 2. Create `pallets//src/weights.rs` manually. Copy the structure from any existing pallet (e.g. [`pallets/drand/src/weights.rs`](/code/pallets/drand/src/weights.rs)) and replace the function signatures with yours, using `Weight::from_parts(0, 0)` as the body so the pallet compiles immediately: ```rust pub trait WeightInfo { fn my_extrinsic() -> Weight; } pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { fn my_extrinsic() -> Weight { Weight::from_parts(0, 0) } } impl WeightInfo for () { fn my_extrinsic() -> Weight { Weight::from_parts(0, 0) } } ``` 3. Add `pub mod weights;` to your pallet's `lib.rs`. 4. Add `type WeightInfo: crate::weights::WeightInfo;` to your pallet's `Config` trait. 5. Use `T::WeightInfo::extrinsic_name()` in `#[pallet::weight(...)]` annotations instead of hardcoded `Weight::from_parts(...)`. 6. Wire up in [`runtime/src/lib.rs`](/code/runtime/src/lib.rs): ```rust type WeightInfo = pallet_::weights::SubstrateWeight; ``` 7. Add `type WeightInfo = ();` to all test mocks implementing your pallet's `Config`. 8. Register the pallet in the `define_benchmarks!` macro in [`runtime/src/lib.rs`](/code/runtime/src/lib.rs) so the benchmark runner can discover it: ```rust define_benchmarks!( // ...existing pallets... [pallet_, PalletInstance] ); ``` The benchmark scripts auto-discover pallets by scanning for directories under `pallets/` that have `src/weights.rs` plus `src/benchmarking.rs` (or `src/benchmarks.rs`), and that are registered in `define_benchmarks!`. No manual registration in scripts is needed. Careful with step 8 (`define_benchmarks!`): an unregistered pallet is filtered out by discovery and **silently skipped** in all-pallets mode and in CI; only the single-pallet invocation (`./scripts/benchmark_all.sh pallet_`) fails, with an "unknown pallet" error. CI will generate real weights automatically when the PR is opened. ## Adding a new extrinsic to an existing pallet [#adding-a-new-extrinsic-to-an-existing-pallet] 1. Write the benchmark in `benchmarking.rs`. 2. Add the function signature to the `WeightInfo` trait in `weights.rs`, and a `Weight::from_parts(0, 0)` body to both the `SubstrateWeight` and `()` impls so the pallet continues to compile: ```rust // in trait WeightInfo: fn new_extrinsic() -> Weight; // in both impls: fn new_extrinsic() -> Weight { Weight::from_parts(0, 0) } ``` 3. Add `#[pallet::weight(T::WeightInfo::new_extrinsic())]` to the extrinsic. CI will generate real weights automatically when the PR is opened. ## Parameterized weights [#parameterized-weights] For extrinsics whose cost scales with an input, use `Linear` parameters in the benchmark. You can use one or more parameters: ```rust // Single parameter #[benchmark] fn refund(k: Linear<1, 100>) { // setup with k contributors... #[extrinsic_call] _(origin, crowdloan_id); } // Multiple parameters #[benchmark] fn transfer_batch(n: Linear<1, 256>, m: Linear<1, 64>) { // setup with n recipients and m tokens each... #[extrinsic_call] _(origin, recipients, amounts); } ``` This generates weight functions with matching signatures: ```rust fn refund(k: u32) -> Weight; fn transfer_batch(n: u32, m: u32) -> Weight; ``` The generated weight includes base values plus per-parameter slope terms (e.g., `base + slope_k * k` for single parameter, or `base + slope_n * n + slope_m * m` for multiple). Reference them as: ```rust #[pallet::weight(T::WeightInfo::refund(T::MaxContributors::get()))] #[pallet::weight(T::WeightInfo::transfer_batch(recipients.len() as u32, max_tokens))] ``` ## CI workflow [#ci-workflow] The `Validate-Benchmarks` workflow (`.github/workflows/run-benchmarks.yml`) runs on every PR: 1. Builds the node with `--features runtime-benchmarks` 2. Runs benchmarks for every pallet, generating new `weights.rs` to temp files 3. Uses `weight-compare` to compare old vs new values with a **40% threshold** * Base weight: threshold-based (allows measurement noise) * Reads/writes: exact match (these are deterministic) * Component slopes: threshold-based for weights, exact for reads/writes 4. If drift is detected, prepares a patch in `.bench_patch/` 5. Adding the `apply-benchmark-patch` label auto-applies the patch To skip benchmarks on a PR, add the `skip-validate-benchmarks` label. This can be added at any point during the job — it's checked between expensive steps. ## Running benchmarks locally [#running-benchmarks-locally] ```sh # Build + generate weights for all pallets ./scripts/benchmark_all.sh # Build + generate weights for a single pallet ./scripts/benchmark_all.sh pallet_subtensor # Run benchmark unit tests (fast, no real measurements — just checks setup) cargo test -p pallet-subtensor --features runtime-benchmarks benchmarks # Compare two weight files cargo run -p subtensor-weight-tools --bin weight-compare -- \ --old pallets/foo/src/weights.rs \ --new /tmp/new_weights.rs \ --threshold 40 ``` ## Weight file structure [#weight-file-structure] Generated `weights.rs` files contain: * `WeightInfo` trait — one function per benchmarked extrinsic * `SubstrateWeight` impl — used in the runtime, references `T::DbWeight` * `()` impl — used in tests, references `RocksDbWeight` The `()` fallback uses `RocksDbWeight` constants directly. The production runtime uses `T::DbWeight::get()`, which [`runtime/src/lib.rs`](/code/runtime/src/lib.rs) also configures as `type DbWeight = RocksDbWeight` (matching the node's default RocksDb backend), so in this repo both impls resolve to the same constants. The files are generated by the `frame-benchmarking-cli` using the Handlebars template at `.maintain/frame-weight-template.hbs`. # Yuma Consensus (/docs/internals/consensus) Bittensor uses a subjective utility consensus mechanism called Yuma Consensus which rewards subnet validators with scoring incentive for producing evaluations of miner-value which are in agreement with the subjective evaluations produced by other subnet validators weighted by stake. Subnet servers receive incentive for their share of the utility according to the subnet validator consensus. Yuma Consensus pertains to subnet validation, instead of blockchain validation (substrate), so this writing will always refer to subnet validators and servers. ### Problem definition [#problem-definition] > How do we automatically detect and penalize reward manipulation in high-volume subjective utility networks? Most utility networks serve objectively measurable utility, such as data storage (Filecoin, Siacoin, Storj, Arweave), computation (Golem, TrueBit, Flux, Render, iExec RLC, Near), wireless networking (Helium), video streaming (Theta, Livepeer), and music streaming (Audius). Each of these networks employs specific, quantifiable metrics for their services, ranging from gigabytes of storage and CPU/GPU cycles to network coverage, bandwidth utilization and streaming quality. Objectively measurable utility makes it easy to detect and penalize reward manipulation, without the need for sophisticated consensus. Cryptographic proofs verify storage claims (Filecoin, Siacoin), outlier detection identifies and isolates inaccurate assessments (Ocean), cross-validation by multiple independent oracles reduce impact of single dishonest oracle (Chainlink), redundant computation prevents false result validation (Golem, TrueBit), random checks and challenges ensure honesty in computation results evaluation (iExec RLC), and continuous verification of actual network coverage (Helium). Objective utility networks typically need only basic majority consensus to correct malicious validation. Subjective utility networks (such as Steemit, Hive, LBRY, Minds, Voice, etc.) typically revolve around community-generated and curated content. Lower-volume content production allows for manual voting and tipping by users, or measurements of user and viewer engagement to calculate rewards. These subjective utility networks predominantly rely on manual mechanisms for reward distribution and detection of malicious activity. Community-driven reporting and moderation (Steemit, Hive, Minds), manual content evaluation (LBRY, Voice), and real identity verification (Voice) deter and penalize reward manipulation. Community oversight (as in Steemit) must identify wrongful downvoting, but only indirect remediation via counter-voting can penalize bad actors. The absence of voting reputation means users can only downvote the content of bad actors as retribution and thereby only damage content reputation, because no automated penalty mechanism exists. Similarly, users can upvote their own content and potentially receive nominal reward according to their stake, so reward manipulation may go unchecked. High-volume, on-demand generative content (as in Bittensor) demands automated evaluation and divide-and-conquer validation, but introduces subjectivity both in the automated value measures and mutually exclusive task subsets across subnet validators. A coalition of validators can collude to skew scoring of subnet servers in their favour, which is harder to detect because of the inherent subjectivity. Existing consensus mechanisms will fail to deter reward manipulation for such high-volume subjective utility networks, so the need for a more sophisticated consensus arises. *** ### Consensus Mechanism [#consensus-mechanism] Yuma Consensus guarantees long-term network honesty despite persistent adversarial presence in high-volume subjective utility networks. It directly penalizes selfish scoring by down-correction to the majority consensus and slashing of cabal voting stake, and also penalizes low-scoring of honest servers via forfeited validator rewards when cabals don’t score at consensus. Yuma Consensus is adversarially-resilient when majority stake is honest, via stake-based median scoring that punishes selfish weighting by minority stake (cabal). We clip excess weight above the maximum weight supported by at least $\kappa$-majority stake, which ensures selfish weighting is corrected and reduces voting stake of cabal validators via bonds penalty $\beta$. **Max weight supported by $\kappa$-majority stake**: We can plot a consensus graph for each active miner by ordering weights (y-axis) set on the target miner and recording cumulative stake (x-axis) of the source validators. The weight read at $x=1-\kappa$ ratio of total active stake is the stake-based median, which means that at least $\kappa$ stake supports that weight decision. Typically $\kappa=0.5$, which means that 50% of stake needs to agree on the relative utility of a miner, and we clip excess weight above this median to encourage subnet validators to vote fairly and uphold consensus. **Cabal sets low weight on honest majority**: The median calculation ignores selfish subsets that vote dishonestly (cabals), if they have minority stake (less than $\kappa$-majority), when they set low weights on the honest majority. This means that a minority cabal cannot negatively influence the consensus weight on honest servers. **Cabal sets high self-weight**: Cabal servers with poor utility will receive low weights from majority stake, and high self-weight from minority cabals will then get reduced to the low consensus. This means that minority cabals lose voting power as penalty for unfair voting while still receiving low consensus weight despite high self-weight. This consensus mechanism thus protects against selfish weighting if the majority stake is honest. *** ### Game-theoretic framework [#game-theoretic-framework] #### Preliminaries [#preliminaries] We consider a two-team game between (protagonist) honest stake ($0.5< S_H\le 1$) and (adversarial) cabal stake ($1 - S_H$), with $|H|$ honest and $|C|$ cabal players, that have $S_H = \sum_{i\in H}S_i$ honest stake and $1-S_H = \sum_{i\in C}S_i$ cabal stake. They compete for total fixed reward $E_H + E_C = 1$, with honest emission $E_H$ and cabal emission $E_C$, respectively. Then the stake updates to $S_H'=S_H+\tau E_H$ and $S_C'=(1 - S_H)+\tau E_C$, where $\tau$ decides the emission schedule. We normalize stake after the emission update, so that $\sum S'=1$. The honest objective $S_H\le E_H$ at least retains scoring power $S_H$ over all action transitions in the game, otherwise when $E_H\le S_H$ honest emission will erode to 0 over time, despite a starting condition of $0.5\lt S_H$. We assume honest stake sets objectively correct weights $W_H$ on itself, and $1 - W_H$ on the cabal, where honest weight $W_H$ represents an ongoing expense of the honest player such as utility production, sustained throughout the game. However, cabal stake has an action policy that freely sets weight $W_C$ on itself, and $1 - W_C$ on the honest player, at no cost to the cabal player. Independent smaller cabals will downweight each other thereby weakening overall adversarial power, so we consider the worst-case of a unified cabal. Specifically, honest players $i\in H$ set $W_H = \sum_{j\in H}W_{ij}$ self-weight and $1-W_H = \sum_{j\in C}W_{ij}$ weight on cabal players, while cabal players $i\in C$ set $W_C = \sum_{j\in C}W_{ij}$ self-weight and $1-W_C = \sum_{j\in H}W_{ij}$ weight on honest players. The cabal has the objective to maximize the required honest self-weight expense $W_H$ via $$ W_C^*=\arg\max_{W_C}E[W_H\ | \ S_H=E_H(S_H,W_H,W_C)].\tag{1} $$ We then assume the honest majority $S_H>0.5$ can counter with a consensus policy $\pi$ allowed to modify all weights modulo player labels, so it is purely based on the anonymous weight distribution itself, optimizing the Nash equilibrium $$ \min_{\pi}\max_{W_C}E[W_H\ | \ S_H=E_H(S_H,\pi(\mathbf{W}))].\tag{2} $$ #### Consensus policy [#consensus-policy] Majority stake enforces an independent and anonymous consensus policy $\pi$ (through a blockchain solution) that modifies the weights to minimize the expense $W_H$, which has been maximized by the cabal applying an objectively incorrect gratis self-weight $W_C$. Consensus aims to produce $\pi(\mathbf{W})\rightarrow (W'_H, W'_C)$ so that $W'_C=1-W'_H$, by correcting the error $\epsilon=W_C+W_H-1>0$. Note that the input cost $W_H$ remains fully expensed, and that $W'_H$ merely modifies the reward distribution that follows, but not knowing which players are honest or cabal (anonymous property). We propose a consensus policy that uses stake-based median as consensus weight $\overline{W_j}$, so that $\kappa$-stake (typically majority, i.e. $\kappa\ge 0.5$) decides the maximum supported weight on each subnet server $j$. The indicator function $\left\lbrace W_{ij} \ge w \right\rbrace$ adds stake $S_i$ if subnet validator $i$ supports a specific weight-level $w$ on server $j$. $$ \overline{W_j}=\arg \max_w \left( \sum_i S_i \cdot \left\lbrace W_{ij} \ge w \right\rbrace \ge \kappa \right)\tag{3} $$ The consensus policy applies weight correction $\overline{W_{ij}} = \min( W_{ij}, \overline{W_j} )$ to weight excess above consensus, which (i) restricts server incentive in case of selfish weighting, and (ii) penalizes selfish validators by slashing their voting stake (bonds) and validation rewards. The bonds penalty $\beta$ controls the degree to which weights for bonds are cut above consensus, which decides the penalty against subnet validator rewards. $$ \widetilde{W_{ij}} = (1-\beta) \cdot W_{ij} + \beta \cdot \overline{W_{ij}}\tag{4} $$ #### Validator bonding [#validator-bonding] A subnet validator $i$ bonds with server $j$, where the instant bond value is the normalized bonds penalty clipped weighted stake. $$ \Delta B_{ij} = S_i \cdot \widetilde{W_{ij}} \left/ \left( \sum_k S_k \cdot \widetilde{W_{kj}} \right) \right.\tag{5} $$ Validators can speculate on server utility by discovering and bonding to promising new servers, but to reward such exploration we use an exponential moving average (EMA) bond over time. Instant bond $\Delta B_{ij}$ at current timestep becomes an EMA observation. We sum typical $\alpha=10$% of the instant bond with remaining $90$% of previous EMA bond, to bond over time and reward early discovery while preventing abrupt changes and its exploitation potential. $$ B_{ij}^{(t)} = \alpha\cdot\Delta B_{ij} + (1-\alpha)\cdot B_{ij}^{(t-1)}\tag{6} $$ #### Reward distribution [#reward-distribution] Emission ratio $\xi$ decides the ratio of emission for validation rewards, and $1-\xi$ the ratio for server incentive. In the implementation $\xi$ is not a settable hyperparameter: incentive and dividends are each normalized to sum to one and then jointly renormalized, which fixes the split at $\xi=0.5$ whenever both sides are nonzero. $$ E_i = \xi \cdot D_i + (1-\xi) \cdot I_i\tag{7} $$ Subnet server incentive $I_j = R_j / \sum_k R_k$ is normalized server rank $R_j = \sum_i S_i \cdot \overline{W_{ij}}$ (sum of consensus-clipped weighted stake). Validation reward $D_i = \sum_j B_{ij} \cdot I_j$ is the subnet validator's EMA bond with server $j$ multiplied with server $j$ incentive. #### Mathematical definitions [#mathematical-definitions] | Variable | Equation | Description | | ------------------------ | ----------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------- | | Weight | $W_{ij}$ | Validator $i$ weight on server $j$. | | Stake | $S_i = S'_i / \sum_k S'_k$ | Validator $i$ relative stake. | | Server prerank | $P_j = \sum_i S_i \cdot W_{ij}$ | Sum of weighted stake. | | Server consensus weight | $\overline{W_j} = \arg \max_w \left( \sum_i S_i \cdot \left\lbrace W_{ij} \ge w \right\rbrace \ge \kappa \right)$ | $\kappa$-stake supported maximum weight on server $j$. | | Consensus-clipped weight | $\overline{W_{ij}} = \min( W_{ij}, \overline{W_j} )$ | Validator $i$ consensus-clipped weight on server $j$. | | Server rank | $R_j = \sum_i S_i \cdot \overline{W_{ij}}$ | Sum of consensus-clipped weighted stake. | | Server incentive | $I_j = R_j / \sum_k R_k$ | Ratio of incentive for server $j$. | | Server trust | $T_j = R_j / P_j$ | Relative server weight remaining after consensus-clip. | | Validator trust | $T_{vi} = \sum_j \overline{W_{ij}}$ | Relative validator weight remaining after consensus-clip. | | Bonds penalty | $\beta \in [0, 1]$ | Degree to cut bonds above consensus weight. | | Weight for bonds | $\widetilde{W_{ij}} = (1-\beta) \cdot W_{ij} + \beta \cdot \overline{W_{ij}}$ | Apply bonds penalty to weights. | | Validator bond | $\Delta B_{ij} = S_i \cdot \widetilde{W_{ij}} \left/ \left( \sum_k S_k \cdot \widetilde{W_{kj}} \right) \right.$ | Validator $i$ bond with server $j$. | | Validator EMA bond | $B_{ij}^{(t)} = \alpha\cdot\Delta B_{ij} + (1-\alpha)\cdot B_{ij}^{(t-1)}$ | Validator $i$ EMA bond with server $j$. | | Validator reward | $D_i = \sum_j B_{ij} \cdot I_j$ | Validator $i$ portion of incentive. | | Emission ratio | $\xi \in [0, 1]$ | Reward/incentive ratio for emission | | Emission | $E_i = \xi \cdot D_i + (1-\xi) \cdot I_i$ | Emission for node $i$. | #### Subtensor epoch [#subtensor-epoch] Subtensor blockchain nodes calculate consensus and rewards during each subnet [`epoch`](/code/pallets/subtensor/src/epoch/run_epoch.rs) with associated code excerpts as follows. Production runs the sparse variant ([`epoch_mechanism`](/code/pallets/subtensor/src/epoch/run_epoch.rs#L599-L1109), invoked per mechanism from coinbase); the dense excerpt below is the test-only analogue with identical math. The EMA-bond lines show the legacy branch — subnets with [`Yuma3On`](/code/pallets/subtensor/src/lib.rs#L2204) enabled compute bonds through the liquid-alpha path ([`compute_bonds`](/code/pallets/subtensor/src/epoch/run_epoch.rs#L1333-L1360)) instead. ```rust let mut weights: Vec> = Self::get_weights(netuid_index); // Weight let consensus: Vec = weighted_median_col(&active_stake, &weights, kappa); // Server consensus weight let mut clipped_weights: Vec> = weights.clone(); inplace_col_clip(&mut clipped_weights, &consensus); // Consensus-clipped weight let validator_trust: Vec = row_sum(&clipped_weights); // Validator trust let mut ranks: Vec = matmul(&clipped_weights, &active_stake); // Server rank inplace_normalize(&mut ranks); let incentive: Vec = ranks.clone(); // Server incentive let weights_for_bonds: Vec> = interpolate(&weights, &clipped_weights, bonds_penalty); // Weight for bonds let mut bonds_delta: Vec> = row_hadamard(&weights_for_bonds, &active_stake); // Validator bond inplace_col_normalize(&mut bonds_delta); let mut ema_bonds = Self::compute_ema_bonds_normal(&bonds_delta, &bonds, netuid); // Validator EMA bond inplace_col_normalize(&mut ema_bonds); let mut dividends: Vec = matmul_transpose(&ema_bonds, &incentive); // Validator reward inplace_normalize(&mut dividends); ``` *** ### Monte Carlo simulations [#monte-carlo-simulations] We consider a two-team game between (protagonist) honest stake ($0.5< S_H\le 1$) and (adversarial) cabal stake ($1 - S_H$), with $|H|$ honest and $|C|$ cabal players, that have $S_H = \sum_{i\in H}S_i$ honest stake and $1-S_H = \sum_{i\in C}S_i$ cabal stake. #### Network sizing [#network-sizing] A network size of $N=|H|+|C|=(|H_V|+|H_S|)+(|C_V|+|C_S|)=512$ and validator count of $|H_V|+|C_V|=64$ is considered for consensus guarantee experiments, and the honest/cabal ratio $|H|/N=S_H$ reflects the honest stake ratio $S_H$, but modifying extremes to ensure that each subset has at least one validator and at least one server. #### Stake sampling [#stake-sampling] For the Monte Carlo simulations we use Gaussian distributions for stake and weight assignments, and ensure that the honest/cabal ratios are met. Note that stake is only assigned to validator nodes $H_V$ and $C_V$ and not servers. Firstly, we sample initial validator ($i\in H_V\cup C_V$) stake values $S'_i \sim \mathcal{N}(1,\sigma_S^{2})$ with a typical $\sigma_S=0.3$ standard deviation, followed by clamping to avoid negative stake: $$ S'_i = \begin{cases} x & \text{if } x \sim \mathcal{N}(1, \sigma_S^2), x \ge 0 \\ 0 & \text{if } x \sim \mathcal{N}(1, \sigma_S^2), x < 0 \end{cases} $$ Then we normalize each honest/cabal subset and multiply by its stake proportion, which thus gives an overall normalized stake and the correct stake ratio for each subset: $$ S_{i\in H_V} = S_H \cdot S'_i \left/ \sum_{k\in H_V} S'_k\right.\qquad\qquad S_{i\in C_V} = (1-S_H)\cdot S'_i \left/ \sum_{k\in C_V}S'_k\right. $$ #### Weight sampling [#weight-sampling] Similarly, we randomize the weights that validators $H_V,C_V$ set on servers $H_S,C_S$. Specifically, honest players $i\in H$ set $W_H = \sum_{j\in H}W_{ij}$ self-weight and $1-W_H = \sum_{j\in C}W_{ij}$ weight on cabal players, while cabal players $i\in C$ set $W_C = \sum_{j\in C}W_{ij}$ self-weight and $1-W_C = \sum_{j\in H}W_{ij}$ weight on honest players. We firstly sample initial weights $W'_{ij} \sim \mathcal{N}(1,\sigma_W^{2})$ with various standard deviations ranging in $0\ge\sigma_W\ge0.4$, but then clamping to avoid negative weights: $$ W'_{ij} = \begin{cases} x & \text{if } x \sim \mathcal{N}(1, \sigma_S^2), x \geq 0 \\ 0 & \text{if } x \sim \mathcal{N}(1, \sigma_S^2), x < 0 \end{cases} $$ Weight setting between the two subsets forms quadrants $H_V\rightarrow H_S$, $H_V\rightarrow C_S$, $C_V\rightarrow H_S$, and $C_V\rightarrow C_S$, so we ensure those weight ratios are met by normalizing each weight subset and multiplying by the corresponding quadrant ratio: $$ W_{i\in H_V, j\in H_S} = W_H\cdot W'_{ij} \left/ \sum_{k\in H_S}W'_{ik}\right.\qquad\qquad W_{i\in H_V, j\in C_S} = (1-W_H)\cdot W'_{ij} \left/ \sum_{k\in C_S}W'_{ik}\right. $$ $$ W_{i\in C_V, j\in H_S} = (1-W_C)\cdot W'_{ij} \left/ \sum_{k\in H_S}W'_{ik}\right.\qquad\qquad W_{i\in C_V, j\in C_S} = W_C\cdot W'_{ij} \left/ \sum_{k\in C_S}W'_{ik}\right. $$ #### Emission calculation [#emission-calculation] Given the simulation parameters of the network size, validator count, a defined major/honest stake $S_H$, a defined major/honest utility $W_H$, and a defined minor/cabal self-weight $W_C$, we have now instantiated the network with randomly sampled stake and weights and can proceed with an emission calculation. We calculate the consensus $\overline{W_j} = \arg \max_w \left( \sum_i S_i \cdot \left\lbrace W_{ij} \ge w \right\rbrace \ge \kappa \right)$ for each server $j$, and calculate consensus-clipped weights $\overline{W_{ij}} = \min( W_{ij}, \overline{W_j} )$. This then gives us the adjusted weights that offers a measure of protection against reward manipulation. To calculate emissions for this epoch, we firstly calculate server rank $R_j = \sum_i S_i \cdot \overline{W_{ij}}$ then incentive $I_j = R_j / \sum_k R_k$, as well as validator bonds $\Delta B_{ij} = S_i \cdot \widetilde{W_{ij}} \left/ \left( \sum_k S_k \cdot \widetilde{W_{kj}} \right) \right.$ and rewards $D_i = \sum_j B_{ij} \cdot I_j$. Then we add up server incentive and validator bonds over honest nodes to obtain honest emission $E_H = \xi \cdot D_{i\in H} + (1-\xi) \cdot I_{i\in H}$ with a typical validator reward ratio of $\xi=0.5$. The objective is to prove major stake retention $S_H\ge E_H$ for a single epoch, which by extension proves retention over many epochs due to additive nature of EMA bonds, so we do not bother with validator EMA bonds in these experiments. The honest objective $S_H\le E_H$ at least retains scoring power $S_H$ over all action transitions in the game, otherwise when $E_H\le S_H$ honest emission will erode to 0 over time, despite a starting condition of $0.5\lt S_H$. *** ### Consensus guarantees [#consensus-guarantees] Yuma Consensus guarantees honest majority stake retention $S_H\le E_H$ even under worst-case adversarial attacks, given sufficiently large honest utility $W_H$. The specific honest stake and utility pairs that delineate the guarantees are complicated by natural variances inside large realistic networks. Therefore, we use extensive random sampling simulations (Monte Carlo studies) of large realistic networks and subject them to varying degrees of adversarial attacks, and calculate comprehensive consensus guarantees under representative conditions. Note the primary assumption is that the majority stake is honest, so we use majority/honest interchangeably, same with minority/cabal. #### Retention graphs [#retention-graphs] Consensus guarantees are dependent on stake, utility, and subnet validator behaviour, so we use 2D contour plots to comprehensively display guarantees across each possible set of conditions. The x-axis is major self-weight and the y-axis is minor self-weight, and each contour line is a specific major stake. Major/honest self-weight $W_H$ is the true honest utility, while minor/cabal self-weight $W_C$ is an arbitrary value a self-serving coalition may self-report.

To understand how we construct these plots, let us first consider contour plot for a single major/honest stake setting $S_H=0.6$. Here each contour value is the honest emission $E_H$, and we highlight at (1) the specific contour $E_H=0.6$ that matches the honest stake. This means that any weight setting on contour $E_H=S_H=0.6$ will retain honest stake, while any setting to the right of it will grow honest stake. Similarly, the specific emission contour plot for $S_H=0.7$, highlights the contour where the emission is $E_H=0.7$, which means with inflation the honest share ratio of $S_H=0.7$ can be retained if honest utility is at least $W_H>0.75$. A compound plot then combines all the highlighted $S_H=E_H$ contours from individual contour plots (e.g. $S_H=0.6$ and $S_H=0.7$), to show the overall retention profile. Generally, the higher the honest stake, the higher the honest utility requirement to retain stake proportion under adversarial weight setting. Retention graphs like these comprehensively capture consensus guarantees across all primary conditions, and we utilize these to analyze the effect of consensus hyperparameters. Subtensor integration tests run Monte Carlo simulations of large realistic networks under adversarial conditions, and constructs retention profiles to confirm consensus guarantees of the actual blockchain implementation. Retention profiles are reproducible by running test [`map_consensus_guarantees()`](https://github.com/RaoFoundation/subtensor/blob/main/pallets/subtensor/src/tests/consensus.rs) and plotting with [`map_consensus.py`](https://github.com/RaoFoundation/subtensor/blob/main/scripts/map_consensus.py). ```bash RUST_BACKTRACE=1 SKIP_WASM_BUILD=1 RUSTFLAGS="-C opt-level=3" cargo test --manifest-path=pallets/subtensor/Cargo.toml -- tests::consensus::map_consensus_guarantees --exact --ignored --nocapture > consensus.txt python scripts/map_consensus.py consensus.txt ``` #### Subjectivity variance [#subjectivity-variance] Yuma Consensus corrects reward manipulation in subjective utility networks, but the extent of subjectivity influences the exact consensus guarantees. In particular, we expect lower subjectivity to offer improved guarantees since there is stronger consensus. However, for higher variance in assigned weights it is easier to hide reward manipulation, we then expect poorer guarantees.

We assume normally distributed weights originating from a particular side, either honest or cabal, then we modify the weight deviation magnitude $\sigma(W)$ in terms of the mean weight $\mu(W)$. Weight deviations of $\sigma=0\mu$, $0.2\mu$, and $0.4\mu$ respectively require 60%, 67%, and 73% honest utility to preserve 60% honest stake. This confirms the expectation that higher subjectivity variance demands greater honest utility for honesty preservation. Bitcoin has its well-known 51% attack, while Yuma Consensus has a 40% stake + 30% utility attack under high subjectivity ($0.4\mu$). This means that a bad actor with 40% stake that produces 30% utility or more can gain majority stake over time. In practice, we typically observe standard deviations of $0.2\mu$ to $0.4\mu$, so Yuma Consensus guarantees honest 60% stake retention for \~70% honest utility or more. #### Majority ratio (κ) [#majority-ratio-κ] Hyperparameter `Kappa` sets the ratio of stake that decides consensus, with a typical recommended value of $\kappa=0.5$, which means that at least 50% of stake needs to be in consensus on the maximum weight assignable to a specific server. Reducing $\kappa$ weakens consensus and allows smaller cabals to manipulate rewards, in particular $\kappa=0.4$ demands higher honest utility when $S_H<0.6$ (such as $0.51$ and $0.55$). Increasing $\kappa$ demands greater honest stake, e.g. when $\kappa=0.6$ there is no protection for $S_H<0.6$ even with $W_H=1$. Hence $\kappa=0.5$ is typically the most sensible setting.

#### Bonds penalty (β) [#bonds-penalty-β] Yuma Consensus separately adjusts server incentive $I_j$ and validation reward $D_i$ to counter manipulation, where the extent of validation reward correction depends on the bonds penalty $\beta$. Server incentive always corrects fully, but validation reward correction is adjustable to control the penalty of out-of-consensus validation. Lower-stake validators may experience lower service priority, which can result in partial validation, or exploratory validators may skew weighting toward emergent high-utility. Full bonds penalty $\beta=1$ may not be desired, due to the presence of non-adversarial cases like these. Note that the chain default (`BondsPenalty`) is nonetheless full penalty $\beta=1$; subnets can lower it via hyperparameter.

We expect that greater bonds penalty will penalize out-of-consensus validators more, which means less emission going to cabals. Comprehensive simulation with $\beta = 0$, $0.5$, and $1$ respectively show 78%, 76%, and 73% honest utility requirement. This confirms the expectation, that greater bonds penalty means greater inflation going to the honest majority. #### Emission ratio (ξ) [#emission-ratio-ξ] Subnet servers need incentive to deliver high utility, and subnet validators need rewards to secure the network. We expect that more emission going to validators will improve security guarantees, since self-serving validation can then be economically disincentivized.

We set validation reward ratio at $\xi=0$, $0.25$, and $0.5$ and respectively observe 82%, 78%, 73% honest utility requirement for 60% honest stake preservation. This means that network security improves as the validation reward ratio is increased, although a significant server incentive ratio still needs to be maintained to ensure overall high utility. *** ### Reproduce Consensus Plots (Runpod) [#reproduce-consensus-plots-runpod] This guide demonstrates how to reproduce consensus retention profile plots on a minimal Runpod CPU instance. #### 1. Deploy Runpod Instance [#1-deploy-runpod-instance] Navigate to [https://www.runpod.io/console/deploy](https://www.runpod.io/console/deploy) and select the following: * **Pod Type:** CPU Pod, CPU5 (5.7 GHz • DDR5 RAM • NVMe) or equivalent. * **Instance Configuration:** Compute-Optimized (\$0.07/hr, 2 vCPUs, 4GB RAM). **Important:** Edit the template and set "Container Disk (Temporary)" to 20GB. This ensures sufficient disk space for the process. Retrieve the connection details, including the SSH command and port, under "Connect" -> "SSH over exposed TCP". You can optionally enable Jupyter access (`8888:localhost:8888`) if desired. Connect to your instance via SSH: ```bash ssh -L 8888:localhost:8888 root@ -p -i ~/.ssh/id_ed25519 # Replace placeholders ``` #### 2. Set up the Environment [#2-set-up-the-environment] 1. **Start a `tmux` session for persistence:** ```bash tmux ``` 2. **Update system packages and install prerequisites (Python, Rust, and dependencies):** ```bash sudo apt-get update && sudo apt install -y build-essential clang curl git make libssl-dev llvm libudev-dev protobuf-compiler python3 python3-pip \ && curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y \ && source ~/.cargo/env \ && rustup default stable \ && rustup update \ && rustup target add wasm32v1-none ``` 3. **Clone the Subtensor repository and checkout the relevant branch:** ```bash git clone https://github.com/RaoFoundation/subtensor.git cd subtensor git checkout main ``` #### 3. Simulate Networks and Generate Data [#3-simulate-networks-and-generate-data] The Subtensor integration tests simulate large, realistic networks under adversarial conditions to generate retention profiles that validate the blockchain's consensus guarantees. Building takes about 10 minutes, and the actual test itself another 15 minutes approximately. ```bash RUST_BACKTRACE=1 SKIP_WASM_BUILD=1 RUSTFLAGS="-C opt-level=3" cargo test --manifest-path=pallets/subtensor/Cargo.toml -- tests::consensus::map_consensus_guarantees --exact --ignored --nocapture > consensus.txt ``` This command runs the `map_consensus_guarantees` test and saves the output to `consensus.txt`. Replace `` with a float e.g. 1.0 (100% bonds penalty). #### 4. Generate Contour Plots [#4-generate-contour-plots] 1. **Create a Python virtual environment and install necessary libraries:** ```bash python3 -m venv .venv source .venv/bin/activate pip install numpy matplotlib jupyterlab ``` 2. **Run the plotting script:** ```bash python3 scripts/map_consensus.py consensus.txt ``` This generates an SVG file named `consensus_plot.svg` in the current directory. #### 5. Explore and Modify (Optional) [#5-explore-and-modify-optional] You can use Jupyter-lab to interactively explore and modify the generated plots: 1. **Start Jupyter-lab (on VPS):** ```bash jupyter-lab --allow-root --port=8888 ``` 2. **Connect to Jupyter:** Open the provided URL (e.g., `http://localhost:8888/tree?token=...`) in your local workstation web browser. 3. **Modify the plotting script:** Edit `scripts/map_consensus.py` to customize the plots, otherwise download the SVG file. #### Disclaimer [#disclaimer] > This reproduction procedure is provided as a guide and may require adjustments depending on your specific VPS environment and configuration. While every effort has been made to ensure accuracy and completeness, variations in system setup, software versions, or network conditions could affect the results. > > Please exercise caution when executing commands with root privileges and ensure you understand the potential implications before proceeding. The author assumes no responsibility for any issues arising from the use of this procedure. If you encounter problems or have suggestions for improvement, please open an issue on this repository. # Contributing (/docs/internals/contributing) ## Lifecycle of a pull request [#lifecycle-of-a-pull-request] 1. Develop your change on a branch and open a [pull request](https://github.com/RaoFoundation/subtensor/compare) targeting `main`. Start it as a draft until it's ready for other developers to look at. Any change to pallet or runtime code must be accompanied by unit and/or integration tests covering its edge cases — see [Testing](/docs/internals/testing) for how to run every suite. 2. CI validates the PR from several angles: * `check-rust.yml` — fmt, clippy, custom lints, zepter, `cargo test` * [Clone Upgrade Check](/docs/internals/mainnet-clone) — sudo-upgrades a clone of live mainnet with your runtime, then runs the clone regression tests, the [Python SDK suites](/docs/internals/sdk-tests), and the docs/website build. A runtime change that breaks the SDK or docs fails the PR. * `try-runtime.yml` — replays your migrations against live network state. * Spec Version Check — runtime-affecting PRs must bump `spec_version` above what mainnet is running, or carry the `no-spec-version-bump` label (see below). 3. Mark the PR "Ready for Review" once the Rust CI is green and request review from the core (Nucleus) team. Reviews may request changes; three positive reviews are required. 4. After approvals, you or an administrator merge to `main` — which starts the release train. New to the codebase? Start with the [repository layout](/docs/internals/repo-layout). ## The release train [#the-release-train] Merging to `main` triggers `release-train.yml`, a build-once promotion pipeline (full detail: [Release process](/docs/internals/release-process)): ``` push to main └─ build wasm once (deterministic srtool build) └─ deploy devnet ── smoke-check devnet └─ deploy testnet ── smoke-check testnet ── publish SDK rc └─ propose mainnet upgrade (environment gate + triumvirate multisig) ``` Key properties: * **The runtime is built exactly once per train.** Promotion between networks is a `setCode` extrinsic plus a smoke suite, never a rebuild — every network runs the identical wasm. * **The ship lever is the `spec_version` bump.** A merge without a bump builds and then no-ops at every deploy guard. This is why the Spec Version Check exists: without a bump (or the explicit opt-out label) your change would silently never ship. * **Human gates live in GitHub environment settings**, not in the workflow: devnet deploys automatically; testnet and mainnet promotion wait for whatever reviewers are configured on those environments. * **Mainnet is never upgraded directly by CI.** The train submits a multisig proposal (CI holds one key of a 2-of-2 deployment multisig with a `SudoUncheckedSetCode` proxy); the triumvirate approves 2-of-3 out-of-band. Once the upgrade executes on chain, `watch-mainnet-release.yml` cuts the GitHub release and publishes Docker images, the Python SDK, Rust crates, and the production website/docs. After your change deploys to testnet, it is your responsibility to verify it works there. If it doesn't, coordinate with a core team administrator and open a follow-up PR promptly. ## PR labels [#pr-labels] | Label | Effect | | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | | `red-team` | Marks feature additions/changes (informational) | | `blue-team` | Marks safety measures / dev-UX improvements (informational) | | `runtime` | Marks substantive runtime or pallet changes (informational) | | `breaking-change` | Notifies the relevant teams automatically — use for anything requiring synchronized changes elsewhere | | `no-spec-version-bump` | Skips the spec-version gate for changes that deliberately ship without a runtime release | | `skip-clone-upgrade` | Skips the mainnet-clone upgrade check — reserve for changes that cannot affect the runtime, SDK, or docs | | `mainnet-clone` | Spins up a live, publicly tunneled mainnet clone running your PR's runtime and posts the `wss://` endpoint as a PR comment for interactive testing | | `apply-benchmark-patch` | Applies the benchmark bot's proposed `weights.rs` patch to the PR | | `skip-cargo-audit` | Skips the dependency audit | ## Documentation [#documentation] All documentation lives in the repo-root `docs/` folder — the single source of 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/`): never hand-edit; regenerate with `website/apps/bittensor-website/scripts/generate.py` * Runtime internals and contributor docs: `docs/internals/` The docs/website build runs as part of the Clone Upgrade Check, so a PR that breaks the docs build fails CI. # Eco-tests (indexer contract) (/docs/internals/eco-tests) `eco-tests/` is a standalone crate (excluded from the cargo workspace) that pins the **storage shapes and runtime API signatures the TAO.com ecosystem indexer depends on**. The indexer reads chain state directly — storage items like `Consensus`, `Incentive`, `Weights`, `Bonds`, ownership and childkey maps, and runtime APIs like [`DelegateInfoRuntimeApi`](/code/pallets/subtensor/runtime-api/src/lib.rs#L27-L31) and [`StakeInfoRuntimeApi`](/code/pallets/subtensor/runtime-api/src/lib.rs#L67-L76). If a runtime change renames a storage item, changes a value type, or alters an API return shape, the indexer breaks in production even though nothing in this repo's own tests would notice. The tests are intentionally shallow: they mostly assert that each pinned storage item still exists with the expected key and value types (the test body is often just a typed `::get()` call that must compile) and that the runtime API signatures are unchanged. A compile failure *is* the signal. ## Running them [#running-them] The crate is excluded from the workspace, so run from its own directory: ```bash cd eco-tests SKIP_WASM_BUILD=1 cargo test ``` CI runs this on every PR (`eco-tests.yml`). ## When one fails on your PR [#when-one-fails-on-your-pr] A failure means your change breaks the data contract the indexer consumes. Two valid resolutions: 1. **Unintended breakage** — adjust your change to preserve the storage/API shape (e.g. keep the old item alongside the new one, or provide a migration plus an unchanged read path). 2. **Intentional change** — update the eco-test to the new shape. Any PR that touches `eco-tests/**` automatically requests review from the indexer liaison (`eco-tests-indexer-notify.yml`), so the indexer team hears about the change before it ships. Don't merge until they've acknowledged it. What you should **not** do is silently change the assertion to make CI green: the whole point of the suite is that the notification fires and the indexer team gets lead time. # Mainnet clone testing (/docs/internals/mainnet-clone) Every PR runs the **Clone Upgrade Check** (`runtime-checks.yml`): it builds your proposed runtime, applies it to a local copy of *real mainnet state* via a sudo upgrade, then runs regression tests and the Python SDK metadata drift gate against the upgraded chain. This catches problems unit tests can't - migrations that break on real storage, changed RPC shapes, SDK incompatibilities - before anything ships. This page explains what the check does and how to reproduce each step locally. ## How it works [#how-it-works] 1. **`node-subtensor build-patched-spec`** (invoked by `clones/scripts/clone-mainnet.sh`) spins up a temporary node, syncs current mainnet state from a bootnode, exports it as a raw chainspec, and patches it for local use: block authorship (Aura/Grandpa authorities) and the sudo key are handed to the well-known dev account **Alice**. The result is written to `clones/mainnet-clone-chainspec.json` (gitignored). 2. **`clones/scripts/start-local-clone.sh`** starts a single local validator from that chainspec on `ws://127.0.0.1:9944`, authoring blocks as Alice. 3. **`npm run runtime:update:alice`** (in `clones/js-tests/`) reads your built runtime wasm from `target/release/wbuild/node-subtensor-runtime/node_subtensor_runtime.compact.compressed.wasm` and submits `sudo(system.setCode(...))` from Alice. The clone is now running mainnet state under **your** runtime, including any migrations it triggered. 4. **`npm test`** runs the smoke test; CI then runs the SDK metadata drift gate (`python -m codegen.check --drift`) against the upgraded chain. ## Reproducing locally [#reproducing-locally] ```bash # 1. Build the node and your runtime cargo build --release -p node-subtensor # 2. Create (or reuse) the patched mainnet clone chainspec. # First run syncs mainnet state — this takes a while and needs disk space. # Later runs reuse the existing chainspec file. ./clones/scripts/clone-mainnet.sh # 3. Start the clone node (leave this running; use a second terminal for the rest) ./clones/scripts/start-local-clone.sh # 4. Sudo-upgrade the clone to your runtime cd clones/js-tests npm ci npm run runtime:update:alice # 5. Run the smoke test (what `npm test` runs in CI) npm test # 6. When you're done cd ../.. ./clones/scripts/stop-local-clone.sh ``` To re-sync fresh mainnet state instead of reusing the cached spec, delete `clones/mainnet-clone-chainspec.json` and rerun `clone-mainnet.sh`. `start-local-clone.sh` wipes the chain database (`clones/mainnet-clone/`) on every start, so each run replays your upgrade from the cached state — restart the node to retry a failed upgrade from scratch. ### Running the SDK drift gate against the clone [#running-the-sdk-drift-gate-against-the-clone] ```bash cd sdk/python uv sync --locked --all-extras --dev uv run pytest # offline unit tests uv run python -m codegen.check --drift ws://127.0.0.1:9944 ``` ### Targeted regression tests [#targeted-regression-tests] `clones/js-tests/package.json` has one npm script per regression test, e.g.: ```bash npm run test:balancer-operation npm run test:locks-conviction npm run test:proxy-filter-security-regressions ``` Point a test at a different endpoint with `WS_ENDPOINT=ws://...`, and at a different runtime wasm with `RUNTIME_WASM_PATH=/path/to/runtime.wasm`. ## When it fails on your PR [#when-it-fails-on-your-pr] * **Upgrade step fails** — your runtime doesn't apply cleanly to mainnet state. Usually a migration panicking on real storage; reproduce with steps 1–4 above and watch the node log. Complement with [try-runtime](/docs/internals/testing#migration-tests-try-runtime). * **Smoke or regression tests fail** — your change altered on-chain behavior or storage shapes that existing features depend on. Run the specific failing script locally (see targeted tests above). * **SDK checks fail** — you changed calls, events, errors, or queries the SDK has codegen'd bindings for. Regenerate `bittensor/_generated/` against your upgraded clone (`python -m codegen ws://127.0.0.1:9944`) and commit the result; see [Testing](/docs/internals/testing#other-suites). * **Skipping**: adding the `skip-clone-upgrade` label to a PR skips the check; reserve that for changes that can't affect the runtime, SDK, or docs. # Release process (/docs/internals/release-process) Shipping is a single pipeline: **`release-train.yml`** runs on every push to `main`, builds the runtime once, and promotes that identical wasm through the networks. A second workflow, **`watch-mainnet-release.yml`**, watches the chain and publishes release artifacts once the mainnet upgrade actually executes. ## The release train [#the-release-train] ``` push to main └─ srtool build (once per train) └─ deploy devnet ───── smoke-check devnet └─ deploy testnet ── smoke-check testnet ── publish SDK rc to PyPI └─ propose mainnet upgrade (multisig) ``` ### Build once (srtool) [#build-once-srtool] The train builds the runtime with [srtool](https://github.com/paritytech/srtool) — a pinned Docker build environment that makes the wasm **byte-reproducible**. The artifact (`subtensor.wasm` + digest) is uploaded once and reused by every deploy job; promotion never rebuilds. Anyone can verify the hash independently by running `scripts/srtool/build-srtool-image.sh` followed by `scripts/srtool/run-srtool.sh` locally (see [Repository scripts](/docs/internals/scripts)). ### The ship lever: `spec_version` [#the-ship-lever-spec_version] Every deploy job first compares the built runtime's [`spec_version`](/code/runtime/src/lib.rs#L238) (`runtime/src/lib.rs`) with what the target chain is running, and **no-ops unless the build is newer**. Consequences: * A merge without a `spec_version` bump builds and then does nothing — this is deliberate, and it's why the Spec Version Check on PRs exists (label `no-spec-version-bump` to opt out knowingly). * Stale queued trains are harmless: they no-op at each guard. * Rollback is not a concept here — you ship a *newer* fixed version. ### Promotion gates [#promotion-gates] Human approval lives in GitHub **environment settings**, not workflow code: | Stage | Environment | Gate | | ------- | ----------- | -------------------------------------------------------------------------------------------- | | devnet | `devnet` | none — deploys automatically, then runs the devnet smoke suite | | testnet | `testnet` | environment reviewers (one-click approval), then smoke suite + SDK release candidate to PyPI | | mainnet | `mainnet` | environment reviewers **plus** the multisig ceremony below | Deploys to devnet and testnet are direct `setCode` transactions via the CI deploy key. After the on-chain `spec_version` is verified, the train moves the corresponding network mirror branch. That branch push triggers both Docker publishers from the exact deployed commit, updating the matching `:devnet` or `:testnet` tag in `ghcr.io/raofoundation/subtensor` and `ghcr.io/raofoundation/subtensor-localnet`. It does not move either package's `:latest` tag. The smoke suite then validates the live network while the independent image builds run. ### The mainnet multisig ceremony [#the-mainnet-multisig-ceremony] CI never upgrades mainnet directly. The train's final job submits a **multisig proposal**: the CI key is one half of a 2-of-2 deployment multisig that holds a `SudoUncheckedSetCode` proxy on the sudo key. The triumvirate then approves the proposal 2-of-3 **out-of-band** — no GitHub credential can unilaterally change the mainnet runtime. Until they sign, nothing happens on chain. For rehearsal, the `mainnet-clone` PR label spins up a live clone of mainnet running your runtime with a public endpoint ([mainnet clone testing](/docs/internals/mainnet-clone)). ## The release watcher [#the-release-watcher] `watch-mainnet-release.yml` polls mainnet every 10 minutes. When the on-chain `spec_version` matches the `main` branch and no GitHub release exists for it yet, it cuts the release train's artifacts: 1. **GitHub release** `v` 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) 5. **Production website/docs** to Vercel This ordering means the release always reflects what is *actually running on mainnet*, not what was merged. 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 publishes the immutable release tag in its separate package; localnet `:latest` remains an alias for the current `:main` branch image, while `:devnet` and `:testnet` follow their respective branches. ## Hotfixes [#hotfixes] The same pipeline applies: land the fix on `main` with a `spec_version` bump and let the train promote it. There is no side channel that skips devnet and testnet validation. # Repository layout (/docs/internals/repo-layout) The repo is a monorepo: the Substrate chain, the Python SDK, the docs, and the websites all live together so a single PR (and a single CI run) can validate a runtime change against everything it might break. ## The chain (Rust, cargo workspace) [#the-chain-rust-cargo-workspace] | Directory | What it is | | -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `pallets/` | The FRAME pallets — the chain's business logic. `pallets/subtensor/` is the core (staking, subnets, emissions, consensus); others include `swap` (liquidity), `admin-utils`, `commitments`, `crowdloan`, `drand`, `limit-orders`, `proxy`, `shield`, `transaction-fee`, `utility` | | `runtime/` | Composes the pallets into the actual runtime (`construct_runtime!`), plus migrations, runtime APIs, and the [`spec_version`](/code/runtime/src/lib.rs#L238) (`runtime/src/lib.rs`) — bump it to ship a release | | `node/` | The node binary: networking, RPC, chain specs for each network (`node/src/chain_spec/`), and the `build-patched-spec` subcommand used by [mainnet clone testing](/docs/internals/mainnet-clone) | | `precompiles/` | EVM precompiles exposing substrate functionality (staking, balance transfers, …) to EVM contracts | | `chain-extensions/` | ink!/wasm contract chain extensions — the substrate-side counterpart of `ink-contract/` (see [Wasm contracts](/docs/internals/wasm-contracts)) | | `primitives/` | Small shared crates: `safe-math`, `share-pool`, `swap-interface` | | `common/` | `subtensor-runtime-common` — types shared between runtime, node, and pallets (`NetUid`, balances, …) | | `support/` | Tooling crates: custom lints (`linting/`), proc macros (`macros/`), `procedural-fork`, `weight-tools` (the `weight-compare` CI gate), `tools` | | `src/` + `build.rs` (root) | A stub crate whose build script runs the custom lints in CI — not application code | | `vendor/` | Vendored third-party crates: `w3f-bls` (upstream 0.1.3 plus a no-std fix, formerly the RaoFoundation/bls fork) — consumed by `pallets/drand`, the runtime, and `sdk/bittensor-core`, and patched over the crates.io version so `tle` uses the same copy | ## Tests and test harnesses [#tests-and-test-harnesses] | Directory | What it is | | ------------ | ------------------------------------------------------------------------------------------------------------- | | `ts-tests/` | Moonwall/zombienet TypeScript integration tests ([Testing](/docs/internals/testing)) | | `clones/` | Mainnet-clone harness: scripts + JS regression tests ([Mainnet clone testing](/docs/internals/mainnet-clone)) | | `eco-tests/` | Indexer contract tests, excluded from the workspace ([Eco-tests](/docs/internals/eco-tests)) | ## SDK, docs, web [#sdk-docs-web] The `sdk/` tree holds everything client-side: one shared Rust core, one thin binding crate per language, and one product directory per language surface. | Directory | What it is | | ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `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/` 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 | ## Chain data and operations [#chain-data-and-operations] | Directory / file | What it is | | ----------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `chainspecs/` | Committed chain specs. The raw finney/testfinney specs are loaded by docker-compose and the clone scripts; **all six files are genesis anchors** for `scripts/build_all_chainspecs.sh` — the genesis wasm is not reproducible, so never delete them | | `snapshot.json` | Genesis state loaded by the embedded finney/test\_finney chain specs and bundled into Docker images | | `scripts/` | CI and developer scripts — each one documented in [Repository scripts](/docs/internals/scripts) | | `.github/workflows/` | CI: PR checks, the [release train](/docs/internals/release-process), Docker publishing | | `Dockerfile` / `docker-compose.yml` | Production node image and compose services for running mainnet/testnet nodes | | `Dockerfile-localnet` / `docker-compose.localnet.yml` | The localnet image used for [local development](/docs/guides/local-development) and Rust SDK e2e tests | # Rust setup (/docs/internals/rust-setup) This guide is for reference only, please check the latest information on getting starting with Substrate [here](https://docs.polkadot.com/develop/parachains/install-polkadot-sdk/). This page will guide you through the **2 steps** needed to prepare a computer for **Substrate** development. Since Substrate is built with [the Rust programming language](https://www.rust-lang.org/), the first thing you will need to do is prepare the computer for Rust development - these steps will vary based on the computer's operating system. Once Rust is configured, you will use its toolchains to interact with Rust projects; the commands for Rust's toolchains will be the same for all supported, Unix-based operating systems. ## Build dependencies [#build-dependencies] Substrate development is easiest on Unix-based operating systems like macOS or Linux. The examples in the [Substrate Docs](https://docs.polkadot.com) use Unix-style terminals to demonstrate how to interact with Substrate from the command line. ### Ubuntu/Debian [#ubuntudebian] Use a terminal shell to execute the following commands: ```bash sudo apt update # May prompt for location information sudo apt install -y git clang curl libssl-dev llvm libudev-dev make pkg-config protobuf-compiler ``` ### Arch Linux [#arch-linux] Run these commands from a terminal: ```bash pacman -Syu --needed --noconfirm curl git clang ``` ### Fedora [#fedora] Run these commands from a terminal: ```bash sudo dnf update sudo dnf install clang curl git openssl-devel ``` ### OpenSUSE [#opensuse] Run these commands from a terminal: ```bash sudo zypper install clang curl git openssl-devel llvm-devel libudev-devel ``` ### macOS [#macos] > **Apple M1 ARM** > If you have an Apple M1 ARM system on a chip, make sure that you have Apple Rosetta 2 > installed through `softwareupdate --install-rosetta`. This is only needed to run the > `protoc` tool during the build. The build itself and the target binaries would remain native. Open the Terminal application and execute the following commands: ```bash # Install Homebrew if necessary https://brew.sh/ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)" # Make sure Homebrew is up-to-date, install protobuf and openssl brew update brew install protobuf openssl llvm@16 ``` Also, add the following lines at the end of your \~/.zshrc: ``` # LLVM 16 from Homebrew export PATH="/opt/homebrew/opt/llvm@16/bin:$PATH" export CC="/opt/homebrew/opt/llvm@16/bin/clang" export CXX="/opt/homebrew/opt/llvm@16/bin/clang++" export LIBCLANG_PATH="/opt/homebrew/opt/llvm@16/lib/libclang.dylib" export LDFLAGS="-L/opt/homebrew/opt/llvm@16/lib" export CPPFLAGS="-I/opt/homebrew/opt/llvm@16/include" ``` ### Windows [#windows] ***PLEASE NOTE:*** Native Windows development of Substrate is *not* very well supported! It is *highly* recommend to use [Windows Subsystem Linux](https://docs.microsoft.com/en-us/windows/wsl/install-win10) (WSL) and follow the following [instructions](https://docs.polkadot.com/develop/parachains/install-polkadot-sdk/#windows-wsl). ## Rust developer environment [#rust-developer-environment] This guide uses the [rustup.rs](https://rustup.rs) installer and the `rustup` tool to manage the Rust toolchain. First install and configure `rustup`: ```bash # Install curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh # Configure source ~/.cargo/env ``` This repository pins its Rust version and the `wasm32v1-none` target in `rust-toolchain.toml`, so `rustup` installs the correct toolchain automatically the first time you run `cargo` inside the repo. Alternatively, run the setup script: ```bash ./scripts/init.sh ``` ## Test your set-up [#test-your-set-up] Now the best way to ensure that you have successfully prepared a computer for Substrate development is to follow the steps in [our first Substrate tutorial](https://docs.polkadot.com/tutorials/v3/create-your-first-substrate-chain/). ## Troubleshooting builds [#troubleshooting-builds] To see what Rust toolchain you are presently using, run: ```bash rustup show ``` Inside the repository the active toolchain should be the version pinned in `rust-toolchain.toml` (with the `wasm32v1-none` target installed), regardless of your global default — `rustup` reads the pin automatically. If the pinned toolchain or its target is missing, install it explicitly: ```bash rustup toolchain install $(grep -oE '[0-9]+\.[0-9]+' rust-toolchain.toml | head -1) rustup target add wasm32v1-none ``` Substrate uses [WebAssembly](https://webassembly.org) (Wasm) to produce portable blockchain runtimes, which is why the `wasm32v1-none` target is required. Unlike upstream Substrate development, this repository does not use nightly Rust for the runtime build — the pinned toolchain builds everything. # Repository scripts (/docs/internals/scripts) Every script in `scripts/` is either wired into CI/Docker or kept as a manual developer tool. Unreferenced scripts were removed in the July 2026 cleanup (`raonet.sh`, `run/subtensor.sh`, `install_rust.sh`, `build.sh`, `publish.sh`, `release_notes.rs`, `specs/raonet.json`, `try-runtime-upgrade.sh`, `update-deps-to-path.sh` — the latter two are replaced by the inline instructions below). ## Used by CI / Docker [#used-by-ci--docker] Do not remove or rename these without updating their consumers. | Script | Used by | | ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `benchmark_action.sh` | `run-benchmarks.yml` — validates benchmark weights on PRs | | `benchmark_all.sh` | Manual companion to the above; regenerates `weights.rs` for all pallets using `.maintain/frame-weight-template.hbs` (see [Benchmarks and weights](/docs/internals/benchmarks-and-weights)) | | `discover_pallets.sh` | Helper sourced by both benchmark scripts | | `build_all_chainspecs.sh` | `update-chainspec.yml` — rebuilds `chainspecs/*.json` while preserving each file's genesis. The genesis wasm is not reproducible across build architectures, so the committed chainspec files are the canonical genesis source. Never delete them. | | `docker_entrypoint.sh` | `Dockerfile` entrypoint for both image targets | | `fix_rust.sh` | PR template, AI-review tooling, and agent skills; runs fmt + clippy fix + zepter | | `install_build_env.sh` | `check-bittensor-e2e-tests.yml`, `docker-localnet.yml`, `Dockerfile-localnet` | | `install_prebuilt_binaries.sh` | `Dockerfile-localnet` | | `localnet.sh` | E2E CI, `Dockerfile-localnet`, SDK EVM setup, and the [local development guide](/docs/guides/local-development) — spins up a local dev chain (writes `specs/local.json`, gitignored) | | `localnet_patch.sh` | E2E CI and `docker-localnet.yml` | | `srtool/build-srtool-image.sh` | `release-train.yml` — builds the local srtool Docker image | ## Chainspec files [#chainspec-files] The six JSON files in `chainspecs/` look like build artifacts but are all load-bearing. The genesis wasm is **not reproducible across build architectures**, so the committed files are the canonical genesis source: `build_all_chainspecs.sh` reads the genesis out of each existing file and splices it into the freshly built spec. Deleting any of them breaks the ability to regenerate specs with a matching genesis. Never delete them. | File | Role | | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `raw_spec_finney.json` | Loaded at runtime by `docker-compose.yml` (mainnet nodes), `clones/scripts/clone-mainnet.sh`, and bundled into Docker images; also the finney genesis anchor | | `raw_spec_testfinney.json` | Loaded by `docker-compose.yml` (testnet nodes) and bundled into Docker images; testfinney genesis anchor | | `raw_spec_devnet.json` | Not loaded directly (the node embeds its devnet config in `node/src/chain_spec/devnet.rs`), but is the devnet genesis anchor for `build_all_chainspecs.sh` | | `plain_spec_finney.json` | Human-readable counterpart maintained by `build_all_chainspecs.sh`; genesis anchor for the plain finney spec | | `plain_spec_testfinney.json` | Same, for testfinney | | `plain_spec_devnet.json` | Same, for devnet | `update-chainspec.yml` (manual dispatch) runs `build_all_chainspecs.sh` and commits the result. The localnet spec is different: `localnet.sh` generates `scripts/specs/local.json` on the fly and it is gitignored. ## Doc-referenced [#doc-referenced] | Script | Purpose | | ------------------ | ----------------------------------------------------------------------------------------------- | | `code-coverage.sh` | Manual `cargo tarpaulin` coverage run over the workspace (requires `cargo-tarpaulin`) | | `init.sh` | Rust toolchain init referenced by the [local development guide](/docs/guides/local-development) | | `map_consensus.py` | Generates the consensus plots referenced in [Yuma Consensus](/docs/internals/consensus) | ## Manual developer tools [#manual-developer-tools] These have no CI consumers but are kept for occasional use. ### `srtool/run-srtool.sh` [#srtoolrun-srtoolsh] Reproduces the deterministic srtool runtime build locally so you can verify that the wasm hash CI proposed on-chain (via `release-train.yml`, which inlines the same `docker run`) matches an independent build. Run `srtool/build-srtool-image.sh` first to create the local `srtool` image. Requires `CARGO_HOME` to be set. Output lands in `runtime/node-subtensor/subtensor-digest.json`. ### Running try-runtime locally [#running-try-runtime-locally] CI tests runtime migrations on every PR via `.github/actions/try-runtime`. For an ad-hoc local run against an arbitrary endpoint or snapshot (requires `try-runtime-cli`): ```bash cargo build -p node-subtensor-runtime --release --features "metadata-hash,try-runtime" # Against a live chain try-runtime \ --runtime ./target/release/wbuild/node-subtensor-runtime/node_subtensor_runtime.compact.wasm \ on-runtime-upgrade \ --no-weight-warnings --disable-spec-version-check --disable-idempotency-checks \ --checks=all --blocktime 12000 \ live --uri wss://your-node:443 # Against a previously captured snapshot: replace the last line with # snap --path ./snapshot.bin ``` ### `test_specific.sh` [#test_specificsh] Runs a single pallet test file or case quickly, skipping the wasm build: ```bash ./scripts/test_specific.sh [pallet] [features] # defaults: pallet-subtensor, pow-faucet ``` Enables `--nocapture --exact` and `RUST_LOG=DEBUG`. ### Working against local `polkadot-sdk` / `frontier` checkouts [#working-against-local-polkadot-sdk--frontier-checkouts] When hacking on the forked `polkadot-sdk` or `frontier` alongside this repo, point the git dependencies at your local checkout with a `[patch]` section at the bottom of the root `Cargo.toml` instead of rewriting each dependency: ```toml [patch."https://github.com/RaoFoundation/polkadot-sdk.git"] sp-core = { path = "../polkadot-sdk/substrate/primitives/core" } # ...one entry per crate you are modifying ``` Cargo resolves everything else from the git dependency as usual. Don't commit the patch section. # Python SDK tests (/docs/internals/sdk-tests) The Python SDK lives in `sdk/python/` and is tested on every PR by the [Runtime Checks](/docs/internals/testing): offline gates first, then a metadata drift gate against a sudo-upgraded mainnet clone. A runtime change can fail your PR through these gates even if you never touched Python - this page explains how to run them and what to regenerate when metadata changes. Everything runs through [uv](https://docs.astral.sh/uv/) against the locked environment, orchestrated by the `justfile` in `sdk/python/`: ```bash cd sdk/python just sync # uv sync --locked --all-extras --dev just check # all offline gates, same as CI: lint, typecheck, unit tests, codegen-static ``` The SDK's native module (`bittensor_core` — crypto, SCALE codec, extrinsic assembly) is the `sdk/bittensor-core-py` crate. In the monorepo it resolves as a uv path source, so `just sync` builds the wheel from source — you need the repo's [Rust toolchain](/docs/internals/rust-setup) for SDK development here (PyPI users get prebuilt wheels). After changing the Rust side, rebuild the installed module: ```bash uv sync --reinstall-package bittensor-core # or: maturin develop --release -m ../bittensor-core-py/Cargo.toml ``` ## The test layers [#the-test-layers] | Layer | Needs a chain? | Command | | -------------------------- | -------------- | -------------------------------- | | Unit tests (`tests/unit/`) | No | `just test` (or `uv run pytest`) | | Codegen static gates | No | `just codegen-static` | | Codegen drift gate | Yes | `just drift [endpoint]` | The Python test tree is offline-only. Run a single test the usual pytest way: ```bash uv run pytest tests/unit/test_codec_golden.py -k storage_keys -x ``` ## How unit tests avoid the network [#how-unit-tests-avoid-the-network] Two mechanisms make the offline suite possible: * **`FakeSubstrate`** (`tests/harness/fake_substrate.py`): the SDK's only chain-access seam is the `Substrate` protocol (`bittensor/_substrate.py`); the executor, intents, reads, and namespaces never touch a websocket directly. `FakeSubstrate` implements the whole protocol over plain dicts — `seed(module, item, params, value)` pins storage entries, unseeded items fall back to permissive defaults, and submitted extrinsics are recorded (never applied) so tests assert on the composed call. * **Golden fixtures** (`tests/fixtures/golden.json`, \~1.3 MB, committed): a corpus of byte-exact artifacts — storage keys, composed calls, signing payloads, extrinsic encodings — recorded from a live localnet along with the raw metadata they were produced against. The golden tests (`test_codec_golden.py`, `test_storage_golden.py`) replay these through the codec using only the recorded metadata, proving the wire format stays byte-identical without a node. * **Shape corpus** (`tests/fixtures/shape_corpus/`, committed): the codec's decoded-*shape* contract — `(type id, SCALE bytes, decoded value)` triples sampled across the whole portable registry. `test_shape_corpus.py` replays it through whatever codec the transport currently uses, and the Rust core runs the same corpus natively (`cargo test -p bittensor-core`), so the Python objects a decode produces can never silently change shape. Re-record with `scripts/record_shape_corpus.py` only when the contract itself is meant to change. ## Chain-facing SDK coverage [#chain-facing-sdk-coverage] The old Python localnet e2e tests were migrated to Rust and live in `sdk/bittensor-core/tests/e2e.rs`. To run them against an existing node: ```bash E2E_ENDPOINT=ws://127.0.0.1:9944 cargo test -p bittensor-core --test e2e -- --nocapture ``` Without `E2E_ENDPOINT`, the Rust harness starts a disposable localnet Docker container from `LOCALNET_IMAGE`. ## After a runtime change: what to regenerate [#after-a-runtime-change-what-to-regenerate] The SDK's call/query/error bindings (`bittensor/_generated/`) are **generated from chain metadata** and committed. CI enforces two things: * **Static gates** (offline, every PR): every call is wrapped or explicitly raw-only (`codegen.check --coverage`), and every classified error name still exists (`codegen.check --names`). * **Drift gate**: the committed `_generated/` must match the node's actual metadata (`codegen.check --drift `). If your runtime change adds/removes/modifies extrinsics, storage, events, or errors, regenerate against a node running your runtime and commit the result: ```bash cd sdk/python just regen # ws://127.0.0.1:9944 by default ``` `just regen` does two things: `python -m codegen ` rewrites `bittensor/_generated/`, and `scripts/record_golden.py` re-records `tests/fixtures/golden.json`. Re-record the golden fixture **deliberately** — it is the byte-exactness baseline, so eyeball the diff before committing. Never hand-edit `_generated/` (ruff excludes it from formatting for the same reason). A convenient node to regen against is the sudo-upgraded [mainnet clone](/docs/internals/mainnet-clone), since that's exactly what CI tests you with. # Testing (/docs/internals/testing) The repo has several test layers. CI runs all of them on every PR, so knowing how to reproduce each locally saves round-trips: | Suite | What it covers | Run locally | CI workflow | | ---------------------------------------------- | ----------------------------------------------------------------------------------------------- | ----------------------------------------- | ------------------------------- | | Rust unit tests | Pallet and runtime logic | `cargo test --workspace` (below) | `check-rust.yml` | | TypeScript / Moonwall | End-to-end chain behavior, EVM, zombienet multi-node | `ts-tests/` (below) | `typescript-e2e.yml` | | Migration checks | [`on_runtime_upgrade`](/code/pallets/subtensor/src/macros/hooks.rs#L41-L189) against live state | try-runtime CLI (below) | `try-runtime.yml` | | [Mainnet clone](/docs/internals/mainnet-clone) | Runtime upgrade + regression tests on cloned mainnet state | `clones/` scripts | `runtime-checks.yml` | | [Python SDK](/docs/internals/sdk-tests) | SDK unit tests and codegen drift gates | `cd sdk/python && just check` | `runtime-checks.yml` | | Rust SDK e2e | Chain-facing SDK behavior against localnet | `cargo test -p bittensor-core --test e2e` | `check-bittensor-e2e-tests.yml` | | [eco-tests](/docs/internals/eco-tests) | Storage/RPC shapes the TAO.com indexer depends on | `cd eco-tests && cargo test` | `eco-tests.yml` | ## Rust tests [#rust-tests] Contributor guide rule: any pallet or runtime change must come with unit tests covering its edge cases. This is how you run them. ### All tests [#all-tests] ```bash SKIP_WASM_BUILD=1 cargo test --workspace ``` or, equivalently (the justfile exports `SKIP_WASM_BUILD=1` for you): ```bash just test ``` `SKIP_WASM_BUILD=1` skips compiling the wasm runtime blob, which the unit tests don't need — without it every test run pays a multi-minute wasm build first. CI (`check-rust.yml`) runs the same thing with all features enabled: ```bash SKIP_WASM_BUILD=1 cargo test --workspace --all-features ``` ### One pallet [#one-pallet] ```bash SKIP_WASM_BUILD=1 cargo test -p pallet-subtensor ``` Pallet tests live inside each crate under `src/tests/` (e.g. `pallets/subtensor/src/tests/staking.rs`), organized as modules of the library target. ### One test (or a group of tests) [#one-test-or-a-group-of-tests] Pass a name filter — every test whose full path contains the string runs: ```bash # Everything in the staking test module SKIP_WASM_BUILD=1 cargo test -p pallet-subtensor tests::staking # One test, with log output visible SKIP_WASM_BUILD=1 cargo test -p pallet-subtensor test_add_stake_ok_no_emission -- --nocapture # Exact match only (skip other tests whose names contain the same prefix) SKIP_WASM_BUILD=1 cargo test -p pallet-subtensor -- --exact tests::staking::test_add_stake_ok_no_emission ``` Add `RUST_LOG=debug` before the command to see runtime log lines, and `--release` if a test is too slow in debug mode (epoch/consensus tests often are). `scripts/test_specific.sh` wraps a release-mode single-test run with these flags preconfigured. ### Feature-gated tests [#feature-gated-tests] Some code only compiles under feature flags. The two you'll hit most: ```bash # Benchmark tests (also what `just benchmarks` runs) SKIP_WASM_BUILD=1 cargo test --workspace --features runtime-benchmarks # The faucet extrinsic used by localnet SKIP_WASM_BUILD=1 cargo test -p pallet-subtensor --features pow-faucet ``` When in doubt, mirror CI with `--all-features`. ### Migration tests (try-runtime) [#migration-tests-try-runtime] Storage migrations get two kinds of coverage. Unit tests live next to the other pallet tests (see `pallets/subtensor/src/tests/migration.rs`). To exercise a migration against **real chain state**, use try-runtime — this is what `try-runtime.yml` does on every PR against devnet, testnet, and mainnet state: ```bash cargo install --git https://github.com/paritytech/try-runtime-cli --locked ``` See [Repository scripts](/docs/internals/scripts#running-try-runtime-locally) for the build and invocation commands to replay `on_runtime_upgrade` against a live endpoint or snapshot. ## TypeScript integration tests [#typescript-integration-tests] TypeScript tests are run with [Moonwall](https://github.com/Moonsong-Labs/moonwall). You will need Node (see `ts-tests/.nvmrc`) and pnpm: ```bash cd ts-tests # Use the correct Node version nvm use # Install pnpm sudo npm i -g pnpm # Install dependencies pnpm i # Run manual seal dev tests pnpm moonwall test dev # Run zombienet tests (one environment per suite) pnpm moonwall test zombienet_staking pnpm moonwall test zombienet_shield pnpm moonwall test zombienet_coldkey_swap pnpm moonwall test zombienet_evm pnpm moonwall test zombienet_subnets # If you have MacOS, you might need to run zombienet tests with sudo, because tmp folder sudo pnpm moonwall test zombienet_staking # Run smoke tests (against devnet, testnet, or mainnet) pnpm moonwall test smoke_devnet pnpm moonwall test smoke_testnet pnpm moonwall test smoke_mainnet ``` Moonwall lets you also run the testing environment without performing any tests on it, as a method for you to manually test certain things: ```bash # Dev tests in run mode pnpm moonwall run dev # Zombienet test with run mode pnpm moonwall run zombienet_staking ``` ## Other suites [#other-suites] * **Mainnet clone** (`clones/`): clones live mainnet state, sudo-upgrades it to your runtime, and runs JS regression tests against it. Full walkthrough: [Mainnet clone testing](/docs/internals/mainnet-clone). * **Python SDK** (`sdk/python/`): `just sync && just check` runs the same offline gates as CI; runtime metadata changes require regenerating the codegen bindings. Full walkthrough: [Python SDK tests](/docs/internals/sdk-tests). * **eco-tests** (`eco-tests/`): excluded from the cargo workspace, so run them from their own directory: `cd eco-tests && cargo test`. They pin the storage and RPC shapes the TAO.com ecosystem indexer consumes — see [Eco-tests](/docs/internals/eco-tests) for what to do when one fails. # Transaction priority (/docs/internals/transaction-priority) In Subtensor, transaction priority is determined by custom transaction extensions, which alter or override the default Substrate SDK behavior. Extensions affecting transaction priority are: * **[`ChargeTransactionPaymentWrapper`](/code/runtime/src/transaction_payment_wrapper.rs#L41)** (wraps `ChargeTransactionPayment`) * **[`DrandPriority`](/code/pallets/drand/src/drand_priority.rs#L21)** Substrate SDK combines priorities from all transaction extensions using addition. ## `ChargeTransactionPaymentWrapper` [#chargetransactionpaymentwrapper] In the Substrate SDK, `ChargeTransactionPayment` normally calculates transaction priority based on: * **Tip** — an extra fee paid by the sender. * **Weight** — computational complexity of the transaction. * **Dispatch class** — category of the transaction (`Normal`, `Operational`, `Mandatory`). However, in Subtensor, `ChargeTransactionPaymentWrapper` **overrides** this logic. It replaces the dynamic calculation with a **flat priority scale** based only on the dispatch class. ### Current priority values [#current-priority-values] | Dispatch Class | Priority Value | Notes | | -------------- | ---------------- | ------------------------------------------------------------ | | `Normal` | `1` | Standard transactions | | `Mandatory` | `1` | Rarely used, same as `Normal` | | `Operational` | `10_000_000_000` | Reserved for critical system extrinsics (e.g.: `sudo` calls) | ## `DrandPriority` [#drandpriority] Special pallet\_drand priority: 10\_000 for [`write_pulse`](/code/pallets/drand/src/lib.rs#L339-L417) extrinsic. # WASM smart contracts (/docs/internals/wasm-contracts) Subtensor supports WebAssembly (WASM) smart contract functionality through the integration of `pallet-contracts`, enabling developers to deploy and execute WASM smart contracts on the network. Contracts are written in [ink!](https://use.ink/), a Rust-based embedded domain-specific language (eDSL) for writing smart contracts on Substrate-based chains. For compatibility, WASM contracts can also be compiled from Solidity using [Solang](https://github.com/hyperledger-solang/solang). If you're looking for information on EVM contracts, see the [EVM guide](/docs/guides/evm). ## Getting Started [#getting-started] For general smart contract development on Subtensor, please refer to the official ink! documentation: * [ink! Documentation](https://use.ink/docs/v5/) * [ink! Getting Started Guide](https://use.ink/docs/v5/getting-started/setup) * [ink! Examples](https://github.com/use-ink/ink-examples/tree/v5.x.x) ink! `>= 6.0` drops support for `pallet-contracts`, please use `ink < 6.0`. See the [ink! 6.0 release notes](https://github.com/use-ink/ink/releases/tag/v6.0.0-alpha). ## Subtensor-Specific Features [#subtensor-specific-features] ### Chain Extension [#chain-extension] Subtensor provides a custom chain extension that allows smart contracts to interact with Subtensor-specific functionality: #### Available Functions [#available-functions] | Function ID | Name | Description | Parameters | Returns | | ----------- | ------------------------------------------ | ----------------------------------------------------------------------------------------- | ------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | | 0 | `get_stake_info_for_hotkey_coldkey_netuid` | Query stake information | `(AccountId, AccountId, NetUid)` | `Option` | | 1 | `add_stake` | Delegate stake from coldkey to hotkey | `(AccountId, NetUid, TaoBalance)` | Error code | | 2 | `remove_stake` | Withdraw stake from hotkey back to coldkey | `(AccountId, NetUid, AlphaBalance)` | Error code | | 3 | `unstake_all` | Unstake all TAO from a hotkey | `(AccountId)` | Error code | | 4 | `unstake_all_alpha` | Unstake all Alpha from a hotkey | `(AccountId)` | Error code | | 5 | `move_stake` | Move stake between hotkeys | `(AccountId, AccountId, NetUid, NetUid, AlphaBalance)` | Error code | | 6 | `transfer_stake` | Transfer stake between coldkeys | `(AccountId, AccountId, NetUid, NetUid, AlphaBalance)` | Error code | | 7 | `swap_stake` | Swap stake allocations between subnets | `(AccountId, NetUid, NetUid, AlphaBalance)` | Error code | | 8 | `add_stake_limit` | Delegate stake with a price limit | `(AccountId, NetUid, TaoBalance, TaoBalance, bool)` | Error code | | 9 | `remove_stake_limit` | Withdraw stake with a price limit | `(AccountId, NetUid, AlphaBalance, TaoBalance, bool)` | Error code | | 10 | `swap_stake_limit` | Swap stake between subnets with price limit | `(AccountId, NetUid, NetUid, AlphaBalance, TaoBalance, bool)` | Error code | | 11 | `remove_stake_full_limit` | Fully withdraw stake with optional price limit | `(AccountId, NetUid, Option)` | Error code | | 12 | `set_coldkey_auto_stake_hotkey` | Configure automatic stake destination | `(NetUid, AccountId)` | Error code | | 13 | `add_proxy` | Add a staking proxy for the caller | `(AccountId)` | Error code | | 14 | `remove_proxy` | Remove a staking proxy for the caller | `(AccountId)` | Error code | | 15 | `get_alpha_price` | Query the current alpha price for a subnet | `(NetUid)` | `u64` (price × 10⁹) | | 16 | `recycle_alpha` | Recycle alpha stake, reducing SubnetAlphaOut (supply reduction) | `(AccountId, NetUid, AlphaBalance)` | `u64` (actual amount recycled) | | 17 | `burn_alpha` | Burn alpha stake without reducing SubnetAlphaOut (supply neutral) | `(AccountId, NetUid, AlphaBalance)` | `u64` (actual amount burned) | | 18 | `add_stake_recycle` | Atomically add stake then recycle the resulting alpha | `(AccountId, NetUid, TaoBalance)` | `u64` (alpha amount recycled) | | 19 | `add_stake_burn` | Atomically add stake then burn the resulting alpha | `(AccountId, NetUid, TaoBalance)` | `u64` (alpha amount burned) | | 20 | `caller_add_stake` | Caller-origin variant of `add_stake` (ID 1) | `(AccountId, NetUid, TaoBalance)` | Error code | | 21 | `caller_remove_stake` | Caller-origin variant of `remove_stake` (ID 2) | `(AccountId, NetUid, AlphaBalance)` | Error code | | 22 | `caller_unstake_all` | Caller-origin variant of `unstake_all` (ID 3) | `(AccountId)` | Error code | | 23 | `caller_unstake_all_alpha` | Caller-origin variant of `unstake_all_alpha` (ID 4) | `(AccountId)` | Error code | | 24 | `caller_move_stake` | Caller-origin variant of `move_stake` (ID 5) | `(AccountId, AccountId, NetUid, NetUid, AlphaBalance)` | Error code | | 25 | `caller_transfer_stake` | Caller-origin variant of `transfer_stake` (ID 6) | `(AccountId, AccountId, NetUid, NetUid, AlphaBalance)` | Error code | | 26 | `caller_swap_stake` | Caller-origin variant of `swap_stake` (ID 7) | `(AccountId, NetUid, NetUid, AlphaBalance)` | Error code | | 27 | `caller_add_stake_limit` | Caller-origin variant of `add_stake_limit` (ID 8) | `(AccountId, NetUid, TaoBalance, TaoBalance, bool)` | Error code | | 28 | `caller_remove_stake_limit` | Caller-origin variant of `remove_stake_limit` (ID 9) | `(AccountId, NetUid, AlphaBalance, TaoBalance, bool)` | Error code | | 29 | `caller_swap_stake_limit` | Caller-origin variant of `swap_stake_limit` (ID 10) | `(AccountId, NetUid, NetUid, AlphaBalance, TaoBalance, bool)` | Error code | | 30 | `caller_remove_stake_full_limit` | Caller-origin variant of `remove_stake_full_limit` (ID 11) | `(AccountId, NetUid, Option)` | Error code | | 31 | `caller_set_coldkey_auto_stake_hotkey` | Caller-origin variant of `set_coldkey_auto_stake_hotkey` (ID 12) | `(NetUid, AccountId)` | Error code | | 32 | `caller_add_proxy` | Caller-origin variant of `add_proxy` (ID 13) | `(AccountId)` | Error code | | 33 | `caller_remove_proxy` | Caller-origin variant of `remove_proxy` (ID 14) | `(AccountId)` | Error code | | 34 | `get_subnet_registration_state` | Query whether a subnet exists and which registration generation currently owns the netuid | `(NetUid)` | `SubnetRegistrationState { netuid, exists, registered_subnet_counter }` | | 35 | `get_coldkey_lock` | Query the current rolled-forward lock state for a coldkey on a subnet | `(AccountId, NetUid)` | `Option` | | 36 | `get_stake_availability` | Query total, locked, and currently available alpha for a coldkey on a subnet | `(AccountId, NetUid)` | `StakeAvailability { netuid: NetUid, total: AlphaBalance, locked: AlphaBalance, available: AlphaBalance }` | Functions **1–14** dispatch with the **contract's own address** as the signed origin. Their caller-origin mirrors (functions **20–33**) dispatch with the **original transaction signer's** origin instead; parameters and return values are identical to the corresponding base function. Functions **16** and **17** use the decoded argument order **`(hotkey, netuid, amount)`**, matching [`SubtensorChainExtension`](/code/chain-extensions/src/lib.rs#L27). If your ink! contract encoded **`(hotkey, amount, netuid)`** for those functions, **recompile and redeploy**; the runtime will decode the older layout incorrectly. Example usage in your ink! contract: ```rust #[ink::chain_extension(extension = 0)] pub trait SubtensorExtension { type ErrorCode = SubtensorError; #[ink(function = 0)] fn get_stake_info( hotkey: AccountId, coldkey: AccountId, netuid: u16, ) -> Result, SubtensorError>; } ``` #### Error Codes [#error-codes] Chain extension functions that modify state return error codes as `u32` values. The following codes are defined: | Code | Name | Description | | ---- | --------------------------------- | ------------------------------------------------- | | 0 | `Success` | Operation completed successfully | | 1 | `RuntimeError` | Unknown runtime error occurred | | 2 | `NotEnoughBalanceToStake` | Insufficient balance to complete stake operation | | 3 | `NonAssociatedColdKey` | Coldkey is not associated with the hotkey | | 4 | `BalanceWithdrawalError` | Error occurred during balance withdrawal | | 5 | `NotRegistered` | Hotkey is not registered in the subnet | | 6 | `NotEnoughStakeToWithdraw` | Insufficient stake available for withdrawal | | 7 | `TxRateLimitExceeded` | Transaction rate limit has been exceeded | | 8 | `SlippageTooHigh` | Price slippage exceeds acceptable threshold | | 9 | `SubnetNotExists` | Specified subnet does not exist | | 10 | `HotKeyNotRegisteredInSubNet` | Hotkey is not registered in the specified subnet | | 11 | `SameAutoStakeHotkeyAlreadySet` | Auto-stake hotkey is already configured | | 12 | `InsufficientBalance` | Account has insufficient balance | | 13 | `AmountTooLow` | Transaction amount is below minimum threshold | | 14 | `InsufficientLiquidity` | Insufficient liquidity for swap operation | | 15 | `SameNetuid` | Source and destination subnets are the same | | 16 | `ProxyTooMany` | Too many proxies registered | | 17 | `ProxyDuplicate` | Proxy already exists | | 18 | `ProxyNoSelfProxy` | Cannot add self as proxy | | 19 | `ProxyNotFound` | Proxy relationship not found | | 20 | `CannotUseSystemAccount` | A system account cannot be used in this operation | | 21 | `CannotBurnOrRecycleOnRootSubnet` | Cannot burn or recycle on the root subnet | | 22 | `SubtokenDisabled` | Subtoken is not enabled for the specified subnet | ### Call Filter [#call-filter] For security, contracts can only directly dispatch a limited set of runtime calls: **Whitelisted Calls:** * `Proxy::proxy` - Execute proxy calls All other runtime calls are restricted and cannot be dispatched from contracts. ### Configuration Parameters [#configuration-parameters] | Parameter | Value | Description | | ------------------------ | -------- | ------------------------------------------ | | Maximum code size | 128 KB | Maximum size of contract WASM code | | Call stack depth | 5 frames | Maximum nested contract call depth | | Runtime memory | 1 GB | Memory available during contract execution | | Validator runtime memory | 2 GB | Memory available for validators | | Transient storage | 1 MB | Maximum transient storage size | ## Additional Resources [#additional-resources] * [cargo-contract CLI Tool](https://github.com/paritytech/cargo-contract) * [Contracts UI](https://contracts-ui.substrate.io/) # add-collateral (/docs/tx/add-collateral) Tops up the hotkey's registration collateral on the subnet — for example to meet a validator-published per-machine requirement on resource subnets. Prefers free alpha already staked on that hotkey and only buys the shortfall with TAO from the coldkey. The signing coldkey must own the hotkey. The locked alpha is real stake (it appreciates with the subnet pool) but is not withdrawable: it is released back to free stake through earned emission at the drain ratio snapshot the hotkey already carries, survives deregistration, and is credited against the collateral requirement on re-registration. There is no direct withdrawal path — see `set_min_collateral` for maintaining a level without re-locking drained funds. Any TAO→alpha buy is fill-or-kill against `rate_tolerance` above spot and must be submitted MEV-shielded — collateral purchases are not allowed to clear unshielded at an unbounded AMM price. | Signer | Origin | Pallet | Wraps | | --------- | -------------------------------------- | --------------- | ------------------------------------------------------------------------------------------------ | | `coldkey` | signed account (pallet role may apply) | SubtensorModule | [`SubtensorModule.add_collateral`](/code/pallets/subtensor/src/macros/dispatches.rs#L2441-L2451) | ## Parameters [#parameters] | Parameter | Type | Required | Description | | ---------------- | ----------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | | `netuid` | integer | yes | Subnet to lock collateral on. | | `amount_alpha` | number \| `"all"` | yes | Alpha of collateral to add. Uses free stake on the hotkey first; only the shortfall is bought with TAO (that remainder must meet the staking minimum). | | `hotkey_ss58` | string | no | Miner hotkey the collateral attaches to. Defaults to the wallet hotkey. | | `rate_tolerance` | number | no | Maximum price move slippage protection accepts, as a fraction (0.05 = 5%). Ignored when slippage protection is disabled. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx add-collateral \ --netuid \ --amount-alpha --dry-run btcli tx add-collateral \ --netuid \ --amount-alpha -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.AddCollateral(netuid=1, amount_alpha=1.0) sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("add_collateral", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `SubtensorModule.add_collateral` — [`pallets/subtensor/src/macros/dispatches.rs#L2443`](/code/pallets/subtensor/src/macros/dispatches.rs#L2441-L2451): ```rust #[pallet::call_index(144)] #[pallet::weight(::WeightInfo::add_collateral())] pub fn add_collateral( origin: OriginFor, netuid: NetUid, hotkey: T::AccountId, alpha: AlphaBalance, limit_price: TaoBalance, ) -> DispatchResult { Self::do_add_collateral(origin, netuid, hotkey, alpha, limit_price) } ``` Delegates to [`do_add_collateral`](/code/pallets/subtensor/src/subnets/collateral.rs#L545). Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # add-proxy (/docs/tx/add-proxy) The foundation of the keep-the-coldkey-offline setup: sign this once from the coldkey, then let the delegate submit day-to-day calls with `proxy_for=`. The chain reserves a small deposit from the signer per delegation (returned on removal). The delegate can act within `proxy_type` immediately (or after announcing, if `delay` > 0) — grant only to keys you control or fully trust. | Signer | Origin | Pallet | Wraps | | --------- | -------------------------------------- | ------ | ------------------------------------------------------------- | | `coldkey` | signed account (pallet role may apply) | Proxy | [`Proxy.add_proxy`](/code/pallets/proxy/src/lib.rs#L265-L276) | ## Parameters [#parameters] | Parameter | Type | Required | Description | | --------------- | ------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `delegate_ss58` | string | yes | Key that will be allowed to sign for this account. | | `proxy_type` | string | no | Scope of calls the delegation covers. One of: Any, Owner, NonCritical, NonTransfer, Senate, NonFungible, Triumvirate, Governance, Staking, Registration, Transfer, SmallTransfer, RootWeights, ChildKeys, SudoUncheckedSetCode, SwapHotkey, SubnetLeaseBeneficiary, RootClaim. Triumvirate, Senate, Governance, and RootWeights are deprecated on the current runtime: they deny all calls, so a proxy of those types can dispatch nothing. Prefer the narrowest type that covers your use; Any can do everything the account can, including transfers. | | `delay` | integer | no | Announcement delay in blocks: the delegate must announce each call and wait this long before executing it, giving you time to veto. 0 executes immediately. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx add-proxy \ --delegate --dry-run btcli tx add-proxy \ --delegate -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.AddProxy(delegate_ss58="5F...") sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("add_proxy", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `Proxy.add_proxy` — [`pallets/proxy/src/lib.rs#L267`](/code/pallets/proxy/src/lib.rs#L265-L276): ```rust #[pallet::call_index(1)] #[pallet::weight(T::WeightInfo::add_proxy(T::MaxProxies::get()))] pub fn add_proxy( origin: OriginFor, delegate: AccountIdLookupOf, proxy_type: T::ProxyType, delay: BlockNumberFor, ) -> DispatchResult { let who = ensure_signed(origin)?; let delegate = T::Lookup::lookup(delegate)?; Self::add_proxy_delegate(&who, delegate, proxy_type, delay) } ``` Delegates to [`add_proxy_delegate`](/code/pallets/proxy/src/lib.rs#L941). Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # add-stake-limit (/docs/tx/add-stake-limit) Same as `add_stake` except the TAO-to-alpha swap only executes while the pool price stays within the limit. With `allow_partial` the call stakes as much as fits under the limit and leaves the rest in the free balance; without it the whole call fails once the limit would be breached. Like `add_stake`, the amount must be at least the chain minimum of 0.002 TAO plus the swap fee (`AmountTooLow`). Prefer this over plain `add_stake` for large amounts or thin pools, where the swap itself moves the price. | Signer | Origin | Pallet | Wraps | | --------- | -------------------------------------- | --------------- | ------------------------------------------------------------------------------------------------- | | `coldkey` | signed account (pallet role may apply) | SubtensorModule | [`SubtensorModule.add_stake_limit`](/code/pallets/subtensor/src/macros/dispatches.rs#L1391-L1410) | ## Parameters [#parameters] | Parameter | Type | Required | Description | | ----------------- | ----------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | | `hotkey_ss58` | string | yes | Hotkey the stake is added to (the validator you are backing). | | `netuid` | integer | yes | Subnet the stake lives on (netuid 0 is the root network). | | `amount_tao` | number \| `"all"` | yes | How much of the coldkey's free balance to stake. | | `limit_price_rao` | integer | yes | Worst pool price you will accept for the swap. The call fails (or fills partially when allow-partial is set) instead of executing beyond this price. | | `allow_partial` | boolean | no | Execute whatever portion fits within the limit price and drop the remainder, instead of failing the whole call when the limit would be breached. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx add-stake-limit \ --hotkey \ --netuid \ --amount-tao \ --limit-price-rao --dry-run btcli tx add-stake-limit \ --hotkey \ --netuid \ --amount-tao \ --limit-price-rao -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.AddStakeLimit(hotkey_ss58="5F...", netuid=1, amount_tao=1.0, limit_price_rao=0) sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("add_stake_limit", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `SubtensorModule.add_stake_limit` — [`pallets/subtensor/src/macros/dispatches.rs#L1393`](/code/pallets/subtensor/src/macros/dispatches.rs#L1391-L1410): ```rust #[pallet::call_index(88)] #[pallet::weight(::WeightInfo::add_stake_limit())] pub fn add_stake_limit( origin: OriginFor, hotkey: T::AccountId, netuid: NetUid, amount_staked: TaoBalance, limit_price: TaoBalance, allow_partial: bool, ) -> DispatchResult { Self::do_add_stake_limit( origin, hotkey, netuid, amount_staked, limit_price, allow_partial, ) .map(|_| ()) } ``` Delegates to [`do_add_stake_limit`](/code/pallets/subtensor/src/staking/add_stake.rs#L95). Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # add-stake (/docs/tx/add-stake) Swaps TAO from the coldkey's free balance into the subnet's alpha at the current pool price and credits the result to your stake on the hotkey; on netuid 0 (root) the stake stays TAO-denominated. The swap moves the pool, so large amounts incur slippage. By default the call is slippage-protected: it fails (`SlippageTooHigh`) instead of filling once the price rises more than `rate_tolerance` (5%) above the price at submission — raise the tolerance or set `slippage_protection` to False to execute at any price, or use `add_stake_limit` to set an explicit limit price. The position's value then follows the pool price and the validator's performance, and can be exited later with `remove_stake`. Fails if the coldkey's free balance cannot cover the amount plus the transaction fee, and with `AmountTooLow` when the amount is below the chain minimum of 0.002 TAO plus the swap fee. Dynamic subnets also reject a single swap larger than 1000x the pool's TAO reserve (`InsufficientLiquidity`). | Signer | Origin | Pallet | Wraps | | --------- | -------------------------------------- | --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `coldkey` | signed account (pallet role may apply) | SubtensorModule | [`SubtensorModule.add_stake`](/code/pallets/subtensor/src/macros/dispatches.rs#L559-L568), [`SubtensorModule.add_stake_limit`](/code/pallets/subtensor/src/macros/dispatches.rs#L1391-L1410) | ## Parameters [#parameters] | Parameter | Type | Required | Description | | --------------------- | ----------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `hotkey_ss58` | string | yes | Hotkey the stake is added to (the validator you are backing). | | `netuid` | integer | yes | Subnet the stake lives on (netuid 0 is the root network). | | `amount_tao` | number \| `"all"` | yes | How much of the coldkey's free balance to stake. | | `slippage_protection` | boolean | no | Bound the price the swap may execute at (on by default): the call fails (`SlippageTooHigh`) instead of filling once the pool price moves more than `rate_tolerance` from the price at submission. Disable to execute at any price. | | `rate_tolerance` | number | no | Maximum price move slippage protection accepts, as a fraction (0.05 = 5%). Ignored when slippage protection is disabled. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx add-stake \ --hotkey \ --netuid \ --amount-tao --dry-run btcli tx add-stake \ --hotkey \ --netuid \ --amount-tao -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.AddStake(hotkey_ss58="5F...", netuid=1, amount_tao=1.0) sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("add_stake", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `SubtensorModule.add_stake` — [`pallets/subtensor/src/macros/dispatches.rs#L561`](/code/pallets/subtensor/src/macros/dispatches.rs#L559-L568): ```rust #[pallet::call_index(2)] #[pallet::weight(::WeightInfo::add_stake())] pub fn add_stake( origin: OriginFor, hotkey: T::AccountId, netuid: NetUid, amount_staked: TaoBalance, ) -> DispatchResult { Self::do_add_stake(origin, hotkey, netuid, amount_staked).map(|_| ()) } ``` Delegates to [`do_add_stake`](/code/pallets/subtensor/src/staking/add_stake.rs#L30). `SubtensorModule.add_stake_limit` — [`pallets/subtensor/src/macros/dispatches.rs#L1393`](/code/pallets/subtensor/src/macros/dispatches.rs#L1391-L1410): ```rust #[pallet::call_index(88)] #[pallet::weight(::WeightInfo::add_stake_limit())] pub fn add_stake_limit( origin: OriginFor, hotkey: T::AccountId, netuid: NetUid, amount_staked: TaoBalance, limit_price: TaoBalance, allow_partial: bool, ) -> DispatchResult { Self::do_add_stake_limit( origin, hotkey, netuid, amount_staked, limit_price, allow_partial, ) .map(|_| ()) } ``` Delegates to [`do_add_stake_limit`](/code/pallets/subtensor/src/staking/add_stake.rs#L95). Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # announce-coldkey-swap (/docs/tx/announce-coldkey-swap) Step one of the two-step coldkey migration: publishes only the BlakeTwo256 hash of `new_coldkey_ss58` — committing to the new key without revealing it — and starts the chain's announcement delay. After the delay, run the `swap_coldkey_announced` intent to move EVERYTHING this coldkey owns (balance, stake, subnets) to the new key; check timing with the `coldkey_swap_announcement` read. The first announcement charges the key-swap cost (0.1 TAO, recycled); re-announcing after the chain's reannouncement delay is free. While an announcement is pending, the chain blocks every other signed extrinsic from this coldkey — only the swap-related calls and shielded (encrypted) submission go through — so the account is operationally locked for the full delay. Before announcing, be certain you control the new coldkey and have its mnemonic backed up. A pending announcement can be cancelled with `clear_coldkey_swap_announcement`, and the legitimate holder can freeze an unauthorized one with `dispute_coldkey_swap`. | Signer | Origin | Pallet | Wraps | | --------- | -------------------------------------- | --------------- | ------------------------------------------------------------------------------------------------------- | | `coldkey` | signed account (pallet role may apply) | SubtensorModule | [`SubtensorModule.announce_coldkey_swap`](/code/pallets/subtensor/src/macros/dispatches.rs#L1985-L2013) | ## Parameters [#parameters] | Parameter | Type | Required | Description | | ------------------ | ------ | -------- | ---------------------------------------------------------------------------------------------------------------- | | `new_coldkey_ss58` | string | yes | Coldkey that will take over everything this coldkey owns once the swap executes; only its hash is published now. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx announce-coldkey-swap \ --new-coldkey --dry-run btcli tx announce-coldkey-swap \ --new-coldkey -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.AnnounceColdkeySwap(new_coldkey_ss58="5F...") sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("announce_coldkey_swap", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `SubtensorModule.announce_coldkey_swap` — [`pallets/subtensor/src/macros/dispatches.rs#L1987`](/code/pallets/subtensor/src/macros/dispatches.rs#L1985-L2013): ```rust #[pallet::call_index(125)] #[pallet::weight(::WeightInfo::announce_coldkey_swap())] pub fn announce_coldkey_swap( origin: OriginFor, new_coldkey_hash: T::Hash, ) -> DispatchResult { let who = ensure_signed(origin)?; let now = >::block_number(); if let Some((when, _)) = ColdkeySwapAnnouncements::::get(who.clone()) { let reannouncement_delay = ColdkeySwapReannouncementDelay::::get(); let min_when = when.saturating_add(reannouncement_delay); ensure!(now >= min_when, Error::::ColdkeySwapReannouncedTooEarly); } else { // Only charge the swap cost on the first announcement let swap_cost = Self::get_key_swap_cost(); Self::charge_swap_cost(&who, swap_cost)?; } let delay = ColdkeySwapAnnouncementDelay::::get(); let when = now.saturating_add(delay); ColdkeySwapAnnouncements::::insert(who.clone(), (when, new_coldkey_hash.clone())); Self::deposit_event(Event::ColdkeySwapAnnounced { who, new_coldkey_hash, }); Ok(()) } ``` Delegates to [`get_key_swap_cost`](/code/pallets/subtensor/src/utils/misc.rs#L827), [`charge_swap_cost`](/code/pallets/subtensor/src/swap/swap_coldkey.rs#L68). Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # associate-evm-key (/docs/tx/associate-evm-key) Links an Ethereum-style (H160) address to the signing hotkey on one subnet, letting EVM-side activity be attributed to that neuron. Signed by the hotkey, and additionally proven by the EVM key itself: `signature` must be an EIP-191 personal-sign signature by the EVM key over the message `hotkey_pubkey (32 bytes) ++ keccak_256(scale(block_number))`, where the block number is SCALE-encoded (u64 little-endian). Use `bittensor.evm.transactions.association_proof` to produce it (it needs the EVM private key); a wrong message, block number, or key makes the chain reject the call. Prerequisites: the hotkey must be registered on `netuid` (else HotKeyNotRegisteredInSubNet) and have an owning coldkey, and re-association is rate-limited to once per 7,200 blocks (\~1 day) per neuron. | Signer | Origin | Pallet | Wraps | | -------- | -------------------------------------- | --------------- | --------------------------------------------------------------------------------------------------- | | `hotkey` | signed account (pallet role may apply) | SubtensorModule | [`SubtensorModule.associate_evm_key`](/code/pallets/subtensor/src/macros/dispatches.rs#L1573-L1587) | ## Parameters [#parameters] | Parameter | Type | Required | Description | | -------------- | ------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `netuid` | integer | yes | Subnet on which the EVM key association is recorded. | | `evm_key` | string | yes | EVM address to link to the hotkey, as 0x-prefixed H160 hex. | | `block_number` | integer | yes | Block number the signature was produced for; part of the signed message. | | `signature` | string | yes | The EVM key's ownership proof, as 0x-prefixed hex: an EIP-191 personal-sign signature over the hotkey public key concatenated with keccak\_256 of the SCALE-encoded block number. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx associate-evm-key \ --netuid \ --evm-key \ --block-number \ --signature --dry-run btcli tx associate-evm-key \ --netuid \ --evm-key \ --block-number \ --signature -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.AssociateEvmKey(netuid=1, evm_key="...", block_number=0, signature="...") sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("associate_evm_key", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `SubtensorModule.associate_evm_key` — [`pallets/subtensor/src/macros/dispatches.rs#L1579`](/code/pallets/subtensor/src/macros/dispatches.rs#L1573-L1587): ```rust #[pallet::call_index(93)] #[pallet::weight(( ::WeightInfo::associate_evm_key(), DispatchClass::Normal, Pays::No ))] pub fn associate_evm_key( origin: OriginFor, netuid: NetUid, evm_key: H160, block_number: u64, signature: Signature, ) -> DispatchResult { Self::do_associate_evm_key(origin, netuid, evm_key, block_number, signature) } ``` Delegates to [`do_associate_evm_key`](/code/pallets/subtensor/src/utils/evm.rs#L46). Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # associate-hotkey (/docs/tx/associate-hotkey) Records on chain that the signing coldkey owns the hotkey, without registering it on any subnet or staking anything. Useful when the pairing should be visible before the hotkey's first registration (registration and staking establish it as a side effect). The chain only associates hotkeys that are not already owned: if the hotkey already belongs to a coldkey the call succeeds but is silently a no-op — it does not error, and it does not take over the hotkey. | Signer | Origin | Pallet | Wraps | | --------- | -------------------------------------- | --------------- | ------------------------------------------------------------------------------------------------------ | | `coldkey` | signed account (pallet role may apply) | SubtensorModule | [`SubtensorModule.try_associate_hotkey`](/code/pallets/subtensor/src/macros/dispatches.rs#L1519-L1527) | ## Parameters [#parameters] | Parameter | Type | Required | Description | | ------------- | ------ | -------- | ------------------------------------------------- | | `hotkey_ss58` | string | no | Hotkey to record as owned by the signing coldkey. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx associate-hotkey --dry-run btcli tx associate-hotkey -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.AssociateHotkey() sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("associate_hotkey", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `SubtensorModule.try_associate_hotkey` — [`pallets/subtensor/src/macros/dispatches.rs#L1521`](/code/pallets/subtensor/src/macros/dispatches.rs#L1519-L1527): ```rust #[pallet::call_index(91)] #[pallet::weight(::WeightInfo::try_associate_hotkey())] pub fn try_associate_hotkey(origin: OriginFor, hotkey: T::AccountId) -> DispatchResult { let coldkey = ensure_signed(origin)?; Self::do_try_associate_hotkey(&coldkey, &hotkey)?; Ok(()) } ``` Delegates to [`do_try_associate_hotkey`](/code/pallets/subtensor/src/staking/account.rs#L4). Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # batch (/docs/tx/batch) Wraps the child calls in `Utility.batch_all`: they run in order and if any one fails the whole extrinsic reverts, so there is never a partially-applied result. Use it for multi-step operations that must land together (e.g. move funds then act on them) instead of submitting the steps separately and risking a half-done state. All children must share one signer — an extrinsic has a single signature — and batches cannot contain other batches. Spend limits and policy checks aggregate across every child, and the transaction plan lists each child's effects. | Signer | Origin | Pallet | Wraps | | --------- | -------------------------------------- | ------- | ----------------------------------------------------------------- | | `coldkey` | signed account (pallet role may apply) | Utility | [`Utility.batch_all`](/code/pallets/utility/src/lib.rs#L308-L362) | ## Parameters [#parameters] | Parameter | Type | Required | Description | | --------- | ----- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | | `intents` | array | yes | The calls to execute, in order, as a JSON list of objects \{"op": \, ...args}. At least one; all must share a signer; batches cannot nest. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx batch \ --intents --dry-run btcli tx batch \ --intents -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.Batch(intents=[...]) sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("batch", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `Utility.batch_all` — [`pallets/utility/src/lib.rs#L314`](/code/pallets/utility/src/lib.rs#L308-L362): ```rust #[pallet::call_index(2)] #[pallet::weight({ let (dispatch_weight, pays) = Pallet::::weight_and_dispatch_class(calls); let dispatch_weight = dispatch_weight.saturating_add(T::WeightInfo::batch_all(calls.len() as u32)); (dispatch_weight, DispatchClass::Normal, pays) })] pub fn batch_all( origin: OriginFor, calls: Vec<::RuntimeCall>, ) -> DispatchResultWithPostInfo { // Do not allow the `None` origin. if ensure_none(origin.clone()).is_ok() { return Err(BadOrigin.into()); } let is_root = ensure_root(origin.clone()).is_ok(); let calls_len = calls.len(); ensure!( calls_len <= Self::batched_calls_limit() as usize, Error::::TooManyCalls ); // Track the actual weight of each of the batch calls. let mut weight = Weight::zero(); for (index, call) in calls.into_iter().enumerate() { let info = call.get_dispatch_info(); // If origin is root, bypass any dispatch filter; root can call anything. let result = if is_root { call.dispatch_bypass_filter(origin.clone()) } else { let mut filtered_origin = origin.clone(); // Don't allow users to nest `batch_all` calls. filtered_origin.add_filter( move |c: &::RuntimeCall| { let c = ::RuntimeCall::from_ref(c); !matches!(c.is_sub_type(), Some(Call::batch_all { .. })) }, ); call.dispatch(filtered_origin) }; // Add the weight of this call. weight = weight.saturating_add(extract_actual_weight(&result, &info)); result.map_err(|mut err| { // Take the weight of this function itself into account. let base_weight = T::WeightInfo::batch_all(index.saturating_add(1) as u32); // Return the actual used weight + base_weight of this call. err.post_info = Some(base_weight.saturating_add(weight)).into(); err })?; Self::deposit_event(Event::ItemCompleted); } Self::deposit_event(Event::BatchCompleted); let base_weight = T::WeightInfo::batch_all(calls_len as u32); Ok(Some(base_weight.saturating_add(weight)).into()) } ``` Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # burned-register (/docs/tx/burned-register) Pays the subnet's current floating registration cost from the signing coldkey and assigns the hotkey a UID. When the subnet's collateral lock share (p) is zero, the full cost is burned/recycled. When p > 0, the `(1 - p)` share is burned and the `p` share is staked to the hotkey and locked as miner collateral (released only through earned emission). The exact TAO charge is only known at execution time, so a configured spend cap blocks this call until raised. Fails with `SubNetRegistrationDisabled` while the subnet's `registration_allowed` toggle is off. On a full subnet, registering evicts the non-immune neuron with the lowest emission (ties broken by older registration block, then lower UID), and the new UID can itself be evicted once its immunity period ends. Use `root_register` instead for the root network (netuid 0). | Signer | Origin | Pallet | Wraps | | --------- | -------------------------------------- | --------------- | ----------------------------------------------------------------------------------------------- | | `coldkey` | signed account (pallet role may apply) | SubtensorModule | [`SubtensorModule.burned_register`](/code/pallets/subtensor/src/macros/dispatches.rs#L816-L824) | ## Parameters [#parameters] | Parameter | Type | Required | Description | | ------------- | ------- | -------- | -------------------------------------------------------------- | | `netuid` | integer | yes | Subnet to register on. | | `hotkey_ss58` | string | no | Hotkey that receives the UID; defaults to the wallet's hotkey. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx burned-register \ --netuid --dry-run btcli tx burned-register \ --netuid -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.BurnedRegister(netuid=1) sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("burned_register", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `SubtensorModule.burned_register` — [`pallets/subtensor/src/macros/dispatches.rs#L818`](/code/pallets/subtensor/src/macros/dispatches.rs#L816-L824): ```rust #[pallet::call_index(7)] #[pallet::weight(::WeightInfo::burned_register())] pub fn burned_register( origin: OriginFor, netuid: NetUid, hotkey: T::AccountId, ) -> DispatchResult { Self::do_register(origin, netuid, hotkey) } ``` Delegates to [`do_register`](/code/pallets/subtensor/src/subnets/registration.rs#L38). Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # claim-root (/docs/tx/claim-root) Pays out the signing coldkey's accrued root-stake dividends from the listed subnets. What the payout looks like depends on the coldkey's root claim type (see `set_root_claim_type`): swapped to TAO, kept as subnet alpha, or a per-subnet mix. At most 5 subnets per call — an empty or longer list fails with `InvalidSubnetNumber`. Subnets whose accrued dividends are below the per-subnet claim threshold (default 500,000 rao; adjustable by the subnet owner or root) are silently skipped while the transaction still succeeds. Unclaimed dividends simply keep accruing — there is no deadline — but each call pays out only the subnets listed. | Signer | Origin | Pallet | Wraps | | --------- | -------------------------------------- | --------------- | -------------------------------------------------------------------------------------------- | | `coldkey` | signed account (pallet role may apply) | SubtensorModule | [`SubtensorModule.claim_root`](/code/pallets/subtensor/src/macros/dispatches.rs#L1889-L1907) | ## Parameters [#parameters] | Parameter | Type | Required | Description | | --------- | ---------------- | -------- | --------------------------------------------------------------------- | | `subnets` | array of integer | yes | Netuids to claim accumulated root dividends from; at most 5 per call. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx claim-root \ --subnets --dry-run btcli tx claim-root \ --subnets -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.ClaimRoot(subnets=[...]) sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("claim_root", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `SubtensorModule.claim_root` — [`pallets/subtensor/src/macros/dispatches.rs#L1891`](/code/pallets/subtensor/src/macros/dispatches.rs#L1889-L1907): ```rust #[pallet::call_index(121)] #[pallet::weight(::WeightInfo::claim_root())] pub fn claim_root( origin: OriginFor, subnets: BTreeSet, ) -> DispatchResultWithPostInfo { let coldkey: T::AccountId = ensure_signed(origin)?; ensure!(!subnets.is_empty(), Error::::InvalidSubnetNumber); ensure!( subnets.len() <= MAX_SUBNET_CLAIMS, Error::::InvalidSubnetNumber ); Self::maybe_add_coldkey_index(&coldkey); let weight = Self::do_root_claim(coldkey, Some(subnets))?; Ok((Some(weight), Pays::Yes).into()) } ``` Delegates to [`maybe_add_coldkey_index`](/code/pallets/subtensor/src/staking/claim_root.rs#L389), [`do_root_claim`](/code/pallets/subtensor/src/staking/claim_root.rs#L355). Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # clear-coldkey-swap-announcement (/docs/tx/clear-coldkey-swap-announcement) Withdraws this coldkey's own pending swap announcement so the swap can no longer be executed — use it if you announced to the wrong key or changed your mind. Only clearable once the current block reaches the swap's execute block plus the reannouncement delay — i.e. roughly the announcement delay (\~5 days) PLUS the reannouncement delay (\~1 day) after announcing — so it cannot be used to rapidly cycle announcements. Nothing moves; a fresh `announce_coldkey_swap` can be made afterwards. If the announcement was made by an attacker, `dispute_coldkey_swap` is the right call instead. | Signer | Origin | Pallet | Wraps | | --------- | -------------------------------------- | --------------- | ----------------------------------------------------------------------------------------------------------------- | | `coldkey` | signed account (pallet role may apply) | SubtensorModule | [`SubtensorModule.clear_coldkey_swap_announcement`](/code/pallets/subtensor/src/macros/dispatches.rs#L2184-L2201) | ## Parameters [#parameters] This operation takes no parameters. Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx clear-coldkey-swap-announcement --dry-run btcli tx clear-coldkey-swap-announcement -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.ClearColdkeySwapAnnouncement() sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("clear_coldkey_swap_announcement", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `SubtensorModule.clear_coldkey_swap_announcement` — [`pallets/subtensor/src/macros/dispatches.rs#L2186`](/code/pallets/subtensor/src/macros/dispatches.rs#L2184-L2201): ```rust #[pallet::call_index(133)] #[pallet::weight(::WeightInfo::clear_coldkey_swap_announcement())] pub fn clear_coldkey_swap_announcement(origin: OriginFor) -> DispatchResult { let who = ensure_signed(origin)?; let now = >::block_number(); let Some((when, _)) = ColdkeySwapAnnouncements::::get(who.clone()) else { return Err(Error::::ColdkeySwapAnnouncementNotFound.into()); }; let delay = ColdkeySwapReannouncementDelay::::get(); let min_when = when.saturating_add(delay); ensure!(now >= min_when, Error::::ColdkeySwapClearTooEarly); ColdkeySwapAnnouncements::::remove(&who); Self::deposit_event(Event::ColdkeySwapCleared { who }); Ok(()) } ``` Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # commit-weights (/docs/tx/commit-weights) Encrypts the weights with drand timelock encryption and submits the ciphertext; the weights stay hidden until the chain auto-decrypts and applies them at the reveal round (no separate reveal call is needed). Hiding weights until the round closes prevents other validators from copying them. Signed by the hotkey, with the same preflight, conforming, and rate-limit behavior as `set_weights`. Use `set_weights` instead unless you need to force the commit path regardless of the subnet's commit-reveal setting. | Signer | Origin | Pallet | Wraps | | -------- | -------------------------------------- | --------------- | --------------------------------------------------------------------------------------------------------------------- | | `hotkey` | signed account (pallet role may apply) | SubtensorModule | [`SubtensorModule.commit_timelocked_mechanism_weights`](/code/pallets/subtensor/src/macros/dispatches.rs#L1850-L1868) | ## Parameters [#parameters] | Parameter | Type | Required | Description | | ----------------------- | ---------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `netuid` | integer | yes | Subnet whose miners the weights score. | | `uids` | array of integer | no | Miner UIDs being weighted, as a list parallel to weights. Omit when weights is given as a uid-to-weight mapping. | | `weights` | array of number | no | Relative weight per miner: either a JSON object mapping uid to weight, or a list parallel to uids. Values are relative, not absolute; they are clipped to the subnet's max-weight limit, normalized, and quantized before submission. | | `mechid` | integer | no | Mechanism index within the subnet. 0 is the default (and for most subnets the only) mechanism. | | `version_key` | integer | no | Weights version key checked against the subnet's required version. Leave 0 unless the subnet owner requires a specific value. | | `commit_reveal_version` | integer | no | Commit-reveal protocol version to tag the commit with. Keep the default unless the subnet requires an older protocol. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx commit-weights \ --netuid --dry-run btcli tx commit-weights \ --netuid -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.CommitWeights(netuid=1) sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("commit_weights", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `SubtensorModule.commit_timelocked_mechanism_weights` — [`pallets/subtensor/src/macros/dispatches.rs#L1852`](/code/pallets/subtensor/src/macros/dispatches.rs#L1850-L1868): ```rust #[pallet::call_index(118)] #[pallet::weight((::WeightInfo::commit_timelocked_mechanism_weights(), DispatchClass::Normal, Pays::No))] pub fn commit_timelocked_mechanism_weights( origin: OriginFor, netuid: NetUid, mecid: MechId, commit: BoundedVec>, reveal_round: u64, commit_reveal_version: u16, ) -> DispatchResult { Self::do_commit_timelocked_mechanism_weights( origin, netuid, mecid, commit, reveal_round, commit_reveal_version, ) } ``` Delegates to [`do_commit_timelocked_mechanism_weights`](/code/pallets/subtensor/src/subnets/weights.rs#L263). Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # contribute-crowdloan (/docs/tx/contribute-crowdloan) Moves the amount from the signer into the crowdloan's pot. The contribution must meet the loan's minimum (and per-contributor maximum, if set) and the loan must still be open — contributing after `end` or past the cap fails. Contributions are not refunded automatically if the loan fails: they come back when the creator calls `refund_crowdloan`, or the contributor can `withdraw_crowdloan` themselves any time before finalization. On success, the funds go to the loan's target or fund its inner call. | Signer | Origin | Pallet | Wraps | | --------- | -------------------------------------- | --------- | ---------------------------------------------------------------------- | | `coldkey` | signed account (pallet role may apply) | Crowdloan | [`Crowdloan.contribute`](/code/pallets/crowdloan/src/lib.rs#L438-L536) | ## Parameters [#parameters] | Parameter | Type | Required | Description | | -------------- | ----------------- | -------- | ------------------------------------------------------------------------------------------------------------------------- | | `crowdloan_id` | integer | yes | Identifier of the crowdloan, assigned when it was created. | | `amount_tao` | number \| `"all"` | yes | Amount to contribute; moved into the crowdloan's pot. Recoverable via refund or withdraw while the loan is not finalized. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx contribute-crowdloan \ --crowdloan-id \ --amount-tao --dry-run btcli tx contribute-crowdloan \ --crowdloan-id \ --amount-tao -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.ContributeCrowdloan(crowdloan_id=0, amount_tao=1.0) sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("contribute_crowdloan", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `Crowdloan.contribute` — [`pallets/crowdloan/src/lib.rs#L440`](/code/pallets/crowdloan/src/lib.rs#L438-L536): ```rust #[pallet::call_index(1)] #[pallet::weight(T::WeightInfo::contribute())] pub fn contribute( origin: OriginFor, #[pallet::compact] crowdloan_id: CrowdloanId, #[pallet::compact] amount: BalanceOf, ) -> DispatchResult { let contributor = ensure_signed(origin)?; let now = frame_system::Pallet::::block_number(); let mut crowdloan = Self::ensure_crowdloan_exists(crowdloan_id)?; // Ensure crowdloan has not ended and has not raised cap ensure!(now < crowdloan.end, Error::::ContributionPeriodEnded); ensure!(crowdloan.raised < crowdloan.cap, Error::::CapRaised); // Ensure contribution is at least the minimum contribution ensure!( amount >= crowdloan.min_contribution, Error::::ContributionTooLow ); // Ensure the crowdloan has not reached the maximum number of contributors ensure!( crowdloan.contributors_count < T::MaxContributors::get(), Error::::MaxContributorsReached ); // Compute how much room is left before the crowdloan reaches its cap. let left_to_raise = crowdloan .cap .checked_sub(&crowdloan.raised) .ok_or(Error::::Underflow)?; // The requested contribution must meet the minimum contribution, but // the accepted amount may be lower when only a smaller remainder can // be accepted before reaching the crowdloan cap or the contributor's // maximum contribution. let amount = if let Some(max_contribution) = MaxContributions::::get(crowdloan_id) { let current_contribution = Contributions::::get(crowdloan_id, &contributor).unwrap_or_else(Zero::zero); ensure!( current_contribution < max_contribution, Error::::MaxContributionReached ); let left_to_contribute = max_contribution .checked_sub(¤t_contribution) .ok_or(Error::::Underflow)?; amount.min(left_to_contribute).min(left_to_raise) } else { amount.min(left_to_raise) }; // Ensure contribution does not overflow the actual raised amount crowdloan.raised = crowdloan .raised .checked_add(&amount) .ok_or(Error::::Overflow)?; // Compute the new total contribution and ensure it does not overflow, we // also increment the contributor count if the contribution is new. let contribution = if let Some(contribution) = Contributions::::get(crowdloan_id, &contributor) { contribution .checked_add(&amount) .ok_or(Error::::Overflow)? } else { // We have a new contribution crowdloan.contributors_count = crowdloan .contributors_count .checked_add(1) .ok_or(Error::::Overflow)?; amount }; // Ensure contributor has enough balance to pay ensure!( CurrencyOf::::balance(&contributor) >= amount, Error::::InsufficientBalance ); CurrencyOf::::transfer( &contributor, &crowdloan.funds_account, amount, Preservation::Expendable, )?; Contributions::::insert(crowdloan_id, &contributor, contribution); Crowdloans::::insert(crowdloan_id, &crowdloan); Self::deposit_event(Event::::Contributed { crowdloan_id, contributor, amount, }); Ok(()) } ``` Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # create-crowdloan (/docs/tx/create-crowdloan) Locks `deposit_tao` from the creator (it counts toward the raise and comes back when the loan is dissolved) and opens the loan for contributions until `end`. Provide exactly one of `target_ss58` (the address that receives the raised funds on finalize) or `call` (an inner intent, `{"op": ..., ...args}`, dispatched with the creator's origin on finalize) — supplying both or neither is rejected. If the cap is reached the creator finalizes with `finalize_crowdloan`. Passing `end` only stops new contributions — nothing is refunded automatically: the creator returns contributions with `refund_crowdloan` (contributors may also `withdraw_crowdloan` themselves) and then `dissolve_crowdloan` to recover the deposit. | Signer | Origin | Pallet | Wraps | | --------- | -------------------------------------- | --------- | ------------------------------------------------------------------ | | `coldkey` | signed account (pallet role may apply) | Crowdloan | [`Crowdloan.create`](/code/pallets/crowdloan/src/lib.rs#L331-L425) | ## Parameters [#parameters] | Parameter | Type | Required | Description | | ---------------------- | ----------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `deposit_tao` | number \| `"all"` | yes | Creator's initial deposit, locked when the crowdloan opens. It counts toward the raise and is returned when the crowdloan is dissolved. | | `min_contribution_tao` | number \| `"all"` | yes | Smallest contribution the crowdloan will accept from a contributor. | | `cap_tao` | number \| `"all"` | yes | Maximum total to raise. Reaching the cap lets the creator finalize. | | `end` | integer | yes | Block number at which the crowdloan stops accepting contributions. Passing it refunds nothing by itself; refunds happen via explicit refund or withdraw calls. | | `target_ss58` | string | no | Account that receives the raised funds on finalize. Provide exactly one of this or call. | | `call` | object | no | Inner intent dispatched with the creator's origin on finalize, as a JSON object \{"op": \, ...args}. Provide exactly one of this or target\_ss58. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx create-crowdloan \ --deposit-tao \ --min-contribution-tao \ --cap-tao \ --end --dry-run btcli tx create-crowdloan \ --deposit-tao \ --min-contribution-tao \ --cap-tao \ --end -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.CreateCrowdloan(deposit_tao=1.0, min_contribution_tao=1.0, cap_tao=1.0, end=0) sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("create_crowdloan", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `Crowdloan.create` — [`pallets/crowdloan/src/lib.rs#L341`](/code/pallets/crowdloan/src/lib.rs#L331-L425): ```rust #[pallet::call_index(0)] #[pallet::weight({ let di = call.as_ref().map(|c| c.get_dispatch_info()); let inner_call_weight = match di { Some(di) => di.call_weight, None => Weight::zero(), }; let base_weight = T::WeightInfo::create(); (base_weight.saturating_add(inner_call_weight), Pays::Yes) })] pub fn create( origin: OriginFor, #[pallet::compact] deposit: BalanceOf, #[pallet::compact] min_contribution: BalanceOf, #[pallet::compact] cap: BalanceOf, #[pallet::compact] end: BlockNumberFor, call: Option::RuntimeCall>>, target_address: Option, ) -> DispatchResult { let creator = ensure_signed(origin)?; let now = frame_system::Pallet::::block_number(); // Ensure the deposit is at least the minimum deposit, cap is greater than deposit // and the minimum contribution is greater than the absolute minimum contribution. ensure!( deposit >= T::MinimumDeposit::get(), Error::::DepositTooLow ); ensure!(cap > deposit, Error::::CapTooLow); ensure!( min_contribution >= T::AbsoluteMinimumContribution::get(), Error::::MinimumContributionTooLow ); ensure!( call.is_some() != target_address.is_some(), Error::::InvalidFinalizationConfig ); Self::ensure_valid_end(now, end)?; // Ensure the creator has enough balance to pay the initial deposit ensure!( CurrencyOf::::balance(&creator) >= deposit, Error::::InsufficientBalance ); let crowdloan_id = NextCrowdloanId::::get(); let next_crowdloan_id = crowdloan_id.checked_add(1).ok_or(Error::::Overflow)?; NextCrowdloanId::::put(next_crowdloan_id); // Derive the funds account and keep track of it let funds_account = Self::funds_account(crowdloan_id); frame_system::Pallet::::inc_providers(&funds_account); // If the call is provided, bound it and store it in the preimage storage let call = if let Some(call) = call { Some(T::Preimages::bound(*call)?) } else { None }; let crowdloan = CrowdloanInfo { creator: creator.clone(), deposit, min_contribution, end, cap, funds_account, raised: deposit, target_address, call, finalized: false, contributors_count: 1, }; Crowdloans::::insert(crowdloan_id, &crowdloan); // Transfer the deposit to the funds account CurrencyOf::::transfer( &creator, &crowdloan.funds_account, deposit, Preservation::Expendable, )?; Contributions::::insert(crowdloan_id, &creator, deposit); Self::deposit_event(Event::::Created { crowdloan_id, creator, end, cap, }); Ok(()) } ``` Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # create-pure-proxy (/docs/tx/create-pure-proxy) The chain derives a new address from the signer, `proxy_type`, `index`, and the creation block. Nobody holds its private key — the spawner controls it purely through the proxy relationship, which makes it useful as a disposable or role-scoped account (e.g. a Staking-only treasury). Record the creation block and extrinsic index (shown in the result): `kill_pure_proxy` needs them to close the account later. Losing the spawner key means losing the pure proxy and anything it holds. | Signer | Origin | Pallet | Wraps | | --------- | -------------------------------------- | ------ | --------------------------------------------------------------- | | `coldkey` | signed account (pallet role may apply) | Proxy | [`Proxy.create_pure`](/code/pallets/proxy/src/lib.rs#L330-L364) | ## Parameters [#parameters] | Parameter | Type | Required | Description | | ------------ | ------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `proxy_type` | string | no | Scope of calls the delegation covers. One of: Any, Owner, NonCritical, NonTransfer, Senate, NonFungible, Triumvirate, Governance, Staking, Registration, Transfer, SmallTransfer, RootWeights, ChildKeys, SudoUncheckedSetCode, SwapHotkey, SubnetLeaseBeneficiary, RootClaim. Triumvirate, Senate, Governance, and RootWeights are deprecated on the current runtime: they deny all calls, so a proxy of those types can dispatch nothing. Prefer the narrowest type that covers your use; Any can do everything the account can, including transfers. | | `delay` | integer | no | Announcement delay in blocks: the delegate must announce each call and wait this long before executing it, giving you time to veto. 0 executes immediately. | | `index` | integer | no | Disambiguator so one signer can create several pure proxies in one block; also part of the derived address. Keep 0 unless batching. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx create-pure-proxy --dry-run btcli tx create-pure-proxy -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.CreatePureProxy() sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("create_pure_proxy", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `Proxy.create_pure` — [`pallets/proxy/src/lib.rs#L332`](/code/pallets/proxy/src/lib.rs#L330-L364): ```rust #[pallet::call_index(4)] #[pallet::weight(T::WeightInfo::create_pure(T::MaxProxies::get()))] pub fn create_pure( origin: OriginFor, proxy_type: T::ProxyType, delay: BlockNumberFor, index: u16, ) -> DispatchResult { let who = ensure_signed(origin)?; let pure = Self::pure_account(&who, &proxy_type, index, None)?; ensure!(!Proxies::::contains_key(&pure), Error::::Duplicate); let proxy_def = ProxyDefinition { delegate: who.clone(), proxy_type: proxy_type.clone(), delay, }; let bounded_proxies: BoundedVec<_, T::MaxProxies> = vec![proxy_def] .try_into() .map_err(|_| Error::::TooMany)?; let deposit = T::ProxyDepositBase::get().saturating_add(T::ProxyDepositFactor::get()); T::Currency::reserve(&who, deposit)?; Proxies::::insert(&pure, (bounded_proxies, deposit)); Self::deposit_event(Event::PureCreated { pure, who, proxy_type, disambiguation_index: index, }); Ok(()) } ``` Delegates to [`pure_account`](/code/pallets/proxy/src/lib.rs#L907). Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # decrease-take (/docs/tx/decrease-take) The delegate take is the fraction of staking emissions a delegate hotkey keeps for itself before distributing the rest to its nominators. This call only moves the take downward — the chain rejects values at or above the current take — and unlike increases it is not rate limited, so lowering your take is always available. Signed by the coldkey that owns the hotkey. Use `set_take` to land on an absolute value without tracking direction. | Signer | Origin | Pallet | Wraps | | --------- | -------------------------------------- | --------------- | --------------------------------------------------------------------------------------------- | | `coldkey` | signed account (pallet role may apply) | SubtensorModule | [`SubtensorModule.decrease_take`](/code/pallets/subtensor/src/macros/dispatches.rs#L490-L498) | ## Parameters [#parameters] | Parameter | Type | Required | Description | | ------------- | --------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------- | | `take` | integer \| number \| string | yes | New take: a 0..1 fraction (e.g. 0.18 for 18 percent) or the raw u16 proportion (0..65535). The chain enforces its configured take bounds. | | `hotkey_ss58` | string | no | Hotkey the operation applies to. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx decrease-take \ --take --dry-run btcli tx decrease-take \ --take -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.DecreaseTake(take="...") sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("decrease_take", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `SubtensorModule.decrease_take` — [`pallets/subtensor/src/macros/dispatches.rs#L492`](/code/pallets/subtensor/src/macros/dispatches.rs#L490-L498): ```rust #[pallet::call_index(65)] #[pallet::weight((::WeightInfo::decrease_take(), DispatchClass::Normal, Pays::Yes))] pub fn decrease_take( origin: OriginFor, hotkey: T::AccountId, take: PerU16, ) -> DispatchResult { Self::do_decrease_take(origin, hotkey, take) } ``` Delegates to [`do_decrease_take`](/code/pallets/subtensor/src/staking/decrease_take.rs#L25). Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # dispute-coldkey-swap (/docs/tx/dispute-coldkey-swap) The recovery path for a compromised coldkey: if a swap was announced on your coldkey that you did not initiate, disputing freezes the account entirely — the chain rejects ALL signed extrinsics from it, including executing the swap, clearing the announcement, or disputing again — until root clears the state via `reset_coldkey_swap`. Sign it from the affected coldkey itself. It does not cancel the announcement or move anything — it locks the situation so an attacker cannot complete the takeover while governance investigates; only root can unfreeze. Use `clear_coldkey_swap_announcement` instead to withdraw an announcement you made yourself. | Signer | Origin | Pallet | Wraps | | --------- | -------------------------------------- | --------------- | ------------------------------------------------------------------------------------------------------ | | `coldkey` | signed account (pallet role may apply) | SubtensorModule | [`SubtensorModule.dispute_coldkey_swap`](/code/pallets/subtensor/src/macros/dispatches.rs#L2054-L2073) | ## Parameters [#parameters] This operation takes no parameters. Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx dispute-coldkey-swap --dry-run btcli tx dispute-coldkey-swap -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.DisputeColdkeySwap() sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("dispute_coldkey_swap", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `SubtensorModule.dispute_coldkey_swap` — [`pallets/subtensor/src/macros/dispatches.rs#L2056`](/code/pallets/subtensor/src/macros/dispatches.rs#L2054-L2073): ```rust #[pallet::call_index(127)] #[pallet::weight(::WeightInfo::dispute_coldkey_swap())] pub fn dispute_coldkey_swap(origin: OriginFor) -> DispatchResult { let coldkey = ensure_signed(origin)?; ensure!( ColdkeySwapAnnouncements::::contains_key(&coldkey), Error::::ColdkeySwapAnnouncementNotFound ); ensure!( !ColdkeySwapDisputes::::contains_key(&coldkey), Error::::ColdkeySwapAlreadyDisputed ); let now = >::block_number(); ColdkeySwapDisputes::::insert(&coldkey, now); Self::deposit_event(Event::ColdkeySwapDisputed { coldkey }); Ok(()) } ``` Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # dissolve-crowdloan (/docs/tx/dissolve-crowdloan) Removes a non-finalized crowdloan from chain state once every non-creator contribution has been returned (run `refund_crowdloan` until no other contributors remain). Dissolving automatically transfers the creator's remaining contribution — including the deposit — back to them. Only the creator may dissolve. Dissolving while other contributions are still outstanding fails — refund first. | Signer | Origin | Pallet | Wraps | | --------- | -------------------------------------- | --------- | -------------------------------------------------------------------- | | `coldkey` | signed account (pallet role may apply) | Crowdloan | [`Crowdloan.dissolve`](/code/pallets/crowdloan/src/lib.rs#L761-L805) | ## Parameters [#parameters] | Parameter | Type | Required | Description | | -------------- | ------- | -------- | ---------------------------------------------------------- | | `crowdloan_id` | integer | yes | Identifier of the crowdloan, assigned when it was created. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx dissolve-crowdloan \ --crowdloan-id --dry-run btcli tx dissolve-crowdloan \ --crowdloan-id -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.DissolveCrowdloan(crowdloan_id=0) sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("dissolve_crowdloan", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `Crowdloan.dissolve` — [`pallets/crowdloan/src/lib.rs#L763`](/code/pallets/crowdloan/src/lib.rs#L761-L805): ```rust #[pallet::call_index(5)] #[pallet::weight(T::WeightInfo::dissolve())] pub fn dissolve( origin: OriginFor, #[pallet::compact] crowdloan_id: CrowdloanId, ) -> DispatchResult { let who = ensure_signed(origin)?; let crowdloan = Self::ensure_crowdloan_exists(crowdloan_id)?; ensure!(!crowdloan.finalized, Error::::AlreadyFinalized); // Only the creator can dissolve the crowdloan ensure!(who == crowdloan.creator, Error::::InvalidOrigin); // It can only be dissolved if the raised amount is the creator's contribution, // meaning there is no contributions or every contribution has been refunded let creator_contribution = Contributions::::get(crowdloan_id, &crowdloan.creator) .ok_or(Error::::NoContribution)?; ensure!( creator_contribution == crowdloan.raised, Error::::NotReadyToDissolve ); // Refund the creator's contribution CurrencyOf::::transfer( &crowdloan.funds_account, &crowdloan.creator, creator_contribution, Preservation::Expendable, )?; Contributions::::remove(crowdloan_id, &crowdloan.creator); // Clear the call from the preimage storage if let Some(call) = crowdloan.call { T::Preimages::drop(&call); } // Remove the crowdloan let _ = frame_system::Pallet::::dec_providers(&crowdloan.funds_account).defensive(); Crowdloans::::remove(crowdloan_id); MaxContributions::::remove(crowdloan_id); Self::deposit_event(Event::::Dissolved { crowdloan_id }); Ok(()) } ``` Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # evm-withdraw (/docs/tx/evm-withdraw) Every native account controls one EVM address: the first 20 bytes of its public key (the *truncated* mapping). TAO sent from MetaMask to that address's mirror can be pulled into the native account with this call — the EVM-to-substrate path that needs no EVM gas. The flow: send TAO from the EVM wallet to the address shown by `btcli evm deposit-address`, then claim it with `btcli evm claim-deposit` (or `btcli tx evm-withdraw`). This is not `btcli evm send-to-ss58`, which spends from a stored EVM key via the balance-transfer precompile. Fails if the mirror holds less than the amount. | Signer | Origin | Pallet | Wraps | | --------- | -------------------------------------- | ------ | -------------- | | `coldkey` | signed account (pallet role may apply) | EVM | `EVM.withdraw` | ## Parameters [#parameters] | Parameter | Type | Required | Description | | ------------ | ----------------- | -------- | ------------------------------------- | | `amount_tao` | number \| `"all"` | yes | How much TAO to pull from the mirror. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx evm-withdraw \ --amount-tao --dry-run btcli tx evm-withdraw \ --amount-tao -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.EvmWithdraw(amount_tao=1.0) sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("evm_withdraw", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `EVM.withdraw` is implemented by Frontier's `pallet_evm` (vendored under `vendor/frontier`), outside this repository's pallets. Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # execute-proxy-announced (/docs/tx/execute-proxy-announced) The delayed-proxy flow: a delegate added with `delay` > 0 first announces the call's hash, waits out the delay (during which the real account can veto), then anyone may dispatch the announced call with this intent. The inner call is given as an intent op name plus its arguments and must hash to exactly what was announced. Fails if the delay has not elapsed or no matching announcement exists. | Signer | Origin | Pallet | Wraps | | --------- | -------------------------------------- | ------ | ------------------------------------------------------------------- | | `coldkey` | signed account (pallet role may apply) | Proxy | [`Proxy.proxy_announced`](/code/pallets/proxy/src/lib.rs#L540-L573) | ## Parameters [#parameters] | Parameter | Type | Required | Description | | ------------------ | ------ | -------- | -------------------------------------------------------------------------------------------------------- | | `delegate_ss58` | string | yes | Delegate that made the announcement. | | `real_ss58` | string | yes | Account the call executes as (the delegation's grantor). | | `inner_op` | string | yes | Intent op name of the announced call (see btcli tools for the catalog). | | `inner_args` | object | yes | Arguments of the announced call, as a JSON object. | | `force_proxy_type` | string | no | Require the delegation to be exactly this proxy type instead of accepting any type that covers the call. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx execute-proxy-announced \ --delegate \ --real \ --inner-op \ --inner-args '' --dry-run btcli tx execute-proxy-announced \ --delegate \ --real \ --inner-op \ --inner-args '' -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.ExecuteProxyAnnounced(delegate_ss58="5F...", real_ss58="5F...", inner_op="...", inner_args={...}) sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("execute_proxy_announced", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `Proxy.proxy_announced` — [`pallets/proxy/src/lib.rs#L549`](/code/pallets/proxy/src/lib.rs#L540-L573): ```rust #[pallet::call_index(9)] #[pallet::weight({ let di = call.get_dispatch_info(); (T::WeightInfo::proxy_announced(T::MaxPending::get(), T::MaxProxies::get()) // AccountData for inner call origin accountdata. .saturating_add(T::DbWeight::get().reads_writes(1, 1)) .saturating_add(di.call_weight), DispatchClass::Normal) })] pub fn proxy_announced( origin: OriginFor, delegate: AccountIdLookupOf, real: AccountIdLookupOf, force_proxy_type: Option, call: Box<::RuntimeCall>, ) -> DispatchResult { ensure_signed(origin)?; let delegate = T::Lookup::lookup(delegate)?; let real = T::Lookup::lookup(real)?; let def = Self::find_proxy(&real, &delegate, force_proxy_type)?; let call_hash = T::CallHasher::hash_of(&call); let now = T::BlockNumberProvider::current_block_number(); Self::edit_announcements(&delegate, |ann| { ann.real != real || ann.call_hash != call_hash || now.saturating_sub(ann.height) < def.delay }) .map_err(|_| Error::::Unannounced)?; Self::do_proxy(def, real, *call); Ok(()) } ``` Delegates to [`find_proxy`](/code/pallets/proxy/src/lib.rs#L1085). Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # finalize-crowdloan (/docs/tx/finalize-crowdloan) Settles a successful raise: the pot is transferred to the loan's target address, or its inner call is dispatched with the creator's origin. Only the creator may finalize, and only once the cap has been reached. After finalization the crowdloan can no longer be updated, refunded, or dissolved. | Signer | Origin | Pallet | Wraps | | --------- | -------------------------------------- | --------- | -------------------------------------------------------------------- | | `coldkey` | signed account (pallet role may apply) | Crowdloan | [`Crowdloan.finalize`](/code/pallets/crowdloan/src/lib.rs#L609-L674) | ## Parameters [#parameters] | Parameter | Type | Required | Description | | -------------- | ------- | -------- | ---------------------------------------------------------- | | `crowdloan_id` | integer | yes | Identifier of the crowdloan, assigned when it was created. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx finalize-crowdloan \ --crowdloan-id --dry-run btcli tx finalize-crowdloan \ --crowdloan-id -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.FinalizeCrowdloan(crowdloan_id=0) sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("finalize_crowdloan", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `Crowdloan.finalize` — [`pallets/crowdloan/src/lib.rs#L611`](/code/pallets/crowdloan/src/lib.rs#L609-L674): ```rust #[pallet::call_index(3)] #[pallet::weight(T::WeightInfo::finalize())] pub fn finalize( origin: OriginFor, #[pallet::compact] crowdloan_id: CrowdloanId, ) -> DispatchResult { let who = ensure_signed(origin)?; let mut crowdloan = Self::ensure_crowdloan_exists(crowdloan_id)?; // Ensure the origin is the creator of the crowdloan and the crowdloan has raised the cap // and is not finalized. ensure!(who == crowdloan.creator, Error::::InvalidOrigin); ensure!(crowdloan.raised == crowdloan.cap, Error::::CapNotRaised); ensure!(!crowdloan.finalized, Error::::AlreadyFinalized); ensure!( CurrentCrowdloanId::::get().is_none(), Error::::AlreadyFinalizing ); crowdloan.finalized = true; Crowdloans::::insert(crowdloan_id, &crowdloan); match (&crowdloan.call, &crowdloan.target_address) { (Some(call), None) => { // Set the current crowdloan id so the dispatched call // can access it temporarily CurrentCrowdloanId::::put(crowdloan_id); // Retrieve the call from the preimage storage let stored_call = match T::Preimages::peek(call) { Ok((call, _)) => call, Err(_) => { // If the call is not found, we drop it from the preimage storage // because it's not needed anymore T::Preimages::drop(call); return Err(Error::::CallUnavailable)?; } }; // Dispatch the call with creator origin stored_call .dispatch(frame_system::RawOrigin::Signed(who).into()) .map(|_| ()) .map_err(|e| e.error)?; // Clear the current crowdloan id CurrentCrowdloanId::::kill(); } (None, Some(target_address)) => { CurrencyOf::::transfer( &crowdloan.funds_account, target_address, crowdloan.raised, Preservation::Expendable, )?; } (_, _) => { return Err(Error::::InvalidFinalizationConfig)?; } } Self::deposit_event(Event::::Finalized { crowdloan_id }); Ok(()) } ``` Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # fund-evm-key (/docs/tx/fund-evm-key) An EVM account's native balance lives at its ss58 *mirror* address (`blake2_256("evm:" ++ h160)`). This intent computes the mirror and transfers TAO to it; the funds then appear as the EVM account's balance in MetaMask or any Ethereum tool (displayed with 18 decimals there: 1 TAO = 1e18). Like any transfer this is irreversible — and only the holder of the EVM private key can move the funds afterwards, so double-check the address. | Signer | Origin | Pallet | Wraps | | --------- | -------------------------------------- | -------- | ------------------------------ | | `coldkey` | signed account (pallet role may apply) | Balances | `Balances.transfer_keep_alive` | ## Parameters [#parameters] | Parameter | Type | Required | Description | | ------------- | ----------------- | -------- | --------------------------------------------- | | `evm_address` | string | yes | EVM address to fund, as 0x-prefixed h160 hex. | | `amount_tao` | number \| `"all"` | yes | How much TAO to send. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx fund-evm-key \ --evm-address \ --amount-tao --dry-run btcli tx fund-evm-key \ --evm-address \ --amount-tao -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.FundEvmKey(evm_address="...", amount_tao=1.0) sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("fund_evm_key", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `Balances.transfer_keep_alive` is implemented by Substrate's `pallet_balances`, outside this repository's pallets. Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # increase-take (/docs/tx/increase-take) The delegate take is the fraction of staking emissions a delegate hotkey keeps for itself before distributing the rest to its nominators. This call only moves the take upward: the chain rejects values at or below the current take, values above the configured maximum, and increases made sooner than the take rate limit allows. Signed by the coldkey that owns the hotkey. Use `set_take` if you just want to land on an absolute value without tracking the direction yourself. | Signer | Origin | Pallet | Wraps | | --------- | -------------------------------------- | --------------- | --------------------------------------------------------------------------------------------- | | `coldkey` | signed account (pallet role may apply) | SubtensorModule | [`SubtensorModule.increase_take`](/code/pallets/subtensor/src/macros/dispatches.rs#L523-L531) | ## Parameters [#parameters] | Parameter | Type | Required | Description | | ------------- | --------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------- | | `take` | integer \| number \| string | yes | New take: a 0..1 fraction (e.g. 0.18 for 18 percent) or the raw u16 proportion (0..65535). The chain enforces its configured take bounds. | | `hotkey_ss58` | string | no | Hotkey the operation applies to. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx increase-take \ --take --dry-run btcli tx increase-take \ --take -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.IncreaseTake(take="...") sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("increase_take", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `SubtensorModule.increase_take` — [`pallets/subtensor/src/macros/dispatches.rs#L525`](/code/pallets/subtensor/src/macros/dispatches.rs#L523-L531): ```rust #[pallet::call_index(66)] #[pallet::weight((::WeightInfo::increase_take(), DispatchClass::Normal, Pays::Yes))] pub fn increase_take( origin: OriginFor, hotkey: T::AccountId, take: PerU16, ) -> DispatchResult { Self::do_increase_take(origin, hotkey, take) } ``` Delegates to [`do_increase_take`](/code/pallets/subtensor/src/staking/increase_take.rs#L27). Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # Transactions (/docs/tx) Every mutation is an **intent**: preview it with `plan` / `--dry-run`, submit it with `execute`. Each CLI command below is `btcli tx `; each Python class is `bt.`. The machine-readable catalog of all of these (with JSON schemas) is at [`/catalog/intents.json`](/catalog/intents.json) or via `btcli tools` / `bt.intents.list_tools()`. ## AdminUtils [#adminutils] | Operation | Signer | Origin | Summary | | --------------------------------------------------------------------- | ------- | ----------------- | ---------------------------------------------------------------- | | [`set-hyperparameter`](/docs/tx/set-hyperparameter) | coldkey | subnet owner | Set an owner-settable subnet hyperparameter (btcli `sudo set`). | | [`set-mechanism-count`](/docs/tx/set-mechanism-count) | coldkey | subnet owner | Set the number of mechanisms on a subnet. | | [`set-subnet-emission-enabled`](/docs/tx/set-subnet-emission-enabled) | coldkey | root (chain sudo) | Enable or disable a subnet's pool-side TAO emission (root only). | | [`trim-subnet`](/docs/tx/trim-subnet) | coldkey | subnet owner | Trim a subnet to at most `max_n` UIDs (subnet owner). | ## Balances [#balances] | Operation | Signer | Origin | Summary | | --------------------------------------- | ------- | -------------------------------------- | ------------------------------------------------------------------ | | [`fund-evm-key`](/docs/tx/fund-evm-key) | coldkey | signed account (pallet role may apply) | Fund an EVM (h160) address with TAO from the signing coldkey. | | [`transfer`](/docs/tx/transfer) | coldkey | signed account (pallet role may apply) | Transfer TAO from the coldkey to a destination address. | | [`transfer-all`](/docs/tx/transfer-all) | coldkey | signed account (pallet role may apply) | Transfer the entire transferable balance to a destination address. | ## Crowdloan [#crowdloan] | Operation | Signer | Origin | Summary | | --------------------------------------------------------------------------------- | ------- | -------------------------------------- | ---------------------------------------------------------------------------------- | | [`contribute-crowdloan`](/docs/tx/contribute-crowdloan) | coldkey | signed account (pallet role may apply) | Contribute TAO to an active crowdloan. | | [`create-crowdloan`](/docs/tx/create-crowdloan) | coldkey | signed account (pallet role may apply) | Open a crowdloan raising up to `cap_tao` by `end` block. | | [`dissolve-crowdloan`](/docs/tx/dissolve-crowdloan) | coldkey | signed account (pallet role may apply) | Dissolve a fully-refunded crowdloan (creator only). | | [`finalize-crowdloan`](/docs/tx/finalize-crowdloan) | coldkey | signed account (pallet role may apply) | Finalize a crowdloan that reached its cap (creator only). | | [`refund-crowdloan`](/docs/tx/refund-crowdloan) | coldkey | signed account (pallet role may apply) | Refund contributors of a non-finalized crowdloan (creator only). | | [`set-crowdloan-max-contribution`](/docs/tx/set-crowdloan-max-contribution) | coldkey | signed account (pallet role may apply) | Set or clear the per-contributor max for a non-finalized crowdloan (creator only). | | [`update-crowdloan-cap`](/docs/tx/update-crowdloan-cap) | coldkey | signed account (pallet role may apply) | Update the cap of a non-finalized crowdloan (creator only). | | [`update-crowdloan-end`](/docs/tx/update-crowdloan-end) | coldkey | signed account (pallet role may apply) | Update the end block of a non-finalized crowdloan (creator only). | | [`update-crowdloan-min-contribution`](/docs/tx/update-crowdloan-min-contribution) | coldkey | signed account (pallet role may apply) | Update the minimum contribution of a non-finalized crowdloan (creator only). | | [`withdraw-crowdloan`](/docs/tx/withdraw-crowdloan) | coldkey | signed account (pallet role may apply) | Withdraw the signer's contribution from a non-finalized crowdloan. | ## EVM [#evm] | Operation | Signer | Origin | Summary | | --------------------------------------- | ------- | -------------------------------------- | ---------------------------------------------------------- | | [`evm-withdraw`](/docs/tx/evm-withdraw) | coldkey | signed account (pallet role may apply) | Claim TAO deposited to the coldkey's truncated EVM mirror. | ## Multisig [#multisig] | Operation | Signer | Origin | Summary | | ------------------------------------------------------- | ------- | -------------------------------------- | ------------------------------------------------------------------------------- | | [`multisig-approve`](/docs/tx/multisig-approve) | coldkey | signed account (pallet role may apply) | Register approval for a multisig call (non-final approvals). | | [`multisig-cancel`](/docs/tx/multisig-cancel) | coldkey | signed account (pallet role may apply) | Cancel an ongoing multisig operation (only the original depositor may). | | [`multisig-execute`](/docs/tx/multisig-execute) | coldkey | signed account (pallet role may apply) | Approve and, if the threshold is met, execute a multisig call (final approval). | | [`multisig-threshold-1`](/docs/tx/multisig-threshold-1) | coldkey | signed account (pallet role may apply) | Dispatch a 1-of-N multisig call immediately (single approval). | ## Proxy [#proxy] | Operation | Signer | Origin | Summary | | ------------------------------------------------------------- | ------- | -------------------------------------- | ----------------------------------------------------------------------- | | [`add-proxy`](/docs/tx/add-proxy) | coldkey | signed account (pallet role may apply) | Authorize a delegate key to sign calls on this account's behalf. | | [`create-pure-proxy`](/docs/tx/create-pure-proxy) | coldkey | signed account (pallet role may apply) | Create a pure proxy: a fresh keyless account controlled via delegation. | | [`execute-proxy-announced`](/docs/tx/execute-proxy-announced) | coldkey | signed account (pallet role may apply) | Execute a proxy call that was announced and has passed its delay. | | [`kill-pure-proxy`](/docs/tx/kill-pure-proxy) | coldkey | signed account (pallet role may apply) | Close a pure proxy account and return its reserved deposit. | | [`remove-proxies`](/docs/tx/remove-proxies) | coldkey | signed account (pallet role may apply) | Revoke every proxy delegation for the signing account at once. | | [`remove-proxy`](/docs/tx/remove-proxy) | coldkey | signed account (pallet role may apply) | Revoke one proxy delegation. | ## SubtensorModule [#subtensormodule] | Operation | Signer | Origin | Summary | | ----------------------------------------------------------------------------- | ------- | -------------------------------------- | ---------------------------------------------------------------------------- | | [`add-collateral`](/docs/tx/add-collateral) | coldkey | signed account (pallet role may apply) | Lock additional miner collateral on your own hotkey. | | [`add-stake`](/docs/tx/add-stake) | coldkey | signed account (pallet role may apply) | Stake TAO from the coldkey onto a hotkey. | | [`add-stake-limit`](/docs/tx/add-stake-limit) | coldkey | signed account (pallet role may apply) | Stake TAO with a limit price (slippage protection). | | [`announce-coldkey-swap`](/docs/tx/announce-coldkey-swap) | coldkey | signed account (pallet role may apply) | Announce (commit to) a coldkey swap; executable after the chain's delay. | | [`associate-evm-key`](/docs/tx/associate-evm-key) | hotkey | signed account (pallet role may apply) | Associate an EVM key with a hotkey on a subnet. | | [`associate-hotkey`](/docs/tx/associate-hotkey) | coldkey | signed account (pallet role may apply) | Associate a hotkey with the signing coldkey. | | [`burned-register`](/docs/tx/burned-register) | coldkey | signed account (pallet role may apply) | Register a hotkey on a subnet by paying the registration cost. | | [`claim-root`](/docs/tx/claim-root) | coldkey | signed account (pallet role may apply) | Claim accumulated root dividends from one or more subnets. | | [`clear-coldkey-swap-announcement`](/docs/tx/clear-coldkey-swap-announcement) | coldkey | signed account (pallet role may apply) | Cancel a pending coldkey swap announcement (after the reannouncement delay). | | [`commit-weights`](/docs/tx/commit-weights) | hotkey | signed account (pallet role may apply) | Timelock-encrypt and commit weights, forcing the commit-reveal path. | | [`decrease-take`](/docs/tx/decrease-take) | coldkey | signed account (pallet role may apply) | Decrease the delegate take of a hotkey. | | [`dispute-coldkey-swap`](/docs/tx/dispute-coldkey-swap) | coldkey | signed account (pallet role may apply) | Freeze this coldkey entirely until root resolves the dispute. | | [`increase-take`](/docs/tx/increase-take) | coldkey | signed account (pallet role may apply) | Increase the delegate take of a hotkey. | | [`lock-stake`](/docs/tx/lock-stake) | coldkey | signed account (pallet role may apply) | Lock alpha stake on a subnet, building conviction toward a hotkey. | | [`move-lock`](/docs/tx/move-lock) | coldkey | signed account (pallet role may apply) | Move an existing lock from one hotkey to another on a subnet. | | [`move-stake`](/docs/tx/move-stake) | coldkey | signed account (pallet role may apply) | Move alpha between hotkeys and/or subnets. | | [`register-leased-network`](/docs/tx/register-leased-network) | coldkey | signed account (pallet role may apply) | Register a new crowdloan-funded leased subnet. | | [`register-subnet`](/docs/tx/register-subnet) | coldkey | signed account (pallet role may apply) | Create a new subnet owned by the signing coldkey. | | [`remove-stake`](/docs/tx/remove-stake) | coldkey | signed account (pallet role may apply) | Unstake alpha from a hotkey back to the coldkey. | | [`remove-stake-limit`](/docs/tx/remove-stake-limit) | coldkey | signed account (pallet role may apply) | Unstake alpha with a limit price (slippage protection). | | [`reset-axon`](/docs/tx/reset-axon) | hotkey | signed account (pallet role may apply) | Reset (stop serving) this hotkey's axon endpoint on a subnet. | | [`reveal-weights`](/docs/tx/reveal-weights) | hotkey | signed account (pallet role may apply) | Reveal previously committed weights (legacy salt-based commit-reveal). | | [`root-register`](/docs/tx/root-register) | coldkey | signed account (pallet role may apply) | Register a hotkey on the root network (netuid 0). | | [`serve-axon`](/docs/tx/serve-axon) | hotkey | signed account (pallet role may apply) | Publish this hotkey's axon endpoint (ip:port) for a subnet. | | [`serve-axon-tls`](/docs/tx/serve-axon-tls) | hotkey | signed account (pallet role may apply) | Publish this hotkey's axon endpoint with a neuron certificate. | | [`serve-prometheus`](/docs/tx/serve-prometheus) | hotkey | signed account (pallet role may apply) | Publish this hotkey's prometheus metrics endpoint (ip:port) for a subnet. | | [`set-auto-stake`](/docs/tx/set-auto-stake) | coldkey | signed account (pallet role may apply) | Auto-stake future mining rewards on a subnet to a chosen hotkey. | | [`set-childkey-take`](/docs/tx/set-childkey-take) | coldkey | signed account (pallet role may apply) | Set the childkey take for a hotkey on a subnet. | | [`set-children`](/docs/tx/set-children) | coldkey | signed account (pallet role may apply) | Assign child hotkeys with stake-weight proportions on a subnet. | | [`set-identity`](/docs/tx/set-identity) | coldkey | signed account (pallet role may apply) | Publish an on-chain identity (name, links, description) for the coldkey. | | [`set-min-collateral`](/docs/tx/set-min-collateral) | coldkey | signed account (pallet role may apply) | Set the self-maintaining collateral floor for your hotkey on a subnet. | | [`set-perpetual-lock`](/docs/tx/set-perpetual-lock) | coldkey | signed account (pallet role may apply) | Enable or disable perpetual lock mode for a coldkey on a subnet. | | [`set-root-claim-type`](/docs/tx/set-root-claim-type) | coldkey | signed account (pallet role may apply) | Set how a coldkey's root alpha emission is claimed. | | [`set-subnet-identity`](/docs/tx/set-subnet-identity) | coldkey | subnet owner | Publish identity metadata for a subnet (signer must be the subnet owner). | | [`set-take`](/docs/tx/set-take) | coldkey | signed account (pallet role may apply) | Set the delegate take to an absolute value. | | [`set-weights`](/docs/tx/set-weights) | hotkey | signed account (pallet role may apply) | Set validator weights, auto-selecting plaintext or commit-reveal. | | [`stake-burn`](/docs/tx/stake-burn) | coldkey | signed account (pallet role may apply) | Buy back / burn stake via the stake-burn extrinsic. | | [`start-call`](/docs/tx/start-call) | coldkey | subnet owner | Activate a subnet (subtoken trading, epochs) as its owner. | | [`swap-coldkey-announced`](/docs/tx/swap-coldkey-announced) | coldkey | signed account (pallet role may apply) | Execute a previously announced coldkey swap (after the delay has passed). | | [`swap-hotkey`](/docs/tx/swap-hotkey) | coldkey | signed account (pallet role may apply) | Swap a hotkey for a new one (all subnets, or one netuid). | | [`swap-stake`](/docs/tx/swap-stake) | coldkey | signed account (pallet role may apply) | Swap stake on one hotkey between two subnets. | | [`terminate-lease`](/docs/tx/terminate-lease) | coldkey | signed account (pallet role may apply) | Terminate an ended lease and take subnet ownership (beneficiary only). | | [`transfer-stake`](/docs/tx/transfer-stake) | coldkey | signed account (pallet role may apply) | Transfer stake ownership to another coldkey. | | [`unstake-all`](/docs/tx/unstake-all) | coldkey | signed account (pallet role may apply) | Unstake everything from a hotkey across all subnets. | | [`unstake-all-alpha`](/docs/tx/unstake-all-alpha) | coldkey | signed account (pallet role may apply) | Unstake all alpha from a hotkey across subnets (moves it to root). | | [`update-symbol`](/docs/tx/update-symbol) | coldkey | subnet owner | Update a subnet's symbol (from the chain's fixed catalog). | ## Utility [#utility] | Operation | Signer | Origin | Summary | | ------------------------- | ------- | -------------------------------------- | --------------------------------------------------------------------- | | [`batch`](/docs/tx/batch) | coldkey | signed account (pallet role may apply) | Execute several intents atomically in one extrinsic (all-or-nothing). | # kill-pure-proxy (/docs/tx/kill-pure-proxy) Must be signed *by the pure proxy itself* (i.e. dispatched through it with `proxy_for=`), and the parameters must reproduce the exact creation: spawner, type, index, plus the block height and extrinsic index of the `create_pure_proxy` call. Irreversible — any funds left in the account become permanently inaccessible, so empty it first. | Signer | Origin | Pallet | Wraps | | --------- | -------------------------------------- | ------ | ------------------------------------------------------------- | | `coldkey` | signed account (pallet role may apply) | Proxy | [`Proxy.kill_pure`](/code/pallets/proxy/src/lib.rs#L382-L410) | ## Parameters [#parameters] | Parameter | Type | Required | Description | | -------------- | ------- | -------- | ----------------------------------------------------------- | | `spawner_ss58` | string | yes | Account that originally created the pure proxy. | | `proxy_type` | string | no | Type the pure proxy was created with (must match exactly). | | `index` | integer | no | Index the pure proxy was created with (must match exactly). | | `height` | integer | no | Block number of the creating create\_pure\_proxy call. | | `ext_index` | integer | no | Extrinsic index of the creating call within that block. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx kill-pure-proxy \ --spawner --dry-run btcli tx kill-pure-proxy \ --spawner -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.KillPureProxy(spawner_ss58="5F...") sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("kill_pure_proxy", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `Proxy.kill_pure` — [`pallets/proxy/src/lib.rs#L384`](/code/pallets/proxy/src/lib.rs#L382-L410): ```rust #[pallet::call_index(5)] #[pallet::weight(T::WeightInfo::kill_pure(T::MaxProxies::get()))] pub fn kill_pure( origin: OriginFor, spawner: AccountIdLookupOf, proxy_type: T::ProxyType, index: u16, #[pallet::compact] height: BlockNumberFor, #[pallet::compact] ext_index: u32, ) -> DispatchResult { let who = ensure_signed(origin)?; let spawner = T::Lookup::lookup(spawner)?; let when = (height, ext_index); let proxy = Self::pure_account(&spawner, &proxy_type, index, Some(when))?; ensure!(proxy == who, Error::::NoPermission); let (_, deposit) = Proxies::::take(&who); T::Currency::unreserve(&spawner, deposit); Self::deposit_event(Event::PureKilled { pure: who, spawner, proxy_type, disambiguation_index: index, }); Ok(()) } ``` Delegates to [`pure_account`](/code/pallets/proxy/src/lib.rs#L907). Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # lock-stake (/docs/tx/lock-stake) Commits part of the signing coldkey's alpha on the subnet as locked stake: the locked amount builds conviction the longer it stays locked. The lock acts as a subnet-wide floor on unstaking, not a hold on a specific position — the coldkey can freely unstake anything above the locked mass, and the locked amount itself keeps earning normally. The coldkey's total alpha on the subnet, summed across all hotkeys, must cover the locked amount; conviction can be pointed at one hotkey while the stake sits on another. If a lock already exists on the subnet, the hotkey must match the existing lock's hotkey or the call fails with `LockHotkeyMismatch` — repeat calls only top up the lock; use `move_lock` to change hotkeys. Whether the lock decays over time or persists is controlled per coldkey per subnet with `set_perpetual_lock`. | Signer | Origin | Pallet | Wraps | | --------- | -------------------------------------- | --------------- | -------------------------------------------------------------------------------------------- | | `coldkey` | signed account (pallet role may apply) | SubtensorModule | [`SubtensorModule.lock_stake`](/code/pallets/subtensor/src/macros/dispatches.rs#L2256-L2266) | ## Parameters [#parameters] | Parameter | Type | Required | Description | | -------------- | ----------------- | -------- | --------------------------------------------------------------------------- | | `netuid` | integer | yes | Subnet the locked stake lives on. | | `amount_alpha` | number \| `"all"` | yes | How much of the existing stake to lock. | | `hotkey_ss58` | string | no | Hotkey the lock's conviction is credited to. Defaults to the wallet hotkey. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx lock-stake \ --netuid \ --amount-alpha --dry-run btcli tx lock-stake \ --netuid \ --amount-alpha -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.LockStake(netuid=1, amount_alpha=1.0) sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("lock_stake", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `SubtensorModule.lock_stake` — [`pallets/subtensor/src/macros/dispatches.rs#L2258`](/code/pallets/subtensor/src/macros/dispatches.rs#L2256-L2266): ```rust #[pallet::call_index(136)] #[pallet::weight(::WeightInfo::lock_stake())] pub fn lock_stake( origin: OriginFor, hotkey: T::AccountId, netuid: NetUid, amount: AlphaBalance, ) -> DispatchResult { let coldkey = ensure_signed(origin)?; Self::do_lock_stake(&coldkey, netuid, &hotkey, amount) } ``` Delegates to [`do_lock_stake`](/code/pallets/subtensor/src/staking/lock.rs#L780). Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # move-lock (/docs/tx/move-lock) Re-points the signing coldkey's entire stake lock on the subnet at the destination hotkey, carrying the locked mass with it. Accrued conviction is preserved only when the origin and destination hotkeys are owned by the same coldkey (e.g. rotating your own validator hotkeys); if the destination hotkey belongs to a different coldkey, conviction resets to zero and matures again from scratch. The destination hotkey must exist on chain (fails with `HotKeyAccountNotExists`). Fails if there is no existing lock on the subnet to move. | Signer | Origin | Pallet | Wraps | | --------- | -------------------------------------- | --------------- | ------------------------------------------------------------------------------------------- | | `coldkey` | signed account (pallet role may apply) | SubtensorModule | [`SubtensorModule.move_lock`](/code/pallets/subtensor/src/macros/dispatches.rs#L2280-L2289) | ## Parameters [#parameters] | Parameter | Type | Required | Description | | ------------------------- | ------- | -------- | ---------------------------- | | `netuid` | integer | yes | Subnet the lock lives on. | | `destination_hotkey_ss58` | string | yes | Hotkey the lock is moved to. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx move-lock \ --netuid \ --destination-hotkey --dry-run btcli tx move-lock \ --netuid \ --destination-hotkey -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.MoveLock(netuid=1, destination_hotkey_ss58="5F...") sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("move_lock", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `SubtensorModule.move_lock` — [`pallets/subtensor/src/macros/dispatches.rs#L2282`](/code/pallets/subtensor/src/macros/dispatches.rs#L2280-L2289): ```rust #[pallet::call_index(137)] #[pallet::weight(::WeightInfo::move_lock())] pub fn move_lock( origin: OriginFor, destination_hotkey: T::AccountId, netuid: NetUid, ) -> DispatchResult { let coldkey = ensure_signed(origin)?; Self::do_move_lock(&coldkey, &destination_hotkey, netuid) } ``` Delegates to [`do_move_lock`](/code/pallets/subtensor/src/staking/lock.rs#L1749). Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # move-stake (/docs/tx/move-stake) Re-delegates an existing position without passing through the coldkey's free balance: the stake leaves the origin hotkey on the origin subnet and lands on the destination hotkey at the destination subnet. Moving within one subnet just changes which validator backs the stake; moving across subnets swaps through both pools and can incur slippage on each leg. Ownership stays with the signing coldkey — use `transfer_stake` to hand the position to another coldkey, or `swap_stake` when only the subnet changes. | Signer | Origin | Pallet | Wraps | | --------- | -------------------------------------- | --------------- | -------------------------------------------------------------------------------------------- | | `coldkey` | signed account (pallet role may apply) | SubtensorModule | [`SubtensorModule.move_stake`](/code/pallets/subtensor/src/macros/dispatches.rs#L1255-L1273) | ## Parameters [#parameters] | Parameter | Type | Required | Description | | -------------------- | ----------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | | `origin_hotkey_ss58` | string | yes | Hotkey the stake moves away from. | | `origin_netuid` | integer | yes | Subnet the stake currently sits on. | | `dest_hotkey_ss58` | string | yes | Hotkey the stake moves to. | | `dest_netuid` | integer | yes | Subnet the stake ends up on. When it differs from the origin, the position is swapped through both subnet pools, which can incur slippage on each leg. | | `amount_alpha` | number \| `"all"` | yes | How much of the origin position to move (an explicit amount; `all` is not accepted). | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx move-stake \ --origin-hotkey \ --origin-netuid \ --dest-hotkey \ --dest-netuid \ --amount-alpha --dry-run btcli tx move-stake \ --origin-hotkey \ --origin-netuid \ --dest-hotkey \ --dest-netuid \ --amount-alpha -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.MoveStake(origin_hotkey_ss58="5F...", origin_netuid=0, dest_hotkey_ss58="5F...", dest_netuid=0, amount_alpha=1.0) sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("move_stake", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `SubtensorModule.move_stake` — [`pallets/subtensor/src/macros/dispatches.rs#L1257`](/code/pallets/subtensor/src/macros/dispatches.rs#L1255-L1273): ```rust #[pallet::call_index(85)] #[pallet::weight(::WeightInfo::move_stake())] pub fn move_stake( origin: OriginFor, origin_hotkey: T::AccountId, destination_hotkey: T::AccountId, origin_netuid: NetUid, destination_netuid: NetUid, alpha_amount: AlphaBalance, ) -> DispatchResult { Self::do_move_stake( origin, origin_hotkey, destination_hotkey, origin_netuid, destination_netuid, alpha_amount, ) } ``` Delegates to [`do_move_stake`](/code/pallets/subtensor/src/staking/move_stake.rs#L30). Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # multisig-approve (/docs/tx/multisig-approve) Records the signer's approval for a pending multisig operation without dispatching anything. The *opening* approval (omit `timepoint`; reserves a deposit from the signer) embeds the full call in the extrinsic, so every co-signer can recover the call — and a ready-to-run command — straight from `multisig pending`, with no out-of-band call data. Intermediate approvals (pass the opening `timepoint`) go up hash-only, which stays cheap even for huge calls like a runtime-upgrade blob. It never executes — once `threshold - 1` approvals exist, the last signatory must send `multisig_execute` with the full call. Approving twice from the same signer, or with a mismatched timepoint, threshold, or signatory set, fails. | Signer | Origin | Pallet | Wraps | | --------- | -------------------------------------- | -------- | ------------------------------------------------ | | `coldkey` | signed account (pallet role may apply) | Multisig | `Multisig.as_multi`, `Multisig.approve_as_multi` | ## Parameters [#parameters] | Parameter | Type | Required | Description | | ------------------- | ------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `threshold` | integer | yes | Number of approvals required to execute, counting the signer. Together with the full signatory set it identifies the multisig account, so it must match on every approval. | | `other_signatories` | array | yes | The other members of the multisig (every signatory except the signer), as a JSON list of addresses. The same set must be given on every approval; order does not matter (sorted automatically). | | `call` | object | yes | The inner call to dispatch from the multisig account, as a JSON object \{"op": \, ...args}. All approvals must describe the identical call — it is matched by hash. Its arguments must be fully explicit, since it runs as the multisig account, not the signer's wallet. | | `timepoint` | object | no | Block height and extrinsic index of the approval that opened the operation, as a JSON object \{"height": ..., "index": ...}. Omit on the first approval; required on every later one (read it with the multisig query). | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx multisig-approve \ --threshold \ --other-signatories \ --call '' --dry-run btcli tx multisig-approve \ --threshold \ --other-signatories \ --call '' -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.MultisigApprove(threshold=0, other_signatories=[...], call={...}) sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("multisig_approve", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `Multisig.as_multi` is implemented by Substrate's `pallet_multisig`, outside this repository's pallets. `Multisig.approve_as_multi` is implemented by Substrate's `pallet_multisig`, outside this repository's pallets. Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # multisig-cancel (/docs/tx/multisig-cancel) Abandons a pending operation before it collects enough approvals: the stored approvals are discarded and the deposit reserved at opening is returned. Only the signatory who opened the operation (and paid the deposit) may cancel it. The threshold, signatory set, call, and opening `timepoint` must all match the pending operation exactly. | Signer | Origin | Pallet | Wraps | | --------- | -------------------------------------- | -------- | -------------------------- | | `coldkey` | signed account (pallet role may apply) | Multisig | `Multisig.cancel_as_multi` | ## Parameters [#parameters] | Parameter | Type | Required | Description | | ------------------- | ------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `threshold` | integer | yes | Number of approvals required to execute, counting the signer. Together with the full signatory set it identifies the multisig account, so it must match on every approval. | | `other_signatories` | array | yes | The other members of the multisig (every signatory except the signer), as a JSON list of addresses. The same set must be given on every approval; order does not matter (sorted automatically). | | `call` | object | yes | The inner call to dispatch from the multisig account, as a JSON object \{"op": \, ...args}. All approvals must describe the identical call — it is matched by hash. Its arguments must be fully explicit, since it runs as the multisig account, not the signer's wallet. | | `timepoint` | object | yes | Block height and extrinsic index of the approval that opened the operation, as a JSON object \{"height": ..., "index": ...}. Required — it identifies which pending operation to cancel. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx multisig-cancel \ --threshold \ --other-signatories \ --call '' \ --timepoint '' --dry-run btcli tx multisig-cancel \ --threshold \ --other-signatories \ --call '' \ --timepoint '' -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.MultisigCancel(threshold=0, other_signatories=[...], call={...}, timepoint={...}) sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("multisig_cancel", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `Multisig.cancel_as_multi` is implemented by Substrate's `pallet_multisig`, outside this repository's pallets. Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # multisig-execute (/docs/tx/multisig-execute) Sends the full inner call along with an approval. If this is the first approval (omit `timepoint`), it opens the operation and reserves a deposit from the signer, returned when the operation completes or is cancelled. If it is the final approval — bringing the count to `threshold` — the inner call executes as the multisig account in the same extrinsic. Intermediate signers can use the cheaper `multisig_approve` (hash only), but whoever approves last must use this intent so the chain has the call to run. Every approval must repeat the same threshold, signatory set, and call; later approvals must also pass the opening `timepoint` or they will not match the pending operation. | Signer | Origin | Pallet | Wraps | | --------- | -------------------------------------- | -------- | ------------------- | | `coldkey` | signed account (pallet role may apply) | Multisig | `Multisig.as_multi` | ## Parameters [#parameters] | Parameter | Type | Required | Description | | ------------------- | ------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `threshold` | integer | yes | Number of approvals required to execute, counting the signer. Together with the full signatory set it identifies the multisig account, so it must match on every approval. | | `other_signatories` | array | yes | The other members of the multisig (every signatory except the signer), as a JSON list of addresses. The same set must be given on every approval; order does not matter (sorted automatically). | | `call` | object | yes | The inner call to dispatch from the multisig account, as a JSON object \{"op": \, ...args}. All approvals must describe the identical call — it is matched by hash. Its arguments must be fully explicit, since it runs as the multisig account, not the signer's wallet. | | `timepoint` | object | no | Block height and extrinsic index of the approval that opened the operation, as a JSON object \{"height": ..., "index": ...}. Omit on the first approval; required on every later one (read it with the multisig query). | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx multisig-execute \ --threshold \ --other-signatories \ --call '' --dry-run btcli tx multisig-execute \ --threshold \ --other-signatories \ --call '' -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.MultisigExecute(threshold=0, other_signatories=[...], call={...}) sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("multisig_execute", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `Multisig.as_multi` is implemented by Substrate's `pallet_multisig`, outside this repository's pallets. Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # multisig-threshold-1 (/docs/tx/multisig-threshold-1) For multisig accounts with threshold 1, where any single member may act alone: the call executes in this same extrinsic, with no approval round, no timepoint, and no deposit. The multisig account is derived from the signer plus `other_signatories`, so the full member set must still be supplied even though nobody else signs. For thresholds above 1 use `multisig_execute` / `multisig_approve` instead. | Signer | Origin | Pallet | Wraps | | --------- | -------------------------------------- | -------- | ------------------------------- | | `coldkey` | signed account (pallet role may apply) | Multisig | `Multisig.as_multi_threshold_1` | ## Parameters [#parameters] | Parameter | Type | Required | Description | | ------------------- | ------ | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `other_signatories` | array | yes | The other members of the multisig (every signatory except the signer), as a JSON list of addresses. The same set must be given on every approval; order does not matter (sorted automatically). | | `call` | object | yes | The inner call to dispatch from the multisig account, as a JSON object \{"op": \, ...args}. All approvals must describe the identical call — it is matched by hash. Its arguments must be fully explicit, since it runs as the multisig account, not the signer's wallet. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx multisig-threshold-1 \ --other-signatories \ --call '' --dry-run btcli tx multisig-threshold-1 \ --other-signatories \ --call '' -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.MultisigThreshold1(other_signatories=[...], call={...}) sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("multisig_threshold_1", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `Multisig.as_multi_threshold_1` is implemented by Substrate's `pallet_multisig`, outside this repository's pallets. Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # refund-crowdloan (/docs/tx/refund-crowdloan) Returns contributions (excluding the creator's) to their contributors. Only the creator may call it. Each call refunds at most 50 contributors, so large loans may need several `refund_crowdloan` calls before everyone is paid back. Once all contributors are refunded, the creator runs `dissolve_crowdloan`, which returns the creator's remaining contribution (including the deposit) and removes the loan. | Signer | Origin | Pallet | Wraps | | --------- | -------------------------------------- | --------- | ------------------------------------------------------------------ | | `coldkey` | signed account (pallet role may apply) | Crowdloan | [`Crowdloan.refund`](/code/pallets/crowdloan/src/lib.rs#L686-L750) | ## Parameters [#parameters] | Parameter | Type | Required | Description | | -------------- | ------- | -------- | ---------------------------------------------------------- | | `crowdloan_id` | integer | yes | Identifier of the crowdloan, assigned when it was created. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx refund-crowdloan \ --crowdloan-id --dry-run btcli tx refund-crowdloan \ --crowdloan-id -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.RefundCrowdloan(crowdloan_id=0) sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("refund_crowdloan", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `Crowdloan.refund` — [`pallets/crowdloan/src/lib.rs#L688`](/code/pallets/crowdloan/src/lib.rs#L686-L750): ```rust #[pallet::call_index(4)] #[pallet::weight(T::WeightInfo::refund(T::RefundContributorsLimit::get()))] pub fn refund( origin: OriginFor, #[pallet::compact] crowdloan_id: CrowdloanId, ) -> DispatchResultWithPostInfo { let who = ensure_signed(origin)?; let mut crowdloan = Self::ensure_crowdloan_exists(crowdloan_id)?; // Ensure the crowdloan is not finalized ensure!(!crowdloan.finalized, Error::::AlreadyFinalized); // Only the creator can refund the crowdloan ensure!(who == crowdloan.creator, Error::::InvalidOrigin); let mut refunded_contributors: Vec = vec![]; let mut refund_count = 0; // Assume everyone can be refunded let mut all_refunded = true; // We try to refund all contributors (excluding the creator) let contributions = Contributions::::iter_prefix(crowdloan_id) .filter(|(contributor, _)| *contributor != crowdloan.creator); for (contributor, amount) in contributions { if refund_count >= T::RefundContributorsLimit::get() { // Not everyone can be refunded all_refunded = false; break; } CurrencyOf::::transfer( &crowdloan.funds_account, &contributor, amount, Preservation::Expendable, )?; refunded_contributors.push(contributor); crowdloan.raised = crowdloan.raised.saturating_sub(amount); refund_count = refund_count.checked_add(1).ok_or(Error::::Overflow)?; } crowdloan.contributors_count = crowdloan .contributors_count .checked_sub(refund_count) .ok_or(Error::::Underflow)?; Crowdloans::::insert(crowdloan_id, &crowdloan); // Clear refunded contributors for contributor in refunded_contributors { Contributions::::remove(crowdloan_id, &contributor); } if all_refunded { Self::deposit_event(Event::::AllRefunded { crowdloan_id }); // The loop didn't run fully, we refund the unused weights. Ok(Some(T::WeightInfo::refund(refund_count)).into()) } else { Self::deposit_event(Event::::PartiallyRefunded { crowdloan_id }); // The loop ran fully, we don't refund anything. Ok(().into()) } } ``` Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # register-leased-network (/docs/tx/register-leased-network) Creates a subnet paid for by a crowdloan rather than a single coldkey: the crowdloan's funds cover the network registration cost (leftover cap is refunded to contributors pro-rata, with any rounding remainder going to the beneficiary), contributors earn `emissions_share` percent of the subnet owner's emission cut (not of total subnet emissions) as dividends, and the beneficiary operates the subnet through a scoped proxy. Must be dispatched in a crowdloan context — it fails as a standalone call. The cost is not cheaply boundable up front, so a configured spend cap blocks this until raised. With an `end_block` the beneficiary can later take full ownership via `terminate_lease`; without one the lease is perpetual and ownership never transfers. | Signer | Origin | Pallet | Wraps | | --------- | -------------------------------------- | --------------- | --------------------------------------------------------------------------------------------------------- | | `coldkey` | signed account (pallet role may apply) | SubtensorModule | [`SubtensorModule.register_leased_network`](/code/pallets/subtensor/src/macros/dispatches.rs#L1671-L1679) | ## Parameters [#parameters] | Parameter | Type | Required | Description | | ----------------- | ------- | -------- | ------------------------------------------------------------------------------------------------- | | `emissions_share` | integer | yes | Percent (0-100) of the subnet owner's emission cut paid to crowdloan contributors as dividends. | | `end_block` | integer | no | Block at which the lease ends and the beneficiary may take ownership; omit for a perpetual lease. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx register-leased-network \ --emissions-share --dry-run btcli tx register-leased-network \ --emissions-share -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.RegisterLeasedNetwork(emissions_share=0) sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("register_leased_network", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `SubtensorModule.register_leased_network` — [`pallets/subtensor/src/macros/dispatches.rs#L1673`](/code/pallets/subtensor/src/macros/dispatches.rs#L1671-L1679): ```rust #[pallet::call_index(110)] #[pallet::weight(::WeightInfo::register_leased_network(T::MaxContributors::get()))] pub fn register_leased_network( origin: OriginFor, emissions_share: Percent, end_block: Option>, ) -> DispatchResultWithPostInfo { Self::do_register_leased_network(origin, emissions_share, end_block) } ``` Delegates to [`do_register_leased_network`](/code/pallets/subtensor/src/subnets/leasing.rs#L70). Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # register-subnet (/docs/tx/register-subnet) Registers a brand-new subnet with the signing coldkey as its owner and the wallet's hotkey as the subnet-owner hotkey. The network registration cost — potentially thousands of TAO — is taken from the coldkey; it doubles after each new subnet registration and decays linearly back over the lock reduction interval, and is only known at execution time, so a configured spend cap blocks this call until raised. The full cost becomes the new subnet's initial TAO pool reserve — a sunk cost, not a refundable deposit. Network registrations are rate-limited per coldkey. If capacity is available, the subnet is created in the registration block. If the chain is at its subnet limit, registration first queues while the non-immune subnet with the lowest EMA price is dissolved across idle block time; SDK execution waits for the matching `NetworkAdded` event before returning by default. The result's `registration_mode` distinguishes the two paths and `netuid` is the subnet actually assigned. The new subnet starts inactive: call `start_call` once the chain's activation delay has passed to activate it; the subnet's share of TAO emission additionally stays off until root enables the subnet's emission-enabled flag. This is a major, expensive commitment — check the current cost before sending. | Signer | Origin | Pallet | Wraps | | --------- | -------------------------------------- | --------------- | -------------------------------------------------------------------------------------------------- | | `coldkey` | signed account (pallet role may apply) | SubtensorModule | [`SubtensorModule.register_network`](/code/pallets/subtensor/src/macros/dispatches.rs#L1002-L1006) | ## Parameters [#parameters] | Parameter | Type | Required | Description | | ------------- | ------ | -------- | ------------------------------------------------------------------------ | | `hotkey_ss58` | string | no | Subnet-owner hotkey for the new subnet; defaults to the wallet's hotkey. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx register-subnet --dry-run btcli tx register-subnet -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.RegisterSubnet() sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("register_subnet", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `SubtensorModule.register_network` — [`pallets/subtensor/src/macros/dispatches.rs#L1004`](/code/pallets/subtensor/src/macros/dispatches.rs#L1002-L1006): ```rust #[pallet::call_index(59)] #[pallet::weight(::WeightInfo::register_network())] pub fn register_network(origin: OriginFor, hotkey: T::AccountId) -> DispatchResult { Self::do_register_network(origin, &hotkey, 1, None) } ``` Delegates to [`do_register_network`](/code/pallets/subtensor/src/subnets/subnet.rs#L142). Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # remove-proxies (/docs/tx/remove-proxies) A cleanup/panic switch: all delegates lose access in one call and all proxy deposits are returned. Careful if this account spawned pure proxies — they are controlled *through* delegations, so removing everything can strand them permanently (there is no key to recover a pure proxy with). | Signer | Origin | Pallet | Wraps | | --------- | -------------------------------------- | ------ | ------------------------------------------------------------------ | | `coldkey` | signed account (pallet role may apply) | Proxy | [`Proxy.remove_proxies`](/code/pallets/proxy/src/lib.rs#L304-L310) | ## Parameters [#parameters] This operation takes no parameters. Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx remove-proxies --dry-run btcli tx remove-proxies -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.RemoveProxies() sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("remove_proxies", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `Proxy.remove_proxies` — [`pallets/proxy/src/lib.rs#L306`](/code/pallets/proxy/src/lib.rs#L304-L310): ```rust #[pallet::call_index(3)] #[pallet::weight(T::WeightInfo::remove_proxies(T::MaxProxies::get()))] pub fn remove_proxies(origin: OriginFor) -> DispatchResult { let who = ensure_signed(origin)?; Self::remove_all_proxy_delegates(&who); Ok(()) } ``` Delegates to [`remove_all_proxy_delegates`](/code/pallets/proxy/src/lib.rs#L1143). Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # remove-proxy (/docs/tx/remove-proxy) The (delegate, proxy\_type, delay) triple must match the original `add_proxy` exactly — a mismatch fails with NotFound rather than removing a different delegation. The proxy deposit reserved at add time is returned to the signer. Check current delegations with `btcli query proxies`. | Signer | Origin | Pallet | Wraps | | --------- | -------------------------------------- | ------ | ---------------------------------------------------------------- | | `coldkey` | signed account (pallet role may apply) | Proxy | [`Proxy.remove_proxy`](/code/pallets/proxy/src/lib.rs#L285-L296) | ## Parameters [#parameters] | Parameter | Type | Required | Description | | --------------- | ------- | -------- | ----------------------------------------------------------- | | `delegate_ss58` | string | yes | Delegate whose authorization to revoke. | | `proxy_type` | string | no | Type the delegation was granted with (must match exactly). | | `delay` | integer | no | Delay the delegation was granted with (must match exactly). | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx remove-proxy \ --delegate --dry-run btcli tx remove-proxy \ --delegate -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.RemoveProxy(delegate_ss58="5F...") sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("remove_proxy", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `Proxy.remove_proxy` — [`pallets/proxy/src/lib.rs#L287`](/code/pallets/proxy/src/lib.rs#L285-L296): ```rust #[pallet::call_index(2)] #[pallet::weight(T::WeightInfo::remove_proxy(T::MaxProxies::get()))] pub fn remove_proxy( origin: OriginFor, delegate: AccountIdLookupOf, proxy_type: T::ProxyType, delay: BlockNumberFor, ) -> DispatchResult { let who = ensure_signed(origin)?; let delegate = T::Lookup::lookup(delegate)?; Self::remove_proxy_delegate(&who, delegate, proxy_type, delay) } ``` Delegates to [`remove_proxy_delegate`](/code/pallets/proxy/src/lib.rs#L986). Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # remove-stake-limit (/docs/tx/remove-stake-limit) Same as `remove_stake` except the alpha-to-TAO swap only executes while the pool price stays within the limit. With `allow_partial` it unstakes what it can within the limit and leaves the rest staked; without it the whole call fails once the limit would be breached. Pass `all` to target the entire position (the build fails if nothing is staked there). Like `remove_stake`, a partial unstake must leave a remainder worth at least 0.002 TAO at the simulated pool price (`AmountTooLow`). Prefer this over plain `remove_stake` when exiting large positions. | Signer | Origin | Pallet | Wraps | | --------- | -------------------------------------- | --------------- | ---------------------------------------------------------------------------------------------------- | | `coldkey` | signed account (pallet role may apply) | SubtensorModule | [`SubtensorModule.remove_stake_limit`](/code/pallets/subtensor/src/macros/dispatches.rs#L1444-L1462) | ## Parameters [#parameters] | Parameter | Type | Required | Description | | ----------------- | ----------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | | `hotkey_ss58` | string | yes | Hotkey the stake is held on (the validator backing the position). | | `netuid` | integer | yes | Subnet the stake lives on (netuid 0 is the root network). | | `amount_alpha` | number \| `"all"` | yes | How much to unstake from this position, or `all`. | | `limit_price_rao` | integer | yes | Worst pool price you will accept for the swap. The call fails (or fills partially when allow-partial is set) instead of executing beyond this price. | | `allow_partial` | boolean | no | Execute whatever portion fits within the limit price and drop the remainder, instead of failing the whole call when the limit would be breached. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx remove-stake-limit \ --hotkey \ --netuid \ --amount-alpha \ --limit-price-rao --dry-run btcli tx remove-stake-limit \ --hotkey \ --netuid \ --amount-alpha \ --limit-price-rao -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.RemoveStakeLimit(hotkey_ss58="5F...", netuid=1, amount_alpha=1.0, limit_price_rao=0) sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("remove_stake_limit", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `SubtensorModule.remove_stake_limit` — [`pallets/subtensor/src/macros/dispatches.rs#L1446`](/code/pallets/subtensor/src/macros/dispatches.rs#L1444-L1462): ```rust #[pallet::call_index(89)] #[pallet::weight(::WeightInfo::remove_stake_limit())] pub fn remove_stake_limit( origin: OriginFor, hotkey: T::AccountId, netuid: NetUid, amount_unstaked: AlphaBalance, limit_price: TaoBalance, allow_partial: bool, ) -> DispatchResult { Self::do_remove_stake_limit( origin, hotkey, netuid, amount_unstaked, limit_price, allow_partial, ) } ``` Delegates to [`do_remove_stake_limit`](/code/pallets/subtensor/src/staking/remove_stake.rs#L298). Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # remove-stake (/docs/tx/remove-stake) Swaps the alpha position back to TAO at the current pool price and credits it to the signing coldkey's free balance. Pass `all` to exit the entire position on that hotkey and subnet (the build fails if nothing is staked there). Like staking, the swap moves the pool, so large amounts incur slippage. By default the call is slippage-protected: it fails (`SlippageTooHigh`) instead of filling once the price falls more than `rate_tolerance` (5%) below the price at submission — raise the tolerance or set `slippage_protection` to False to execute at any price, or use `remove_stake_limit` to set an explicit limit price. The hotkey and netuid must match where the stake is actually held, and the subnet must have subtoken trading enabled. The requested amount is capped to the stake currently available. A partial unstake must leave a remainder worth at least 0.002 TAO at the simulated pool price — exit the full position instead of leaving dust (`AmountTooLow`). | Signer | Origin | Pallet | Wraps | | --------- | -------------------------------------- | --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `coldkey` | signed account (pallet role may apply) | SubtensorModule | [`SubtensorModule.remove_stake`](/code/pallets/subtensor/src/macros/dispatches.rs#L593-L602), [`SubtensorModule.remove_stake_limit`](/code/pallets/subtensor/src/macros/dispatches.rs#L1444-L1462) | ## Parameters [#parameters] | Parameter | Type | Required | Description | | --------------------- | ----------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `hotkey_ss58` | string | yes | Hotkey the stake is held on (the validator backing the position). | | `netuid` | integer | yes | Subnet the stake lives on (netuid 0 is the root network). | | `amount_alpha` | number \| `"all"` | yes | How much to unstake from this position, or `all`. | | `slippage_protection` | boolean | no | Bound the price the swap may execute at (on by default): the call fails (`SlippageTooHigh`) instead of filling once the pool price moves more than `rate_tolerance` from the price at submission. Disable to execute at any price. | | `rate_tolerance` | number | no | Maximum price move slippage protection accepts, as a fraction (0.05 = 5%). Ignored when slippage protection is disabled. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx remove-stake \ --hotkey \ --netuid \ --amount-alpha --dry-run btcli tx remove-stake \ --hotkey \ --netuid \ --amount-alpha -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.RemoveStake(hotkey_ss58="5F...", netuid=1, amount_alpha=1.0) sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("remove_stake", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `SubtensorModule.remove_stake` — [`pallets/subtensor/src/macros/dispatches.rs#L595`](/code/pallets/subtensor/src/macros/dispatches.rs#L593-L602): ```rust #[pallet::call_index(3)] #[pallet::weight(::WeightInfo::remove_stake())] pub fn remove_stake( origin: OriginFor, hotkey: T::AccountId, netuid: NetUid, amount_unstaked: AlphaBalance, ) -> DispatchResult { Self::do_remove_stake(origin, hotkey, netuid, amount_unstaked) } ``` Delegates to [`do_remove_stake`](/code/pallets/subtensor/src/staking/remove_stake.rs#L35). `SubtensorModule.remove_stake_limit` — [`pallets/subtensor/src/macros/dispatches.rs#L1446`](/code/pallets/subtensor/src/macros/dispatches.rs#L1444-L1462): ```rust #[pallet::call_index(89)] #[pallet::weight(::WeightInfo::remove_stake_limit())] pub fn remove_stake_limit( origin: OriginFor, hotkey: T::AccountId, netuid: NetUid, amount_unstaked: AlphaBalance, limit_price: TaoBalance, allow_partial: bool, ) -> DispatchResult { Self::do_remove_stake_limit( origin, hotkey, netuid, amount_unstaked, limit_price, allow_partial, ) } ``` Delegates to [`do_remove_stake_limit`](/code/pallets/subtensor/src/staking/remove_stake.rs#L298). Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # reset-axon (/docs/tx/reset-axon) Republishes the axon record as a placeholder (ip 0, port 1, protocol 4) so validators stop routing traffic to the old address; the storage entry is not removed. Signed by the hotkey. Use this when taking a miner offline or before moving it, then publish the new address with `serve_axon` when it is back up. | Signer | Origin | Pallet | Wraps | | -------- | -------------------------------------- | --------------- | ------------------------------------------------------------------------------------------ | | `hotkey` | signed account (pallet role may apply) | SubtensorModule | [`SubtensorModule.serve_axon`](/code/pallets/subtensor/src/macros/dispatches.rs#L640-L665) | ## Parameters [#parameters] | Parameter | Type | Required | Description | | --------- | ------- | -------- | ---------------------------------------------- | | `netuid` | integer | yes | Subnet whose published axon endpoint to clear. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx reset-axon \ --netuid --dry-run btcli tx reset-axon \ --netuid -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.ResetAxon(netuid=1) sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("reset_axon", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `SubtensorModule.serve_axon` — [`pallets/subtensor/src/macros/dispatches.rs#L642`](/code/pallets/subtensor/src/macros/dispatches.rs#L640-L665): ```rust #[pallet::call_index(4)] #[pallet::weight((::WeightInfo::serve_axon(), DispatchClass::Normal, Pays::No))] pub fn serve_axon( origin: OriginFor, netuid: NetUid, version: u32, ip: u128, port: u16, ip_type: u8, protocol: u8, placeholder1: u8, placeholder2: u8, ) -> DispatchResult { Self::do_serve_axon( origin, netuid, version, ip, port, ip_type, protocol, placeholder1, placeholder2, None, ) } ``` Delegates to [`do_serve_axon`](/code/pallets/subtensor/src/subnets/serving.rs#L42). Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # reveal-weights (/docs/tx/reveal-weights) Completes the old two-step commit-reveal flow: the uids, weights, salt, and version key must hash to exactly what was committed earlier. The reveal is valid on exactly one epoch — the commit's epoch plus the subnet's reveal period; revealing earlier fails, and once that epoch has passed the commit is expired and dropped. The chain hashes the quantized u16 values, so the inputs here must be the same values passed at commit time (`build()` re-normalizes floats: identical proportions produce identical u16s). Signed by the hotkey that made the commit. Only needed for legacy salt-based commits — the timelocked path used by `set_weights` and `commit_weights` is auto-revealed by the chain and never needs this call. | Signer | Origin | Pallet | Wraps | | -------- | -------------------------------------- | --------------- | ---------------------------------------------------------------------------------------------- | | `hotkey` | signed account (pallet role may apply) | SubtensorModule | [`SubtensorModule.reveal_weights`](/code/pallets/subtensor/src/macros/dispatches.rs#L275-L286) | ## Parameters [#parameters] | Parameter | Type | Required | Description | | ------------- | ---------------- | -------- | --------------------------------------------------------------- | | `netuid` | integer | yes | Subnet whose miners the weights score. | | `uids` | array of integer | yes | Miner UIDs exactly as committed, as a list parallel to weights. | | `weights` | array of number | yes | Weights exactly as committed, as a list parallel to uids. | | `salt` | array of integer | yes | Salt used when the commit was made; must match to reveal. | | `version_key` | integer | no | Version key used when the commit was made. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx reveal-weights \ --netuid \ --uids \ --weights \ --salt --dry-run btcli tx reveal-weights \ --netuid \ --uids \ --weights \ --salt -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.RevealWeights(netuid=1, uids=[...], weights=[...], salt=[...]) sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("reveal_weights", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `SubtensorModule.reveal_weights` — [`pallets/subtensor/src/macros/dispatches.rs#L277`](/code/pallets/subtensor/src/macros/dispatches.rs#L275-L286): ```rust #[pallet::call_index(97)] #[pallet::weight((::WeightInfo::reveal_weights(), DispatchClass::Normal, Pays::No))] pub fn reveal_weights( origin: OriginFor, netuid: NetUid, uids: Vec, values: Vec, salt: Vec, version_key: u64, ) -> DispatchResult { Self::do_reveal_weights(origin, netuid, uids, values, salt, version_key) } ``` Delegates to [`do_reveal_weights`](/code/pallets/subtensor/src/subnets/weights.rs#L393). Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # root-register (/docs/tx/root-register) Joins the hotkey to the root network, the TAO staking pool (netuid 0 has no miners and no alpha; validators register here to receive root stake). Placement is stake-based rather than burn-based: root slots are limited, so joining a full root network evicts the member with the least stake, and a hotkey without enough stake behind it will not hold a seat. Root registrations are also capped per block (`max_registrations_per_block`) and per interval (three times `target_registrations_per_interval`); hitting either cap fails until the window passes. Use `burned_register` for ordinary subnets. | Signer | Origin | Pallet | Wraps | | --------- | -------------------------------------- | --------------- | --------------------------------------------------------------------------------------------- | | `coldkey` | signed account (pallet role may apply) | SubtensorModule | [`SubtensorModule.root_register`](/code/pallets/subtensor/src/macros/dispatches.rs#L809-L813) | ## Parameters [#parameters] | Parameter | Type | Required | Description | | ------------- | ------ | -------- | ------------------------------------------------------------------------ | | `hotkey_ss58` | string | no | Hotkey to register on the root network; defaults to the wallet's hotkey. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx root-register --dry-run btcli tx root-register -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.RootRegister() sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("root_register", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `SubtensorModule.root_register` — [`pallets/subtensor/src/macros/dispatches.rs#L811`](/code/pallets/subtensor/src/macros/dispatches.rs#L809-L813): ```rust #[pallet::call_index(62)] #[pallet::weight(::WeightInfo::root_register())] pub fn root_register(origin: OriginFor, hotkey: T::AccountId) -> DispatchResult { Self::do_root_register(origin, hotkey) } ``` Delegates to [`do_root_register`](/code/pallets/subtensor/src/coinbase/root.rs#L77). Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # serve-axon-tls (/docs/tx/serve-axon-tls) Same as `serve_axon` plus a compact neuron certificate stored on chain: one algorithm byte followed by up to 64 bytes of public key — not an X.509 TLS certificate blob (anything else fails to decode). The chain only publishes the key for peers to fetch; there is no chain-side TLS handshake, and running any TLS endpoint is up to the caller. Signed by the hotkey, which must be registered on the subnet. Use plain `serve_axon` when peers do not need a published key. | Signer | Origin | Pallet | Wraps | | -------- | -------------------------------------- | --------------- | ---------------------------------------------------------------------------------------------- | | `hotkey` | signed account (pallet role may apply) | SubtensorModule | [`SubtensorModule.serve_axon_tls`](/code/pallets/subtensor/src/macros/dispatches.rs#L706-L732) | ## Parameters [#parameters] | Parameter | Type | Required | Description | | ------------- | ------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `netuid` | integer | yes | Subnet on which to publish the endpoint. | | `ip` | string | yes | Public IPv4 or IPv6 address of the endpoint, in standard dotted or colon notation. | | `port` | integer | yes | TCP port the endpoint listens on. | | `certificate` | string | yes | Neuron certificate as 0x-prefixed hex: 1 algorithm byte followed by up to 64 bytes of public key. Not an X.509 certificate; other formats fail to decode on chain. | | `protocol` | integer | no | Application protocol tag stored alongside the endpoint; its meaning is subnet-defined. | | `version` | integer | no | Version number of the serving neuron's software, stored with the endpoint. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx serve-axon-tls \ --netuid \ --ip \ --port \ --certificate --dry-run btcli tx serve-axon-tls \ --netuid \ --ip \ --port \ --certificate -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.ServeAxonTls(netuid=1, ip="...", port=0, certificate="...") sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("serve_axon_tls", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `SubtensorModule.serve_axon_tls` — [`pallets/subtensor/src/macros/dispatches.rs#L708`](/code/pallets/subtensor/src/macros/dispatches.rs#L706-L732): ```rust #[pallet::call_index(40)] #[pallet::weight((::WeightInfo::serve_axon_tls(), DispatchClass::Normal, Pays::No))] pub fn serve_axon_tls( origin: OriginFor, netuid: NetUid, version: u32, ip: u128, port: u16, ip_type: u8, protocol: u8, placeholder1: u8, placeholder2: u8, certificate: Vec, ) -> DispatchResult { Self::do_serve_axon( origin, netuid, version, ip, port, ip_type, protocol, placeholder1, placeholder2, Some(certificate), ) } ``` Delegates to [`do_serve_axon`](/code/pallets/subtensor/src/subnets/serving.rs#L42). Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # serve-axon (/docs/tx/serve-axon) Writes the connection info to chain storage so validators on the subnet know where to reach this miner or validator; it does not start any server — running the actual axon service is up to the caller. Signed by the hotkey, which must be registered on the subnet, and subject to the chain's serving rate limit (re-publishing too soon fails). Use `serve_axon_tls` if peers should verify a TLS certificate, and `reset_axon` to stop advertising the endpoint. | Signer | Origin | Pallet | Wraps | | -------- | -------------------------------------- | --------------- | ------------------------------------------------------------------------------------------ | | `hotkey` | signed account (pallet role may apply) | SubtensorModule | [`SubtensorModule.serve_axon`](/code/pallets/subtensor/src/macros/dispatches.rs#L640-L665) | ## Parameters [#parameters] | Parameter | Type | Required | Description | | ---------- | ------- | -------- | -------------------------------------------------------------------------------------- | | `netuid` | integer | yes | Subnet on which to publish the endpoint. | | `ip` | string | yes | Public IPv4 or IPv6 address of the endpoint, in standard dotted or colon notation. | | `port` | integer | yes | TCP port the endpoint listens on. | | `protocol` | integer | no | Application protocol tag stored alongside the endpoint; its meaning is subnet-defined. | | `version` | integer | no | Version number of the serving neuron's software, stored with the endpoint. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx serve-axon \ --netuid \ --ip \ --port --dry-run btcli tx serve-axon \ --netuid \ --ip \ --port -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.ServeAxon(netuid=1, ip="...", port=0) sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("serve_axon", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `SubtensorModule.serve_axon` — [`pallets/subtensor/src/macros/dispatches.rs#L642`](/code/pallets/subtensor/src/macros/dispatches.rs#L640-L665): ```rust #[pallet::call_index(4)] #[pallet::weight((::WeightInfo::serve_axon(), DispatchClass::Normal, Pays::No))] pub fn serve_axon( origin: OriginFor, netuid: NetUid, version: u32, ip: u128, port: u16, ip_type: u8, protocol: u8, placeholder1: u8, placeholder2: u8, ) -> DispatchResult { Self::do_serve_axon( origin, netuid, version, ip, port, ip_type, protocol, placeholder1, placeholder2, None, ) } ``` Delegates to [`do_serve_axon`](/code/pallets/subtensor/src/subnets/serving.rs#L42). Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # serve-prometheus (/docs/tx/serve-prometheus) Advertises where this neuron's prometheus metrics can be scraped, separate from the axon endpoint used for inter-neuron traffic. Signed by the hotkey, which must be registered on the subnet, and subject to the same serving rate limit as `serve_axon`. Like the axon calls, this only writes chain data — running the metrics server is up to the caller. | Signer | Origin | Pallet | Wraps | | -------- | -------------------------------------- | --------------- | ------------------------------------------------------------------------------------------------ | | `hotkey` | signed account (pallet role may apply) | SubtensorModule | [`SubtensorModule.serve_prometheus`](/code/pallets/subtensor/src/macros/dispatches.rs#L748-L759) | ## Parameters [#parameters] | Parameter | Type | Required | Description | | --------- | ------- | -------- | ---------------------------------------------------------------------------------- | | `netuid` | integer | yes | Subnet on which to publish the endpoint. | | `ip` | string | yes | Public IPv4 or IPv6 address of the endpoint, in standard dotted or colon notation. | | `port` | integer | yes | TCP port the endpoint listens on. | | `version` | integer | no | Version number of the serving neuron's software, stored with the endpoint. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx serve-prometheus \ --netuid \ --ip \ --port --dry-run btcli tx serve-prometheus \ --netuid \ --ip \ --port -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.ServePrometheus(netuid=1, ip="...", port=0) sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("serve_prometheus", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `SubtensorModule.serve_prometheus` — [`pallets/subtensor/src/macros/dispatches.rs#L750`](/code/pallets/subtensor/src/macros/dispatches.rs#L748-L759): ```rust #[pallet::call_index(5)] #[pallet::weight((::WeightInfo::serve_prometheus(), DispatchClass::Normal, Pays::No))] pub fn serve_prometheus( origin: OriginFor, netuid: NetUid, version: u32, ip: u128, port: u16, ip_type: u8, ) -> DispatchResult { Self::do_serve_prometheus(origin, netuid, version, ip, port, ip_type) } ``` Delegates to [`do_serve_prometheus`](/code/pallets/subtensor/src/subnets/serving.rs#L135). Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # set-auto-stake (/docs/tx/set-auto-stake) Sets the coldkey's autostake destination for the subnet: all future rewards earned there are automatically staked to the chosen hotkey (defaulting to the wallet's own hotkey) instead of accumulating unstaked. A configuration change only — it moves no funds by itself and applies just to that subnet. The hotkey must be registered on the subnet (`HotKeyNotRegisteredInSubNet`), and setting the hotkey that is already the destination fails (`SameAutoStakeHotkeyAlreadySet`). Call it again with a different hotkey to redirect; read the current setting back with the `auto_stake` read. | Signer | Origin | Pallet | Wraps | | --------- | -------------------------------------- | --------------- | --------------------------------------------------------------------------------------------------------------- | | `coldkey` | signed account (pallet role may apply) | SubtensorModule | [`SubtensorModule.set_coldkey_auto_stake_hotkey`](/code/pallets/subtensor/src/macros/dispatches.rs#L1784-L1826) | ## Parameters [#parameters] | Parameter | Type | Required | Description | | ------------- | ------- | -------- | ---------------------------------------------------------------------- | | `netuid` | integer | yes | Subnet whose future rewards are auto-staked. | | `hotkey_ss58` | string | no | Hotkey the rewards are staked to. Defaults to the wallet's own hotkey. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx set-auto-stake \ --netuid --dry-run btcli tx set-auto-stake \ --netuid -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.SetAutoStake(netuid=1) sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("set_auto_stake", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `SubtensorModule.set_coldkey_auto_stake_hotkey` — [`pallets/subtensor/src/macros/dispatches.rs#L1786`](/code/pallets/subtensor/src/macros/dispatches.rs#L1784-L1826): ```rust #[pallet::call_index(114)] #[pallet::weight(::WeightInfo::set_coldkey_auto_stake_hotkey())] pub fn set_coldkey_auto_stake_hotkey( origin: OriginFor, netuid: NetUid, hotkey: T::AccountId, ) -> DispatchResult { let coldkey = ensure_signed(origin)?; ensure!(Self::if_subnet_exist(netuid), Error::::SubnetNotExists); ensure!( Uids::::contains_key(netuid, &hotkey), Error::::HotKeyNotRegisteredInSubNet ); let current_hotkey = AutoStakeDestination::::get(coldkey.clone(), netuid); if let Some(current_hotkey) = current_hotkey { ensure!( current_hotkey != hotkey, Error::::SameAutoStakeHotkeyAlreadySet ); // Remove the coldkey from the old hotkey (if present) AutoStakeDestinationColdkeys::::mutate(current_hotkey.clone(), netuid, |v| { v.retain(|c| c != &coldkey); }); } // Add the coldkey to the new hotkey (if not already present) AutoStakeDestination::::insert(coldkey.clone(), netuid, hotkey.clone()); AutoStakeDestinationColdkeys::::mutate(hotkey.clone(), netuid, |v| { if !v.contains(&coldkey) { v.push(coldkey.clone()); } }); Self::deposit_event(Event::AutoStakeDestinationSet { coldkey, netuid, hotkey, }); Ok(()) } ``` Delegates to [`if_subnet_exist`](/code/pallets/subtensor/src/subnets/subnet.rs#L39). Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # set-childkey-take (/docs/tx/set-childkey-take) The childkey take is the fraction of emissions a child hotkey keeps from the stake weight its parents delegate to it, before passing the remainder through. It is set per subnet, unlike the global delegate take. Signed by the coldkey that owns the child hotkey. The chain enforces both its minimum and maximum childkey take bounds, and rate-limits only increases — decreases apply immediately, so lowering the take is always available. | Signer | Origin | Pallet | Wraps | | --------- | -------------------------------------- | --------------- | ------------------------------------------------------------------------------------------------- | | `coldkey` | signed account (pallet role may apply) | SubtensorModule | [`SubtensorModule.set_childkey_take`](/code/pallets/subtensor/src/macros/dispatches.rs#L925-L937) | ## Parameters [#parameters] | Parameter | Type | Required | Description | | ------------- | --------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------- | | `netuid` | integer | yes | Subnet the childkey take applies to. | | `take` | integer \| number \| string | yes | New take: a 0..1 fraction (e.g. 0.18 for 18 percent) or the raw u16 proportion (0..65535). The chain enforces its configured take bounds. | | `hotkey_ss58` | string | no | Hotkey the operation applies to. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx set-childkey-take \ --netuid \ --take --dry-run btcli tx set-childkey-take \ --netuid \ --take -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.SetChildkeyTake(netuid=1, take="...") sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("set_childkey_take", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `SubtensorModule.set_childkey_take` — [`pallets/subtensor/src/macros/dispatches.rs#L927`](/code/pallets/subtensor/src/macros/dispatches.rs#L925-L937): ```rust #[pallet::call_index(75)] #[pallet::weight(::WeightInfo::set_childkey_take())] pub fn set_childkey_take( origin: OriginFor, hotkey: T::AccountId, netuid: NetUid, take: PerU16, ) -> DispatchResult { let coldkey = ensure_signed(origin)?; // Call the utility function to set the childkey take Self::do_set_childkey_take(coldkey, hotkey, netuid, take) } ``` Delegates to [`do_set_childkey_take`](/code/pallets/subtensor/src/staking/set_children.rs#L707). Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # set-children (/docs/tx/set-children) Childkeys let a parent hotkey delegate a fraction of its stake weight to other hotkeys on one subnet — commonly used to split validation duties or point stake at a separate validating key without moving the stake itself. Each entry in `children` is a pair of proportion and hotkey ss58. A proportion with a decimal point is the human 0..1 fraction of the parent's stake weight (e.g. 0.5); a plain integer is the raw u64 share of u64::MAX. The proportions must not sum past the whole. The call replaces the full child set, so pass an empty list to revoke all children. Signed by the coldkey that owns the parent hotkey, and subject to the chain's childkey rate limit. Chain guards: not allowed on the root subnet; at most 5 children per hotkey per subnet; duplicate children are rejected; a hotkey that is a parent of this hotkey cannot be added as its child (relations stay bipartite); and the parent hotkey needs a minimum own stake (StakeThreshold) unless it is the subnet-owner hotkey. Changes take effect after a chain-defined cooldown, except on subnets whose subtoken is not yet enabled, where they apply immediately. | Signer | Origin | Pallet | Wraps | | --------- | -------------------------------------- | --------------- | ---------------------------------------------------------------------------------------------- | | `coldkey` | signed account (pallet role may apply) | SubtensorModule | [`SubtensorModule.set_children`](/code/pallets/subtensor/src/macros/dispatches.rs#L1077-L1087) | ## Parameters [#parameters] | Parameter | Type | Required | Description | | ------------- | ------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `netuid` | integer | yes | Subnet on which the child relationships apply. | | `children` | array | yes | JSON list of proportion-and-hotkey pairs; each proportion is a 0..1 fraction (e.g. 0.5) or a raw u64 share of u64::MAX of the parent's stake weight delegated to that child. An empty list revokes all children. | | `hotkey_ss58` | string | no | Hotkey the operation applies to. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx set-children \ --netuid \ --children --dry-run btcli tx set-children \ --netuid \ --children -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.SetChildren(netuid=1, children=[...]) sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("set_children", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `SubtensorModule.set_children` — [`pallets/subtensor/src/macros/dispatches.rs#L1079`](/code/pallets/subtensor/src/macros/dispatches.rs#L1077-L1087): ```rust #[pallet::call_index(67)] #[pallet::weight((::WeightInfo::set_children(children.len() as u32), DispatchClass::Normal, Pays::Yes))] pub fn set_children( origin: OriginFor, hotkey: T::AccountId, netuid: NetUid, children: Vec<(u64, T::AccountId)>, ) -> DispatchResultWithPostInfo { Self::do_schedule_children(origin, hotkey, netuid, children)?; Ok(().into()) } ``` Delegates to [`do_schedule_children`](/code/pallets/subtensor/src/staking/set_children.rs#L475). Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # set-crowdloan-max-contribution (/docs/tx/set-crowdloan-max-contribution) Caps how much any single contributor can put in, useful to keep a raise broadly distributed. Omitting the amount clears the limit. Only the creator may change it, and only while the loan has not been finalized. | Signer | Origin | Pallet | Wraps | | --------- | -------------------------------------- | --------- | -------------------------------------------------------------------------------- | | `coldkey` | signed account (pallet role may apply) | Crowdloan | [`Crowdloan.set_max_contribution`](/code/pallets/crowdloan/src/lib.rs#L933-L967) | ## Parameters [#parameters] | Parameter | Type | Required | Description | | -------------------------- | ----------------- | -------- | ------------------------------------------------------------------------- | | `crowdloan_id` | integer | yes | Identifier of the crowdloan, assigned when it was created. | | `new_max_contribution_tao` | number \| `"all"` | no | Largest total any single contributor may put in. Omit to clear the limit. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx set-crowdloan-max-contribution \ --crowdloan-id --dry-run btcli tx set-crowdloan-max-contribution \ --crowdloan-id -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.SetCrowdloanMaxContribution(crowdloan_id=0) sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("set_crowdloan_max_contribution", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `Crowdloan.set_max_contribution` — [`pallets/crowdloan/src/lib.rs#L935`](/code/pallets/crowdloan/src/lib.rs#L933-L967): ```rust #[pallet::call_index(9)] #[pallet::weight(T::WeightInfo::set_max_contribution())] pub fn set_max_contribution( origin: OriginFor, #[pallet::compact] crowdloan_id: CrowdloanId, new_max_contribution: Option>, ) -> DispatchResult { let who = ensure_signed(origin)?; let crowdloan = Self::ensure_crowdloan_exists(crowdloan_id)?; ensure!(!crowdloan.finalized, Error::::AlreadyFinalized); // Only the creator can update the max contribution. ensure!(who == crowdloan.creator, Error::::InvalidOrigin); if let Some(max_contribution) = new_max_contribution { let creator_contribution = Contributions::::get(crowdloan_id, &crowdloan.creator) .unwrap_or_else(Zero::zero); ensure!( max_contribution >= crowdloan.min_contribution && max_contribution >= creator_contribution, Error::::MaximumContributionTooLow ); MaxContributions::::insert(crowdloan_id, max_contribution); } else { MaxContributions::::remove(crowdloan_id); } Self::deposit_event(Event::::MaxContributionUpdated { crowdloan_id, new_max_contribution, }); Ok(()) } ``` Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # set-hyperparameter (/docs/tx/set-hyperparameter) Dispatches the matching AdminUtils `sudo_set_*` call for the named 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 fail. Read current values back with the `subnet_hyperparameters` read. | Signer | Origin | Pallet | Wraps | | --------- | ------------ | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `coldkey` | subnet owner | AdminUtils | [`AdminUtils.sudo_set_tempo`](/code/pallets/admin-utils/src/lib.rs#L1021-L1036), [`AdminUtils.sudo_set_immunity_period`](/code/pallets/admin-utils/src/lib.rs#L474-L502), [`AdminUtils.sudo_set_min_allowed_weights`](/code/pallets/admin-utils/src/lib.rs#L507-L535), [`AdminUtils.sudo_set_weights_version_key`](/code/pallets/admin-utils/src/lib.rs#L359-L389), [`AdminUtils.sudo_set_activity_cutoff_factor`](/code/pallets/admin-utils/src/lib.rs#L679-L695), [`AdminUtils.sudo_set_min_burn`](/code/pallets/admin-utils/src/lib.rs#L761-L795), [`AdminUtils.sudo_set_max_burn`](/code/pallets/admin-utils/src/lib.rs#L800-L834), [`AdminUtils.sudo_set_bonds_moving_average`](/code/pallets/admin-utils/src/lib.rs#L892-L926), [`AdminUtils.sudo_set_bonds_penalty`](/code/pallets/admin-utils/src/lib.rs#L931-L957), [`AdminUtils.sudo_set_serving_rate_limit`](/code/pallets/admin-utils/src/lib.rs#L276-L297), [`AdminUtils.sudo_set_commit_reveal_weights_interval`](/code/pallets/admin-utils/src/lib.rs#L1374-L1403), [`AdminUtils.sudo_set_max_allowed_uids`](/code/pallets/admin-utils/src/lib.rs#L540-L585), [`AdminUtils.sudo_set_burn_increase_mult`](/code/pallets/admin-utils/src/lib.rs#L2149-L2189), [`AdminUtils.sudo_set_burn_half_life`](/code/pallets/admin-utils/src/lib.rs#L2107-L2145), [`AdminUtils.sudo_set_collateral_lock_share`](/code/pallets/admin-utils/src/lib.rs#L2273-L2308), [`AdminUtils.sudo_set_collateral_drain_ratio`](/code/pallets/admin-utils/src/lib.rs#L2316-L2357), [`AdminUtils.sudo_set_adjustment_alpha`](/code/pallets/admin-utils/src/lib.rs#L443-L469), [`AdminUtils.sudo_set_rho`](/code/pallets/admin-utils/src/lib.rs#L606-L628), [`AdminUtils.sudo_set_max_difficulty`](/code/pallets/admin-utils/src/lib.rs#L326-L354), [`AdminUtils.sudo_set_alpha_sigmoid_steepness`](/code/pallets/admin-utils/src/lib.rs#L1625-L1659), [`AdminUtils.sudo_set_min_childkey_take_per_subnet`](/code/pallets/admin-utils/src/lib.rs#L1198-L1232), [`AdminUtils.sudo_set_owner_immune_neuron_limit`](/code/pallets/admin-utils/src/lib.rs#L1811-L1831), [`AdminUtils.sudo_set_alpha_values`](/code/pallets/admin-utils/src/lib.rs#L1299-L1324), [`AdminUtils.sudo_set_commit_reveal_weights_enabled`](/code/pallets/admin-utils/src/lib.rs#L1237-L1264), [`AdminUtils.sudo_set_liquid_alpha_enabled`](/code/pallets/admin-utils/src/lib.rs#L1275-L1296), [`AdminUtils.sudo_set_network_pow_registration_allowed`](/code/pallets/admin-utils/src/lib.rs#L721-L729), [`AdminUtils.sudo_set_yuma3_enabled`](/code/pallets/admin-utils/src/lib.rs#L1670-L1693), [`AdminUtils.sudo_set_bonds_reset_enabled`](/code/pallets/admin-utils/src/lib.rs#L1704-L1727), [`AdminUtils.sudo_set_toggle_transfer`](/code/pallets/admin-utils/src/lib.rs#L1463-L1485), [`AdminUtils.sudo_set_owner_cut_enabled`](/code/pallets/admin-utils/src/lib.rs#L2193-L2213), [`AdminUtils.sudo_set_owner_cut_auto_lock_enabled`](/code/pallets/admin-utils/src/lib.rs#L2217-L2237) | ## Parameters [#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\_factor, adjustment\_alpha, alpha\_high, alpha\_low, alpha\_sigmoid\_steepness, bonds\_moving\_avg, bonds\_penalty, bonds\_reset\_enabled, burn\_half\_life, burn\_increase\_mult, collateral\_drain\_ratio, collateral\_lock\_share, 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. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx set-hyperparameter \ --netuid \ --name \ --value --dry-run btcli tx set-hyperparameter \ --netuid \ --name \ --value -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.SetHyperparameter(netuid=1, name="...", value="...") sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("set_hyperparameter", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] | Chain call | Source | | ------------------------------------------------------ | ------------------------------------------------------------------------------------------ | | `AdminUtils.sudo_set_tempo` | [`pallets/admin-utils/src/lib.rs#L1032`](/code/pallets/admin-utils/src/lib.rs#L1021-L1036) | | `AdminUtils.sudo_set_immunity_period` | [`pallets/admin-utils/src/lib.rs#L476`](/code/pallets/admin-utils/src/lib.rs#L474-L502) | | `AdminUtils.sudo_set_min_allowed_weights` | [`pallets/admin-utils/src/lib.rs#L509`](/code/pallets/admin-utils/src/lib.rs#L507-L535) | | `AdminUtils.sudo_set_weights_version_key` | [`pallets/admin-utils/src/lib.rs#L361`](/code/pallets/admin-utils/src/lib.rs#L359-L389) | | `AdminUtils.sudo_set_activity_cutoff_factor` | [`pallets/admin-utils/src/lib.rs#L681`](/code/pallets/admin-utils/src/lib.rs#L679-L695) | | `AdminUtils.sudo_set_min_burn` | [`pallets/admin-utils/src/lib.rs#L763`](/code/pallets/admin-utils/src/lib.rs#L761-L795) | | `AdminUtils.sudo_set_max_burn` | [`pallets/admin-utils/src/lib.rs#L802`](/code/pallets/admin-utils/src/lib.rs#L800-L834) | | `AdminUtils.sudo_set_bonds_moving_average` | [`pallets/admin-utils/src/lib.rs#L894`](/code/pallets/admin-utils/src/lib.rs#L892-L926) | | `AdminUtils.sudo_set_bonds_penalty` | [`pallets/admin-utils/src/lib.rs#L933`](/code/pallets/admin-utils/src/lib.rs#L931-L957) | | `AdminUtils.sudo_set_serving_rate_limit` | [`pallets/admin-utils/src/lib.rs#L278`](/code/pallets/admin-utils/src/lib.rs#L276-L297) | | `AdminUtils.sudo_set_commit_reveal_weights_interval` | [`pallets/admin-utils/src/lib.rs#L1376`](/code/pallets/admin-utils/src/lib.rs#L1374-L1403) | | `AdminUtils.sudo_set_max_allowed_uids` | [`pallets/admin-utils/src/lib.rs#L542`](/code/pallets/admin-utils/src/lib.rs#L540-L585) | | `AdminUtils.sudo_set_burn_increase_mult` | [`pallets/admin-utils/src/lib.rs#L2151`](/code/pallets/admin-utils/src/lib.rs#L2149-L2189) | | `AdminUtils.sudo_set_burn_half_life` | [`pallets/admin-utils/src/lib.rs#L2109`](/code/pallets/admin-utils/src/lib.rs#L2107-L2145) | | `AdminUtils.sudo_set_collateral_lock_share` | [`pallets/admin-utils/src/lib.rs#L2275`](/code/pallets/admin-utils/src/lib.rs#L2273-L2308) | | `AdminUtils.sudo_set_collateral_drain_ratio` | [`pallets/admin-utils/src/lib.rs#L2318`](/code/pallets/admin-utils/src/lib.rs#L2316-L2357) | | `AdminUtils.sudo_set_adjustment_alpha` | [`pallets/admin-utils/src/lib.rs#L445`](/code/pallets/admin-utils/src/lib.rs#L443-L469) | | `AdminUtils.sudo_set_rho` | [`pallets/admin-utils/src/lib.rs#L608`](/code/pallets/admin-utils/src/lib.rs#L606-L628) | | `AdminUtils.sudo_set_max_difficulty` | [`pallets/admin-utils/src/lib.rs#L328`](/code/pallets/admin-utils/src/lib.rs#L326-L354) | | `AdminUtils.sudo_set_alpha_sigmoid_steepness` | [`pallets/admin-utils/src/lib.rs#L1627`](/code/pallets/admin-utils/src/lib.rs#L1625-L1659) | | `AdminUtils.sudo_set_min_childkey_take_per_subnet` | [`pallets/admin-utils/src/lib.rs#L1200`](/code/pallets/admin-utils/src/lib.rs#L1198-L1232) | | `AdminUtils.sudo_set_owner_immune_neuron_limit` | [`pallets/admin-utils/src/lib.rs#L1813`](/code/pallets/admin-utils/src/lib.rs#L1811-L1831) | | `AdminUtils.sudo_set_alpha_values` | [`pallets/admin-utils/src/lib.rs#L1301`](/code/pallets/admin-utils/src/lib.rs#L1299-L1324) | | `AdminUtils.sudo_set_commit_reveal_weights_enabled` | [`pallets/admin-utils/src/lib.rs#L1239`](/code/pallets/admin-utils/src/lib.rs#L1237-L1264) | | `AdminUtils.sudo_set_liquid_alpha_enabled` | [`pallets/admin-utils/src/lib.rs#L1277`](/code/pallets/admin-utils/src/lib.rs#L1275-L1296) | | `AdminUtils.sudo_set_network_pow_registration_allowed` | [`pallets/admin-utils/src/lib.rs#L723`](/code/pallets/admin-utils/src/lib.rs#L721-L729) | | `AdminUtils.sudo_set_yuma3_enabled` | [`pallets/admin-utils/src/lib.rs#L1672`](/code/pallets/admin-utils/src/lib.rs#L1670-L1693) | | `AdminUtils.sudo_set_bonds_reset_enabled` | [`pallets/admin-utils/src/lib.rs#L1706`](/code/pallets/admin-utils/src/lib.rs#L1704-L1727) | | `AdminUtils.sudo_set_toggle_transfer` | [`pallets/admin-utils/src/lib.rs#L1465`](/code/pallets/admin-utils/src/lib.rs#L1463-L1485) | | `AdminUtils.sudo_set_owner_cut_enabled` | [`pallets/admin-utils/src/lib.rs#L2195`](/code/pallets/admin-utils/src/lib.rs#L2193-L2213) | | `AdminUtils.sudo_set_owner_cut_auto_lock_enabled` | [`pallets/admin-utils/src/lib.rs#L2219`](/code/pallets/admin-utils/src/lib.rs#L2217-L2237) | Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # set-identity (/docs/tx/set-identity) Stores public, human-readable metadata against the signing coldkey so explorers, wallets, and delegators can recognize it — useful for validator operators and subnet owners who want a public face. The signing coldkey must own at least one hotkey registered on some subnet, else the call fails with `HotKeyNotRegisteredInNetwork`. Everything submitted is public and permanent history on chain, so include nothing sensitive. Calling again overwrites the whole identity (empty fields clear their previous values); the chain enforces length limits on each field. Purely cosmetic: no effect on balances, stake, or permissions. | Signer | Origin | Pallet | Wraps | | --------- | -------------------------------------- | --------------- | ---------------------------------------------------------------------------------------------- | | `coldkey` | signed account (pallet role may apply) | SubtensorModule | [`SubtensorModule.set_identity`](/code/pallets/subtensor/src/macros/dispatches.rs#L1118-L1140) | ## Parameters [#parameters] | Parameter | Type | Required | Description | | ------------- | ------ | -------- | ------------------------------------------------------- | | `name` | string | yes | Display name shown for this coldkey. | | `url` | string | no | Website associated with this identity. | | `github_repo` | string | no | GitHub repository URL. | | `image` | string | no | Avatar or logo image URL. | | `discord` | string | no | Discord handle or server invite. | | `description` | string | no | Short free-text description of who this key belongs to. | | `additional` | string | no | Any extra free-text information to publish. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx set-identity \ --name --dry-run btcli tx set-identity \ --name -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.SetIdentity(name="...") sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("set_identity", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `SubtensorModule.set_identity` — [`pallets/subtensor/src/macros/dispatches.rs#L1120`](/code/pallets/subtensor/src/macros/dispatches.rs#L1118-L1140): ```rust #[pallet::call_index(68)] #[pallet::weight(::WeightInfo::set_identity())] pub fn set_identity( origin: OriginFor, name: Vec, url: Vec, github_repo: Vec, image: Vec, discord: Vec, description: Vec, additional: Vec, ) -> DispatchResult { Self::do_set_identity( origin, name, url, github_repo, image, discord, description, additional, ) } ``` Delegates to [`do_set_identity`](/code/pallets/subtensor/src/utils/identity.rs#L27). Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # set-mechanism-count (/docs/tx/set-mechanism-count) Mechanisms are independent incentive sub-markets within one subnet, each running its own weights and consensus; this owner-only call sets how many 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. Rate-limited and blocked during the end-of-epoch admin freeze window. | Signer | Origin | Pallet | Wraps | | --------- | ------------ | ---------- | ----------------------------------------------------------------------------------------- | | `coldkey` | subnet owner | AdminUtils | [`AdminUtils.sudo_set_mechanism_count`](/code/pallets/admin-utils/src/lib.rs#L1871-L1893) | ## Parameters [#parameters] | Parameter | Type | Required | Description | | ----------------- | ------- | -------- | -------------------------------------------------- | | `netuid` | integer | yes | Subnet to configure; the signer must be its owner. | | `mechanism_count` | integer | yes | Number of mechanisms the subnet should run. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx set-mechanism-count \ --netuid \ --mechanism-count --dry-run btcli tx set-mechanism-count \ --netuid \ --mechanism-count -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.SetMechanismCount(netuid=1, mechanism_count=0) sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("set_mechanism_count", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `AdminUtils.sudo_set_mechanism_count` — [`pallets/admin-utils/src/lib.rs#L1873`](/code/pallets/admin-utils/src/lib.rs#L1871-L1893): ```rust #[pallet::call_index(76)] #[pallet::weight(::WeightInfo::sudo_set_mechanism_count())] pub fn sudo_set_mechanism_count( origin: OriginFor, netuid: NetUid, mechanism_count: MechId, ) -> DispatchResult { let maybe_owner = pallet_subtensor::Pallet::::ensure_sn_owner_or_root_with_limits( origin, netuid, &[TransactionType::MechanismCountUpdate], )?; pallet_subtensor::Pallet::::ensure_admin_window_open(netuid)?; pallet_subtensor::Pallet::::do_set_mechanism_count(netuid, mechanism_count)?; pallet_subtensor::Pallet::::record_owner_rl( maybe_owner, netuid, &[TransactionType::MechanismCountUpdate], ); Ok(()) } ``` Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # set-min-collateral (/docs/tx/set-min-collateral) The collateral drain never releases the lock below the floor, and while the lock is under it, earned emission is captured into the lock until the floor is met — so a miner tracking a validator-published collateral requirement does not need to keep re-locking drained funds. Raising the floor above the current lock does not require fresh capital: the shortfall fills from future emission (use `add_collateral` to fund it immediately). Zero clears the floor and restores pure drain behavior. | Signer | Origin | Pallet | Wraps | | --------- | -------------------------------------- | --------------- | ---------------------------------------------------------------------------------------------------- | | `coldkey` | signed account (pallet role may apply) | SubtensorModule | [`SubtensorModule.set_min_collateral`](/code/pallets/subtensor/src/macros/dispatches.rs#L2476-L2485) | ## Parameters [#parameters] | Parameter | Type | Required | Description | | ------------- | ----------------- | -------- | ----------------------------------------------------------------- | | `netuid` | integer | yes | Subnet the floor applies to. | | `min_alpha` | number \| `"all"` | yes | The floor, in the subnet's alpha. Zero clears it. | | `hotkey_ss58` | string | no | Miner hotkey the floor applies to. Defaults to the wallet hotkey. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx set-min-collateral \ --netuid \ --min-alpha --dry-run btcli tx set-min-collateral \ --netuid \ --min-alpha -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.SetMinCollateral(netuid=1, min_alpha=1.0) sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("set_min_collateral", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `SubtensorModule.set_min_collateral` — [`pallets/subtensor/src/macros/dispatches.rs#L2478`](/code/pallets/subtensor/src/macros/dispatches.rs#L2476-L2485): ```rust #[pallet::call_index(145)] #[pallet::weight(::WeightInfo::set_min_collateral())] pub fn set_min_collateral( origin: OriginFor, netuid: NetUid, hotkey: T::AccountId, min_locked: AlphaBalance, ) -> DispatchResult { Self::do_set_min_collateral(origin, netuid, hotkey, min_locked) } ``` Delegates to [`do_set_min_collateral`](/code/pallets/subtensor/src/subnets/collateral.rs#L663). Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # set-perpetual-lock (/docs/tx/set-perpetual-lock) Switches how the signing coldkey's stake lock on the subnet behaves over time: perpetual mode keeps the lock (and its conviction) in force indefinitely, while decaying mode lets it wind down over time so the stake eventually becomes liquid again. A per-coldkey, per-subnet setting that moves no funds by itself — it changes the behavior of locks created with `lock_stake`. Enabling perpetual mode means the locked stake stays illiquid until you switch back to decaying and the lock runs off. | Signer | Origin | Pallet | Wraps | | --------- | -------------------------------------- | --------------- | ---------------------------------------------------------------------------------------------------- | | `coldkey` | signed account (pallet role may apply) | SubtensorModule | [`SubtensorModule.set_perpetual_lock`](/code/pallets/subtensor/src/macros/dispatches.rs#L2296-L2305) | ## Parameters [#parameters] | Parameter | Type | Required | Description | | --------- | ------- | -------- | --------------------------------------------------------------------- | | `netuid` | integer | yes | Subnet whose lock mode is changed. | | `enabled` | boolean | yes | True for perpetual mode (lock never decays), false for decaying mode. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx set-perpetual-lock \ --netuid \ --enabled/--no-enabled --dry-run btcli tx set-perpetual-lock \ --netuid \ --enabled/--no-enabled -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.SetPerpetualLock(netuid=1, enabled=True) sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("set_perpetual_lock", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `SubtensorModule.set_perpetual_lock` — [`pallets/subtensor/src/macros/dispatches.rs#L2298`](/code/pallets/subtensor/src/macros/dispatches.rs#L2296-L2305): ```rust #[pallet::call_index(138)] #[pallet::weight(::WeightInfo::set_perpetual_lock())] pub fn set_perpetual_lock( origin: OriginFor, netuid: NetUid, enabled: bool, ) -> DispatchResult { let coldkey = ensure_signed(origin)?; Self::do_set_perpetual_lock(&coldkey, netuid, enabled) } ``` Delegates to [`do_set_perpetual_lock`](/code/pallets/subtensor/src/staking/lock.rs#L655). Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # set-root-claim-type (/docs/tx/set-root-claim-type) Controls what happens to root dividends when they are claimed (see `claim_root`): `Swap` converts all alpha emission to TAO (the chain default), `Keep` keeps everything as subnet alpha, and `KeepSubnets` keeps alpha on the listed `subnets` while swapping the rest. The setting is per-coldkey and persists until changed again; it does not move anything already claimed. Read it back with the `root_claim_type` read. | Signer | Origin | Pallet | Wraps | | --------- | -------------------------------------- | --------------- | ----------------------------------------------------------------------------------------------------- | | `coldkey` | signed account (pallet role may apply) | SubtensorModule | [`SubtensorModule.set_root_claim_type`](/code/pallets/subtensor/src/macros/dispatches.rs#L1916-L1932) | ## Parameters [#parameters] | Parameter | Type | Required | Description | | ------------ | ------ | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `claim_type` | string | no | How root alpha emission is claimed. One of: Swap, Keep, KeepSubnets. Swap converts all alpha emission to TAO, Keep keeps everything as alpha, KeepSubnets keeps alpha only on the subnets given via --subnets and swaps the rest. | | `subnets` | array | no | Netuids to keep alpha on; required for KeepSubnets, invalid otherwise. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx set-root-claim-type --dry-run btcli tx set-root-claim-type -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.SetRootClaimType() sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("set_root_claim_type", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `SubtensorModule.set_root_claim_type` — [`pallets/subtensor/src/macros/dispatches.rs#L1918`](/code/pallets/subtensor/src/macros/dispatches.rs#L1916-L1932): ```rust #[pallet::call_index(122)] #[pallet::weight(::WeightInfo::set_root_claim_type())] pub fn set_root_claim_type( origin: OriginFor, new_root_claim_type: RootClaimTypeEnum, ) -> DispatchResult { let coldkey: T::AccountId = ensure_signed(origin)?; if let RootClaimTypeEnum::KeepSubnets { subnets } = &new_root_claim_type { ensure!(!subnets.is_empty(), Error::::InvalidSubnetNumber); } Self::maybe_add_coldkey_index(&coldkey); Self::change_root_claim_type(&coldkey, new_root_claim_type); Ok(()) } ``` Delegates to [`maybe_add_coldkey_index`](/code/pallets/subtensor/src/staking/claim_root.rs#L389), [`change_root_claim_type`](/code/pallets/subtensor/src/staking/claim_root.rs#L450). Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # set-subnet-emission-enabled (/docs/tx/set-subnet-emission-enabled) Flips the `SubnetEmissionEnabled` storage flag for each listed subnet — the ROOT-ONLY switch that gates whether a subnet earns its share of TAO emission on the pool side. Distinct from the owner's one-shot `start_call` (`subnet_is_active`): a subnet can be active — epochs running, alpha trading — yet earn no TAO emission share until root flips this flag on. Disabling only zeros the pool-side `alpha_in` / `tao_in` / `excess_tao` chain-buy paths; it does not remove the subnet from emission share calculation and does not touch `alpha_out`, the owner cut, root proportion, or pending server/validator emission. Requires the chain sudo key: `Executor` wraps the built call in `Sudo.sudo` because `origin` is `root`, and when root is a multisig that `Sudo.sudo` call is what the multisig must dispatch. Multiple netuids batch atomically via `Utility.batch_all` inside the single sudo call. Verify the effect afterwards with the `subnet_emission_enabled` read. | Signer | Origin | Pallet | Wraps | | --------- | ----------------- | ---------- | -------------------------------------------------------------------------------------------------------------- | | `coldkey` | root (chain sudo) | AdminUtils | [`AdminUtils.sudo_set_subnet_emission_enabled`](/code/pallets/admin-utils/src/lib.rs#L2245-L2265), `Sudo.sudo` | ## Verify [#verify] After inclusion, confirm the effect with the [`subnet-emission-enabled`](/docs/query/subnet-emission-enabled) read: ```bash btcli query subnet-emission-enabled --netuid --json ``` ## Parameters [#parameters] | Parameter | Type | Required | Description | | --------- | ---------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------- | | `netuids` | array of integer | yes | Subnets to toggle; multiple netuids batch atomically. | | `enabled` | boolean | yes | True to let the subnets earn their pool-side TAO emission share, false to zero the pool-side alpha\_in/tao\_in/excess\_tao chain-buy paths. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx set-subnet-emission-enabled \ --netuids \ --enabled/--no-enabled --dry-run btcli tx set-subnet-emission-enabled \ --netuids \ --enabled/--no-enabled -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.SetSubnetEmissionEnabled(netuids=[...], enabled=True) sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("set_subnet_emission_enabled", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `AdminUtils.sudo_set_subnet_emission_enabled` — [`pallets/admin-utils/src/lib.rs#L2247`](/code/pallets/admin-utils/src/lib.rs#L2245-L2265): ```rust #[pallet::call_index(94)] #[pallet::weight(::WeightInfo::sudo_set_subnet_emission_enabled())] pub fn sudo_set_subnet_emission_enabled( origin: OriginFor, netuid: NetUid, enabled: bool, ) -> DispatchResult { ensure_root(origin)?; ensure!( pallet_subtensor::Pallet::::if_subnet_exist(netuid), Error::::SubnetDoesNotExist ); ensure!(!netuid.is_root(), Error::::NotPermittedOnRootSubnet); pallet_subtensor::SubnetEmissionEnabled::::insert(netuid, enabled); Self::deposit_event(Event::SubnetEmissionEnabledSet { netuid, enabled }); log::debug!("SubnetEmissionEnabledSet( netuid: {netuid:?}, enabled: {enabled:?} )"); Ok(()) } ``` `Sudo.sudo` is implemented by Substrate's `pallet_sudo`, outside this repository's pallets. Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # set-subnet-identity (/docs/tx/set-subnet-identity) Stores the subnet's public profile — name, links, contact, logo — so explorers and participants can identify it. Owner-only: the signing coldkey must own the subnet. Everything submitted is public and permanent history on chain. Calling again overwrites the whole record (empty fields clear their previous values); the chain enforces length limits on each field. Purely cosmetic — for the token ticker use `update_symbol`, and for economics use `set_hyperparameter`. | Signer | Origin | Pallet | Wraps | | --------- | ------------ | --------------- | ----------------------------------------------------------------------------------------------------- | | `coldkey` | subnet owner | SubtensorModule | [`SubtensorModule.set_subnet_identity`](/code/pallets/subtensor/src/macros/dispatches.rs#L1153-L1179) | ## Parameters [#parameters] | Parameter | Type | Required | Description | | ---------------- | ------- | -------- | --------------------------------------------------------- | | `netuid` | integer | yes | Subnet the identity is for; the signer must be its owner. | | `subnet_name` | string | yes | Display name shown for the subnet. | | `github_repo` | string | no | GitHub repository URL for the subnet's code. | | `subnet_contact` | string | no | Contact address for the subnet operators. | | `subnet_url` | string | no | Website for the subnet. | | `discord` | string | no | Discord handle or server invite. | | `description` | string | no | Short free-text description of what the subnet does. | | `logo_url` | string | no | Logo image URL for the subnet. | | `additional` | string | no | Any extra free-text information to publish. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx set-subnet-identity \ --netuid \ --subnet-name --dry-run btcli tx set-subnet-identity \ --netuid \ --subnet-name -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.SetSubnetIdentity(netuid=1, subnet_name="...") sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("set_subnet_identity", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `SubtensorModule.set_subnet_identity` — [`pallets/subtensor/src/macros/dispatches.rs#L1155`](/code/pallets/subtensor/src/macros/dispatches.rs#L1153-L1179): ```rust #[pallet::call_index(78)] #[pallet::weight(::WeightInfo::set_subnet_identity())] pub fn set_subnet_identity( origin: OriginFor, netuid: NetUid, subnet_name: Vec, github_repo: Vec, subnet_contact: Vec, subnet_url: Vec, discord: Vec, description: Vec, logo_url: Vec, additional: Vec, ) -> DispatchResult { Self::do_set_subnet_identity( origin, netuid, subnet_name, github_repo, subnet_contact, subnet_url, discord, description, logo_url, additional, ) } ``` Delegates to [`do_set_subnet_identity`](/code/pallets/subtensor/src/utils/identity.rs#L98). Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # set-take (/docs/tx/set-take) The delegate take is the fraction of staking emissions a delegate hotkey keeps before distributing the rest to its nominators. This is sugar over the chain's directional `increase_take` / `decrease_take`: it reads the current take and dispatches whichever call moves it to `take`, so you do not need to know the current value. Signed by the coldkey that owns the hotkey. If the move is upward it inherits the increase path's constraints (take maximum and rate limit on increases). | Signer | Origin | Pallet | Wraps | | --------- | -------------------------------------- | --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `coldkey` | signed account (pallet role may apply) | SubtensorModule | [`SubtensorModule.increase_take`](/code/pallets/subtensor/src/macros/dispatches.rs#L523-L531), [`SubtensorModule.decrease_take`](/code/pallets/subtensor/src/macros/dispatches.rs#L490-L498) | ## Parameters [#parameters] | Parameter | Type | Required | Description | | ------------- | --------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------- | | `take` | integer \| number \| string | yes | New take: a 0..1 fraction (e.g. 0.18 for 18 percent) or the raw u16 proportion (0..65535). The chain enforces its configured take bounds. | | `hotkey_ss58` | string | no | Hotkey the operation applies to. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx set-take \ --take --dry-run btcli tx set-take \ --take -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.SetTake(take="...") sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("set_take", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `SubtensorModule.increase_take` — [`pallets/subtensor/src/macros/dispatches.rs#L525`](/code/pallets/subtensor/src/macros/dispatches.rs#L523-L531): ```rust #[pallet::call_index(66)] #[pallet::weight((::WeightInfo::increase_take(), DispatchClass::Normal, Pays::Yes))] pub fn increase_take( origin: OriginFor, hotkey: T::AccountId, take: PerU16, ) -> DispatchResult { Self::do_increase_take(origin, hotkey, take) } ``` Delegates to [`do_increase_take`](/code/pallets/subtensor/src/staking/increase_take.rs#L27). `SubtensorModule.decrease_take` — [`pallets/subtensor/src/macros/dispatches.rs#L492`](/code/pallets/subtensor/src/macros/dispatches.rs#L490-L498): ```rust #[pallet::call_index(65)] #[pallet::weight((::WeightInfo::decrease_take(), DispatchClass::Normal, Pays::Yes))] pub fn decrease_take( origin: OriginFor, hotkey: T::AccountId, take: PerU16, ) -> DispatchResult { Self::do_decrease_take(origin, hotkey, take) } ``` Delegates to [`do_decrease_take`](/code/pallets/subtensor/src/staking/decrease_take.rs#L25). Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # set-weights (/docs/tx/set-weights) The one entry point validators need for scoring miners: it conforms the weights to the subnet's hyperparameters (max-weight clip, u16 quantization, minimum weight count) and submits via whichever path the subnet runs — a plain `set_weights` when commit-reveal is off, or a timelock-encrypted commit (auto-revealed by the chain at the drand reveal round) when it is on. Signed by the hotkey, which must be registered on the subnet. Before signing it preflights registration and the rate limit, so those failures are caught fast with the same error the chain would return; the rate-limit error says how many blocks to wait. The chain additionally enforces checks that are not preflighted: the hotkey must hold the minimum stake to set weights, must hold a validator permit to set non-self weights (the subnet owner is exempt), and `version_key` must not be older than the subnet's required version. Prefer this over `commit_weights`/`reveal_weights` unless you specifically need to force one path. | Signer | Origin | Pallet | Wraps | | -------- | -------------------------------------- | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `hotkey` | signed account (pallet role may apply) | SubtensorModule | [`SubtensorModule.set_mechanism_weights`](/code/pallets/subtensor/src/macros/dispatches.rs#L128-L143), [`SubtensorModule.commit_timelocked_mechanism_weights`](/code/pallets/subtensor/src/macros/dispatches.rs#L1850-L1868) | ## Parameters [#parameters] | Parameter | Type | Required | Description | | ------------- | ---------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `netuid` | integer | yes | Subnet whose miners the weights score. | | `uids` | array of integer | no | Miner UIDs being weighted, as a list parallel to weights. Omit when weights is given as a uid-to-weight mapping. | | `weights` | array of number | no | Relative weight per miner: either a JSON object mapping uid to weight, or a list parallel to uids. Values are relative, not absolute; they are clipped to the subnet's max-weight limit, normalized, and quantized before submission. | | `mechid` | integer | no | Mechanism index within the subnet. 0 is the default (and for most subnets the only) mechanism. | | `version_key` | integer | no | Weights version key checked against the subnet's required version. Leave 0 unless the subnet owner requires a specific value. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx set-weights \ --netuid --dry-run btcli tx set-weights \ --netuid -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.SetWeights(netuid=1) sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("set_weights", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `SubtensorModule.set_mechanism_weights` — [`pallets/subtensor/src/macros/dispatches.rs#L130`](/code/pallets/subtensor/src/macros/dispatches.rs#L128-L143): ```rust #[pallet::call_index(119)] #[pallet::weight((::WeightInfo::set_mechanism_weights(dests.len() as u32), DispatchClass::Normal, Pays::No))] pub fn set_mechanism_weights( origin: OriginFor, netuid: NetUid, mecid: MechId, dests: Vec, weights: Vec, version_key: u64, ) -> DispatchResult { if Self::get_commit_reveal_weights_enabled(netuid) { Err(Error::::CommitRevealEnabled.into()) } else { Self::do_set_mechanism_weights(origin, netuid, mecid, dests, weights, version_key) } } ``` Delegates to [`get_commit_reveal_weights_enabled`](/code/pallets/subtensor/src/utils/misc.rs#L607), [`do_set_mechanism_weights`](/code/pallets/subtensor/src/subnets/weights.rs#L916). `SubtensorModule.commit_timelocked_mechanism_weights` — [`pallets/subtensor/src/macros/dispatches.rs#L1852`](/code/pallets/subtensor/src/macros/dispatches.rs#L1850-L1868): ```rust #[pallet::call_index(118)] #[pallet::weight((::WeightInfo::commit_timelocked_mechanism_weights(), DispatchClass::Normal, Pays::No))] pub fn commit_timelocked_mechanism_weights( origin: OriginFor, netuid: NetUid, mecid: MechId, commit: BoundedVec>, reveal_round: u64, commit_reveal_version: u16, ) -> DispatchResult { Self::do_commit_timelocked_mechanism_weights( origin, netuid, mecid, commit, reveal_round, commit_reveal_version, ) } ``` Delegates to [`do_commit_timelocked_mechanism_weights`](/code/pallets/subtensor/src/subnets/weights.rs#L263). Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # stake-burn (/docs/tx/stake-burn) Spends TAO from the signing coldkey to buy the subnet's alpha and burn it, reducing alpha supply (a buyback-and-burn) rather than adding to the signer's stake. The TAO is spent permanently — nothing lands in your stake, so this is not an investment call; use a regular add-stake intent to acquire a position. Fails on the root subnet (`CannotBurnOrRecycleOnRootSubnet`). The chain accepts an optional limit (omitted = market order), but this intent always requires `limit_price` and executes all-or-nothing: the swap fails instead of partially filling at a worse rate. Counts against a configured spend cap. | Signer | Origin | Pallet | Wraps | | --------- | -------------------------------------- | --------------- | ------------------------------------------------------------------------------------------------ | | `coldkey` | signed account (pallet role may apply) | SubtensorModule | [`SubtensorModule.add_stake_burn`](/code/pallets/subtensor/src/macros/dispatches.rs#L2168-L2178) | ## Parameters [#parameters] | Parameter | Type | Required | Description | | ------------- | ----------------- | -------- | -------------------------------------------------------------------------------------- | | `netuid` | integer | yes | Subnet whose alpha is bought and burned. | | `amount_tao` | number \| `"all"` | yes | Spent from the coldkey to buy alpha that is then burned. | | `limit_price` | integer | yes | Worst acceptable price in rao per alpha; the call fails rather than filling beyond it. | | `hotkey_ss58` | string | no | Hotkey the burn is routed through; defaults to the wallet's hotkey. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx stake-burn \ --netuid \ --amount-tao \ --limit-price --dry-run btcli tx stake-burn \ --netuid \ --amount-tao \ --limit-price -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.StakeBurn(netuid=1, amount_tao=1.0, limit_price=0) sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("stake_burn", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `SubtensorModule.add_stake_burn` — [`pallets/subtensor/src/macros/dispatches.rs#L2170`](/code/pallets/subtensor/src/macros/dispatches.rs#L2168-L2178): ```rust #[pallet::call_index(132)] #[pallet::weight(::WeightInfo::add_stake_burn())] pub fn add_stake_burn( origin: OriginFor, hotkey: T::AccountId, netuid: NetUid, amount: TaoBalance, limit: Option, ) -> DispatchResult { Self::do_add_stake_burn(origin, hotkey, netuid, amount, limit) } ``` Delegates to [`do_add_stake_burn`](/code/pallets/subtensor/src/staking/recycle_alpha.rs#L131). Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # start-call (/docs/tx/start-call) Flips a freshly registered subnet from inactive to active: the subnet token becomes tradable and alpha emission into the subnet's epochs begins. It does not enable the subnet's share of TAO emission — that additionally requires the root-gated emission-enabled flag, which only root can set. Owner-only, callable once per subnet, and only after the chain's minimum delay since the subnet was registered — calling too early fails. Until this is called the subnet earns nothing, so run it as soon as the delay allows. | Signer | Origin | Pallet | Wraps | | --------- | ------------ | --------------- | -------------------------------------------------------------------------------------------- | | `coldkey` | subnet owner | SubtensorModule | [`SubtensorModule.start_call`](/code/pallets/subtensor/src/macros/dispatches.rs#L1537-L1542) | ## Parameters [#parameters] | Parameter | Type | Required | Description | | --------- | ------- | -------- | ------------------------------------------------- | | `netuid` | integer | yes | Subnet to activate; the signer must be its owner. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx start-call \ --netuid --dry-run btcli tx start-call \ --netuid -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.StartCall(netuid=1) sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("start_call", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `SubtensorModule.start_call` — [`pallets/subtensor/src/macros/dispatches.rs#L1539`](/code/pallets/subtensor/src/macros/dispatches.rs#L1537-L1542): ```rust #[pallet::call_index(92)] #[pallet::weight(::WeightInfo::start_call())] pub fn start_call(origin: OriginFor, netuid: NetUid) -> DispatchResult { Self::do_start_call(origin, netuid)?; Ok(()) } ``` Delegates to [`do_start_call`](/code/pallets/subtensor/src/subnets/subnet.rs#L531). Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # swap-coldkey-announced (/docs/tx/swap-coldkey-announced) Step two of the two-step migration: reveals the new coldkey and moves everything the signing coldkey owns — balance, stake, and subnet ownership — to it. Irreversible once included. The revealed key must hash to exactly what `announce_coldkey_swap` committed to, and the call fails if the announcement delay has not elapsed, no announcement exists, or the swap is frozen by a dispute. After it succeeds, the old coldkey is empty; all future operations sign with the new coldkey. | Signer | Origin | Pallet | Wraps | | --------- | -------------------------------------- | --------------- | -------------------------------------------------------------------------------------------------------- | | `coldkey` | signed account (pallet role may apply) | SubtensorModule | [`SubtensorModule.swap_coldkey_announced`](/code/pallets/subtensor/src/macros/dispatches.rs#L2023-L2045) | ## Parameters [#parameters] | Parameter | Type | Required | Description | | ------------------ | ------ | -------- | ------------------------------------------------------------------------------- | | `new_coldkey_ss58` | string | yes | Coldkey receiving everything; must match the previously announced hash exactly. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx swap-coldkey-announced \ --new-coldkey --dry-run btcli tx swap-coldkey-announced \ --new-coldkey -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.SwapColdkeyAnnounced(new_coldkey_ss58="5F...") sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("swap_coldkey_announced", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `SubtensorModule.swap_coldkey_announced` — [`pallets/subtensor/src/macros/dispatches.rs#L2025`](/code/pallets/subtensor/src/macros/dispatches.rs#L2023-L2045): ```rust #[pallet::call_index(126)] #[pallet::weight(::WeightInfo::swap_coldkey_announced())] pub fn swap_coldkey_announced( origin: OriginFor, new_coldkey: T::AccountId, ) -> DispatchResult { let who = ensure_signed(origin)?; let (when, new_coldkey_hash) = ColdkeySwapAnnouncements::::take(who.clone()) .ok_or(Error::::ColdkeySwapAnnouncementNotFound)?; ensure!( new_coldkey_hash == T::Hashing::hash_of(&new_coldkey), Error::::AnnouncedColdkeyHashDoesNotMatch ); let now = >::block_number(); ensure!(now >= when, Error::::ColdkeySwapTooEarly); Self::do_swap_coldkey(&who, &new_coldkey)?; Ok(()) } ``` Delegates to [`do_swap_coldkey`](/code/pallets/subtensor/src/swap/swap_coldkey.rs#L8). Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # swap-hotkey (/docs/tx/swap-hotkey) Re-keys the neuron identity: the old hotkey's registrations, stake, and history move to `new_hotkey_ss58`, either everywhere (`netuid` omitted) or on a single subnet. The all-subnets swap recycles 0.1 TAO from the coldkey; the per-subnet swap recycles 0.001 TAO. Both respect a 7,200-block (one day) per-(subnet, coldkey) cooldown — the all-subnets swap checks and records it on every subnet the old hotkey participates in. The old hotkey stops earning immediately, so update running miners/validators to sign with the new key at the same time. The new hotkey must not already be registered where the swap applies, so plan the change rather than iterating. This wraps the legacy `swap_hotkey` extrinsic, deprecated on chain in favor of `swap_hotkey_v2`; behavior is identical to `swap_hotkey_v2` with `keep_stake=false` (stake moves to the new hotkey). This rotates a leaked hotkey without touching the coldkey; a compromised coldkey needs a coldkey swap instead. | Signer | Origin | Pallet | Wraps | | --------- | -------------------------------------- | --------------- | ------------------------------------------------------------------------------------------- | | `coldkey` | signed account (pallet role may apply) | SubtensorModule | [`SubtensorModule.swap_hotkey`](/code/pallets/subtensor/src/macros/dispatches.rs#L836-L849) | ## Parameters [#parameters] | Parameter | Type | Required | Description | | ----------------- | ------- | -------- | ---------------------------------------------------------------------------- | | `new_hotkey_ss58` | string | yes | Replacement hotkey that takes over the old hotkey's registrations and stake. | | `hotkey_ss58` | string | no | Hotkey being replaced; defaults to the wallet's hotkey. | | `netuid` | integer | no | Limit the swap to this subnet; omit to swap across all subnets. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx swap-hotkey \ --new-hotkey --dry-run btcli tx swap-hotkey \ --new-hotkey -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.SwapHotkey(new_hotkey_ss58="5F...") sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("swap_hotkey", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `SubtensorModule.swap_hotkey` — [`pallets/subtensor/src/macros/dispatches.rs#L842`](/code/pallets/subtensor/src/macros/dispatches.rs#L836-L849): ```rust #[pallet::call_index(70)] #[pallet::weight(( ::WeightInfo::swap_hotkey(), DispatchClass::Normal, Pays::Yes ))] pub fn swap_hotkey( origin: OriginFor, hotkey: T::AccountId, new_hotkey: T::AccountId, netuid: Option, ) -> DispatchResultWithPostInfo { Self::do_swap_hotkey(origin, &hotkey, &new_hotkey, netuid, false) } ``` Delegates to [`do_swap_hotkey`](/code/pallets/subtensor/src/swap/swap_hotkey.rs#L72). Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # swap-stake (/docs/tx/swap-stake) Moves part of a position from the origin subnet to the destination subnet while staying on the same hotkey: the alpha is swapped to TAO in the origin pool and then to alpha in the destination pool, so both legs can incur slippage. By default the call is slippage-protected: it fails (`SlippageTooHigh`) instead of filling once the origin/destination price ratio falls more than `rate_tolerance` (5%) below the ratio at submission — raise the tolerance or set `slippage_protection` to False to execute at any price. The two netuids must differ (`SameNetuid`). Use `move_stake` when the hotkey should change too, and `remove_stake` plus `add_stake` only if you want to control each leg separately. | Signer | Origin | Pallet | Wraps | | --------- | -------------------------------------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `coldkey` | signed account (pallet role may apply) | SubtensorModule | [`SubtensorModule.swap_stake`](/code/pallets/subtensor/src/macros/dispatches.rs#L1341-L1357), [`SubtensorModule.swap_stake_limit`](/code/pallets/subtensor/src/macros/dispatches.rs#L1489-L1509) | ## Parameters [#parameters] | Parameter | Type | Required | Description | | --------------------- | ----------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `hotkey_ss58` | string | yes | Hotkey the stake is held on (the validator backing the position). | | `origin_netuid` | integer | yes | Subnet the stake currently sits on. | | `dest_netuid` | integer | yes | Subnet the stake ends up on. When it differs from the origin, the position is swapped through both subnet pools, which can incur slippage on each leg. | | `amount_alpha` | number \| `"all"` | yes | How much of the origin position to swap across (an explicit amount; `all` is not accepted). | | `slippage_protection` | boolean | no | Bound the price the swap may execute at (on by default): the call fails (`SlippageTooHigh`) instead of filling once the pool price moves more than `rate_tolerance` from the price at submission. Disable to execute at any price. | | `rate_tolerance` | number | no | Maximum price move slippage protection accepts, as a fraction (0.05 = 5%). Ignored when slippage protection is disabled. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx swap-stake \ --hotkey \ --origin-netuid \ --dest-netuid \ --amount-alpha --dry-run btcli tx swap-stake \ --hotkey \ --origin-netuid \ --dest-netuid \ --amount-alpha -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.SwapStake(hotkey_ss58="5F...", origin_netuid=0, dest_netuid=0, amount_alpha=1.0) sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("swap_stake", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `SubtensorModule.swap_stake` — [`pallets/subtensor/src/macros/dispatches.rs#L1343`](/code/pallets/subtensor/src/macros/dispatches.rs#L1341-L1357): ```rust #[pallet::call_index(87)] #[pallet::weight(::WeightInfo::swap_stake())] pub fn swap_stake( origin: OriginFor, hotkey: T::AccountId, origin_netuid: NetUid, destination_netuid: NetUid, alpha_amount: AlphaBalance, ) -> DispatchResult { Self::do_swap_stake( origin, hotkey, origin_netuid, destination_netuid, alpha_amount, ) } ``` Delegates to [`do_swap_stake`](/code/pallets/subtensor/src/staking/move_stake.rs#L260). `SubtensorModule.swap_stake_limit` — [`pallets/subtensor/src/macros/dispatches.rs#L1491`](/code/pallets/subtensor/src/macros/dispatches.rs#L1489-L1509): ```rust #[pallet::call_index(90)] #[pallet::weight(::WeightInfo::swap_stake_limit())] pub fn swap_stake_limit( origin: OriginFor, hotkey: T::AccountId, origin_netuid: NetUid, destination_netuid: NetUid, alpha_amount: AlphaBalance, limit_price: TaoBalance, allow_partial: bool, ) -> DispatchResult { Self::do_swap_stake_limit( origin, hotkey, origin_netuid, destination_netuid, alpha_amount, limit_price, allow_partial, ) } ``` Delegates to [`do_swap_stake_limit`](/code/pallets/subtensor/src/staking/move_stake.rs#L325). Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # terminate-lease (/docs/tx/terminate-lease) Ends the lease and transfers full subnet ownership to the beneficiary: contributor dividends stop and the subnet becomes an ordinary owned subnet. Only the lease's beneficiary can call it, and only after the lease's end block has passed — earlier attempts fail, and perpetual leases (no end block) can never be terminated this way. Check the lease's end block with the `lease` read before calling. | Signer | Origin | Pallet | Wraps | | --------- | -------------------------------------- | --------------- | ------------------------------------------------------------------------------------------------- | | `coldkey` | signed account (pallet role may apply) | SubtensorModule | [`SubtensorModule.terminate_lease`](/code/pallets/subtensor/src/macros/dispatches.rs#L1694-L1702) | ## Parameters [#parameters] | Parameter | Type | Required | Description | | ------------- | ------- | -------- | ------------------------------------------------------------------------------------------ | | `lease_id` | integer | yes | Lease to terminate (see the leases read). | | `hotkey_ss58` | string | no | Beneficiary hotkey recorded as the subnet's owner hotkey; defaults to the wallet's hotkey. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx terminate-lease \ --lease-id --dry-run btcli tx terminate-lease \ --lease-id -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.TerminateLease(lease_id=0) sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("terminate_lease", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `SubtensorModule.terminate_lease` — [`pallets/subtensor/src/macros/dispatches.rs#L1696`](/code/pallets/subtensor/src/macros/dispatches.rs#L1694-L1702): ```rust #[pallet::call_index(111)] #[pallet::weight(::WeightInfo::terminate_lease(T::MaxContributors::get()))] pub fn terminate_lease( origin: OriginFor, lease_id: LeaseId, hotkey: T::AccountId, ) -> DispatchResultWithPostInfo { Self::do_terminate_lease(origin, lease_id, hotkey) } ``` Delegates to [`do_terminate_lease`](/code/pallets/subtensor/src/subnets/leasing.rs#L181). Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # transfer-all (/docs/tx/transfer-all) Drains the signing coldkey's transferable balance to the destination in one irreversible call — equivalent to `transfer` with `all` as the amount. Staked or otherwise reserved funds are not included; unstake first to move those. A spend-cap policy treats this as an unbounded spend and blocks it until the cap is raised. With `keep_alive` (the default) the existential deposit stays behind; disable it to empty and reap the account. | Signer | Origin | Pallet | Wraps | | --------- | -------------------------------------- | -------- | ----------------------- | | `coldkey` | signed account (pallet role may apply) | Balances | `Balances.transfer_all` | ## Parameters [#parameters] | Parameter | Type | Required | Description | | ------------ | ------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------- | | `dest_ss58` | string | yes | Account the funds are sent to. | | `keep_alive` | boolean | no | Refuse to drop the sender below the existential deposit. Disable it only when you intend to empty the account, which lets the chain reap it. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx transfer-all \ --dest --dry-run btcli tx transfer-all \ --dest -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.TransferAll(dest_ss58="5F...") sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("transfer_all", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `Balances.transfer_all` is implemented by Substrate's `pallet_balances`, outside this repository's pallets. Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # transfer-stake (/docs/tx/transfer-stake) Hands the position itself to the destination coldkey: after this call that coldkey — not you — controls and can unstake those funds, so this is a transfer of value and is irreversible. Double-check the destination address. The stake stays on the same hotkey by default; pass a destination hotkey to re-delegate it in the same call (dispatched as `transfer_stake_and_hotkey`). It can also land on a different subnet, swapping through both pools (with slippage) when the netuids differ. Fails with `TransferDisallowed` when the subnet owner has disabled stake transfers on the origin or destination subnet. A spend-cap policy treats this as an unbounded spend and blocks it until the cap is raised. Use `move_stake` to re-delegate without changing owners. | Signer | Origin | Pallet | Wraps | | --------- | -------------------------------------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `coldkey` | signed account (pallet role may apply) | SubtensorModule | [`SubtensorModule.transfer_stake`](/code/pallets/subtensor/src/macros/dispatches.rs#L1299-L1317), [`SubtensorModule.transfer_stake_and_hotkey`](/code/pallets/subtensor/src/macros/dispatches.rs#L2383-L2403) | ## Parameters [#parameters] | Parameter | Type | Required | Description | | ------------------- | ----------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | | `dest_coldkey_ss58` | string | yes | Coldkey that becomes the new owner of the stake. | | `hotkey_ss58` | string | yes | Hotkey the stake is held on (the validator backing the position). | | `origin_netuid` | integer | yes | Subnet the stake currently sits on. | | `dest_netuid` | integer | yes | Subnet the stake ends up on. When it differs from the origin, the position is swapped through both subnet pools, which can incur slippage on each leg. | | `amount_alpha` | number \| `"all"` | yes | How much of the position to hand over (an explicit amount; `all` is not accepted). | | `dest_hotkey_ss58` | string | no | Hotkey the stake lands on. Defaults to the origin hotkey (the position stays with the same validator). | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx transfer-stake \ --dest-coldkey \ --hotkey \ --origin-netuid \ --dest-netuid \ --amount-alpha --dry-run btcli tx transfer-stake \ --dest-coldkey \ --hotkey \ --origin-netuid \ --dest-netuid \ --amount-alpha -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.TransferStake(dest_coldkey_ss58="5F...", hotkey_ss58="5F...", origin_netuid=0, dest_netuid=0, amount_alpha=1.0) sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("transfer_stake", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `SubtensorModule.transfer_stake` — [`pallets/subtensor/src/macros/dispatches.rs#L1301`](/code/pallets/subtensor/src/macros/dispatches.rs#L1299-L1317): ```rust #[pallet::call_index(86)] #[pallet::weight(::WeightInfo::transfer_stake())] pub fn transfer_stake( origin: OriginFor, destination_coldkey: T::AccountId, hotkey: T::AccountId, origin_netuid: NetUid, destination_netuid: NetUid, alpha_amount: AlphaBalance, ) -> DispatchResult { Self::do_transfer_stake( origin, destination_coldkey, hotkey, origin_netuid, destination_netuid, alpha_amount, ) } ``` Delegates to [`do_transfer_stake`](/code/pallets/subtensor/src/staking/move_stake.rs#L120). `SubtensorModule.transfer_stake_and_hotkey` — [`pallets/subtensor/src/macros/dispatches.rs#L2385`](/code/pallets/subtensor/src/macros/dispatches.rs#L2383-L2403): ```rust #[pallet::call_index(143)] #[pallet::weight(::WeightInfo::transfer_stake_and_hotkey())] pub fn transfer_stake_and_hotkey( origin: OriginFor, destination_coldkey: T::AccountId, origin_hotkey: T::AccountId, destination_hotkey: T::AccountId, origin_netuid: NetUid, destination_netuid: NetUid, alpha_amount: AlphaBalance, ) -> DispatchResult { Self::do_transfer_stake_and_hotkey( origin, destination_coldkey, origin_hotkey, destination_hotkey, origin_netuid, destination_netuid, alpha_amount, ) } ``` Delegates to [`do_transfer_stake_and_hotkey`](/code/pallets/subtensor/src/staking/move_stake.rs#L193). Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # transfer (/docs/tx/transfer) Sends free balance from the signing coldkey to the destination. On-chain transfers are irreversible — funds sent to a wrong address cannot be recovered, so double-check the destination. A transaction fee is deducted from the sender on top of the amount, and the call fails if the free balance cannot cover both. Pass `all` to sweep the entire transferable balance. With `keep_alive` (the default) the transfer refuses to drop the sender below the existential deposit; disable it only when intentionally emptying the account. | Signer | Origin | Pallet | Wraps | | --------- | -------------------------------------- | -------- | ---------------------------------------------------------------------------------------- | | `coldkey` | signed account (pallet role may apply) | Balances | `Balances.transfer_keep_alive`, `Balances.transfer_allow_death`, `Balances.transfer_all` | ## Parameters [#parameters] | Parameter | Type | Required | Description | | ------------ | ----------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------- | | `dest_ss58` | string | yes | Account the funds are sent to. | | `amount_tao` | number \| `"all"` | yes | How much to send. | | `keep_alive` | boolean | no | Refuse to drop the sender below the existential deposit. Disable it only when you intend to empty the account, which lets the chain reap it. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx transfer \ --dest \ --amount-tao --dry-run btcli tx transfer \ --dest \ --amount-tao -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.Transfer(dest_ss58="5F...", amount_tao=1.0) sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("transfer", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `Balances.transfer_keep_alive` is implemented by Substrate's `pallet_balances`, outside this repository's pallets. `Balances.transfer_allow_death` is implemented by Substrate's `pallet_balances`, outside this repository's pallets. `Balances.transfer_all` is implemented by Substrate's `pallet_balances`, outside this repository's pallets. Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # trim-subnet (/docs/tx/trim-subnet) Lowers the subnet's UID capacity and immediately deregisters the lowest-emission neurons above the new limit — those miners lose their slots and would have to re-register (paying the burn cost) to return. `max_n` must be between the chain's minimum allowed UIDs (64) and the subnet's current `max_allowed_uids`. Owner-immune and temporally immune UIDs are skipped, and the call fails if immune UIDs would exceed 80% of `max_n`. Surviving UIDs are renumbered consecutively from zero, so UID values change. Rate-limited to once per 216,000 blocks (30 days) and blocked during the end-of-epoch admin freeze window. Owner-only and disruptive to affected participants, so announce it before shrinking a live subnet. To simply cap future growth without evicting anyone, set the `max_allowed_uids` hyperparameter to a value at or above the current UID count instead. | Signer | Origin | Pallet | Wraps | | --------- | ------------ | ---------- | ---------------------------------------------------------------------------------------------- | | `coldkey` | subnet owner | AdminUtils | [`AdminUtils.sudo_trim_to_max_allowed_uids`](/code/pallets/admin-utils/src/lib.rs#L1925-L1947) | ## Parameters [#parameters] | Parameter | Type | Required | Description | | --------- | ------- | -------- | --------------------------------------------------------------------------- | | `netuid` | integer | yes | Subnet to trim; the signer must be its owner. | | `max_n` | integer | yes | New maximum number of UIDs; lowest-emission neurons above this are removed. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx trim-subnet \ --netuid \ --max-n --dry-run btcli tx trim-subnet \ --netuid \ --max-n -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.TrimSubnet(netuid=1, max_n=0) sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("trim_subnet", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `AdminUtils.sudo_trim_to_max_allowed_uids` — [`pallets/admin-utils/src/lib.rs#L1927`](/code/pallets/admin-utils/src/lib.rs#L1925-L1947): ```rust #[pallet::call_index(78)] #[pallet::weight(::WeightInfo::sudo_trim_to_max_allowed_uids())] pub fn sudo_trim_to_max_allowed_uids( origin: OriginFor, netuid: NetUid, max_n: u16, ) -> DispatchResult { let maybe_owner = pallet_subtensor::Pallet::::ensure_sn_owner_or_root_with_limits( origin.clone(), netuid, &[TransactionType::MaxUidsTrimming], )?; pallet_subtensor::Pallet::::ensure_admin_window_open(netuid)?; pallet_subtensor::Pallet::::trim_to_max_allowed_uids(netuid, max_n)?; pallet_subtensor::Pallet::::record_owner_rl( maybe_owner, netuid, &[TransactionType::MaxUidsTrimming], ); Ok(()) } ``` Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # unstake-all-alpha (/docs/tx/unstake-all-alpha) Sells every alpha position the signing coldkey holds on this hotkey and restakes the proceeds as TAO on the root network (netuid 0), instead of releasing them to the free balance. Subnets where subtoken trading is disabled or where the position fails validation are silently skipped. Use it to consolidate onto root while keeping funds staked; use `unstake_all` to exit to free balance instead. Each pool swap happens at the current price with no limit protection, so large positions can incur slippage. | Signer | Origin | Pallet | Wraps | | --------- | -------------------------------------- | --------------- | --------------------------------------------------------------------------------------------------- | | `coldkey` | signed account (pallet role may apply) | SubtensorModule | [`SubtensorModule.unstake_all_alpha`](/code/pallets/subtensor/src/macros/dispatches.rs#L1234-L1238) | ## Parameters [#parameters] | Parameter | Type | Required | Description | | ------------- | ------ | -------- | ------------------------------------------ | | `hotkey_ss58` | string | yes | Hotkey whose alpha stake is moved to root. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx unstake-all-alpha \ --hotkey --dry-run btcli tx unstake-all-alpha \ --hotkey -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.UnstakeAllAlpha(hotkey_ss58="5F...") sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("unstake_all_alpha", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `SubtensorModule.unstake_all_alpha` — [`pallets/subtensor/src/macros/dispatches.rs#L1236`](/code/pallets/subtensor/src/macros/dispatches.rs#L1234-L1238): ```rust #[pallet::call_index(84)] #[pallet::weight(::WeightInfo::unstake_all_alpha())] pub fn unstake_all_alpha(origin: OriginFor, hotkey: T::AccountId) -> DispatchResult { Self::do_unstake_all_alpha(origin, hotkey) } ``` Delegates to [`do_unstake_all_alpha`](/code/pallets/subtensor/src/staking/remove_stake.rs#L188). Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # unstake-all (/docs/tx/unstake-all) Sweeps the signing coldkey's entire stake held on this hotkey across every subnet (root included) back to TAO in the coldkey's free balance. Subnets where subtoken trading is disabled or where the position fails validation (e.g. dust below the chain minimum) are silently skipped, so the call can succeed while leaving some positions untouched. Alpha positions are sold at each pool's current price with no limit protection, so large positions can incur significant slippage. Use `remove_stake` to exit a single subnet, or `unstake_all_alpha` to consolidate onto root while staying staked. | Signer | Origin | Pallet | Wraps | | --------- | -------------------------------------- | --------------- | --------------------------------------------------------------------------------------------- | | `coldkey` | signed account (pallet role may apply) | SubtensorModule | [`SubtensorModule.unstake_all`](/code/pallets/subtensor/src/macros/dispatches.rs#L1210-L1214) | ## Parameters [#parameters] | Parameter | Type | Required | Description | | ------------- | ------ | -------- | ------------------------------------- | | `hotkey_ss58` | string | yes | Hotkey whose entire stake is removed. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx unstake-all \ --hotkey --dry-run btcli tx unstake-all \ --hotkey -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.UnstakeAll(hotkey_ss58="5F...") sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("unstake_all", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `SubtensorModule.unstake_all` — [`pallets/subtensor/src/macros/dispatches.rs#L1212`](/code/pallets/subtensor/src/macros/dispatches.rs#L1210-L1214): ```rust #[pallet::call_index(83)] #[pallet::weight(::WeightInfo::unstake_all())] pub fn unstake_all(origin: OriginFor, hotkey: T::AccountId) -> DispatchResult { Self::do_unstake_all(origin, hotkey) } ``` Delegates to [`do_unstake_all`](/code/pallets/subtensor/src/staking/remove_stake.rs#L110). Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # update-crowdloan-cap (/docs/tx/update-crowdloan-cap) Raises or lowers the total the crowdloan can collect. Only the creator may change it, and only while the loan has not been finalized. Reaching the (new) cap is what allows the creator to finalize. | Signer | Origin | Pallet | Wraps | | --------- | -------------------------------------- | --------- | ---------------------------------------------------------------------- | | `coldkey` | signed account (pallet role may apply) | Crowdloan | [`Crowdloan.update_cap`](/code/pallets/crowdloan/src/lib.rs#L896-L923) | ## Parameters [#parameters] | Parameter | Type | Required | Description | | -------------- | ----------------- | -------- | ---------------------------------------------------------- | | `crowdloan_id` | integer | yes | Identifier of the crowdloan, assigned when it was created. | | `new_cap_tao` | number \| `"all"` | yes | New maximum total the crowdloan can raise. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx update-crowdloan-cap \ --crowdloan-id \ --new-cap-tao --dry-run btcli tx update-crowdloan-cap \ --crowdloan-id \ --new-cap-tao -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.UpdateCrowdloanCap(crowdloan_id=0, new_cap_tao=1.0) sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("update_crowdloan_cap", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `Crowdloan.update_cap` — [`pallets/crowdloan/src/lib.rs#L898`](/code/pallets/crowdloan/src/lib.rs#L896-L923): ```rust #[pallet::call_index(8)] #[pallet::weight(T::WeightInfo::update_cap())] pub fn update_cap( origin: OriginFor, #[pallet::compact] crowdloan_id: CrowdloanId, #[pallet::compact] new_cap: BalanceOf, ) -> DispatchResult { let who = ensure_signed(origin)?; // The cap can only be updated if the crowdloan has not been finalized. let mut crowdloan = Self::ensure_crowdloan_exists(crowdloan_id)?; ensure!(!crowdloan.finalized, Error::::AlreadyFinalized); // Only the creator can update the cap. ensure!(who == crowdloan.creator, Error::::InvalidOrigin); // The new cap should be greater than the actual raised amount. ensure!(new_cap >= crowdloan.raised, Error::::CapTooLow); crowdloan.cap = new_cap; Crowdloans::::insert(crowdloan_id, &crowdloan); Self::deposit_event(Event::::CapUpdated { crowdloan_id, new_cap, }); Ok(()) } ``` Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # update-crowdloan-end (/docs/tx/update-crowdloan-end) Extends or shortens the contribution window. The new end is re-validated against the current block: it must be in the future and fall within the chain's minimum/maximum crowdloan duration (roughly 7 to 60 days) measured from now. Only the creator may change it, and only while the loan has not been finalized. | Signer | Origin | Pallet | Wraps | | --------- | -------------------------------------- | --------- | ---------------------------------------------------------------------- | | `coldkey` | signed account (pallet role may apply) | Crowdloan | [`Crowdloan.update_end`](/code/pallets/crowdloan/src/lib.rs#L861-L887) | ## Parameters [#parameters] | Parameter | Type | Required | Description | | -------------- | ------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------ | | `crowdloan_id` | integer | yes | Identifier of the crowdloan, assigned when it was created. | | `new_end` | integer | yes | New block number at which the crowdloan stops accepting contributions. Must be in the future and roughly 7-60 days from the current block. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx update-crowdloan-end \ --crowdloan-id \ --new-end --dry-run btcli tx update-crowdloan-end \ --crowdloan-id \ --new-end -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.UpdateCrowdloanEnd(crowdloan_id=0, new_end=0) sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("update_crowdloan_end", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `Crowdloan.update_end` — [`pallets/crowdloan/src/lib.rs#L863`](/code/pallets/crowdloan/src/lib.rs#L861-L887): ```rust #[pallet::call_index(7)] #[pallet::weight(T::WeightInfo::update_end())] pub fn update_end( origin: OriginFor, #[pallet::compact] crowdloan_id: CrowdloanId, #[pallet::compact] new_end: BlockNumberFor, ) -> DispatchResult { let who = ensure_signed(origin)?; let now = frame_system::Pallet::::block_number(); let mut crowdloan = Self::ensure_crowdloan_exists(crowdloan_id)?; ensure!(!crowdloan.finalized, Error::::AlreadyFinalized); // Only the creator can update the min contribution. ensure!(who == crowdloan.creator, Error::::InvalidOrigin); Self::ensure_valid_end(now, new_end)?; crowdloan.end = new_end; Crowdloans::::insert(crowdloan_id, &crowdloan); Self::deposit_event(Event::::EndUpdated { crowdloan_id, new_end, }); Ok(()) } ``` Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # update-crowdloan-min-contribution (/docs/tx/update-crowdloan-min-contribution) Changes the smallest contribution the crowdloan will accept from that point on; contributions already made are unaffected. Only the creator may change it, and only while the loan has not been finalized. | Signer | Origin | Pallet | Wraps | | --------- | -------------------------------------- | --------- | ----------------------------------------------------------------------------------- | | `coldkey` | signed account (pallet role may apply) | Crowdloan | [`Crowdloan.update_min_contribution`](/code/pallets/crowdloan/src/lib.rs#L817-L852) | ## Parameters [#parameters] | Parameter | Type | Required | Description | | -------------------------- | ----------------- | -------- | ---------------------------------------------------------- | | `crowdloan_id` | integer | yes | Identifier of the crowdloan, assigned when it was created. | | `new_min_contribution_tao` | number \| `"all"` | yes | New smallest contribution the crowdloan will accept. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx update-crowdloan-min-contribution \ --crowdloan-id \ --new-min-contribution-tao --dry-run btcli tx update-crowdloan-min-contribution \ --crowdloan-id \ --new-min-contribution-tao -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.UpdateCrowdloanMinContribution(crowdloan_id=0, new_min_contribution_tao=1.0) sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("update_crowdloan_min_contribution", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `Crowdloan.update_min_contribution` — [`pallets/crowdloan/src/lib.rs#L819`](/code/pallets/crowdloan/src/lib.rs#L817-L852): ```rust #[pallet::call_index(6)] #[pallet::weight(T::WeightInfo::update_min_contribution())] pub fn update_min_contribution( origin: OriginFor, #[pallet::compact] crowdloan_id: CrowdloanId, #[pallet::compact] new_min_contribution: BalanceOf, ) -> DispatchResult { let who = ensure_signed(origin)?; let mut crowdloan = Self::ensure_crowdloan_exists(crowdloan_id)?; ensure!(!crowdloan.finalized, Error::::AlreadyFinalized); // Only the creator can update the min contribution. ensure!(who == crowdloan.creator, Error::::InvalidOrigin); // The new min contribution should be greater than absolute minimum contribution. ensure!( new_min_contribution >= T::AbsoluteMinimumContribution::get(), Error::::MinimumContributionTooLow ); if let Some(max_contribution) = MaxContributions::::get(crowdloan_id) { ensure!( new_min_contribution <= max_contribution, Error::::MinimumContributionTooHigh ); } crowdloan.min_contribution = new_min_contribution; Crowdloans::::insert(crowdloan_id, &crowdloan); Self::deposit_event(Event::::MinContributionUpdated { crowdloan_id, new_min_contribution, }); Ok(()) } ``` Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # update-symbol (/docs/tx/update-symbol) Cosmetic call for the subnet owner (or root): changes the short ticker shown for the subnet's alpha token in wallets, explorers, and CLIs. Symbols are not arbitrary strings — the chain keeps a fixed catalog of roughly 439 predefined symbols, and anything outside it is rejected with `SymbolDoesNotExist`. A symbol already taken by another subnet is rejected with `SymbolAlreadyInUse`. No economic effect — balances, stake, and emissions are untouched. | Signer | Origin | Pallet | Wraps | | --------- | ------------ | --------------- | ----------------------------------------------------------------------------------------------- | | `coldkey` | subnet owner | SubtensorModule | [`SubtensorModule.update_symbol`](/code/pallets/subtensor/src/macros/dispatches.rs#L1719-L1736) | ## Parameters [#parameters] | Parameter | Type | Required | Description | | --------- | ------- | -------- | ------------------------------------------------------------------------------------------------- | | `netuid` | integer | yes | Subnet whose symbol to change; the signer must be its owner. | | `symbol` | string | yes | New token symbol; must be one of the chain's predefined symbols and not in use by another subnet. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx update-symbol \ --netuid \ --symbol --dry-run btcli tx update-symbol \ --netuid \ --symbol -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.UpdateSymbol(netuid=1, symbol="...") sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("update_symbol", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `SubtensorModule.update_symbol` — [`pallets/subtensor/src/macros/dispatches.rs#L1721`](/code/pallets/subtensor/src/macros/dispatches.rs#L1719-L1736): ```rust #[pallet::call_index(112)] #[pallet::weight(::WeightInfo::update_symbol())] pub fn update_symbol( origin: OriginFor, netuid: NetUid, symbol: Vec, ) -> DispatchResult { Self::ensure_subnet_owner_or_root(origin, netuid)?; ensure!(Self::if_subnet_exist(netuid), Error::::SubnetNotExists); Self::ensure_symbol_exists(&symbol)?; Self::ensure_symbol_available(&symbol)?; TokenSymbol::::insert(netuid, symbol.clone()); Self::deposit_event(Event::SymbolUpdated { netuid, symbol }); Ok(()) } ``` Delegates to [`ensure_subnet_owner_or_root`](/code/pallets/subtensor/src/utils/misc.rs#L12), [`if_subnet_exist`](/code/pallets/subtensor/src/subnets/subnet.rs#L39), [`ensure_symbol_exists`](/code/pallets/subtensor/src/subnets/symbols.rs#L960). Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # withdraw-crowdloan (/docs/tx/withdraw-crowdloan) Contributors can pull their contribution back out of any crowdloan that has not finalized, including while it is still raising. The creator may only withdraw the part of their contribution above the initial deposit; the deposit itself stays locked until `dissolve_crowdloan` returns it. Fails once the crowdloan has finalized or if the signer has nothing left to withdraw. | Signer | Origin | Pallet | Wraps | | --------- | -------------------------------------- | --------- | -------------------------------------------------------------------- | | `coldkey` | signed account (pallet role may apply) | Crowdloan | [`Crowdloan.withdraw`](/code/pallets/crowdloan/src/lib.rs#L546-L592) | ## Parameters [#parameters] | Parameter | Type | Required | Description | | -------------- | ------- | -------- | ---------------------------------------------------------- | | `crowdloan_id` | integer | yes | Identifier of the crowdloan, assigned when it was created. | Address parameters (`--hotkey`, `--coldkey`, `--dest`, ...) accept a raw ss58 address, an address-book or proxy-book name, or a local wallet/hotkey name. ## CLI [#cli] Preview with `--dry-run` (shows fee, effects, and policy result without submitting), then submit: ```bash btcli tx withdraw-crowdloan \ --crowdloan-id --dry-run btcli tx withdraw-crowdloan \ --crowdloan-id -w my_coldkey ``` ## Python [#python] ```python import bittensor as bt from bittensor.wallet import Wallet wallet = Wallet(name="my_coldkey", hotkey="my_hotkey") intent = bt.WithdrawCrowdloan(crowdloan_id=0) sub = bt.Subtensor() plan = sub.plan(intent, wallet) # fee, effects, policy — no submission result = sub.execute(intent, wallet) if not result.success: print(result.error.code, result.error.remediation) ``` (`bt.Subtensor` is also the async client — `async with bt.Subtensor() as client:` — see [The client](/docs/concepts/client).) Or build the intent by op name, as an agent would: ```python result = sub.execute_tool("withdraw_crowdloan", {...}, wallet) ``` ## On-chain implementation [#on-chain-implementation] `Crowdloan.withdraw` — [`pallets/crowdloan/src/lib.rs#L548`](/code/pallets/crowdloan/src/lib.rs#L546-L592): ```rust #[pallet::call_index(2)] #[pallet::weight(T::WeightInfo::withdraw())] pub fn withdraw( origin: OriginFor, #[pallet::compact] crowdloan_id: CrowdloanId, ) -> DispatchResult { let who = ensure_signed(origin)?; let mut crowdloan = Self::ensure_crowdloan_exists(crowdloan_id)?; ensure!(!crowdloan.finalized, Error::::AlreadyFinalized); // Ensure contributor has balance left in the crowdloan account let mut amount = Contributions::::get(crowdloan_id, &who).unwrap_or_else(Zero::zero); ensure!(amount > Zero::zero(), Error::::NoContribution); if who == crowdloan.creator { // Ensure the deposit is kept amount = amount.saturating_sub(crowdloan.deposit); ensure!(amount > Zero::zero(), Error::::DepositCannotBeWithdrawn); Contributions::::insert(crowdloan_id, &who, crowdloan.deposit); } else { Contributions::::remove(crowdloan_id, &who); crowdloan.contributors_count = crowdloan .contributors_count .checked_sub(1) .ok_or(Error::::Underflow)?; } CurrencyOf::::transfer( &crowdloan.funds_account, &who, amount, Preservation::Expendable, )?; // Update the crowdloan raised amount to reflect the withdrawal. crowdloan.raised = crowdloan.raised.saturating_sub(amount); Crowdloans::::insert(crowdloan_id, &crowdloan); Self::deposit_event(Event::::Withdrew { contributor: who, crowdloan_id, amount, }); Ok(()) } ``` Every file is browsable under [/code](/code) exactly as built into the runtime, or as plain text under `/code/raw/` (index: [`/code/index.json`](/code/index.json)). # alpha-price (/docs/query/alpha-price) **Category:** Prices & swaps ## Parameters [#parameters] | Parameter | Type | Description | | --------- | ------- | --------------------------------- | | `netuid` | integer | Subnet whose alpha price to read. | ## CLI [#cli] ```bash btcli query alpha-price --netuid --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.prices.alpha_price(netuid=1) ``` Or dispatch by name, as an agent would: ```python result = sub.read("alpha_price", netuid=1) ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Runtime API [`SwapRuntimeApi.current_alpha_price`](/code/pallets/subtensor/src/staking/order_swap.rs#L99-L101) Every file is browsable under [/code](/code) exactly as built into the runtime. # alpha-prices (/docs/query/alpha-prices) **Category:** Prices & swaps ## Parameters [#parameters] This read takes no parameters. ## CLI [#cli] ```bash btcli query alpha-prices --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.prices.alpha_prices() ``` Or dispatch by name, as an agent would: ```python result = sub.read("alpha_prices") ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Runtime API [`SwapRuntimeApi.current_alpha_price_all`](/code/runtime/src/lib.rs#L2425-L2435) Every file is browsable under [/code](/code) exactly as built into the runtime. # associated-evm-key (/docs/query/associated-evm-key) **Category:** Neurons & registration ## Parameters [#parameters] | Parameter | Type | Description | | --------- | ------- | ----------------------------------- | | `netuid` | integer | Subnet the neuron is registered on. | | `uid` | integer | UID of the neuron on that subnet. | ## CLI [#cli] ```bash btcli query associated-evm-key --netuid --uid --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.neurons.associated_evm_key(netuid=1, uid=1) ``` Or dispatch by name, as an agent would: ```python result = sub.read("associated_evm_key", netuid=1, uid=1) ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Storage [`SubtensorModule.AssociatedEvmAddress`](/code/pallets/subtensor/src/lib.rs#L2745) Every file is browsable under [/code](/code) exactly as built into the runtime. # auto-stake-all (/docs/query/auto-stake-all) One entry per subnet where a destination hotkey is set; subnets with no entry have auto-stake unset. **Category:** Staking ## Parameters [#parameters] | Parameter | Type | Description | | -------------- | ------ | ---------------------------------------------- | | `coldkey_ss58` | string | Coldkey whose auto-stake destinations to list. | ## CLI [#cli] ```bash btcli query auto-stake-all --coldkey --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.staking.auto_stake_all(coldkey_ss58="5F...") ``` Or dispatch by name, as an agent would: ```python result = sub.read("auto_stake_all", coldkey_ss58="5F...") ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Storage [`SubtensorModule.AutoStakeDestination`](/code/pallets/subtensor/src/lib.rs#L1564) Every file is browsable under [/code](/code) exactly as built into the runtime. # auto-stake (/docs/query/auto-stake) **Category:** Staking ## Parameters [#parameters] | Parameter | Type | Description | | -------------- | ------- | --------------------------------------------- | | `coldkey_ss58` | string | Coldkey whose auto-stake destination to read. | | `netuid` | integer | Subnet to query. | ## CLI [#cli] ```bash btcli query auto-stake --coldkey --netuid --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.staking.auto_stake(coldkey_ss58="5F...", netuid=1) ``` Or dispatch by name, as an agent would: ```python result = sub.read("auto_stake", coldkey_ss58="5F...", netuid=1) ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Storage [`SubtensorModule.AutoStakeDestination`](/code/pallets/subtensor/src/lib.rs#L1564) Every file is browsable under [/code](/code) exactly as built into the runtime. # balance (/docs/query/balance) **Category:** Accounts & keys ## Parameters [#parameters] | Parameter | Type | Description | | -------------- | ------ | ----------------------------------- | | `coldkey_ss58` | string | Coldkey whose free balance to read. | ## CLI [#cli] ```bash btcli query balance --coldkey --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.balances.balance(coldkey_ss58="5F...") ``` Or dispatch by name, as an agent would: ```python result = sub.read("balance", coldkey_ss58="5F...") ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Storage `System.Account` — implemented by Substrate's `frame_system`, outside this repository's pallets. Every file is browsable under [/code](/code) exactly as built into the runtime. # balances (/docs/query/balances) **Category:** Accounts & keys ## Parameters [#parameters] | Parameter | Type | Description | | --------------- | ----- | ------------------------------------- | | `coldkey_ss58s` | array | Coldkeys whose free balances to read. | ## CLI [#cli] ```bash btcli query balances --coldkeys --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.balances.balances(coldkey_ss58s=[...]) ``` Or dispatch by name, as an agent would: ```python result = sub.read("balances", coldkey_ss58s=[...]) ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Storage `System.Account` — implemented by Substrate's `frame_system`, outside this repository's pallets. Every file is browsable under [/code](/code) exactly as built into the runtime. # block-info (/docs/query/block-info) Programmatic callers wanting the fully decoded extrinsics and raw header should use `client.block_info()` instead. **Category:** Chain ## Parameters [#parameters] | Parameter | Type | Description | | --------- | ------- | ------------------------- | | `block` | integer | Block number to describe. | ## CLI [#cli] ```bash btcli query block-info --block --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.chain.block_info(block=1) ``` Or dispatch by name, as an agent would: ```python result = sub.read("block_info", block=1) ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. # block-time (/docs/query/block-time) **Category:** Chain ## Parameters [#parameters] This read takes no parameters. ## CLI [#cli] ```bash btcli query block-time --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.chain.block_time() ``` Or dispatch by name, as an agent would: ```python result = sub.read("block_time") ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. # blocks-since-last-step (/docs/query/blocks-since-last-step) **Category:** Epochs & timing ## Parameters [#parameters] | Parameter | Type | Description | | --------- | ------- | ---------------- | | `netuid` | integer | Subnet to query. | ## CLI [#cli] ```bash btcli query blocks-since-last-step --netuid --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.epochs.blocks_since_last_step(netuid=1) ``` Or dispatch by name, as an agent would: ```python result = sub.read("blocks_since_last_step", netuid=1) ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Storage [`SubtensorModule.BlocksSinceLastStep`](/code/pallets/subtensor/src/lib.rs#L2094) Every file is browsable under [/code](/code) exactly as built into the runtime. # blocks-since-last-update (/docs/query/blocks-since-last-update) (mechanism 0), or None. On commit-reveal subnets LastUpdate is bumped at commit time, not when the weights are revealed and applied. None means the subnet has no LastUpdate vector or the uid does not exist. The standard "is this validator still setting weights" health check. **Category:** Epochs & timing ## Parameters [#parameters] | Parameter | Type | Description | | --------- | ------- | --------------------------------- | | `netuid` | integer | Subnet to query. | | `uid` | integer | UID of the neuron on that subnet. | ## CLI [#cli] ```bash btcli query blocks-since-last-update --netuid --uid --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.epochs.blocks_since_last_update(netuid=1, uid=1) ``` Or dispatch by name, as an agent would: ```python result = sub.read("blocks_since_last_update", netuid=1, uid=1) ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Storage [`SubtensorModule.LastUpdate`](/code/pallets/subtensor/src/lib.rs#L2477) Every file is browsable under [/code](/code) exactly as built into the runtime. # blocks-until-next-epoch (/docs/query/blocks-until-next-epoch) Derived from the chain's own `next_epoch_start_block` at one pinned block. The actual fire can shift by a few blocks (per-block epoch cap defers, owner triggers pull earlier) — to act on the epoch itself, use `client.wait_for_epoch()`. **Category:** Epochs & timing ## Parameters [#parameters] | Parameter | Type | Description | | --------- | ------- | ---------------- | | `netuid` | integer | Subnet to query. | ## CLI [#cli] ```bash btcli query blocks-until-next-epoch --netuid --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.epochs.blocks_until_next_epoch(netuid=1) ``` Or dispatch by name, as an agent would: ```python result = sub.read("blocks_until_next_epoch", netuid=1) ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Runtime API [`SubnetInfoRuntimeApi.get_next_epoch_start_block`](/code/pallets/subtensor/src/coinbase/run_coinbase.rs#L1183-L1197) Every file is browsable under [/code](/code) exactly as built into the runtime. # bonds (/docs/query/bonds) Bonds are the slow EMA of a validator's stake-weighted weights that pays its dividends; unlike weights their magnitude is meaningful, so values are scaled by the u16 maximum (1.0 = the largest bond the chain can store) rather than row-normalized. **Category:** Weights & bonds ## Parameters [#parameters] | Parameter | Type | Description | | --------- | ------- | ----------------------------------------------------------------------- | | `netuid` | integer | Subnet whose bond matrix to fetch. | | `mechid` | integer | Mechanism index within the subnet; 0 (the default) on ordinary subnets. | ## CLI [#cli] ```bash btcli query bonds --netuid --mechid --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.weights.bonds(netuid=1, mechid=1) ``` Or dispatch by name, as an agent would: ```python result = sub.read("bonds", netuid=1, mechid=1) ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Storage [`SubtensorModule.Bonds`](/code/pallets/subtensor/src/lib.rs#L2505) Every file is browsable under [/code](/code) exactly as built into the runtime. # burn (/docs/query/burn) **Category:** Subnets ## Parameters [#parameters] | Parameter | Type | Description | | --------- | ------- | ---------------- | | `netuid` | integer | Subnet to query. | ## CLI [#cli] ```bash btcli query burn --netuid --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.subnets.burn(netuid=1) ``` Or dispatch by name, as an agent would: ```python result = sub.read("burn", netuid=1) ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Storage [`SubtensorModule.Burn`](/code/pallets/subtensor/src/lib.rs#L2248) Every file is browsable under [/code](/code) exactly as built into the runtime. # children (/docs/query/children) Proportions are u64-normalized fractions of the parent's stake, where u64::MAX means 100%. **Category:** Delegation ## Parameters [#parameters] | Parameter | Type | Description | | ------------- | ------- | ------------------------------------- | | `hotkey_ss58` | string | Parent hotkey whose children to list. | | `netuid` | integer | Subnet to query. | ## CLI [#cli] ```bash btcli query children --hotkey --netuid --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.delegation.children(hotkey_ss58="5F...", netuid=1) ``` Or dispatch by name, as an agent would: ```python result = sub.read("children", hotkey_ss58="5F...", netuid=1) ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Storage [`SubtensorModule.ChildKeys`](/code/pallets/subtensor/src/lib.rs#L1387) Every file is browsable under [/code](/code) exactly as built into the runtime. # coldkey-lock (/docs/query/coldkey-lock) `locked_alpha` is denominated in the subnet's alpha and rolled forward (decayed) to the current block; `is_perpetual` locks do not decay. **Category:** Locks & conviction ## Parameters [#parameters] | Parameter | Type | Description | | -------------- | ------- | --------------------------- | | `coldkey_ss58` | string | Coldkey whose lock to read. | | `netuid` | integer | Subnet to query. | ## CLI [#cli] ```bash btcli query coldkey-lock --coldkey --netuid --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.locks.coldkey_lock(coldkey_ss58="5F...", netuid=1) ``` Or dispatch by name, as an agent would: ```python result = sub.read("coldkey_lock", coldkey_ss58="5F...", netuid=1) ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Storage [`SubtensorModule.Lock`](/code/pallets/subtensor/src/lib.rs#L1687) Every file is browsable under [/code](/code) exactly as built into the runtime. # coldkey-swap-announcement (/docs/query/coldkey-swap-announcement) This is the `swap-check` status: `ColdkeySwapAnnouncements` stores the block at which the swap becomes executable and the BlakeTwo256 hash committed to. **Category:** Accounts & keys ## Parameters [#parameters] | Parameter | Type | Description | | -------------- | ------ | ------------------------------------------------- | | `coldkey_ss58` | string | Coldkey whose pending swap announcement to check. | ## CLI [#cli] ```bash btcli query coldkey-swap-announcement --coldkey --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.balances.coldkey_swap_announcement(coldkey_ss58="5F...") ``` Or dispatch by name, as an agent would: ```python result = sub.read("coldkey_swap_announcement", coldkey_ss58="5F...") ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Storage [`SubtensorModule.ColdkeySwapAnnouncements`](/code/pallets/subtensor/src/lib.rs#L1599) * Storage [`SubtensorModule.ColdkeySwapDisputes`](/code/pallets/subtensor/src/lib.rs#L1605) Every file is browsable under [/code](/code) exactly as built into the runtime. # collateral-policy (/docs/query/collateral-policy) `lock_share` is the fraction of the registration price locked as collateral instead of burned (0 disables collateral); `drain_ratio` is the alpha released per alpha of hotkey emission earned, snapshot per miner at registration. **Category:** Mining & collateral ## Parameters [#parameters] | Parameter | Type | Description | | --------- | ------- | ---------------- | | `netuid` | integer | Subnet to query. | ## CLI [#cli] ```bash btcli query collateral-policy --netuid --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.collateral.collateral_policy(netuid=1) ``` Or dispatch by name, as an agent would: ```python result = sub.read("collateral_policy", netuid=1) ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. # commit-reveal-enabled (/docs/query/commit-reveal-enabled) **Category:** Subnets ## Parameters [#parameters] | Parameter | Type | Description | | --------- | ------- | ---------------- | | `netuid` | integer | Subnet to query. | ## CLI [#cli] ```bash btcli query commit-reveal-enabled --netuid --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.subnets.commit_reveal_enabled(netuid=1) ``` Or dispatch by name, as an agent would: ```python result = sub.read("commit_reveal_enabled", netuid=1) ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Storage [`SubtensorModule.CommitRevealWeightsEnabled`](/code/pallets/subtensor/src/lib.rs#L2243) Every file is browsable under [/code](/code) exactly as built into the runtime. # commitment (/docs/query/commitment) **Category:** Identity & commitments ## Parameters [#parameters] | Parameter | Type | Description | | ------------- | ------- | --------------------------------------- | | `netuid` | integer | Subnet the commitment was published on. | | `hotkey_ss58` | string | Hotkey that published the commitment. | ## CLI [#cli] ```bash btcli query commitment --netuid --hotkey --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.identity.commitment(netuid=1, hotkey_ss58="5F...") ``` Or dispatch by name, as an agent would: ```python result = sub.read("commitment", netuid=1, hotkey_ss58="5F...") ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Storage [`Commitments.CommitmentOf`](/code/pallets/commitments/src/lib.rs#L137) Every file is browsable under [/code](/code) exactly as built into the runtime. # commitments (/docs/query/commitments) One row per hotkey that has (or had) a commitment — including sealed timelocked payloads waiting on drand (`is_revealed` false, `reveals_at` set) and fully-revealed ones whose live storage entry the chain already dropped. `commitment` is the currently visible content: the plaintext, or the latest chain-decrypted payload; null while still sealed. **Category:** Identity & commitments ## Parameters [#parameters] | Parameter | Type | Description | | --------- | ------- | --------------------------------- | | `netuid` | integer | Subnet whose commitments to list. | ## CLI [#cli] ```bash btcli query commitments --netuid --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.identity.commitments(netuid=1) ``` Or dispatch by name, as an agent would: ```python result = sub.read("commitments", netuid=1) ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. # crowdloan-contributors (/docs/query/crowdloan-contributors) **Category:** Leases & crowdloans ## Parameters [#parameters] | Parameter | Type | Description | | -------------- | ------- | ----------------------------------------------- | | `crowdloan_id` | integer | Id of the crowdloan whose contributors to list. | ## CLI [#cli] ```bash btcli query crowdloan-contributors --crowdloan-id --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.leasing.crowdloan_contributors(crowdloan_id=1) ``` Or dispatch by name, as an agent would: ```python result = sub.read("crowdloan_contributors", crowdloan_id=1) ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Storage [`Crowdloan.Contributions`](/code/pallets/crowdloan/src/lib.rs#L161) Every file is browsable under [/code](/code) exactly as built into the runtime. # crowdloan (/docs/query/crowdloan) Amounts are TAO; `end` is the block number at which contributions close. **Category:** Leases & crowdloans ## Parameters [#parameters] | Parameter | Type | Description | | -------------- | ------- | ------------------------------- | | `crowdloan_id` | integer | Id of the crowdloan to look up. | ## CLI [#cli] ```bash btcli query crowdloan --crowdloan-id --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.leasing.crowdloan(crowdloan_id=1) ``` Or dispatch by name, as an agent would: ```python result = sub.read("crowdloan", crowdloan_id=1) ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Storage [`Crowdloan.Crowdloans`](/code/pallets/crowdloan/src/lib.rs#L152) Every file is browsable under [/code](/code) exactly as built into the runtime. # crowdloans (/docs/query/crowdloans) **Category:** Leases & crowdloans ## Parameters [#parameters] This read takes no parameters. ## CLI [#cli] ```bash btcli query crowdloans --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.leasing.crowdloans() ``` Or dispatch by name, as an agent would: ```python result = sub.read("crowdloans") ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Storage [`Crowdloan.Crowdloans`](/code/pallets/crowdloan/src/lib.rs#L152) Every file is browsable under [/code](/code) exactly as built into the runtime. # delegate-take (/docs/query/delegate-take) `take`, `min`, and `max` are fractions in 0..1; `take_u16` is the raw on-chain u16 value. **Category:** Delegation ## Parameters [#parameters] | Parameter | Type | Description | | ------------- | ------ | ----------------------------------- | | `hotkey_ss58` | string | Delegate hotkey whose take to read. | ## CLI [#cli] ```bash btcli query delegate-take --hotkey --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.delegation.delegate_take(hotkey_ss58="5F...") ``` Or dispatch by name, as an agent would: ```python result = sub.read("delegate_take", hotkey_ss58="5F...") ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Storage [`SubtensorModule.Delegates`](/code/pallets/subtensor/src/lib.rs#L1357) * Storage [`SubtensorModule.MinDelegateTake`](/code/pallets/subtensor/src/lib.rs#L1330) * Storage [`SubtensorModule.MaxDelegateTake`](/code/pallets/subtensor/src/lib.rs#L1326) Every file is browsable under [/code](/code) exactly as built into the runtime. # delegate (/docs/query/delegate) **Category:** Delegation ## Parameters [#parameters] | Parameter | Type | Description | | ------------- | ------ | --------------------------- | | `hotkey_ss58` | string | Delegate hotkey to look up. | ## CLI [#cli] ```bash btcli query delegate --hotkey --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.delegation.delegate(hotkey_ss58="5F...") ``` Or dispatch by name, as an agent would: ```python result = sub.read("delegate", hotkey_ss58="5F...") ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Runtime API [`DelegateInfoRuntimeApi.get_delegate`](/code/pallets/subtensor/src/rpc_info/delegate_info.rs#L135-L143) Every file is browsable under [/code](/code) exactly as built into the runtime. # delegated (/docs/query/delegated) Each `stake` is denominated in the subnet's own currency: subnet alpha, or TAO when netuid is 0. **Category:** Delegation ## Parameters [#parameters] | Parameter | Type | Description | | -------------- | ------ | ---------------------------------- | | `coldkey_ss58` | string | Coldkey whose nominations to list. | ## CLI [#cli] ```bash btcli query delegated --coldkey --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.delegation.delegated(coldkey_ss58="5F...") ``` Or dispatch by name, as an agent would: ```python result = sub.read("delegated", coldkey_ss58="5F...") ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Runtime API [`DelegateInfoRuntimeApi.get_delegated`](/code/pallets/subtensor/src/rpc_info/delegate_info.rs#L159-L187) Every file is browsable under [/code](/code) exactly as built into the runtime. # delegates (/docs/query/delegates) **Category:** Delegation ## Parameters [#parameters] This read takes no parameters. ## CLI [#cli] ```bash btcli query delegates --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.delegation.delegates() ``` Or dispatch by name, as an agent would: ```python result = sub.read("delegates") ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Runtime API [`DelegateInfoRuntimeApi.get_delegates`](/code/pallets/subtensor/src/rpc_info/delegate_info.rs#L147-L155) Every file is browsable under [/code](/code) exactly as built into the runtime. # difficulty (/docs/query/difficulty) **Category:** Hyperparameters ## Parameters [#parameters] | Parameter | Type | Description | | --------- | ------- | ---------------- | | `netuid` | integer | Subnet to query. | ## CLI [#cli] ```bash btcli query difficulty --netuid --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.hyperparameters.difficulty(netuid=1) ``` Or dispatch by name, as an agent would: ```python result = sub.read("difficulty", netuid=1) ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Storage [`SubtensorModule.Difficulty`](/code/pallets/subtensor/src/lib.rs#L2252) Every file is browsable under [/code](/code) exactly as built into the runtime. # epoch-status (/docs/query/epoch-status) Bundles tempo, the last epoch run, the chain-computed next start block, and the remaining blocks/seconds (seconds use the chain's detected block time, so they are correct on fast-blocks chains too). `pending_epoch_at` is the block of an owner-triggered epoch, or None when none is pending. **Category:** Epochs & timing ## Parameters [#parameters] | Parameter | Type | Description | | --------- | ------- | ---------------- | | `netuid` | integer | Subnet to query. | ## CLI [#cli] ```bash btcli query epoch-status --netuid --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.epochs.epoch_status(netuid=1) ``` Or dispatch by name, as an agent would: ```python result = sub.read("epoch_status", netuid=1) ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Storage [`SubtensorModule.Tempo`](/code/pallets/subtensor/src/lib.rs#L1952) * Storage [`SubtensorModule.LastEpochBlock`](/code/pallets/subtensor/src/lib.rs#L1977) * Storage [`SubtensorModule.BlocksSinceLastStep`](/code/pallets/subtensor/src/lib.rs#L2094) * Storage [`SubtensorModule.PendingEpochAt`](/code/pallets/subtensor/src/lib.rs#L1983) * Storage [`SubtensorModule.SubnetEpochIndex`](/code/pallets/subtensor/src/lib.rs#L1989) * Runtime API [`SubnetInfoRuntimeApi.get_next_epoch_start_block`](/code/pallets/subtensor/src/coinbase/run_coinbase.rs#L1183-L1197) Every file is browsable under [/code](/code) exactly as built into the runtime. # existential-deposit (/docs/query/existential-deposit) **Category:** Accounts & keys ## Parameters [#parameters] This read takes no parameters. ## CLI [#cli] ```bash btcli query existential-deposit --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.balances.existential_deposit() ``` Or dispatch by name, as an agent would: ```python result = sub.read("existential_deposit") ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Constant [`Balances.ExistentialDeposit`](/code/runtime/src/lib.rs#L476) Every file is browsable under [/code](/code) exactly as built into the runtime. # hotkey-conviction (/docs/query/hotkey-conviction) Returns `conviction_alpha` denominated in the subnet's alpha, rolled forward to the current block. **Category:** Locks & conviction ## Parameters [#parameters] | Parameter | Type | Description | | ------------- | ------- | -------------------------------- | | `hotkey_ss58` | string | Hotkey whose conviction to read. | | `netuid` | integer | Subnet to query. | ## CLI [#cli] ```bash btcli query hotkey-conviction --hotkey --netuid --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.locks.hotkey_conviction(hotkey_ss58="5F...", netuid=1) ``` Or dispatch by name, as an agent would: ```python result = sub.read("hotkey_conviction", hotkey_ss58="5F...", netuid=1) ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Runtime API [`StakeInfoRuntimeApi.get_hotkey_conviction`](/code/runtime/src/lib.rs#L2338-L2340) Every file is browsable under [/code](/code) exactly as built into the runtime. # hotkey-identities (/docs/query/hotkey-identities) **Category:** Identity & commitments ## Parameters [#parameters] | Parameter | Type | Description | | -------------- | ----- | ---------------------------------- | | `hotkey_ss58s` | array | Hotkeys to resolve identities for. | ## CLI [#cli] ```bash btcli query hotkey-identities --hotkeys --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.identity.hotkey_identities(hotkey_ss58s=[...]) ``` Or dispatch by name, as an agent would: ```python result = sub.read("hotkey_identities", hotkey_ss58s=[...]) ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Storage [`SubtensorModule.Owner`](/code/pallets/subtensor/src/lib.rs#L1347) * Storage [`SubtensorModule.IdentitiesV2`](/code/pallets/subtensor/src/lib.rs#L2567) Every file is browsable under [/code](/code) exactly as built into the runtime. # hotkey-owner (/docs/query/hotkey-owner) **Category:** Neurons & registration ## Parameters [#parameters] | Parameter | Type | Description | | ------------- | ------ | --------------------------------------- | | `hotkey_ss58` | string | Hotkey whose owning coldkey to look up. | ## CLI [#cli] ```bash btcli query hotkey-owner --hotkey --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.neurons.hotkey_owner(hotkey_ss58="5F...") ``` Or dispatch by name, as an agent would: ```python result = sub.read("hotkey_owner", hotkey_ss58="5F...") ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Storage [`SubtensorModule.Owner`](/code/pallets/subtensor/src/lib.rs#L1347) Every file is browsable under [/code](/code) exactly as built into the runtime. # identity (/docs/query/identity) **Category:** Identity & commitments ## Parameters [#parameters] | Parameter | Type | Description | | -------------- | ------ | ------------------------------- | | `coldkey_ss58` | string | Coldkey whose identity to read. | ## CLI [#cli] ```bash btcli query identity --coldkey --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.identity.identity(coldkey_ss58="5F...") ``` Or dispatch by name, as an agent would: ```python result = sub.read("identity", coldkey_ss58="5F...") ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Storage [`SubtensorModule.IdentitiesV2`](/code/pallets/subtensor/src/lib.rs#L2567) Every file is browsable under [/code](/code) exactly as built into the runtime. # immunity-period (/docs/query/immunity-period) **Category:** Hyperparameters ## Parameters [#parameters] | Parameter | Type | Description | | --------- | ------- | ---------------- | | `netuid` | integer | Subnet to query. | ## CLI [#cli] ```bash btcli query immunity-period --netuid --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.hyperparameters.immunity_period(netuid=1) ``` Or dispatch by name, as an agent would: ```python result = sub.read("immunity_period", netuid=1) ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Storage [`SubtensorModule.ImmunityPeriod`](/code/pallets/subtensor/src/lib.rs#L2162) Every file is browsable under [/code](/code) exactly as built into the runtime. # Queries (/docs/query) Reads are free, unsigned, and safe to call at any time. Each CLI command is `btcli query ` (add `--json` for machine-readable output). In Python every read is a typed method on its category's namespace — `sub.subnets.subnet_registration_cost()`, `sub.delegation.delegates()` on `sub = bt.Subtensor()` (awaited on the async client) — with autocomplete and signature help; `sub.read("", ...)` dispatches the same read by name. The machine-readable catalog is at [`/catalog/reads.json`](/catalog/reads.json) or via `bt.reads.list_reads()`. ## Accounts & keys [#accounts--keys] | Query | Summary | | -------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | | [`balance`](/docs/query/balance) | Free TAO balance of a coldkey address. | | [`balances`](/docs/query/balances) | Free TAO balance for several coldkey addresses in one batched request. | | [`coldkey-swap-announcement`](/docs/query/coldkey-swap-announcement) | A coldkey's pending swap announcement (execute block, new-key hash, disputed), or None. | | [`existential-deposit`](/docs/query/existential-deposit) | Minimum balance an account must keep to stay alive. | | [`multisig`](/docs/query/multisig) | A pending multisig operation (opening timepoint, approvals, depositor), or None. | | [`proxies`](/docs/query/proxies) | Proxy delegations of an account: who may sign on its behalf, and the reserved deposit. | ## Chain [#chain] | Query | Summary | | -------------------------------------------------------- | ------------------------------------------------------------------------------------ | | [`block-info`](/docs/query/block-info) | A block's hash, timestamp, and extrinsics (as module.function summaries). | | [`block-time`](/docs/query/block-time) | Seconds per block, detected from the chain (12.0 mainnet, 0.25 fast-blocks). | | [`is-fast-blocks`](/docs/query/is-fast-blocks) | Whether the chain runs fast blocks (0.25s slots, local/e2e testing mode). | | [`mev-shield-next-key`](/docs/query/mev-shield-next-key) | The MEV Shield ML-KEM-768 public key (0x-hex) used to encrypt shielded txs, or None. | | [`timestamp`](/docs/query/timestamp) | Current chain time (from the Timestamp pallet) and the block it was read at. | | [`tx-rate-limit`](/docs/query/tx-rate-limit) | Global transaction rate limit in blocks. | ## Delegation [#delegation] | Query | Summary | | -------------------------------------------------- | ------------------------------------------------------------------------------- | | [`children`](/docs/query/children) | Child hotkeys of a parent on a subnet, as (proportion, child\_ss58) pairs. | | [`delegate`](/docs/query/delegate) | Delegate info for one hotkey, or None if it is not a delegate. | | [`delegate-take`](/docs/query/delegate-take) | A hotkey's delegate take (emission fraction it keeps) with the allowed min/max. | | [`delegated`](/docs/query/delegated) | Every nomination a coldkey holds: (delegate, netuid, stake) per position. | | [`delegates`](/docs/query/delegates) | Every delegate hotkey on the network, with take and registrations. | | [`is-delegate`](/docs/query/is-delegate) | Whether a hotkey is a delegate. | | [`parents`](/docs/query/parents) | Parent hotkeys of a child on a subnet, as (proportion, parent\_ss58) pairs. | | [`pending-children`](/docs/query/pending-children) | Proposed child hotkeys of a parent still in cooldown, and when they apply. | ## Epochs & timing [#epochs--timing] | Query | Summary | | ------------------------------------------------------------------ | ---------------------------------------------------------------------------------- | | [`blocks-since-last-step`](/docs/query/blocks-since-last-step) | Blocks since the subnet's last epoch step ran. | | [`blocks-since-last-update`](/docs/query/blocks-since-last-update) | Blocks since a neuron last set or committed weights on a subnet | | [`blocks-until-next-epoch`](/docs/query/blocks-until-next-epoch) | Blocks until the subnet's next epoch fires, or None if tempo is 0. | | [`epoch-status`](/docs/query/epoch-status) | Where a subnet is in its epoch cycle, in one block-pinned read. | | [`next-epoch-start-block`](/docs/query/next-epoch-start-block) | Block at which the subnet's next epoch is expected to fire, or None if tempo is 0. | ## Hyperparameters [#hyperparameters] | Query | Summary | | ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | [`difficulty`](/docs/query/difficulty) | Current PoW registration difficulty for a subnet. | | [`immunity-period`](/docs/query/immunity-period) | Blocks a newly registered neuron is immune from deregistration. | | [`max-weight-limit`](/docs/query/max-weight-limit) | Normalized-fraction cap on any single weight: after the chain normalizes the submitted vector, no weight may exceed max\_weight\_limit/65535 of the total. Not a raw u16 ceiling per weight. | | [`min-allowed-weights`](/docs/query/min-allowed-weights) | Minimum number of weights a validator must set on a subnet. A pure self-weight (a single entry pointing at the caller's own uid) bypasses the minimum, and the subnet owner bypasses validator-permit rules. | | [`reveal-period`](/docs/query/reveal-period) | Commit-reveal reveal window, in epochs, for a subnet. | | [`subnet-emission-enabled`](/docs/query/subnet-emission-enabled) | Root-controlled pool-side TAO emission flag for a subnet (1 = enabled). When 0, the subnet earns no TAO emission share even while subnet\_is\_active is true; only root can flip it (see the set\_subnet\_emission\_enabled intent). | | [`weights-rate-limit`](/docs/query/weights-rate-limit) | Blocks a hotkey must wait between weight sets on a subnet. | ## Identity & commitments [#identity--commitments] | Query | Summary | | -------------------------------------------------------- | ------------------------------------------------------------------------------------------- | | [`commitment`](/docs/query/commitment) | The commitment a hotkey has published on a subnet, or None. | | [`commitments`](/docs/query/commitments) | Every commitment on a subnet, newest first: hotkey, uid, content, block, age, reveal state. | | [`hotkey-identities`](/docs/query/hotkey-identities) | On-chain identities for hotkeys (via each hotkey's owner coldkey), keyed by hotkey. | | [`identity`](/docs/query/identity) | The on-chain identity of a coldkey (name, links, description), or None. | | [`revealed-commitment`](/docs/query/revealed-commitment) | The revealed (timelock-decrypted) commitment for a hotkey on a subnet, or None. | ## Leases & crowdloans [#leases--crowdloans] | Query | Summary | | -------------------------------------------------------------- | -------------------------------------------------------------------------------------- | | [`crowdloan`](/docs/query/crowdloan) | A crowdloan's state (creator, deposit, raised, cap, end, target/call), or None. | | [`crowdloan-contributors`](/docs/query/crowdloan-contributors) | Contributors and amounts for a crowdloan, with amounts in TAO. | | [`crowdloans`](/docs/query/crowdloans) | All crowdloans on chain (id and summary fields). | | [`lease`](/docs/query/lease) | A subnet lease by id (beneficiary, emissions share, end block, netuid, cost), or None. | | [`leases`](/docs/query/leases) | Every subnet lease on the network. | ## Locks & conviction [#locks--conviction] | Query | Summary | | ------------------------------------------------------------ | ------------------------------------------------------------------- | | [`coldkey-lock`](/docs/query/coldkey-lock) | Lock state for a coldkey on a subnet, or None if no lock exists. | | [`hotkey-conviction`](/docs/query/hotkey-conviction) | Conviction metrics for a hotkey on a subnet. | | [`locks-for-coldkey`](/docs/query/locks-for-coldkey) | Every lock a coldkey holds (one per subnet), rolled forward to now. | | [`most-convicted-hotkey`](/docs/query/most-convicted-hotkey) | Hotkey with the highest conviction on a subnet, if any. | | [`subnet-convictions`](/docs/query/subnet-convictions) | Every hotkey with locked stake on a subnet, rolled forward to now. | ## Mining & collateral [#mining--collateral] | Query | Summary | | ---------------------------------------------------- | ------------------------------------------------------------------------ | | [`collateral-policy`](/docs/query/collateral-policy) | A subnet's collateral configuration. | | [`miner-collateral`](/docs/query/miner-collateral) | A `(hotkey, coldkey)` stake position's standing collateral, or None. | | [`subnet-collateral`](/docs/query/subnet-collateral) | Every `(hotkey, coldkey)` position with standing collateral on a subnet. | ## Neurons & registration [#neurons--registration] | Query | Summary | | ------------------------------------------------------ | ------------------------------------------------------------------------------------ | | [`associated-evm-key`](/docs/query/associated-evm-key) | The EVM address associated with a neuron (by netuid + uid) and the block it was set. | | [`hotkey-owner`](/docs/query/hotkey-owner) | The coldkey that owns a hotkey, or None if unowned. | | [`netuids-for-hotkey`](/docs/query/netuids-for-hotkey) | Every subnet a hotkey is registered on, as a sorted list of netuids. | | [`neurons`](/docs/query/neurons) | Every neuron on a subnet in ONE runtime-API call (the metagraph fast path). | | [`owned-hotkeys`](/docs/query/owned-hotkeys) | Every hotkey owned by a coldkey (the reverse of `hotkey_owner`). | | [`uid`](/docs/query/uid) | UID of a hotkey on a subnet, or None if not registered there. | ## Prices & swaps [#prices--swaps] | Query | Summary | | -------------------------------------------- | ---------------------------------------------------------------------------------- | | [`alpha-price`](/docs/query/alpha-price) | Current spot alpha price for a subnet, as TAO per alpha. | | [`alpha-prices`](/docs/query/alpha-prices) | Spot alpha price for every subnet, as TAO per alpha keyed by netuid. | | [`quote-stake`](/docs/query/quote-stake) | Simulate staking `amount_tao` TAO into a subnet: alpha out, fee, and slippage. | | [`quote-unstake`](/docs/query/quote-unstake) | Simulate unstaking `amount_alpha` alpha from a subnet: TAO out, fee, and slippage. | ## Staking [#staking] | Query | Summary | | ------------------------------------------------------------------ | -------------------------------------------------------------------------------------- | | [`auto-stake`](/docs/query/auto-stake) | The hotkey a coldkey auto-stakes its rewards to on a subnet, or None if unset. | | [`auto-stake-all`](/docs/query/auto-stake-all) | Every auto-stake destination configured for a coldkey. | | [`root-claim-type`](/docs/query/root-claim-type) | How a coldkey claims root alpha emission: Swap, Keep, or KeepSubnets(+subnets). | | [`stake`](/docs/query/stake) | Alpha staked by a coldkey to a hotkey on a subnet (TAO when netuid is 0). | | [`stake-for-coldkey`](/docs/query/stake-for-coldkey) | Every stake position held by a coldkey, across all hotkeys and subnets. | | [`stake-for-coldkeys`](/docs/query/stake-for-coldkeys) | Every stake position for several coldkeys at once, in one runtime call. | | [`stake-value-for-coldkey`](/docs/query/stake-value-for-coldkey) | A coldkey's stake marked to TAO at spot prices (excludes slippage/fees), block-pinned. | | [`stake-value-for-coldkeys`](/docs/query/stake-value-for-coldkeys) | Spot-price stake valuation for several coldkeys at once, at one block. | | [`staking-hotkeys`](/docs/query/staking-hotkeys) | Every hotkey a coldkey has stake with, owned or nominated. | ## Subnets [#subnets] | Query | Summary | | ------------------------------------------------------------------ | ----------------------------------------------------------------------------------- | | [`burn`](/docs/query/burn) | Current burn (recycle) cost to register on a subnet. | | [`commit-reveal-enabled`](/docs/query/commit-reveal-enabled) | Whether commit-reveal weights are enabled on a subnet. | | [`mechanism-count`](/docs/query/mechanism-count) | Current mechanism count configured for a subnet. | | [`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, 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. | | [`subnet-start-schedule`](/docs/query/subnet-start-schedule) | When a registered subnet can call `start_call`. | | [`subnets`](/docs/query/subnets) | Info for every subnet, fetched in four batched map queries rather than | | [`token-symbols`](/docs/query/token-symbols) | Chain-registered token symbol of every subnet, keyed by netuid. | ## Weights & bonds [#weights--bonds] | Query | Summary | | -------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | | [`bonds`](/docs/query/bonds) | Validator bond rows as `{validator_uid: {miner_uid: bond}}`, scaled to 0..1. | | [`timelocked-weight-commits`](/docs/query/timelocked-weight-commits) | Pending (still-encrypted) commit-reveal weight commits, grouped by epoch. | | [`weights`](/docs/query/weights) | Validator weight rows as `{validator_uid: {miner_uid: fraction}}`, each row summing to 1. | # is-delegate (/docs/query/is-delegate) **Category:** Delegation ## Parameters [#parameters] | Parameter | Type | Description | | ------------- | ------ | ------------------------------------ | | `hotkey_ss58` | string | Hotkey to check for delegate status. | ## CLI [#cli] ```bash btcli query is-delegate --hotkey --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.delegation.is_delegate(hotkey_ss58="5F...") ``` Or dispatch by name, as an agent would: ```python result = sub.read("is_delegate", hotkey_ss58="5F...") ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Runtime API [`DelegateInfoRuntimeApi.get_delegate`](/code/pallets/subtensor/src/rpc_info/delegate_info.rs#L135-L143) Every file is browsable under [/code](/code) exactly as built into the runtime. # is-fast-blocks (/docs/query/is-fast-blocks) **Category:** Chain ## Parameters [#parameters] This read takes no parameters. ## CLI [#cli] ```bash btcli query is-fast-blocks --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.chain.is_fast_blocks() ``` Or dispatch by name, as an agent would: ```python result = sub.read("is_fast_blocks") ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. # lease (/docs/query/lease) `emissions_share` is a percentage from 0 to 100; `end_block` is None for a perpetual lease. **Category:** Leases & crowdloans ## Parameters [#parameters] | Parameter | Type | Description | | ---------- | ------- | ---------------------------------- | | `lease_id` | integer | Id of the subnet lease to look up. | ## CLI [#cli] ```bash btcli query lease --lease-id --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.leasing.lease(lease_id=1) ``` Or dispatch by name, as an agent would: ```python result = sub.read("lease", lease_id=1) ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Storage [`SubtensorModule.SubnetLeases`](/code/pallets/subtensor/src/lib.rs#L2763) Every file is browsable under [/code](/code) exactly as built into the runtime. # leases (/docs/query/leases) **Category:** Leases & crowdloans ## Parameters [#parameters] This read takes no parameters. ## CLI [#cli] ```bash btcli query leases --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.leasing.leases() ``` Or dispatch by name, as an agent would: ```python result = sub.read("leases") ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Storage [`SubtensorModule.SubnetLeases`](/code/pallets/subtensor/src/lib.rs#L2763) Every file is browsable under [/code](/code) exactly as built into the runtime. # locks-for-coldkey (/docs/query/locks-for-coldkey) **Category:** Locks & conviction ## Parameters [#parameters] | Parameter | Type | Description | | -------------- | ------ | ---------------------------- | | `coldkey_ss58` | string | Coldkey whose locks to list. | ## CLI [#cli] ```bash btcli query locks-for-coldkey --coldkey --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.locks.locks_for_coldkey(coldkey_ss58="5F...") ``` Or dispatch by name, as an agent would: ```python result = sub.read("locks_for_coldkey", coldkey_ss58="5F...") ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Storage [`SubtensorModule.Lock`](/code/pallets/subtensor/src/lib.rs#L1687) Every file is browsable under [/code](/code) exactly as built into the runtime. # max-weight-limit (/docs/query/max-weight-limit) **Category:** Hyperparameters ## Parameters [#parameters] | Parameter | Type | Description | | --------- | ------- | ---------------- | | `netuid` | integer | Subnet to query. | ## CLI [#cli] ```bash btcli query max-weight-limit --netuid --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.hyperparameters.max_weight_limit(netuid=1) ``` Or dispatch by name, as an agent would: ```python result = sub.read("max_weight_limit", netuid=1) ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Storage [`SubtensorModule.MaxWeightsLimit`](/code/pallets/subtensor/src/lib.rs#L2178) Every file is browsable under [/code](/code) exactly as built into the runtime. # mechanism-count (/docs/query/mechanism-count) **Category:** Subnets ## Parameters [#parameters] | Parameter | Type | Description | | --------- | ------- | ---------------- | | `netuid` | integer | Subnet to query. | ## CLI [#cli] ```bash btcli query mechanism-count --netuid --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.subnets.mechanism_count(netuid=1) ``` Or dispatch by name, as an agent would: ```python result = sub.read("mechanism_count", netuid=1) ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Storage [`SubtensorModule.MechanismCountCurrent`](/code/pallets/subtensor/src/lib.rs#L2837) Every file is browsable under [/code](/code) exactly as built into the runtime. # mechanism-emission-split (/docs/query/mechanism-emission-split) Returns the raw on-chain per-mechanism share values, in mechanism order; an empty list means no explicit split is set. **Category:** Subnets ## Parameters [#parameters] | Parameter | Type | Description | | --------- | ------- | ---------------- | | `netuid` | integer | Subnet to query. | ## CLI [#cli] ```bash btcli query mechanism-emission-split --netuid --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.subnets.mechanism_emission_split(netuid=1) ``` Or dispatch by name, as an agent would: ```python result = sub.read("mechanism_emission_split", netuid=1) ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Storage [`SubtensorModule.MechanismEmissionSplit`](/code/pallets/subtensor/src/lib.rs#L2842) Every file is browsable under [/code](/code) exactly as built into the runtime. # metagraph (/docs/query/metagraph) `name` and `symbol` are decoded to text (the wire carries them as compact-u16 vectors of utf-8 bytes, unlike every other text field). On runtimes with miner collateral (spec >= 435), each neuron also carries `collateral_locked`, `collateral_min`, and `collateral_earned` (alpha balances; `None` on older nodes). **Category:** Subnets ## Parameters [#parameters] | Parameter | Type | Description | | --------- | ------- | -------------------------------- | | `netuid` | integer | Subnet whose metagraph to fetch. | ## CLI [#cli] ```bash btcli query metagraph --netuid --json ``` ## Python [#python] ```python import bittensor as bt sub = bt.Subtensor() result = sub.read("metagraph", netuid=1) ``` ## On-chain implementation [#on-chain-implementation] * Runtime API [`SubnetInfoRuntimeApi.get_metagraph`](/code/pallets/subtensor/src/rpc_info/metagraph.rs#L653-L828) Every file is browsable under [/code](/code) exactly as built into the runtime. # mev-shield-next-key (/docs/query/mev-shield-next-key) **Category:** Chain ## Parameters [#parameters] This read takes no parameters. ## CLI [#cli] ```bash btcli query mev-shield-next-key --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.chain.mev_shield_next_key() ``` Or dispatch by name, as an agent would: ```python result = sub.read("mev_shield_next_key") ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Storage [`MevShield.NextKey`](/code/pallets/shield/src/lib.rs#L115) Every file is browsable under [/code](/code) exactly as built into the runtime. # min-allowed-weights (/docs/query/min-allowed-weights) **Category:** Hyperparameters ## Parameters [#parameters] | Parameter | Type | Description | | --------- | ------- | ---------------- | | `netuid` | integer | Subnet to query. | ## CLI [#cli] ```bash btcli query min-allowed-weights --netuid --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.hyperparameters.min_allowed_weights(netuid=1) ``` Or dispatch by name, as an agent would: ```python result = sub.read("min_allowed_weights", netuid=1) ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Storage [`SubtensorModule.MinAllowedWeights`](/code/pallets/subtensor/src/lib.rs#L2188) Every file is browsable under [/code](/code) exactly as built into the runtime. # miner-collateral (/docs/query/miner-collateral) Collateral is keyed by hotkey + coldkey so nominators on the same hotkey are never charged for the owner's bond. When `coldkey_ss58` is omitted, the hotkey owner is used. `locked_alpha` is non-withdrawable stake released through earned emission (miner incentive and validator dividends) at `drain_ratio` alpha per alpha earned; `min_locked_alpha` is the miner-set floor the lock self-maintains around (the drain stops at it and emission fills any shortfall); `earned_alpha` is lifetime emission since the collateral entry existed. Derived: `headroom_alpha` (draining portion above the floor), `shortfall_alpha` (capture in progress below the floor), and `releasable_work_alpha` (emission still needed to release the headroom). The lock survives deregistration and is credited on re-registration. **Category:** Mining & collateral ## Parameters [#parameters] | Parameter | Type | Description | | -------------- | ------- | ------------------------------------------------------------------- | | `netuid` | integer | Subnet to query. | | `hotkey_ss58` | string | Miner hotkey of the bonded stake position. | | `coldkey_ss58` | string | Coldkey of the bonded stake position. Defaults to the hotkey owner. | ## CLI [#cli] ```bash btcli query miner-collateral --netuid --hotkey --coldkey --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.collateral.miner_collateral(netuid=1, hotkey_ss58="5F...", coldkey_ss58="5F...") ``` Or dispatch by name, as an agent would: ```python result = sub.read("miner_collateral", netuid=1, hotkey_ss58="5F...", coldkey_ss58="5F...") ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Storage [`SubtensorModule.Owner`](/code/pallets/subtensor/src/lib.rs#L1347) Every file is browsable under [/code](/code) exactly as built into the runtime. # most-convicted-hotkey (/docs/query/most-convicted-hotkey) **Category:** Locks & conviction ## Parameters [#parameters] | Parameter | Type | Description | | --------- | ------- | ---------------- | | `netuid` | integer | Subnet to query. | ## CLI [#cli] ```bash btcli query most-convicted-hotkey --netuid --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.locks.most_convicted_hotkey(netuid=1) ``` Or dispatch by name, as an agent would: ```python result = sub.read("most_convicted_hotkey", netuid=1) ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Runtime API [`StakeInfoRuntimeApi.get_most_convicted_hotkey_on_subnet`](/code/runtime/src/lib.rs#L2342-L2344) Every file is browsable under [/code](/code) exactly as built into the runtime. # multisig (/docs/query/multisig) **Category:** Accounts & keys ## Parameters [#parameters] | Parameter | Type | Description | | -------------- | ------ | ------------------------------------------------------ | | `account_ss58` | string | Multisig account the pending operation belongs to. | | `call_hash` | string | Hash of the multisig call, as 0x-prefixed or bare hex. | ## CLI [#cli] ```bash btcli query multisig --account --call-hash --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.balances.multisig(account_ss58="5F...", call_hash="...") ``` Or dispatch by name, as an agent would: ```python result = sub.read("multisig", account_ss58="5F...", call_hash="...") ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Storage `Multisig.Multisigs` — implemented by Substrate's `pallet_multisig`, outside this repository's pallets. Every file is browsable under [/code](/code) exactly as built into the runtime. # netuids-for-hotkey (/docs/query/netuids-for-hotkey) **Category:** Neurons & registration ## Parameters [#parameters] | Parameter | Type | Description | | ------------- | ------ | ------------------------------------------ | | `hotkey_ss58` | string | Hotkey whose subnet registrations to list. | ## CLI [#cli] ```bash btcli query netuids-for-hotkey --hotkey --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.neurons.netuids_for_hotkey(hotkey_ss58="5F...") ``` Or dispatch by name, as an agent would: ```python result = sub.read("netuids_for_hotkey", hotkey_ss58="5F...") ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Storage [`SubtensorModule.IsNetworkMember`](/code/pallets/subtensor/src/lib.rs#L2020) Every file is browsable under [/code](/code) exactly as built into the runtime. # neurons (/docs/query/neurons) `lite=true` (the default) omits per-neuron weights/bonds, which is far smaller and what almost every caller wants. **Category:** Neurons & registration ## Parameters [#parameters] | Parameter | Type | Description | | --------- | ------- | --------------------------------------------------------------------- | | `netuid` | integer | Subnet whose neurons to list. | | `lite` | boolean | Omit per-neuron weights/bonds (much smaller; what most callers want). | ## CLI [#cli] ```bash btcli query neurons --netuid --lite/--no-lite --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.neurons.neurons(netuid=1, lite=True) ``` Or dispatch by name, as an agent would: ```python result = sub.read("neurons", netuid=1, lite=True) ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Runtime API [`NeuronInfoRuntimeApi.get_neurons_lite`](/code/pallets/subtensor/src/rpc_info/neuron_info.rs#L209-L225) * Runtime API [`NeuronInfoRuntimeApi.get_neurons`](/code/pallets/subtensor/src/rpc_info/neuron_info.rs#L58-L74) Every file is browsable under [/code](/code) exactly as built into the runtime. # next-epoch-start-block (/docs/query/next-epoch-start-block) Chain-computed: `LastEpochBlock + tempo`, pulled earlier by any pending owner-triggered epoch. This is the source of truth — the epoch schedule is stateful, so the legacy client-side modular formula is wrong on subnets whose tempo changed or whose owner triggered an epoch. **Category:** Epochs & timing ## Parameters [#parameters] | Parameter | Type | Description | | --------- | ------- | ---------------- | | `netuid` | integer | Subnet to query. | ## CLI [#cli] ```bash btcli query next-epoch-start-block --netuid --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.epochs.next_epoch_start_block(netuid=1) ``` Or dispatch by name, as an agent would: ```python result = sub.read("next_epoch_start_block", netuid=1) ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Runtime API [`SubnetInfoRuntimeApi.get_next_epoch_start_block`](/code/pallets/subtensor/src/coinbase/run_coinbase.rs#L1183-L1197) Every file is browsable under [/code](/code) exactly as built into the runtime. # owned-hotkeys (/docs/query/owned-hotkeys) **Category:** Neurons & registration ## Parameters [#parameters] | Parameter | Type | Description | | -------------- | ------ | ------------------------------ | | `coldkey_ss58` | string | Coldkey whose hotkeys to list. | ## CLI [#cli] ```bash btcli query owned-hotkeys --coldkey --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.neurons.owned_hotkeys(coldkey_ss58="5F...") ``` Or dispatch by name, as an agent would: ```python result = sub.read("owned_hotkeys", coldkey_ss58="5F...") ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Storage [`SubtensorModule.OwnedHotkeys`](/code/pallets/subtensor/src/lib.rs#L1559) Every file is browsable under [/code](/code) exactly as built into the runtime. # parents (/docs/query/parents) Proportions are u64-normalized fractions of the parent's stake, where u64::MAX means 100%. **Category:** Delegation ## Parameters [#parameters] | Parameter | Type | Description | | ------------- | ------- | ----------------------------------- | | `hotkey_ss58` | string | Child hotkey whose parents to list. | | `netuid` | integer | Subnet to query. | ## CLI [#cli] ```bash btcli query parents --hotkey --netuid --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.delegation.parents(hotkey_ss58="5F...", netuid=1) ``` Or dispatch by name, as an agent would: ```python result = sub.read("parents", hotkey_ss58="5F...", netuid=1) ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Storage [`SubtensorModule.ParentKeys`](/code/pallets/subtensor/src/lib.rs#L1400) Every file is browsable under [/code](/code) exactly as built into the runtime. # pending-children (/docs/query/pending-children) `set_children` normally does not take effect immediately: the proposal is parked here until `cooldown_block`, then promoted to the finalized set that the `children` read returns. On subnets whose subtoken is not yet enabled the cooldown is skipped and children apply immediately, so nothing lingers here. `children` is (proportion, child\_ss58) pairs with u64-normalized proportions, matching the `children` read. **Category:** Delegation ## Parameters [#parameters] | Parameter | Type | Description | | ------------- | ------- | --------------------------------------------- | | `hotkey_ss58` | string | Parent hotkey whose pending children to list. | | `netuid` | integer | Subnet to query. | ## CLI [#cli] ```bash btcli query pending-children --hotkey --netuid --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.delegation.pending_children(hotkey_ss58="5F...", netuid=1) ``` Or dispatch by name, as an agent would: ```python result = sub.read("pending_children", hotkey_ss58="5F...", netuid=1) ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Storage [`SubtensorModule.PendingChildKeys`](/code/pallets/subtensor/src/lib.rs#L1374) Every file is browsable under [/code](/code) exactly as built into the runtime. # proxies (/docs/query/proxies) **Category:** Accounts & keys ## Parameters [#parameters] | Parameter | Type | Description | | -------------- | ------ | ---------------------------------------- | | `coldkey_ss58` | string | Account whose proxy delegations to list. | ## CLI [#cli] ```bash btcli query proxies --coldkey --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.balances.proxies(coldkey_ss58="5F...") ``` Or dispatch by name, as an agent would: ```python result = sub.read("proxies", coldkey_ss58="5F...") ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Storage [`Proxy.Proxies`](/code/pallets/proxy/src/lib.rs#L811) Every file is browsable under [/code](/code) exactly as built into the runtime. # quote-stake (/docs/query/quote-stake) Use this to compute a safe `limit_price_rao` for the `add_stake_limit` intent. **Category:** Prices & swaps ## Parameters [#parameters] | Parameter | Type | Description | | ------------ | ------- | -------------------------------- | | `netuid` | integer | Subnet to simulate staking into. | | `amount_tao` | number | TAO amount to simulate staking. | ## CLI [#cli] ```bash btcli query quote-stake --netuid --amount-tao --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.prices.quote_stake(netuid=1, amount_tao=1.0) ``` Or dispatch by name, as an agent would: ```python result = sub.read("quote_stake", netuid=1, amount_tao=1.0) ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Runtime API [`SwapRuntimeApi.sim_swap_tao_for_alpha`](/code/runtime/src/lib.rs#L2437-L2465) Every file is browsable under [/code](/code) exactly as built into the runtime. # quote-unstake (/docs/query/quote-unstake) **Category:** Prices & swaps ## Parameters [#parameters] | Parameter | Type | Description | | -------------- | ------- | ----------------------------------- | | `netuid` | integer | Subnet to simulate unstaking from. | | `amount_alpha` | number | Alpha amount to simulate unstaking. | ## CLI [#cli] ```bash btcli query quote-unstake --netuid --amount-alpha --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.prices.quote_unstake(netuid=1, amount_alpha=1.0) ``` Or dispatch by name, as an agent would: ```python result = sub.read("quote_unstake", netuid=1, amount_alpha=1.0) ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Runtime API [`SwapRuntimeApi.sim_swap_alpha_for_tao`](/code/runtime/src/lib.rs#L2467-L2495) Every file is browsable under [/code](/code) exactly as built into the runtime. # reveal-period (/docs/query/reveal-period) **Category:** Hyperparameters ## Parameters [#parameters] | Parameter | Type | Description | | --------- | ------- | ---------------- | | `netuid` | integer | Subnet to query. | ## CLI [#cli] ```bash btcli query reveal-period --netuid --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.hyperparameters.reveal_period(netuid=1) ``` Or dispatch by name, as an agent would: ```python result = sub.read("reveal_period", netuid=1) ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Storage [`SubtensorModule.RevealPeriodEpochs`](/code/pallets/subtensor/src/lib.rs#L2680) Every file is browsable under [/code](/code) exactly as built into the runtime. # revealed-commitment (/docs/query/revealed-commitment) **Category:** Identity & commitments ## Parameters [#parameters] | Parameter | Type | Description | | ------------- | ------- | --------------------------------------- | | `netuid` | integer | Subnet the commitment was published on. | | `hotkey_ss58` | string | Hotkey that published the commitment. | ## CLI [#cli] ```bash btcli query revealed-commitment --netuid --hotkey --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.identity.revealed_commitment(netuid=1, hotkey_ss58="5F...") ``` Or dispatch by name, as an agent would: ```python result = sub.read("revealed_commitment", netuid=1, hotkey_ss58="5F...") ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Storage [`Commitments.RevealedCommitments`](/code/pallets/commitments/src/lib.rs#L173) Every file is browsable under [/code](/code) exactly as built into the runtime. # root-claim-type (/docs/query/root-claim-type) **Category:** Staking ## Parameters [#parameters] | Parameter | Type | Description | | -------------- | ------ | ----------------------------------------- | | `coldkey_ss58` | string | Coldkey whose root-claim setting to read. | ## CLI [#cli] ```bash btcli query root-claim-type --coldkey --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.staking.root_claim_type(coldkey_ss58="5F...") ``` Or dispatch by name, as an agent would: ```python result = sub.read("root_claim_type", coldkey_ss58="5F...") ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Storage [`SubtensorModule.RootClaimType`](/code/pallets/subtensor/src/lib.rs#L2722) Every file is browsable under [/code](/code) exactly as built into the runtime. # stake-for-coldkey (/docs/query/stake-for-coldkey) Each position's `stake` is denominated in the subnet's own currency: subnet alpha, or TAO when netuid is 0. Use `stake_value_for_coldkey` to mark alpha positions to TAO. **Category:** Staking ## Parameters [#parameters] | Parameter | Type | Description | | -------------- | ------ | -------------------------------------- | | `coldkey_ss58` | string | Coldkey whose stake positions to list. | ## CLI [#cli] ```bash btcli query stake-for-coldkey --coldkey --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.staking.stake_for_coldkey(coldkey_ss58="5F...") ``` Or dispatch by name, as an agent would: ```python result = sub.read("stake_for_coldkey", coldkey_ss58="5F...") ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Runtime API [`StakeInfoRuntimeApi.get_stake_info_for_coldkey`](/code/pallets/subtensor/src/rpc_info/stake_info.rs#L102-L116) Every file is browsable under [/code](/code) exactly as built into the runtime. # stake-for-coldkeys (/docs/query/stake-for-coldkeys) Each position's `stake` is denominated in the subnet's own currency: subnet alpha, or TAO when netuid is 0. **Category:** Staking ## Parameters [#parameters] | Parameter | Type | Description | | --------------- | ----- | --------------------------------------- | | `coldkey_ss58s` | array | Coldkeys whose stake positions to list. | ## CLI [#cli] ```bash btcli query stake-for-coldkeys --coldkeys --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.staking.stake_for_coldkeys(coldkey_ss58s=[...]) ``` Or dispatch by name, as an agent would: ```python result = sub.read("stake_for_coldkeys", coldkey_ss58s=[...]) ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Runtime API [`StakeInfoRuntimeApi.get_stake_info_for_coldkeys`](/code/pallets/subtensor/src/rpc_info/stake_info.rs#L92-L100) Every file is browsable under [/code](/code) exactly as built into the runtime. # stake-value-for-coldkey (/docs/query/stake-value-for-coldkey) **Category:** Staking ## Parameters [#parameters] | Parameter | Type | Description | | -------------- | ------ | ----------------------------- | | `coldkey_ss58` | string | Coldkey whose stake to value. | ## CLI [#cli] ```bash btcli query stake-value-for-coldkey --coldkey --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.staking.stake_value_for_coldkey(coldkey_ss58="5F...") ``` Or dispatch by name, as an agent would: ```python result = sub.read("stake_value_for_coldkey", coldkey_ss58="5F...") ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Runtime API [`StakeInfoRuntimeApi.get_stake_info_for_coldkey`](/code/pallets/subtensor/src/rpc_info/stake_info.rs#L102-L116) Every file is browsable under [/code](/code) exactly as built into the runtime. # stake-value-for-coldkeys (/docs/query/stake-value-for-coldkeys) **Category:** Staking ## Parameters [#parameters] | Parameter | Type | Description | | --------------- | ----- | ------------------------------ | | `coldkey_ss58s` | array | Coldkeys whose stake to value. | ## CLI [#cli] ```bash btcli query stake-value-for-coldkeys --coldkeys --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.staking.stake_value_for_coldkeys(coldkey_ss58s=[...]) ``` Or dispatch by name, as an agent would: ```python result = sub.read("stake_value_for_coldkeys", coldkey_ss58s=[...]) ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Runtime API [`StakeInfoRuntimeApi.get_stake_info_for_coldkeys`](/code/pallets/subtensor/src/rpc_info/stake_info.rs#L92-L100) Every file is browsable under [/code](/code) exactly as built into the runtime. # stake (/docs/query/stake) **Category:** Staking ## Parameters [#parameters] | Parameter | Type | Description | | -------------- | ------- | ---------------------------- | | `coldkey_ss58` | string | Coldkey that owns the stake. | | `hotkey_ss58` | string | Hotkey the stake is held on. | | `netuid` | integer | Subnet to query. | ## CLI [#cli] ```bash btcli query stake --coldkey --hotkey --netuid --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.staking.stake(coldkey_ss58="5F...", hotkey_ss58="5F...", netuid=1) ``` Or dispatch by name, as an agent would: ```python result = sub.read("stake", coldkey_ss58="5F...", hotkey_ss58="5F...", netuid=1) ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Runtime API [`StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid`](/code/pallets/subtensor/src/rpc_info/stake_info.rs#L118-L144) Every file is browsable under [/code](/code) exactly as built into the runtime. # staking-hotkeys (/docs/query/staking-hotkeys) Unlike `owned_hotkeys` this includes delegates the coldkey has nominated. Use `stake_for_coldkey` for the per-subnet amounts behind each entry. **Category:** Staking ## Parameters [#parameters] | Parameter | Type | Description | | -------------- | ------ | ---------------------------------------- | | `coldkey_ss58` | string | Coldkey whose staked-to hotkeys to list. | ## CLI [#cli] ```bash btcli query staking-hotkeys --coldkey --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.staking.staking_hotkeys(coldkey_ss58="5F...") ``` Or dispatch by name, as an agent would: ```python result = sub.read("staking_hotkeys", coldkey_ss58="5F...") ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Storage [`SubtensorModule.StakingHotkeys`](/code/pallets/subtensor/src/lib.rs#L1554) Every file is browsable under [/code](/code) exactly as built into the runtime. # subnet-collateral (/docs/query/subnet-collateral) The list validator code reads to enforce a per-machine collateral requirement: each record carries the locked amount, the miner's self-maintained floor, the drain-ratio snapshot, and the hotkey's current `uid` (None when the hotkey is deregistered but its collateral persists) — ready to join against the metagraph when scoring. **Category:** Mining & collateral ## Parameters [#parameters] | Parameter | Type | Description | | --------- | ------- | ---------------- | | `netuid` | integer | Subnet to query. | ## CLI [#cli] ```bash btcli query subnet-collateral --netuid --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.collateral.subnet_collateral(netuid=1) ``` Or dispatch by name, as an agent would: ```python result = sub.read("subnet_collateral", netuid=1) ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Storage [`SubtensorModule.Uids`](/code/pallets/subtensor/src/lib.rs#L2430) Every file is browsable under [/code](/code) exactly as built into the runtime. # subnet-convictions (/docs/query/subnet-convictions) Per hotkey: locked mass, conviction, and the estimated blocks until its conviction reaches 10% of the subnet's outstanding alpha. That per-hotkey figure is a projection heuristic, not a takeover trigger: the ownership takeover in `change_subnet_owner_if_needed` requires the subnet to be at least \~1 year old (2,629,800 blocks) and the total aggregate conviction across all lockers to reach 10% of `SubnetAlphaOut`, at which point the highest-conviction hotkey's coldkey becomes the subnet owner. Projections assume the lock rates and alpha out stay constant. **Category:** Locks & conviction ## Parameters [#parameters] | Parameter | Type | Description | | --------- | ------- | ---------------- | | `netuid` | integer | Subnet to query. | ## CLI [#cli] ```bash btcli query subnet-convictions --netuid --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.locks.subnet_convictions(netuid=1) ``` Or dispatch by name, as an agent would: ```python result = sub.read("subnet_convictions", netuid=1) ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Storage [`SubtensorModule.HotkeyLock`](/code/pallets/subtensor/src/lib.rs#L1713) * Storage [`SubtensorModule.DecayingHotkeyLock`](/code/pallets/subtensor/src/lib.rs#L1725) * Storage [`SubtensorModule.OwnerLock`](/code/pallets/subtensor/src/lib.rs#L1737) * Storage [`SubtensorModule.DecayingOwnerLock`](/code/pallets/subtensor/src/lib.rs#L1741) * Storage [`SubtensorModule.SubnetOwnerHotkey`](/code/pallets/subtensor/src/lib.rs#L2109) * Storage [`SubtensorModule.SubnetAlphaOut`](/code/pallets/subtensor/src/lib.rs#L1545) * Storage [`SubtensorModule.UnlockRate`](/code/pallets/subtensor/src/lib.rs#L1779) * Storage [`SubtensorModule.MaturityRate`](/code/pallets/subtensor/src/lib.rs#L1775) * Storage [`SubtensorModule.NetworkRegisteredAt`](/code/pallets/subtensor/src/lib.rs#L2043) Every file is browsable under [/code](/code) exactly as built into the runtime. # subnet-emission-enabled (/docs/query/subnet-emission-enabled) **Category:** Hyperparameters ## Parameters [#parameters] | Parameter | Type | Description | | --------- | ------- | ---------------- | | `netuid` | integer | Subnet to query. | ## CLI [#cli] ```bash btcli query subnet-emission-enabled --netuid --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.hyperparameters.subnet_emission_enabled(netuid=1) ``` Or dispatch by name, as an agent would: ```python result = sub.read("subnet_emission_enabled", netuid=1) ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Storage [`SubtensorModule.SubnetEmissionEnabled`](/code/pallets/subtensor/src/lib.rs#L1515) Every file is browsable under [/code](/code) exactly as built into the runtime. # subnet-hyperparameters (/docs/query/subnet-hyperparameters) 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 ## Parameters [#parameters] | Parameter | Type | Description | | --------- | ------- | ---------------- | | `netuid` | integer | Subnet to query. | ## CLI [#cli] ```bash btcli query subnet-hyperparameters --netuid --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.subnets.subnet_hyperparameters(netuid=1) ``` Or dispatch by name, as an agent would: ```python result = sub.read("subnet_hyperparameters", netuid=1) ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Runtime API [`SubnetInfoRuntimeApi.get_subnet_hyperparams_v3`](/code/pallets/subtensor/src/rpc_info/subnet_info.rs#L468-L641) Every file is browsable under [/code](/code) exactly as built into the runtime. # subnet-identity (/docs/query/subnet-identity) **Category:** Subnets ## Parameters [#parameters] | Parameter | Type | Description | | --------- | ------- | ------------------------------ | | `netuid` | integer | Subnet whose identity to read. | ## CLI [#cli] ```bash btcli query subnet-identity --netuid --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.subnets.subnet_identity(netuid=1) ``` Or dispatch by name, as an agent would: ```python result = sub.read("subnet_identity", netuid=1) ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Storage [`SubtensorModule.SubnetIdentitiesV3`](/code/pallets/subtensor/src/lib.rs#L2572) Every file is browsable under [/code](/code) exactly as built into the runtime. # subnet-names (/docs/query/subnet-names) **Category:** Subnets ## Parameters [#parameters] This read takes no parameters. ## CLI [#cli] ```bash btcli query subnet-names --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.subnets.subnet_names() ``` Or dispatch by name, as an agent would: ```python result = sub.read("subnet_names") ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Storage [`SubtensorModule.SubnetIdentitiesV3`](/code/pallets/subtensor/src/lib.rs#L2572) Every file is browsable under [/code](/code) exactly as built into the runtime. # subnet-registration-cost (/docs/query/subnet-registration-cost) Denominated in TAO. The cost is dynamic: it rises when subnets register and decays back down over time. **Category:** Subnets ## Parameters [#parameters] This read takes no parameters. ## CLI [#cli] ```bash btcli query subnet-registration-cost --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.subnets.subnet_registration_cost() ``` Or dispatch by name, as an agent would: ```python result = sub.read("subnet_registration_cost") ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Runtime API [`SubnetRegistrationRuntimeApi.get_network_registration_cost`](/code/runtime/src/lib.rs#L2348-L2350) Every file is browsable under [/code](/code) exactly as built into the runtime. # subnet-start-schedule (/docs/query/subnet-start-schedule) All values are block numbers: the subnet can start once the current block reaches `earliest_start_block` (registration block plus the chain's delay). **Category:** Subnets ## Parameters [#parameters] | Parameter | Type | Description | | --------- | ------- | ---------------- | | `netuid` | integer | Subnet to query. | ## CLI [#cli] ```bash btcli query subnet-start-schedule --netuid --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.subnets.subnet_start_schedule(netuid=1) ``` Or dispatch by name, as an agent would: ```python result = sub.read("subnet_start_schedule", netuid=1) ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Storage [`SubtensorModule.NetworkRegisteredAt`](/code/pallets/subtensor/src/lib.rs#L2043) * Constant [`SubtensorModule.InitialStartCallDelay`](/code/runtime/src/lib.rs#L851) Every file is browsable under [/code](/code) exactly as built into the runtime. # subnet (/docs/query/subnet) **Category:** Subnets ## Parameters [#parameters] | Parameter | Type | Description | | --------- | ------- | ---------------- | | `netuid` | integer | Subnet to query. | ## CLI [#cli] ```bash btcli query subnet --netuid --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.subnets.subnet(netuid=1) ``` Or dispatch by name, as an agent would: ```python result = sub.read("subnet", netuid=1) ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Storage [`SubtensorModule.Tempo`](/code/pallets/subtensor/src/lib.rs#L1952) * Storage [`SubtensorModule.Burn`](/code/pallets/subtensor/src/lib.rs#L2248) * Storage [`SubtensorModule.SubnetworkN`](/code/pallets/subtensor/src/lib.rs#L2011) Every file is browsable under [/code](/code) exactly as built into the runtime. # subnets (/docs/query/subnets) one-query-per-subnet. This is what listing should use. **Category:** Subnets ## Parameters [#parameters] This read takes no parameters. ## CLI [#cli] ```bash btcli query subnets --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.subnets.subnets() ``` Or dispatch by name, as an agent would: ```python result = sub.read("subnets") ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Storage [`SubtensorModule.NetworksAdded`](/code/pallets/subtensor/src/lib.rs#L2015) * Storage [`SubtensorModule.Tempo`](/code/pallets/subtensor/src/lib.rs#L1952) * Storage [`SubtensorModule.Burn`](/code/pallets/subtensor/src/lib.rs#L2248) * Storage [`SubtensorModule.SubnetworkN`](/code/pallets/subtensor/src/lib.rs#L2011) Every file is browsable under [/code](/code) exactly as built into the runtime. # timelocked-weight-commits (/docs/query/timelocked-weight-commits) Each entry carries the committing `hotkey`, the `commit_block`, the drand `reveal_round` at which the chain auto-decrypts and applies it, `reveals_at` (that round's UTC time, computed locally), and the `ciphertext`. Entries disappear from this map once revealed — revealed weights show up in the `weights` read. **Category:** Weights & bonds ## Parameters [#parameters] | Parameter | Type | Description | | --------- | ------- | ----------------------------------------------------------------------- | | `netuid` | integer | Subnet whose pending weight commits to list. | | `mechid` | integer | Mechanism index within the subnet; 0 (the default) on ordinary subnets. | ## CLI [#cli] ```bash btcli query timelocked-weight-commits --netuid --mechid --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.weights.timelocked_weight_commits(netuid=1, mechid=1) ``` Or dispatch by name, as an agent would: ```python result = sub.read("timelocked_weight_commits", netuid=1, mechid=1) ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Storage [`SubtensorModule.TimelockedWeightCommits`](/code/pallets/subtensor/src/lib.rs#L2628) Every file is browsable under [/code](/code) exactly as built into the runtime. # timestamp (/docs/query/timestamp) **Category:** Chain ## Parameters [#parameters] This read takes no parameters. ## CLI [#cli] ```bash btcli query timestamp --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.chain.timestamp() ``` Or dispatch by name, as an agent would: ```python result = sub.read("timestamp") ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. # token-symbols (/docs/query/token-symbols) Connecting runs this automatically (through a disk cache) and installs the result as the connection's display symbols, so balances decoded by that client render with each subnet's real symbol. **Category:** Subnets ## Parameters [#parameters] This read takes no parameters. ## CLI [#cli] ```bash btcli query token-symbols --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.subnets.token_symbols() ``` Or dispatch by name, as an agent would: ```python result = sub.read("token_symbols") ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Storage [`SubtensorModule.TokenSymbol`](/code/pallets/subtensor/src/lib.rs#L1793) Every file is browsable under [/code](/code) exactly as built into the runtime. # tx-rate-limit (/docs/query/tx-rate-limit) **Category:** Chain ## Parameters [#parameters] This read takes no parameters. ## CLI [#cli] ```bash btcli query tx-rate-limit --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.chain.tx_rate_limit() ``` Or dispatch by name, as an agent would: ```python result = sub.read("tx_rate_limit") ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Storage [`SubtensorModule.TxRateLimit`](/code/pallets/subtensor/src/lib.rs#L2306) Every file is browsable under [/code](/code) exactly as built into the runtime. # uid (/docs/query/uid) **Category:** Neurons & registration ## Parameters [#parameters] | Parameter | Type | Description | | ------------- | ------- | ---------------------------- | | `hotkey_ss58` | string | Hotkey whose UID to look up. | | `netuid` | integer | Subnet to query. | ## CLI [#cli] ```bash btcli query uid --hotkey --netuid --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.neurons.uid(hotkey_ss58="5F...", netuid=1) ``` Or dispatch by name, as an agent would: ```python result = sub.read("uid", hotkey_ss58="5F...", netuid=1) ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Storage [`SubtensorModule.Uids`](/code/pallets/subtensor/src/lib.rs#L2430) Every file is browsable under [/code](/code) exactly as built into the runtime. # weights-rate-limit (/docs/query/weights-rate-limit) **Category:** Hyperparameters ## Parameters [#parameters] | Parameter | Type | Description | | --------- | ------- | ---------------- | | `netuid` | integer | Subnet to query. | ## CLI [#cli] ```bash btcli query weights-rate-limit --netuid --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.hyperparameters.weights_rate_limit(netuid=1) ``` Or dispatch by name, as an agent would: ```python result = sub.read("weights_rate_limit", netuid=1) ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Storage [`SubtensorModule.WeightsSetRateLimit`](/code/pallets/subtensor/src/lib.rs#L2218) Every file is browsable under [/code](/code) exactly as built into the runtime. # weights (/docs/query/weights) The chain stores max-upscaled u16 values whose absolute scale carries no meaning — consensus only uses within-row proportions — so rows are normalized to fractions here. On commit-reveal subnets a validator's row updates only when its timelocked commit reveals (see `timelocked_weight_commits` for what is pending). **Category:** Weights & bonds ## Parameters [#parameters] | Parameter | Type | Description | | --------- | ------- | ----------------------------------------------------------------------- | | `netuid` | integer | Subnet whose weight matrix to fetch. | | `mechid` | integer | Mechanism index within the subnet; 0 (the default) on ordinary subnets. | ## CLI [#cli] ```bash btcli query weights --netuid --mechid --json ``` ## Python [#python] Namespace method (autocomplete, signature help): ```python import bittensor as bt sub = bt.Subtensor() result = sub.weights.weights(netuid=1, mechid=1) ``` Or dispatch by name, as an agent would: ```python result = sub.read("weights", netuid=1, mechid=1) ``` Async is the same surface awaited: `async with bt.Subtensor() as client:`. ## On-chain implementation [#on-chain-implementation] * Storage [`SubtensorModule.Weights`](/code/pallets/subtensor/src/lib.rs#L2492) Every file is browsable under [/code](/code) exactly as built into the runtime. # Deploy a contract (/docs/guides/evm/deploy-a-contract) Subtensor's EVM accepts standard Solidity contracts deployed with standard tooling. This walkthrough deploys a small vault contract that holds native TAO, first with Hardhat, then with Remix. Everything here works identically on localnet, testnet, and mainnet — only the RPC URL and the source of funds change. **Prerequisites:** * A funded EVM key: the [quick start](/docs/guides/evm#quick-start-your-first-evm-key) gets you one in four commands. Gas costs fractions of a TAO; develop against testnet or a [localnet](/docs/guides/local-development) (where the faucet mints test TAO) rather than spending mainnet TAO. * Node.js 20+ for the Hardhat path. Two settings prevent almost every mysterious deploy failure on subtensor: compile with **Solidity 0.8.24 or lower** and **EVM target Cancun**. And on a fresh localnet, run `btcli evm setup-localnet -w alice` once — it sets the EVM chain ID and disables the deployment whitelist, the two things that otherwise fail your first deploy. ## The contract [#the-contract] `TaoVault.sol` — anyone can deposit native TAO, only the owner can withdraw. Small, but it exercises the thing that differs from a toy chain: real value in `msg.value`. ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.24; contract TaoVault { address public immutable owner; constructor() { owner = msg.sender; } receive() external payable {} function balance() external view returns (uint256) { return address(this).balance; // wei: 1 TAO = 1e18 } function withdraw(uint256 amountWei) external { require(msg.sender == owner, "not owner"); (bool ok, ) = owner.call{value: amountWei}(""); require(ok, "transfer failed"); } } ``` Remember the [decimals rule](/docs/guides/evm#the-decimals-trap): balances and `msg.value` are in wei (1 TAO = 1e18); only precompile amount parameters use rao. ## Path A: Hardhat [#path-a-hardhat] ### 1. Project setup [#1-project-setup] ```bash mkdir tao-vault && cd tao-vault npm init -y npm install --save-dev hardhat ethers mkdir contracts scripts # save TaoVault.sol into contracts/ ``` ### 2. Configure for subtensor [#2-configure-for-subtensor] `btcli evm config --format hardhat` prints a ready config for your configured network — save it as `hardhat.config.js`. It pins the compiler settings, points at the right RPC, and takes the deployer key from the environment as either a raw key (`ETH_PRIVATE_KEY`) or an encrypted keystore file (`ETH_KEYSTORE` + `ETH_KEYSTORE_PASSWORD`), which it decrypts with ethers at load time. ### 3. Provide the deployer key [#3-provide-the-deployer-key] The emitted config accepts either form: ```bash # simplest: raw key in the environment (prompts for the keystore password) export ETH_PRIVATE_KEY=$(btcli evm key export --private-key -w my_coldkey) # or keep the key encrypted: the config decrypts the keystore file itself btcli evm key export --out key.json -w my_coldkey export ETH_KEYSTORE=./key.json ETH_KEYSTORE_PASSWORD=… ``` The keystore route means no raw key ever sits in an environment variable — prefer it for anything long-lived. ### 4. Deploy [#4-deploy] `scripts/deploy.js`: ```javascript const hre = require("hardhat"); async function main() { const [deployer] = await hre.ethers.getSigners(); console.log("deploying from", deployer.address); const vault = await hre.ethers.deployContract("TaoVault"); await vault.waitForDeployment(); console.log("TaoVault at", await vault.getAddress()); } main().catch((e) => { console.error(e); process.exit(1); }); ``` ```bash npx hardhat run scripts/deploy.js --network subtensor ``` ```text deploying from 0x1074… TaoVault at 0x5FbDB2315678afecb367f032d93F642f64180aa3 ``` Deployment lands in the next \~12-second block. If it fails at gas estimation, remember that subtensor's `eth_estimateGas` rejects *any* invalid transaction — an unfunded deployer and the localnet whitelist are the usual suspects, not gas itself. ### 5. Interact [#5-interact] `scripts/use.js` — deposit 0.1 TAO, read the balance back: ```javascript const hre = require("hardhat"); async function main() { const vault = await hre.ethers.getContractAt("TaoVault", process.env.VAULT); const [signer] = await hre.ethers.getSigners(); const tx = await signer.sendTransaction({ to: await vault.getAddress(), value: hre.ethers.parseEther("0.1"), // 0.1 TAO as 1e17 wei }); await tx.wait(); console.log("vault holds", hre.ethers.formatEther(await vault.balance()), "TAO"); } main().catch((e) => { console.error(e); process.exit(1); }); ``` ```bash VAULT=0x5FbDB… npx hardhat run scripts/use.js --network subtensor ``` The contract's TAO is real native currency: the contract address also has an ss58 mirror (`btcli evm mirror 0x5FbDB…`), and if the contract calls a precompile, that mirror is its coldkey — the seam explained in [Two address domains](/docs/guides/evm#two-address-domains). ### Alternative: deploy the artifact with btcli [#alternative-deploy-the-artifact-with-btcli] Once Hardhat has compiled (`npx hardhat compile`), you don't need a deploy script at all — btcli reads the artifact directly: ```bash btcli evm deploy artifacts/contracts/TaoVault.sol/TaoVault.json -w my_coldkey ``` `--dry-run` previews gas and fee; on success it prints the contract address *and its ss58 mirror* (the contract's identity for native TAO and precompile calls). Constructor arguments follow the artifact path, and `bytes32` parameters accept ss58 addresses. Foundry artifacts and raw `.bin` files work too. Interact the same way: ```bash btcli evm call 0xCONTRACT… balance --abi artifacts/contracts/TaoVault.sol/TaoVault.json ``` ## Path B: Remix [#path-b-remix] No local toolchain — everything happens in the browser through MetaMask. 1. **Connect MetaMask to subtensor.** `btcli evm config --format metamask` prints the network settings; add them under *Add network → Add manually*. Import a funded key (`btcli evm key export --out key.json`, then import the JSON in MetaMask). 2. **Configure the compiler.** `btcli evm config --format remix` prints the settings: compiler **0.8.24 or lower**, *Advanced Configurations → EVM version* **cancun**. 3. **Compile.** Paste `TaoVault.sol` into a new file in [remix.ethereum.org](https://remix.ethereum.org) and compile. 4. **Deploy.** In *Deploy & run transactions*, set environment to **Injected Provider – MetaMask** (confirm the chain ID is 964 for mainnet or 945 for testnet), then Deploy and approve in MetaMask. 5. **Interact.** Use the deployed-contract panel: put an amount in the *Value* field (unit: ether — 1 "ether" = 1 TAO) and press the low-level *Transact* button to deposit; `balance` and `withdraw` appear as buttons. ## Where to go next [#where-to-go-next] A contract that only holds TAO doesn't need Bittensor. The interesting part is that contracts can call the chain's own operations through [precompiles](/docs/guides/evm#precompiles): * [Read chain state](/docs/guides/evm/read-chain-state) — query the metagraph from Solidity and JSON-RPC. * [Stake from the EVM](/docs/guides/evm/stake-from-evm) — a contract that stakes the TAO it receives. # EVM (/docs/guides/evm) Subtensor runs a full EVM runtime: standard smart contracts, standard `eth_*` JSON-RPC, MetaMask, Hardhat, and Remix all work. Contracts execute on the Bittensor chain (not Ethereum) with **TAO** as the native currency. This guide starts with the smallest working path — create a key, fund it, check the balance — then walks through every `btcli evm` command and the deeper concepts (address mappings, decimals, precompiles). ## Walkthroughs [#walkthroughs] End-to-end tutorials that build on the commands below: ## Quick start: your first EVM key [#quick-start-your-first-evm-key] **Prerequisites:** `bittensor` installed (includes `eth-account` for EVM signing). A configured coldkey wallet (`btcli wallet list`). ### 1. Check the EVM endpoint [#1-check-the-evm-endpoint] ```bash btcli evm doctor ``` Confirms the JSON-RPC URL, chain ID, gas price, and (if you have one) your default EVM key's balance. On localnet, start the chain first; if chain ID is unset, see [Connectivity](#connectivity) below. ### 2. Create an encrypted EVM key [#2-create-an-encrypted-evm-key] ```bash btcli evm key new -w my_coldkey btcli evm key list -w my_coldkey ``` Creates a random secp256k1 key stored as **Ethereum keystore V3** JSON next to your hotkeys (`~/.bittensor/wallets//evmkeys/`). The file imports directly into MetaMask (`btcli evm key export`). The default EVM key is **not** derived from your coldkey mnemonic — its compromise surface (browser wallets, dapp signing) stays separate from the coldkey seed. Use `btcli evm key import --mnemonic` only if you manage that seed yourself. ### 3. Fund the key from your coldkey [#3-fund-the-key-from-your-coldkey] ```bash btcli evm fund --amount-tao 1 -w my_coldkey btcli evm balance -w my_coldkey ``` `fund` transfers TAO from the coldkey to the EVM key's **ss58 mirror** (substrate extrinsic, coldkey-signed). The balance then appears on the EVM side — MetaMask shows it with **18 decimals** (see [Decimals](#the-decimals-trap)). The mirror is an ordinary substrate account, so the existential deposit (500 rao) stays locked in it and is invisible from the EVM side: fund 1 TAO into a fresh key and the EVM balance reads τ0.999999500. It's withheld once, not per transfer. Preview first with `--dry-run`: ```bash btcli evm fund --amount-tao 1 --dry-run -w my_coldkey ``` ### 4. (Optional) Connect MetaMask [#4-optional-connect-metamask] ```bash btcli evm config --format metamask ``` Paste the network settings into MetaMask (Add network → Add manually). Import the keystore with `btcli evm key export --out ./mykey.json`. You now have a funded EVM account on Subtensor. The sections below explain *why* funding uses a mirror address, what every other command does, and how to move TAO back to ss58. ## Two address domains [#two-address-domains] **Takeaways:** * **ss58** (`5…`) — native accounts; sign extrinsics with sr25519/ed25519. * **h160** (`0x…`) — EVM accounts; sign transactions with secp256k1. * **Mirror mapping** — fund an arbitrary EVM address: `btcli evm mirror 0x…` * **Truncated mapping** — claim a MetaMask send into your coldkey: `btcli evm deposit-address` Utility commands: ```bash btcli evm mirror 0x742d35Cc6634C0532925a3b844Bc454e4438f44e # ss58 mirror of an h160 btcli evm pubkey 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY # bytes32 for precompiles btcli evm deposit-address -w my_coldkey # where MetaMask should send ``` ## Money flows [#money-flows] ### Naming cheat sheet [#naming-cheat-sheet] | Goal | Command | Who signs | Pays gas | | -------------------------- | ------------------------- | --------- | --------- | | Coldkey → EVM key balance | `btcli evm fund` | coldkey | substrate | | EVM → EVM | `btcli evm send` | EVM key | EVM (wei) | | EVM key → any ss58 | `btcli evm send-to-ss58` | EVM key | EVM (wei) | | MetaMask deposit → coldkey | `btcli evm claim-deposit` | coldkey | substrate | **Do not confuse** `send-to-ss58` with `claim-deposit`. The first spends from a **stored EVM key** via the BalanceTransfer precompile. The second pulls a **MetaMask deposit** into your coldkey via the substrate `EVM.withdraw` extrinsic (`btcli tx evm-withdraw` is the same intent). ### Worked example: fund → send → exit to ss58 [#worked-example-fund--send--exit-to-ss58] ```bash # Fund btcli evm fund --amount-tao 2 -w my_coldkey # Send 0.5 TAO to another EVM address btcli evm send --to 0xabc… --amount-tao 0.5 -w my_coldkey # Send 0.5 TAO to a friend's ss58 (they need no EVM key) btcli evm send-to-ss58 --to 5F… --amount-tao 0.5 -w my_coldkey ``` ### Worked example: MetaMask → coldkey [#worked-example-metamask--coldkey] ```bash btcli evm deposit-address -w my_coldkey # Send TAO from MetaMask to the printed 0x deposit address btcli evm claim-deposit --amount-tao 1 -w my_coldkey ``` ## `btcli evm` command reference [#btcli-evm-command-reference] All commands accept `-w` / `--wallet`, `-n` / `--network`, and `--json` like the rest of btcli; commands that submit something also accept `--dry-run` and `--yes` (read-only commands don't). EVM transaction commands additionally accept `--rpc-url` to override the network's JSON-RPC endpoint. ### EVM keys (`btcli evm key …`) [#evm-keys-btcli-evm-key-] | Command | What it does | | ------------ | --------------------------------------------------------------------------------------------------------------------- | | `key new` | Generate a random EVM key; store encrypted keystore V3. | | `key import` | Import from `--private-key`, `--keystore` file, or `--mnemonic`. | | `key export` | Print or write keystore JSON (`--out` writes mode `0600`); `--private-key` decrypts to raw hex for `ETH_PRIVATE_KEY`. | | `key list` | Name, h160 address, and ss58 mirror for each stored key. | | `key show` | Details for one key. | `--evm-key` elsewhere accepts a key **name** (`default`) or `WALLET/NAME`. ### Address helpers [#address-helpers] | Command | What it does | | ------------------ | --------------------------------------------------------------------- | | `mirror [ADDRESS]` | ss58 mirror of an h160 (where native TAO lands for that EVM account). | | `pubkey SS58` | 32-byte public key as 0x-hex (what precompiles expect as `bytes32`). | | `deposit-address` | Truncated h160 + mirror for MetaMask → coldkey deposits. | ### Money movement [#money-movement] | Command | What it does | | --------------------------------------- | -------------------------------------------------------------------------- | | `balance [ADDRESS]` | EVM balance in TAO and wei via JSON-RPC. | | `fund --amount-tao N` | Coldkey → EVM key mirror ([`fund-evm-key`](/docs/tx/fund-evm-key)). | | `send --to 0x… --amount-tao N` | Ordinary EVM value transfer between h160 accounts. | | `send-to-ss58 --to SS58 --amount-tao N` | EVM key → ss58 via BalanceTransfer precompile. | | `claim-deposit --amount-tao N` | Coldkey claims MetaMask deposit ([`evm-withdraw`](/docs/tx/evm-withdraw)). | ### Hotkey association [#hotkey-association] | Command | What it does | | ---------------------- | --------------------------------------------------------------------- | | `associate --netuid N` | Link stored EVM key to wallet hotkey on a subnet (proof + extrinsic). | Some subnets require an associated EVM key. The command: 1. Reads substrate block height (not EVM RPC). 2. Unlocks the EVM key and produces an **EIP-191** signature over `hotkey_pubkey (32B) ++ keccak(SCALE-u64(block_number))`. 3. Submits [`associate-evm-key`](/docs/tx/associate-evm-key) signed by the hotkey. Query the link with [`associated-evm-key`](/docs/query/associated-evm-key). ### Precompiles & contracts [#precompiles--contracts] | Command | What it does | | ------------------------- | ------------------------------------------------------------------------------------------------------- | | `precompiles` | Catalog: name, address, description. | | `abi NAME` | Address + ABI JSON for Hardhat/ethers/viem. | | `call NAME [FN] [ARGS…]` | View calls free via `eth_call`; writes need `--evm-key`. Also takes a 0x contract address with `--abi`. | | `deploy ARTIFACT [ARGS…]` | Deploy a compiled contract (Hardhat/Foundry artifact or `.bin`). | | `stake add/remove/show` | Staking-v2 precompile sugar (TAO/alpha unit conversion handled). | Examples: ```bash btcli evm call metagraph getUidCount 1 # view — no key, no gas btcli evm call staking-v2 getStake HOTKEY MIRROR 1 # view stake position btcli evm stake add --netuid 1 --hotkey 5F… --amount-tao 2 btcli evm deploy artifacts/…/TaoVault.json # your own contracts btcli evm call 0xCONTRACT… balance --abi artifacts/…/TaoVault.json ``` Before a write is signed, the CLI echoes the decoded arguments — `bytes32` keys shown as ss58, rao amounts shown in TAO — and states which substrate role the EVM key's mirror plays in the dispatched call (coldkey, hotkey, owner). Deprecated functions are flagged in listings and warned about when called. ### Setup & diagnosis [#setup--diagnosis] | Command | What it does | | -------------------------- | ------------------------------------------------------------------------------------------------------------------ | | `networks` | Chain ID and RPC URL per preset network. | | `config --format metamask` | Paste-ready MetaMask / Hardhat / Remix settings. | | `doctor` | Probe RPC reachability, chain ID, gas price, key balance. | | `setup-localnet` | Make a fresh localnet EVM-ready: set the chain ID, disable the deploy whitelist (two sudo calls, signed by Alice). | ## Connectivity [#connectivity] | Network | EVM RPC | Chain ID | | ---------------- | ---------------------------------- | ------------- | | Mainnet (finney) | `https://lite.chain.opentensor.ai` | 964 | | Testnet | `https://test.chain.opentensor.ai` | 945 | | Localnet | `http://127.0.0.1:9944` | 42 at genesis | Mainnet is also on [ChainList](https://chainlist.org) as chain **964**. A fresh localnet boots with the generic substrate chain ID **42** and blocks contract deployment behind a whitelist — `btcli evm setup-localnet -w alice` fixes both in one command (`--chain-id 945` by default). Override the RPC for any command: ```bash btcli evm balance --rpc-url http://127.0.0.1:9944 export BT_EVM_ENDPOINT=http://127.0.0.1:9944 # local preset default ``` ## The decimals trap [#the-decimals-trap] Native TAO has **9 decimals** (1 TAO = 1e9 rao). The EVM side uses Ethereum's **18 decimals**: 1 TAO = 1e18 wei in a transaction's `value` field. MetaMask always assumes 18, so displayed EVM balances use a different exponent than substrate wallets — **the funds are the same**. Contract code forwarding `msg.value` into a precompile must convert to rao: ```solidity uint256 amountRao = msg.value / 1e9; ``` Precompile **amount parameters** are in rao, not 1e18 TAO. ## Precompiles [#precompiles] Beyond standard Ethereum precompiles (ECRecover, Sha256, …), subtensor exposes chain operations at fixed addresses (the 20-byte zero-padded **index** — e.g. index `2053` → `0x…0805`): | Name | Index | Address | Purpose | | ---------------- | ----- | ------- | ----------------------------------------------------------- | | balance-transfer | 2048 | `0x800` | Send TAO from EVM to any ss58 (`msg.value`). | | staking (v1) | 2049 | `0x801` | Deprecated — use staking-v2. | | metagraph | 2050 | `0x802` | Read neuron/subnet state. | | subnet | 2051 | `0x803` | Subnet registration and owner params. | | neuron | 2052 | `0x804` | Weights, registration, serving. | | staking-v2 | 2053 | `0x805` | add/remove/move/transfer stake (amounts in rao). | | uid-lookup | 2054 | `0x806` | UIDs for an EVM address on a subnet. | | storage-query | 2055 | `0x807` | Raw substrate storage reads (allow-listed pallet prefixes). | | alpha | 2056 | `0x808` | Subnet alpha token info. | | crowdloan | 2057 | `0x809` | Crowdloan from EVM. | | leasing | 2058 | `0x80a` | Subnet leasing. | | proxy | 2059 | `0x80b` | Proxy delegations. | | address-mapping | 2060 | `0x80c` | On-chain h160 → mirror bytes32. | | voting-power | 2061 | `0x80d` | Validator voting power (stake EMA) per subnet. | | balance | 2062 | `0x80e` | Free TAO balance for any ss58 coldkey. | | ed25519-verify | 1026 | `0x402` | Verify ed25519 (prove ss58 ownership). | | sr25519-verify | 1027 | `0x403` | Verify sr25519. | Semantics that surprise Ethereum developers: * When a **contract** calls a precompile, the **contract address** is the coldkey — it must hold the funds. * Hotkey/coldkey arguments are **`bytes32` public keys**, not ss58 strings. `btcli evm call` converts ss58 automatically; Solidity must pass raw keys. * Amount parameters are in **rao** (see decimals above). Source: [subtensor/precompiles](https://github.com/RaoFoundation/subtensor/tree/main/precompiles). Examples: [RaoFoundation/evm-bittensor](https://github.com/RaoFoundation/evm-bittensor/tree/main/examples). ## Python SDK [#python-sdk] The same layer lives in `bittensor.evm` (included in the base install): ```python import bittensor as bt from bittensor.evm import h160_to_ss58, association_proof from bittensor.evm.keys import create_evm_key, unlock_evm_key from bittensor.evm.precompiles import encode_call, get_precompile ``` Substrate-side intents ([`fund-evm-key`](/docs/tx/fund-evm-key), [`evm-withdraw`](/docs/tx/evm-withdraw), [`associate-evm-key`](/docs/tx/associate-evm-key)) use the normal `client.plan` / `client.execute` flow. EVM-side sends use `bittensor.evm.rpc` and `bittensor.evm.transactions` directly. ## Hosted bridges [#hosted-bridges] Third-party services (not part of this SDK): * [tao.app/bridge](https://tao.app/bridge) — TAO between substrate and EVM wallets. * vTAO — liquid-staked TAO ERC-20 on the Bittensor EVM, bridgeable to Base. ## Tooling notes [#tooling-notes] * Compile with **Solidity 0.8.24 or lower, EVM target Cancun**. Newer targets can fail deploy with `InvalidCode(Opcode)` or gas-estimation errors. * `eth_estimateGas` failures mean *any* invalid transaction — insufficient balance, bad calldata, unset chain ID, or localnet deployment whitelist — not just gas problems. When an estimate fails, btcli probes for the usual causes and reports what it finds. * MetaMask nonces stuck after a localnet restart: Settings → Advanced → "Clear activity tab data". * Localnet contract deployment is blocked by a whitelist until `btcli evm setup-localnet` disables it. ## See also [#see-also] * [Local development](/docs/guides/local-development) — run a chain for testing. * [Transfer](/docs/tx/transfer) — send TAO to an EVM mirror manually. * [Staking guide](/docs/guides/staking) — native-side stake; compare with `btcli evm stake add` for EVM-key-funded stake. # Read chain state (/docs/guides/evm/read-chain-state) Everything a subnet dashboard needs — stake, incentive, axon endpoints, pool prices — is exposed to the EVM through read-only precompiles. View calls go through `eth_call`: no key, no gas, no setup. This walkthrough queries subnet 1 on mainnet three ways: `btcli evm call`, the Python SDK, and from a contract. If you just want the data (not the EVM), the native reads are richer and one call: [`metagraph`](/docs/query/metagraph), [`alpha-price`](/docs/query/alpha-price). The precompiles matter when the consumer *is* EVM code — a contract, an ethers app, an indexer speaking JSON-RPC. ## Discover what's callable [#discover-whats-callable] The catalog and per-precompile function listings come from the CLI: ```bash btcli evm precompiles # every precompile: name, address, description btcli evm call metagraph # its functions, inputs, outputs, mutability btcli evm abi metagraph # address + ABI JSON for ethers/viem/Hardhat ``` ## The metagraph precompile [#the-metagraph-precompile] `metagraph` (address `0x…0802`) answers per-neuron questions keyed by `(netuid, uid)`: ```bash btcli evm call metagraph getUidCount 1 # neurons on subnet 1 btcli evm call metagraph getHotkey 1 0 # uid 0's hotkey (bytes32 pubkey) btcli evm call metagraph getStake 1 0 # uid 0's stake, in rao btcli evm call metagraph getIncentive 1 0 # u16-normalized (0–65535) btcli evm call metagraph getAxon 1 0 # (block, version, ip, port, ip_type, protocol) btcli evm call metagraph getValidatorStatus 1 0 # holds a validator permit? ``` Three data-shape rules cover every surprise: * **Keys are `bytes32` public keys**, not ss58 strings. Convert with `btcli evm pubkey SS58` one way and `python -c "from bittensor.evm import pubkey_to_ss58; print(pubkey_to_ss58('0x…'))"` the other. * **Amounts are rao** (1 TAO = 1e9 rao) — not the EVM's 18-decimal wei. * **Scores are u16-normalized**: incentive, consensus, dividends, and vtrust come back as 0–65535, meaning 0.0–1.0. Divide by 65535. (`getRank` and `getTrust` are deprecated and always return 0 — compute standing from incentive instead.) ## Prices and pools: the alpha precompile [#prices-and-pools-the-alpha-precompile] `alpha` (address `0x…0808`) exposes the subnet pool: ```bash btcli evm call alpha getAlphaPrice 1 # TAO per alpha, 18-decimal fixed point btcli evm call alpha getTaoInPool 1 # pool TAO reserve, in rao btcli evm call alpha getAlphaInPool 1 # pool alpha reserve, in rao-scale alpha btcli evm call alpha simSwapTaoForAlpha 1 1000000000 # what 1 TAO buys right now ``` `getAlphaPrice` returns the price scaled to 1e18 (EVM balance scale), so `2.5e17` means 0.25 TAO per alpha. The `simSwap*` functions quote a swap including slippage — the EVM equivalent of [`quote-stake`](/docs/query/quote-stake). ## From an EVM address to its UIDs [#from-an-evm-address-to-its-uids] `uid-lookup` (address `0x…0806`) answers "which neurons belong to this EVM key?" — it only returns results for hotkeys that have [associated an EVM key](/docs/guides/evm#hotkey-association): ```bash btcli evm call uid-lookup uidLookup 1 0x1074Ad… 16 # up to 16 (uid, block_associated) pairs ``` ## Coldkey free balances [#coldkey-free-balances] `balance` (address `0x…080e`) reads any native ss58 account's free TAO balance. Like other key-shaped precompile arguments, Solidity passes the raw 32-byte public key and the CLI accepts ss58 directly: ```bash btcli evm call balance getFreeBalance 5F... ``` The return value is rao. ## From Python [#from-python] The SDK's EVM layer encodes and decodes for you — this is exactly what `btcli evm call` does internally, usable from any script without web3: ```python from bittensor.evm import EvmRpc, encode_call, decode_result, get_precompile, evm_network rpc = EvmRpc(evm_network("finney").rpc_url) metagraph = get_precompile("metagraph") fn = metagraph.function("getUidCount") raw = rpc.eth_call({"to": metagraph.address, "data": encode_call(fn, [1])}) (count,) = decode_result(fn, raw) fn = metagraph.function("getStake") stakes = [] for uid in range(count): raw = rpc.eth_call({"to": metagraph.address, "data": encode_call(fn, [1, uid])}) (rao,) = decode_result(fn, raw) stakes.append(rao / 1e9) print(f"subnet 1: {count} neurons, top stake {max(stakes):,.0f} TAO") ``` `encode_call` accepts ss58 addresses wherever the ABI wants a `bytes32` key, so you never hand-convert. ## From Solidity [#from-solidity] Contracts call precompiles like any other contract — copy the interface from [`precompiles/src/solidity/`](https://github.com/RaoFoundation/subtensor/tree/main/precompiles/src/solidity) (or extract it from `btcli evm abi metagraph`). A contract that pays out only to registered validators: ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.24; interface IMetagraph { function getUidCount(uint16 netuid) external view returns (uint16); function getValidatorStatus(uint16 netuid, uint16 uid) external view returns (bool); function getHotkey(uint16 netuid, uint16 uid) external view returns (bytes32); } contract ValidatorGate { IMetagraph constant METAGRAPH = IMetagraph(0x0000000000000000000000000000000000000802); uint16 public immutable netuid; constructor(uint16 _netuid) { netuid = _netuid; } function isValidatorHotkey(bytes32 hotkey) public view returns (bool) { uint16 count = METAGRAPH.getUidCount(netuid); for (uint16 uid = 0; uid < count; uid++) { if (METAGRAPH.getHotkey(netuid, uid) == hotkey) { return METAGRAPH.getValidatorStatus(netuid, uid); } } return false; } } ``` (A per-call loop over 256 UIDs is fine for view calls, which are free; inside a paid transaction you'd pass the `uid` in and verify it instead.) Deploy it exactly like [any other contract](/docs/guides/evm/deploy-a-contract), and from ethers the reads are ordinary: ```javascript const abi = /* output of: btcli evm abi metagraph */; const metagraph = new ethers.Contract("0x0000000000000000000000000000000000000802", abi.abi, provider); console.log(await metagraph.getUidCount(1)); ``` ## See also [#see-also] * [Stake from the EVM](/docs/guides/evm/stake-from-evm) — the write-side counterpart. * [Metagraph query](/docs/query/metagraph) — the native read with everything in one call. # Stake from the EVM (/docs/guides/evm/stake-from-evm) The `staking-v2` precompile (address `0x…0805`) lets EVM accounts — including contracts — hold real stake positions: add, remove, move, and transfer stake with the same semantics as the native [`add-stake`](/docs/tx/add-stake) family. This walkthrough goes from the one-line CLI version down to a contract that stakes TAO sent to it. **How the coldkey works here:** the precompile dispatches the staking call with the **caller's ss58 mirror** as the coldkey. For an externally-owned account that's your EVM key's mirror (`btcli evm key list` shows it). When a *contract* calls the precompile, the *contract address's* mirror is the coldkey — the position belongs to the contract, and the TAO being staked must sit in the contract's balance. ## CLI: the three-command version [#cli-the-three-command-version] `btcli evm stake` wraps the precompile with unit conversion (the precompile speaks rao; you type TAO/alpha): ```bash # stake 2 TAO from the EVM key's balance to a validator on subnet 1 btcli evm stake add --netuid 1 --hotkey 5F4tQyWrhfGVcNhoqeiNsR6KjD4wMZ2kfhLj4oHYuyHbZAc3 --amount-tao 2 # the position (free view call; the EVM key's mirror is the coldkey) btcli evm stake show --netuid 1 --hotkey 5F4tQyWr… # unstake — note the unit is alpha, the subnet's own currency btcli evm stake remove --netuid 1 --hotkey 5F4tQyWr… --amount-alpha 10 ``` Like all btcli transactions, `--dry-run` previews (here: the exact EVM transaction — calldata, gas, fee) and `--json` gives machine-readable output. Staking TAO buys the subnet's **alpha** at the pool price, exactly as on the native side — the [staking guide](/docs/guides/staking) explains positions, slippage, and how to pick a validator; all of it applies unchanged. ## Raw calls: any staking-v2 function [#raw-calls-any-staking-v2-function] The sugar covers add/remove/show. Everything else on the precompile goes through `btcli evm call` — same argument conveniences (ss58 accepted for `bytes32`, amounts in rao): ```bash btcli evm call staking-v2 # list all functions btcli evm call staking-v2 getTotalHotkeyStake 5F4tQyWr… # view, free btcli evm call staking-v2 moveStake 5Forigin… 5Fdest… 1 4 1000000000 --evm-key default btcli evm call staking-v2 addStakeLimit 5F4tQyWr… 1000000000 250000000 true 1 --evm-key default ``` `addStakeLimit`/`removeStakeLimit` are the price-protected variants (`limit_price` in rao per alpha, `allow_partial` for partial fills) — the EVM mirror of [`add-stake`'s](/docs/tx/add-stake) limit options. ## From Python [#from-python] ```python from bittensor.evm import ( EvmRpc, encode_call, get_precompile, evm_network, prepare_transaction, send_transaction, ) from bittensor.evm.keys import unlock_evm_key rpc = EvmRpc(evm_network("test").rpc_url) staking = get_precompile("staking-v2") account = unlock_evm_key("default", "my_coldkey") # prompts for keystore password data = encode_call( staking.function("addStake"), ["5F4tQyWrhfGVcNhoqeiNsR6KjD4wMZ2kfhLj4oHYuyHbZAc3", 2 * 10**9, 1], # 2 TAO in rao ) preview = prepare_transaction(rpc, account.address, staking.address, data=data) print(f"gas {preview.gas}, max fee {preview.max_fee}") result = send_transaction(rpc, account, preview) print(result) # {"tx_hash": …, "block_number": …, "success": True} ``` ## A contract that stakes what it receives [#a-contract-that-stakes-what-it-receives] The composable version: TAO deposited into this contract is staked to a fixed validator hotkey, and the *contract* owns the position (its mirror is the coldkey). This is the primitive under liquid-staking tokens. ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.24; interface IStaking { function addStake(bytes32 hotkey, uint256 amount, uint256 netuid) external payable; function removeStake(bytes32 hotkey, uint256 amount, uint256 netuid) external payable; function getStake(bytes32 hotkey, bytes32 coldkey, uint256 netuid) external view returns (uint256); } contract AutoStaker { IStaking constant STAKING = IStaking(0x0000000000000000000000000000000000000805); address public immutable owner; bytes32 public immutable hotkey; // validator hotkey as bytes32 pubkey uint16 public immutable netuid; constructor(bytes32 _hotkey, uint16 _netuid) { owner = msg.sender; hotkey = _hotkey; netuid = _netuid; } receive() external payable { // msg.value is wei (1e18/TAO); the precompile amount is rao (1e9/TAO) STAKING.addStake(hotkey, msg.value / 1e9, netuid); } function unstake(uint256 amountAlphaRao) external { require(msg.sender == owner, "not owner"); // unstaked TAO lands on the contract's balance STAKING.removeStake(hotkey, amountAlphaRao, netuid); (bool ok, ) = owner.call{value: address(this).balance}(""); require(ok, "transfer failed"); } } ``` Two details to notice: * **The rao conversion.** `msg.value / 1e9` is the single most important line: passing a wei-scale number as the precompile's rao-scale amount would try to stake a billion times too much and revert. * **The constructor's `bytes32` hotkey.** Get it from `btcli evm pubkey `. Deploy with [Hardhat](/docs/guides/evm/deploy-a-contract), passing the constructor arguments, then verify from the terminal: ```bash # deposit 1 TAO into the contract (it stakes automatically) btcli evm send --to 0xCONTRACT… --amount-tao 1 -w my_coldkey # the contract's position: its coldkey is its own mirror btcli evm call staking-v2 getStake 5F4tQyWr… $(btcli evm mirror 0xCONTRACT… --json | jq -r .ss58_mirror) 1 ``` ## Allowances: the ERC-20-shaped layer [#allowances-the-erc-20-shaped-layer] `staking-v2` also carries `approve` / `allowance` / `increaseAllowance` / `decreaseAllowance` / `transferStakeFrom`, mirroring ERC-20 semantics over stake: an account approves a spender (typically a contract) for a subnet's alpha, and the spender can later `transferStakeFrom` source to destination. This is what lets escrow- and DEX-shaped contracts move stake with permission instead of custody: ```bash btcli evm call staking-v2 approve 0xSPENDER… 1 1000000000 --evm-key default btcli evm call staking-v2 allowance 0xME… 0xSPENDER… 1 ``` ## See also [#see-also] * [Staking guide](/docs/guides/staking) — validators, slippage, positions; all semantics shared with the native side. * [Read chain state](/docs/guides/evm/read-chain-state) — the alpha precompile for prices and swap quotes before you stake. # Subnet and neuron operations (/docs/guides/evm/subnet-and-neuron) The `subnet` (`0x…0803`) and `neuron` (`0x…0804`) precompiles expose the subnet lifecycle to EVM accounts: everything the [subnet](/docs/guides/subnets), [mining](/docs/guides/mining), and [validating](/docs/guides/validating) guides do natively, dispatched from an EVM transaction instead of a substrate extrinsic. This walkthrough runs the loop on a [localnet](/docs/guides/local-development). **The one rule that explains everything here:** a precompile dispatches the underlying chain call with the **caller's ss58 mirror** as the signing account. Whatever the native extrinsic expects of its signer — coldkey for registration, hotkey for weights — that role falls on your EVM key's mirror. Consequences: * `burnedRegister(netuid, hotkey)` — the mirror is the **coldkey** (pays the burn, owns the neuron); the `hotkey` argument can be any key. * `setWeights(...)`, `serveAxon(...)` — the mirror is the **hotkey**. For an EVM key to perform these, the registered hotkey must *be* its mirror. So: to run a neuron entirely from one EVM key, register with the key's own mirror as the hotkey. ## Setup [#setup] Start a localnet and fund an EVM key (via Alice, who is pre-funded): ```bash 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 ``` (On a source-built localnet the EVM RPC may be on another port, e.g. `http://127.0.0.1:9945` — pass `--rpc-url` or `export BT_EVM_ENDPOINT`. On a fresh localnet, run `btcli evm setup-localnet -w alice --network local` once: it sets the EVM chain ID and disables the contract-deployment whitelist.) Create a subnet to work on (natively, as in the [development loop](/docs/guides/local-development#a-typical-development-loop)), or from the EVM as shown next. ## Register a subnet from the EVM [#register-a-subnet-from-the-evm] ```bash btcli evm call subnet registerNetwork 5Fhotkey… --evm-key default --network local ``` The caller's mirror becomes the subnet **owner coldkey**. There are also overloads that set the subnet identity in the same call (name, repo, contact, URL, discord, description, logo) — `btcli evm call subnet` lists them. Not every owner operation is exposed as a precompile. In particular **activating emissions ([`start-call`](/docs/tx/start-call)) is substrate-only**, and a mirror account has no private key that can sign a substrate extrinsic. An EVM-registered subnet can still be configured and used (hyperparameters below work fine), but plan ownership deliberately: most owners register natively and use the EVM side for reads and hyperparameters. ### Hyperparameters [#hyperparameters] Every owner hyperparameter has a `get`/`set` pair on the subnet precompile — the EVM face of [`set-hyperparameter`](/docs/tx/set-hyperparameter). Reads are free; writes must come from the owner (or root): ```bash btcli evm call subnet getImmunityPeriod 2 btcli evm call subnet getWeightsSetRateLimit 2 btcli evm call subnet setMinBurn 2 500000 --evm-key default --network local btcli evm call subnet setCommitRevealWeightsEnabled 2 true --evm-key default --network local ``` ## Register a neuron [#register-a-neuron] Burned registration, paid by the EVM key's mirror. Pass the mirror itself as the hotkey so the same key can set weights later: ```bash MIRROR=$(btcli evm key list -w alice --json | jq -r '.[0].ss58_mirror') btcli evm call neuron burnedRegister 2 $MIRROR --evm-key default --network local ``` Confirm from either side: ```bash btcli query metagraph --netuid 2 --network local --json | jq '.neurons[].hotkey' btcli evm call uid-lookup uidLookup 2 0xYOURKEY… 16 # after associate-evm-key ``` ([`uid-lookup`](/docs/guides/evm/read-chain-state#from-an-evm-address-to-its-uids) resolves only after the hotkey↔EVM association exists; with the mirror *as* the hotkey you can skip association and just query the metagraph.) ## Serve an axon [#serve-an-axon] `serveAxon` publishes the neuron's endpoint, like the native [`serve-axon`](/docs/tx/serve-axon). The IP is a `uint128` (IPv4 packed into the low 32 bits), `ipType` is 4 or 6: ```bash # 192.168.1.10 = 0xC0A8010A = 3232235786, port 8091, IPv4, protocol 0 btcli evm call neuron serveAxon 2 1 3232235786 8091 4 0 0 0 --evm-key default --network local ``` There is also `serveAxonTls` (adds a certificate parameter) and `servePrometheus`. ## Set weights [#set-weights] The caller's mirror must be a registered hotkey with weight-setting permission (on localnet, register both a validator neuron and something to weigh — see the [validating guide](/docs/guides/validating) for permits and rate limits, which apply unchanged): ```bash # uids [0, 1] with weights [0, 65535], version key 0 btcli evm call neuron setWeights 2 0,1 0,65535 0 --evm-key default --network local ``` Weights are `uint16[]` (0–65535, normalized on chain). If the subnet has [commit-reveal](/docs/guides/validating#commit-reveal) enabled, use `commitWeights(netuid, commitHash)` and `revealWeights(...)` instead of direct `setWeights` — same scheme as the native flow. ## From Solidity [#from-solidity] Contracts can drive the same functions — a contract that registers neurons on behalf of users, with the contract's mirror paying the burn: ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.24; interface INeuron { function burnedRegister(uint16 netuid, bytes32 hotkey) external payable; } contract Registrar { INeuron constant NEURON = INeuron(0x0000000000000000000000000000000000000804); uint16 public immutable netuid; constructor(uint16 _netuid) { netuid = _netuid; } // Fund the contract with enough TAO for burns, then register any hotkey. function register(bytes32 hotkey) external { NEURON.burnedRegister(netuid, hotkey); } receive() external payable {} } ``` The burn is paid from the **contract's** native balance (its mirror is the coldkey), so fund the contract first — `btcli evm send --to 0xCONTRACT…`. ## See also [#see-also] * [Running a subnet](/docs/guides/subnets) — the full native lifecycle these precompiles mirror. * [Verify substrate keys](/docs/guides/evm/verify-keys) — associating an EVM key with a hotkey, and proving key ownership in contracts. # Verify substrate keys (/docs/guides/evm/verify-keys) Ethereum tooling can only verify secp256k1 signatures (`ecrecover`), but Bittensor identity lives in substrate keys — sr25519 and ed25519. Two precompiles bridge the gap, letting a contract check "does the holder of this hotkey/coldkey approve this?" without any substrate extrinsic: | Precompile | Address | Checks | | ---------------- | --------- | --------------------------------------------- | | `sr25519-verify` | `0x…0403` | sr25519 — what btcli wallets use by default. | | `ed25519-verify` | `0x…0402` | ed25519 — keys created with that crypto type. | Both have one function with the same shape: ```solidity function verify(bytes32 message, bytes32 publicKey, bytes32 r, bytes32 s) external pure returns (bool); ``` The **message is exactly 32 bytes** — you sign a digest of your payload, not the payload itself. The 64-byte substrate signature splits into `r` (first 32 bytes) and `s` (last 32). ## Round trip from the terminal [#round-trip-from-the-terminal] Sign a digest with an SDK keypair, verify it through the precompile — no gas, no contract: ```python import hashlib from bittensor.keyfiles import Keypair keypair = Keypair.create_from_mnemonic("your twelve words …") # sr25519 by default message = hashlib.sha256(b"claim:0x1074Ad…").digest() # any 32-byte digest signature = keypair.sign(message) print("message ", "0x" + message.hex()) print("pubkey ", keypair.ss58_address) print("r ", "0x" + signature[:32].hex()) print("s ", "0x" + signature[32:].hex()) ``` ```bash btcli evm call sr25519-verify verify 0xMESSAGE… 5Fpubkey… 0xR… 0xS… # → true ``` (`btcli evm call` converts the ss58 address to the `bytes32` public key; flip any byte of the signature and the call returns `false`.) ## In a contract: gate an action on hotkey approval [#in-a-contract-gate-an-action-on-hotkey-approval] The canonical use: an EVM airdrop or registry where claiming for a hotkey requires that hotkey's signature over the claimer's EVM address — so nobody can claim on someone else's identity: ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.24; interface ISr25519Verify { function verify(bytes32 message, bytes32 publicKey, bytes32 r, bytes32 s) external pure returns (bool); } contract HotkeyClaim { ISr25519Verify constant VERIFY = ISr25519Verify(0x0000000000000000000000000000000000000403); mapping(bytes32 => address) public claimedBy; function claim(bytes32 hotkey, bytes32 r, bytes32 s) external { require(claimedBy[hotkey] == address(0), "already claimed"); // the hotkey must have signed sha256("claim:" ++ caller address) bytes32 message = sha256(abi.encodePacked("claim:", msg.sender)); require(VERIFY.verify(message, hotkey, r, s), "bad signature"); claimedBy[hotkey] = msg.sender; } } ``` The off-chain side produces `r`/`s` with the Python snippet above (signing `sha256(b"claim:" + bytes.fromhex(evm_address[2:]))`). Use `ed25519-verify` at `0x…0402` for ed25519 keys — same interface. ## On-chain association: hotkey ↔ EVM key [#on-chain-association-hotkey--evm-key] Signature checks prove ownership per call. For a **standing** link the chain stores, associate the EVM key with a hotkey on a subnet — this is what makes [`uid-lookup`](/docs/guides/evm/read-chain-state#from-an-evm-address-to-its-uids) resolve, and some subnets require it: ```bash btcli evm associate --netuid 1 -w my_wallet -H my_hotkey 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 (EIP-191) over the hotkey's public key and a recent block number, and the **hotkey** submits [`associate-evm-key`](/docs/tx/associate-evm-key) carrying that signature. In Python: ```python import bittensor as bt from bittensor.evm import association_proof from bittensor.evm.keys import unlock_evm_key wallet = bt.Wallet(name="my_wallet", hotkey="my_hotkey") account = unlock_evm_key("default", "my_wallet") async with bt.Subtensor() as client: signature, block = association_proof( account, wallet.hotkey.ss58_address, await client.block() ) result = await client.execute( bt.AssociateEvmKey( netuid=1, evm_key=account.address, block_number=block, signature=signature, ), wallet, # associate_evm_key is signed by the hotkey ) ``` There is also `address-mapping` (`0x…080c`), the chain's own h160 → mirror conversion as a precompile — useful when a contract needs the ss58 mirror of an address it only knows as h160: ```bash btcli evm call address-mapping addressMapping 0x1074Ad… # → bytes32 mirror pubkey ``` ## See also [#see-also] * [Hotkey association](/docs/guides/evm#hotkey-association) — the CLI command reference. * [Two address domains](/docs/guides/evm#two-address-domains) — mirrors, truncated mappings, and where balances live. # Registration burn (/docs/guides/mining/burn) Every subnet charges a single floating **registration price**, read live with [`burn`](/docs/query/burn). What happens to your payment is set by the subnet's [`collateral_lock_share`](/docs/hyperparameters/collateral-lock-share) (call it `p`): * the `(1 − p)` share is **burned** — sunk, never refunded; * the `p` share is **locked as collateral** — stake you earn back by mining, covered in [registration collateral](/docs/guides/mining/collateral). With `p = 0` (the chain default) the whole price is burned, which is the classic burned registration this page describes. Everything here about how the price moves applies identically on collateral subnets — only the split of the payment changes. ## How the price floats [#how-the-price-floats] Admission is governed entirely by price, with no registration windows: * The price **decays over time** ([`update_registration_prices_for_networks`](/code/pallets/subtensor/src/subnets/registration.rs#L489-L531)), halving every [`burn_half_life`](/code/pallets/subtensor/src/lib.rs#L2778) blocks (default 360, about one tempo; owner-settable). * Each successful registration **multiplies the price** ([`bump_registration_price_after_registration`](/code/pallets/subtensor/src/subnets/registration.rs#L533-L557)) by [`burn_increase_mult`](/code/pallets/subtensor/src/lib.rs#L2783) (default 1.26), so a registration rush gets expensive fast. * The price is clamped between [`min_burn`](/docs/hyperparameters/min-burn) (default 500,000 rao = τ0.0005) and [`max_burn`](/docs/hyperparameters/max-burn) (default 100 TAO). All are per-subnet hyperparameters. The result is price discovery: the price settles where the marginal registrant's willingness to pay meets supply. A miner who expects a registration slot to return `X` is willing to pay up to `X` for it, and the decay/bump dynamics float the price to that level. An idle subnet rests at exactly `min_burn`. ## What the burned share buys (and where it goes) [#what-the-burned-share-buys-and-where-it-goes] The burned TAO is **recycled, not staked** — deregistering does not refund it. Under the hood ([`do_register`](/code/pallets/subtensor/src/subnets/registration.rs#L110-L125)), the TAO is swapped into the subnet's pool and the resulting alpha is removed from the subnet's outstanding supply. Economically, the burn is the subnet's spam throttle. It prices the *registration event itself*: every attempt costs it, no matter how the attempt turns out, which is what keeps registration races and UID-lottery spam expensive. What the burn cannot do is price *behavior after registration* — a registered miner has nothing left at stake. That gap is what [collateral](/docs/guides/mining/collateral) exists to fill. ## Worked example [#worked-example] Suppose netuid 42 has `min_burn` = τ0.5, `burn_increase_mult` = 1.26, `burn_half_life` = 360, and the price is currently τ1.00 with no collateral configured (`p = 0`): 1. You register and pay τ1.00. All of it is burned. The price immediately jumps to τ1.26 for the next registrant. 2. Two more miners register in the same tempo: they pay τ1.26 and τ1.59. The price is now τ2.00. 3. Nobody registers for the next 360 blocks: the price decays back to τ1.00, and after another 360 quiet blocks it would rest at min\_burn's clamp path (τ0.5 floor). Your τ1.00 is gone regardless of what happens next — if you are pruned in a week, re-registering costs whatever the price is then. ## Owner controls [#owner-controls] All owner-settable through `btcli sudo set` (rate-limited, and only outside the subnet's weights window): ```bash btcli sudo set --netuid 42 --name min_burn --value 500000000 # rao btcli sudo set --netuid 42 --name max_burn --value 100000000000 # rao btcli sudo set --netuid 42 --name burn_half_life --value 360 # blocks btcli sudo set --netuid 42 --name burn_increase_mult --value 1.26 ``` * [`min_burn`](/docs/hyperparameters/min-burn) — the resting price of an idle subnet; keeps registration from becoming free. On collateral subnets this floors the *total* price, of which only `(1 − p)` is burned. * [`max_burn`](/docs/hyperparameters/max-burn) — ceiling during registration rushes. * [`burn_half_life`](/docs/hyperparameters/burn-half-life) — how fast the price cools; shorter half-life means cheaper re-entry after a rush. * [`burn_increase_mult`](/docs/hyperparameters/burn-increase-mult) — how hard each registration pushes the price up; higher values throttle rushes more aggressively. ## Related [#related] [Registration collateral](/docs/guides/mining/collateral), [`collateral_lock_share`](/docs/hyperparameters/collateral-lock-share), [`burn` query](/docs/query/burn), [`burned-register`](/docs/tx/burned-register) # Registration collateral (/docs/guides/mining/collateral) Subnets can turn part of the registration price into **collateral**: instead of burning the whole price, the subnet locks a share of it as alpha stake on your hotkey. The lock is released back to you at a fixed rate per alpha of emission you earn (miner incentive and validator dividends) — so the only way to recover it is to earn on that subnet. It survives deregistration, is credited when you re-register the same hotkey, and has no other exit: quit for good and the remainder stays frozen on the hotkey indefinitely. The design in one line: **the burn prices the registration event; the collateral prices your behavior after it.** A hotkey that earns honestly for its career pays only the burn. A hotkey that registers and never earns — a sybil, a UID squatter, a spam registration — forfeits the lock in practice, because nothing ever releases it. And a hotkey that cheats loses it too: see [enforcement](#enforcement-the-off-chain-blacklist) below. ## Mechanics [#mechanics] Two per-subnet hyperparameters define the whole system: | — | name | meaning | default | | --- | ------------------------------------------------------------------------ | -------------------------------------------------------- | ------------ | | `p` | [`collateral_lock_share`](/docs/hyperparameters/collateral-lock-share) | share of the registration price locked instead of burned | 0 (disabled) | | `k` | [`collateral_drain_ratio`](/docs/hyperparameters/collateral-drain-ratio) | alpha released per alpha of hotkey emission earned | 1.0 | With price `T` (the same floating price described in [registration burn](/docs/guides/mining/burn)), registration charges: ``` burned = (1 − p) × T — recycled, sunk locked collateral = max(0, p × T − standing collateral) — staked to your hotkey, locked ``` There is no special first-time case: a fresh hotkey has zero standing collateral and pays the full `p × T`; a returning hotkey's existing lock is valued at the subnet's moving-average price and credited, so it usually pays only the burned share. This is implemented in [`do_register`](/code/pallets/subtensor/src/subnets/registration.rs#L66-L127) via [`get_collateral_topup_tao`](/code/pallets/subtensor/src/subnets/collateral.rs#L87-L109) and [`pay_registration`](/code/pallets/subtensor/src/subnets/collateral.rs). The locked alpha is real stake owned by your coldkey on your hotkey — it appreciates and depreciates with the subnet's pool like any alpha — but it is **not withdrawable**: the unstake guard ([`stake_availability`](/code/pallets/subtensor/src/staking/lock.rs#L746-L755)) subtracts it from what your coldkey can remove, and a per-hotkey check ([`ensure_hotkey_covers_collateral`](/code/pallets/subtensor/src/subnets/collateral.rs)) keeps the bond on that hotkey (sibling free stake cannot cover it). If the subnet dissolves, the lock metadata is cleared after alpha is settled to coldkey free TAO with everyone else's stake — there is no separate collateral refund step. ### Earning it back [#earning-it-back] Each tempo, when the chain credits your hotkey emission — miner incentive or validator dividends ([`distribute_dividends_and_incentives`](/code/pallets/subtensor/src/coinbase/run_coinbase.rs#L669-L766)) — it also releases collateral: ``` released = min(locked − min_locked, k × emission) ``` (`min_locked` is your optional self-maintained floor, described below; it is zero unless you set one, giving the plain `min(locked, k × emission)`.) ([`settle_miner_collateral`](/code/pallets/subtensor/src/subnets/collateral.rs#L193-L229)). Released collateral simply becomes withdrawable stake — it was your alpha all along; only the lock flag moves. At `k = 1`, every alpha you earn frees one alpha of collateral: a hotkey that has earned emissions equal to their lock is fully released. At `k = 0.5` you must earn two alpha per alpha released — subnets use low `k` to keep hotkeys collateralized longer. Your `k` is **snapshot at registration** into your collateral entry: owner changes to the drain ratio apply to future registrations, never to your standing lock. ### Voluntary collateral: `add_collateral` and `set_min_collateral` [#voluntary-collateral-add_collateral-and-set_min_collateral] Beyond the registration lock, two miner-initiated calls let a subnet run deposit-style policies (for example, "post X alpha per machine you bring"): | Call | What it does | Units | | --------------------------------------------------- | ------------------------------------------- | --------------------------------------------------- | | [`add_collateral`](/docs/tx/add-collateral) | Lock more alpha on your own hotkey | alpha (free stake first; shortfall bought with TAO) | | [`set_min_collateral`](/docs/tx/set-min-collateral) | Set a self-maintaining floor under the lock | alpha | Both require the signing coldkey to own the hotkey. Neither is a subnet hyperparameter: owners publish the required amount off chain (docs, validator code); miners fund and park it; validators enforce by reading the metagraph and refusing to score under-collateralized hotkeys. The full Lium-style pattern is in [a GPU marketplace with per-machine deposits](#a-gpu-marketplace-with-per-machine-deposits-lium) below. #### `add_collateral` [#add_collateral] [`add_collateral`](/docs/tx/add-collateral) tops up the hotkey's locked collateral by the alpha amount you pass. Funding order: 1. **Free stake first.** Alpha already staked on that `(hotkey, coldkey)` position, above what is already locked, is reclassified as collateral. No swap, no slip. 2. **Buy the shortfall.** Only the remainder is bought with coldkey TAO (keep-alive fill-or-kill: a partial buy that would leave you under ED fails with `NotEnoughBalanceToStake`). The buy is priced like `add_stake_limit` — fill-or-kill against spot × (1 + `--rate-tolerance`, default 5%) — and the CLI / SDK always submit it MEV-shielded (`--no-mev-shield` is refused). Registration collateral buys use the same 5% runtime bound when a lock share is due. The new lock joins the same entry as registration collateral: same drain ratio snapshot (or the subnet's current `k` if this is your first lock on the hotkey), same persistence across deregistration, same credit on re-registration. There is still no withdrawal path other than earned emission. ```bash # Prefer the collateral subcommand; btcli tx add-collateral is equivalent. btcli collateral add --netuid 51 --amount-alpha 100 -w my_coldkey -H my_hotkey btcli collateral add --netuid 51 --amount-alpha 100 -w my_coldkey -H my_hotkey --dry-run ``` ```python # execute() redirects AddCollateral to submit_shielded automatically. await client.execute( bt.AddCollateral(netuid=51, amount_alpha=100, hotkey_ss58=wallet.hotkey_address), wallet, ) ``` Emits `CollateralLocked { locked, total_locked }` for the amount just added. Fails with `AmountTooLow` if `alpha` is zero, `NonAssociatedColdKey` if you don't own the hotkey, `StakeUnavailable` if free stake moved out from under the preflight, `SlippageTooHigh` if the shortfall buy would clear above the limit, or the usual staking errors when buying the shortfall. #### `set_min_collateral` [#set_min_collateral] [`set_min_collateral`](/docs/tx/set-min-collateral) sets a **floor** (`min_locked`) the lock self-maintains around. It does not move stake by itself: ``` released each tempo = min(locked − min_locked, k × emission) # never below the floor while locked < min_locked: earned emission is captured into the lock until the floor is met ``` Without a floor, tracking a validator-published requirement means re-running `add_collateral` every tempo as the drain releases headroom. With one, the chain parks the lock and tops it up from emission when it dips. **Zero clears the floor** and restores pure drain behavior (the exit step in the Lium wind-down). ```bash btcli collateral set-min --netuid 51 --min-alpha 100 -w my_coldkey -H my_hotkey btcli collateral set-min --netuid 51 --min-alpha 0 -w my_coldkey -H my_hotkey # clear ``` ```python await client.execute( bt.SetMinCollateral(netuid=51, min_alpha=100, hotkey_ss58=wallet.hotkey_address), wallet, ) ``` Emits `MinCollateralSet { min_locked }`. Raising the floor above your current lock needs no fresh capital: the shortfall fills from future emission. But validators see your **actual** `collateral_locked`, not your floor, so fund with `add_collateral` when coverage must hold now (before the next scored tempos). Typical sequence for a deposit policy: ```bash btcli collateral add --netuid 51 --amount-alpha 100 -w my_coldkey -H my_hotkey btcli collateral set-min --netuid 51 --min-alpha 100 -w my_coldkey -H my_hotkey btcli collateral show --netuid 51 -H my_hotkey btcli collateral list --netuid 51 # what validators read ``` ```python await client.execute(bt.AddCollateral(netuid=51, amount_alpha=100), wallet) await client.execute(bt.SetMinCollateral(netuid=51, min_alpha=100), wallet) state = await client.collateral.miner_collateral( netuid=51, hotkey_ss58=wallet.hotkey_address, ) # state["locked_alpha"], ["min_locked_alpha"], ["earned_alpha"], ["headroom_alpha"], ... ``` Query the same fields on-chain with [`miner-collateral`](/docs/query/miner-collateral) (one position) or [`subnet-collateral`](/docs/query/subnet-collateral) (every entry on the subnet, including dormant bonds). Registered neurons also expose `collateral_locked`, `collateral_min`, and `collateral_earned` on the [`metagraph`](/docs/query/metagraph). The whole lifecycle in one picture. Drag the sliders for lock share, drain ratio, floor, and a mid-career `add_collateral`, and switch to the blacklisted scenario to see stranding: ### Deregistration and re-registration [#deregistration-and-re-registration] Being pruned does not touch collateral. The lock freezes in place, attached to `(netuid, hotkey)`, and waits. When you re-register the same hotkey, it is credited against the current requirement — the single registration formula above handles it — so a pruned miner returns for roughly the burned share alone. If the price or lock share rose since, you top up the difference; if your standing lock exceeds the current requirement, you pay only the burn and the excess keeps draining as you earn. There is deliberately **no withdrawal path**. Collateral converts back to withdrawable stake only through earned emission. Abandoning a hotkey strands whatever is still locked (a standing incentive to come back and earn it out); [`swap-hotkey`](/docs/tx/swap-hotkey) moves the lock to the new hotkey along with everything else ([`swap_miner_collateral`](/code/pallets/subtensor/src/subnets/collateral.rs#L348-L364)). ### Enforcement: the off-chain blacklist [#enforcement-the-off-chain-blacklist] The chain never adjudicates miner behavior — there is no ban extrinsic. The teeth are simpler: validators score miners off-chain, and a validator that concludes a hotkey is cheating simply **stops evaluating it**. Zero incentive means zero collateral release, forever. The lock strands — economically confiscated, without any judicial machinery: * It is **stake-weighted by construction**: one validator refusing to score you reduces your incentive by its weight in consensus; a supermajority blacklist freezes your collateral outright. * It is **reversible**: if validators resume evaluating (wrong call, or the miner reformed), the lock resumes draining. * Nobody profits from it: frozen collateral pays no one, so there is no bounty for banning. The residual escape — rotating to a fresh hotkey — costs the full registration price including a fresh full lock. A bonded hotkey may still swap with `keep_stake=false` (the lock migrates with the UID); swaps with `keep_stake=true` are refused while any collateral remains ([`KeepStakeBlockedByCollateral`](/docs/errors/chain/KeepStakeBlockedByCollateral)). Per-subnet lineage maps (`HotkeyRoot` / `HotkeySuccessor`) let validators follow hotkey renames without archives; coldkey swaps write global `ColdkeyRoot` / `ColdkeySuccessor` for owner identity. Prefer `hotkey_root` / `coldkey_root` over tip walks. See [The V437 Upgrade](/releases/v436-upgrade). The system's invariant is exactly that: *forgetting is never cheaper than `T`.* **Note for validator authors**: score by **hotkey root** (or persist successor edges), not UID. UIDs are recycled slots; a miner must not be able to launder a bad track record by cycling registration or renaming. Treat scoring gaps conservatively — absence should never improve a score. ## Worked examples [#worked-examples] ### A trading subnet deters tail-risk farming [#a-trading-subnet-deters-tail-risk-farming] Netuid 42 rewards trading signals by Sortino ratio. The metric is blind to strategies that sell tail risk: a martingale posts a top-percentile score for months, farms emissions, then blows up. Under pure burn (price τ10), a farmer who collects τ80 of emissions before the blow-up nets τ70 per cycle and repeats with a fresh hotkey. The owner sets `p = 90%` and `k = 0.2`: ```bash btcli sudo set --netuid 42 --name collateral_lock_share --value 0.9 # 90% (raw 58982 also accepted) btcli sudo set --netuid 42 --name collateral_drain_ratio --value 0.2 ``` Registration now charges the same floating price, split τ1 burned + τ9 locked. The farmer's arithmetic inverts: farming `E` before validators stop scoring the hotkey yields `E` (emissions) `+ 0.2·E` (released collateral) `− T` (price paid) — they must farm `E* = T / 1.2 ≈ 0.83·T` before detection just to break even, and each detected cycle strands the rest of a fresh τ9 lock. The honest miner's costs are nearly unchanged: τ1 sunk (the burn), and a τ9 lock they release by doing exactly what they registered to do. **Validator enforcement.** The chain side needs nothing from validators — the lock and drain run themselves. The validator's whole job is the two invariants from [enforcement](#enforcement-the-off-chain-blacklist): score by hotkey with persistent history, and stop scoring hotkeys whose returns show the martingale signature. Stranding follows automatically: ```python import bittensor as bt blacklist: set[str] = load_state("blacklist") # persists across restarts history: dict[str, list[float]] = load_state("returns") # keyed by HOTKEY, not uid async with bt.Subtensor() as client: metagraph = await client.subnets.metagraph(42) for neuron in metagraph.neurons: returns = history.setdefault(neuron.hotkey, []) returns.extend(await fetch_new_returns(neuron)) # gaps never improve a score # A top-percentile Sortino with no downside observations IS the signature. if is_martingale(returns) or neuron.hotkey in blacklist: blacklist.add(neuron.hotkey) scores[neuron.uid] = 0.0 # zero incentive: drain freezes, bond strands else: scores[neuron.uid] = sortino(returns) await client.execute(bt.SetWeights(netuid=42, weights=normalize(scores)), wallet) ``` ### A GPU marketplace with per-machine deposits (Lium) [#a-gpu-marketplace-with-per-machine-deposits-lium] The [Lium](https://lium.io) pattern: miners bring machines, each machine must be backed by a deposit, pulling a rented machine forfeits it, and an honest departure winds down cleanly. The chain provides amount-agnostic rails; the *per-machine* policy lives in validator code. **Owner config** — a modest registration bond plus deposit-friendly drain: ```bash btcli sudo set --netuid 51 --name collateral_lock_share --value 0.5 # p = 50% btcli sudo set --netuid 51 --name collateral_drain_ratio --value 1.0 # k = 1 ``` The subnet publishes its policy in validator code: **25α of collateral per machine**. Sizing the deposit in incentive-terms matters: if one machine earns \~1α/tempo, a 25α deposit at `k = 1` takes \~25 tempos of scored work to drain — so an exiting miner's wind-down lasts about one rental term, which is exactly the notice period the business wants. **Miner flow** — bring 4 machines, lock 100α of collateral (free stake on the hotkey is used first; only the shortfall is bought with TAO), then set a 100α floor so the lock holds: ```bash btcli collateral add --netuid 51 --amount-alpha 100 -w my_coldkey -H my_hotkey btcli collateral set-min --netuid 51 --min-alpha 100 -w my_coldkey -H my_hotkey ``` Both the top-up and the floor are in alpha. The floor stops the drain at 100α, and if the lock ever dips under (say the miner raises the floor for a fifth machine before funding it), incentive fills the gap automatically. No re-locking, no bot. **Validator enforcement** — collateral rides the metagraph, so the check costs zero extra calls: every neuron row carries `collateral_locked`, `collateral_min` (the miner's floor), and `collateral_earned` (lifetime incentive since the bond existed): ```python import bittensor as bt PER_MACHINE_ALPHA = 25 async with bt.Subtensor() as client: metagraph = await client.subnets.metagraph(51) for neuron in metagraph.neurons: machines = await verify_hardware(neuron) # the subnet's own attestation required = machines * PER_MACHINE_ALPHA if neuron.collateral_locked is None or neuron.collateral_locked.alpha < required: scores[neuron.uid] = 0.0 # under-collateralized: no rentals, no incentive continue route_rentals(neuron, machines) scores[neuron.uid] = availability_score(neuron) ``` For hotkeys *not* in the metagraph — deregistered miners whose bonds persist (dormant bonds, the population most likely to return) — use [`subnet_collateral`](/docs/query/subnet-collateral): every collateral entry on the subnet with `uid` (None when deregistered), owning `coldkey`, and the derived terms `headroom_alpha` (portion actively draining above the floor), `shortfall_alpha` (capture in progress below it), and `releasable_work_alpha` (incentive still needed to release the headroom). **Exit protocol** — the deposit leaves through the same door it drains through: 1. The miner signals departure (the subnet's API, or an on-chain [`set_commitment`](/docs/concepts/advanced#raw-calls-the-escape-hatch) raw call) and clears the floor: `btcli collateral set-min --netuid 51 --min-alpha 0`. 2. Validators stop routing *new* rentals to them but keep scoring the machines still serving existing rentals — and waive the coverage floor for a miner in wind-down. 3. With the floor cleared and emission still flowing, the ordinary drain returns the deposit at `k × emission` per tempo; by the time the last rental ends, most of the 100α is withdrawable again. 4. **Pulling a rented machine instead** zeroes their score immediately: drain stops, the remaining deposit strands, and it stays stranded unless validators resume scoring them (forgiveness) or they return and mine it out. Their track record — keyed by hotkey — is waiting for them either way. The deterrence is the same stranding mechanism as everywhere else in this system; what the marketplace adds is only *sizing* (per machine) and the *wind-down convention*, both of which live in the subnet's own code. ### A miner's lifecycle on a collateral subnet [#a-miners-lifecycle-on-a-collateral-subnet] Price τ10, `p = 80%`, `k = 1`: 1. **Register**: pay τ10 — τ2 burned, τ8 staked to your hotkey and locked (about 8 alpha at an alpha price of τ1). A `CollateralLocked` event records it; query your entry under `MinerCollateral(netuid, hotkey)`. 2. **Earn**: you earn 0.5 alpha of emission per tempo. Each tempo releases 0.5 alpha of the lock. After \~16 tempos of that rate you are fully released; everything you earn is withdrawable immediately either way. 3. **Get pruned** at 3 alpha still locked: the 3 alpha freezes. Nothing to do, nothing lost. 4. **Re-register** next week: requirement is 80% of the current price, your 3 alpha lock is credited at the subnet's moving-average price, and you top up only the shortfall — plus the burned share, which is always charged. 5. **Quit forever**: the remaining lock stays frozen on the hotkey. It is still yours, still earning pool appreciation, but only mining releases it. ## Budgeting notes for miners [#budgeting-notes-for-miners] * Total capital at the door is the full price `T` regardless of `p`; the difference is how much of it you can earn back. Check both the price and the split before registering: ```bash btcli query burn --netuid 42 --json btcli sudo get --netuid 42 --name collateral_lock_share btcli sudo get --netuid 42 --name collateral_drain_ratio ``` * The lock is denominated in **alpha units** at lock time. Its TAO value floats with the pool — collateral on a subnet you believe in is not dead weight, and collateral on a subnet you attack devalues with your attack. * Unstake math: your withdrawable balance on the subnet is your total alpha minus conviction locks minus collateral locks. `remove_stake`, `recycle`, `burn_alpha`, and stake moves all respect the guard (they fail with `StakeUnavailable` if they would dip into locked collateral). * Registration fails with `NotEnoughBalanceToStake` if your coldkey cannot cover the burned share plus your collateral top-up. ## Owner guidance [#owner-guidance] `p` decides how much of the entry price is accountability rather than toll; `k` decides how long it takes a good miner to work it off: * **`p = 0`** — pure burn, exactly the pre-collateral chain behavior. The safe default; nothing changes for your subnet until you opt in. * **Moderate bond (`p = 75%`, `k = 1`)** — general-purpose deterrence: sybils and squatters forfeit; honest miners recover at par. * **Anti-tail-risk (`p = 83–90%`, `k = 0.2–0.5`)** — subnets whose scoring can be gamed on short horizons (trading, forecasting): a large lock with a slow drain keeps your fastest *apparent* earners collateralized through your detection window. * **Fast release (`p = 67%`, `k = 2`)** — capital gate at entry with a minimal ongoing lockup, for low-stakes work. Both settings apply **prospectively only** — every miner's lock and drain ratio are snapshot at their registration — so raising `p` never retro-locks incumbents, and neither parameter can be used to rug standing collateral. The lock share is capped at 95% ([`MaxCollateralLockShare`](/code/pallets/subtensor/src/lib.rs#L433-L437)): the burned share must stay positive so re-registration always costs a nonzero, floating burn. The drain ratio must be positive (a `k` of zero would make the lock unrecoverable — a burn with extra steps) and at most 10. ## Chain reference [#chain-reference] **Terms** — the shared vocabulary for validators and dashboards, as returned by [`subnet_collateral`](/docs/query/subnet-collateral) (and, for registered neurons, as per-uid vectors on the [`metagraph`](/docs/query/metagraph)): * **locked** — the bond: what a blacklist strands and re-registration credits. * **min\_locked** — the floor: the miner's standing commitment. * **earned** — lifetime emission since the collateral relationship began; compare against the detection budget `E* = max(T ÷ (1 + k), (1 − p)·T)` to see how far a hotkey is from having "paid off" their bond. Lives and dies with the entry. * **headroom** = `locked − min_locked` — the portion actively draining; zero with a floor set means parked at commitment. * **shortfall** = `min_locked − locked` — capture in progress: emission is filling a raised floor instead of being paid out. * **releasable work** = `headroom ÷ k` — emission still needed to release everything above the floor. * **dormant bond** — `locked > 0` with no `uid`: a deregistered miner whose capital waits on the subnet; the population most likely to return. **Storage** — [`CollateralLockShare`](/code/pallets/subtensor/src/lib.rs#L2786-L2793), [`CollateralDrainRatio`](/code/pallets/subtensor/src/lib.rs#L2795-L2801), [`MinerCollateral`](/code/pallets/subtensor/src/lib.rs#L2803-L2818) holding [`MinerCollateralState { locked, drain_ratio, min_locked, earned }`](/code/pallets/subtensor/src/lib.rs#L364-L389). **Extrinsics** — [`add_collateral`](/code/pallets/subtensor/src/subnets/collateral.rs#L235-L292) and [`set_min_collateral`](/code/pallets/subtensor/src/subnets/collateral.rs#L299-L342) (miner, must own the hotkey); [`sudo_set_collateral_lock_share`](/code/pallets/admin-utils/src/lib.rs#L2267-L2309), [`sudo_set_collateral_drain_ratio`](/code/pallets/admin-utils/src/lib.rs#L2311-L2354) (owner-or-root, rate-limited, admin-window gated). **Events** — `CollateralLocked { netuid, hotkey, locked, total_locked }` on registration and `add_collateral`; `MinCollateralSet { netuid, hotkey, min_locked }`; `CollateralLockShareSet` / `CollateralDrainRatioSet` on hyperparameter changes. **Errors** — [`CollateralLockShareTooHigh`](/docs/errors/chain/CollateralLockShareTooHigh) (owner tried to set `p` above 95%), [`CollateralDrainRatioOutOfBounds`](/docs/errors/chain/CollateralDrainRatioOutOfBounds) (owner tried to set `k ≤ 0` or above 10). Miners hit the pre-existing `NotEnoughBalanceToStake` (can't cover burn + top-up) and `StakeUnavailable` (unstake would dip into locked collateral). ## Related [#related] [Registration burn](/docs/guides/mining/burn), [`add_collateral`](/docs/tx/add-collateral), [`set_min_collateral`](/docs/tx/set-min-collateral), [`miner-collateral`](/docs/query/miner-collateral), [`subnet-collateral`](/docs/query/subnet-collateral), [`collateral_lock_share`](/docs/hyperparameters/collateral-lock-share), [`collateral_drain_ratio`](/docs/hyperparameters/collateral-drain-ratio), [emissions](/docs/concepts/emissions) # Mining (/docs/guides/mining) A miner is a hotkey registered on a subnet, doing whatever work that subnet's incentive mechanism rewards. The chain side of mining is small: register, tell validators where to reach you, and don't get evicted. The work itself — the model, the service, the actual commodity — is defined by each subnet's own codebase, not by the chain. Registering costs the subnet's floating **registration price**, and what that payment buys depends on the subnet's configuration: * On most subnets the whole price is **burned** — a sunk, non-refundable entry fee. See [registration burn](/docs/guides/mining/burn). * Subnets can instead lock a share of the price as **collateral** — stake attached to your hotkey that you earn back by mining. See [registration collateral](/docs/guides/mining/collateral). Some also ask for extra deposits via [`add_collateral`](/docs/tx/add-collateral) and a floor via [`set_min_collateral`](/docs/tx/set-min-collateral). Mining (and validating) is not supported on Windows. Wallet operations work under WSL 2, but run miners on Linux or macOS. ## 1. Scope the subnet [#1-scope-the-subnet] ```bash btcli subnets list btcli subnets show 1 btcli query burn --netuid 1 --json # current registration price btcli query subnet-hyperparameters --netuid 1 --json # immunity, limits, collateral_* btcli query collateral-policy --netuid 1 --json # lock share + drain ratio only btcli query metagraph --netuid 1 --json # who you're competing with ``` Choosing where to start matters more than the registration price. Eviction only happens when a subnet is at capacity, so an under-capacity or new subnet lets you learn without deregistration risk; a mature high-emission subnet means competing against miners tuned over months. Each subnet's repo conventionally documents hardware needs (`min_compute.yml` or the README), and most subnets run on a testnet netuid — test there or on a local chain before paying the registration price. Check the subnet's collateral policy before budgeting: [`collateral_lock_share`](/docs/hyperparameters/collateral-lock-share) (also on `subnet-hyperparameters` / [`collateral-policy`](/docs/query/collateral-policy)) tells you what fraction of the price is a recoverable lock rather than a burn. Note also whose hotkey you register: mining on the subnet owner's hotkey (or its associated hotkeys) is pointless — miner emission directed at owner-associated hotkeys is never paid out, it is burned or recycled (burned by default, per the subnet's `RecycleOrBurn` setting). ## 2. Register [#2-register] [`burned-register`](/docs/tx/burned-register) charges the subnet's current registration price from your coldkey and assigns your hotkey a **UID**: ```bash btcli tx burned-register --netuid 1 --dry-run -w my_coldkey btcli tx burned-register --netuid 1 -w my_coldkey ``` ```python price = await client.subnets.burn(netuid=1) # current registration cost await client.execute(bt.BurnedRegister(netuid=1), wallet) ``` The UID goes to the wallet's hotkey by default; pass `hotkey_ss58` to register a different one. Registration is continuous — there are no registration windows. Admission is governed entirely by the dynamic price described in [registration burn](/docs/guides/mining/burn): it decays every block and jumps after every successful registration, so a rush gets expensive fast. The cost floats and is only known at execution time, so a configured `Policy` spend cap blocks this call until raised. Confirm with [`uid`](/docs/query/uid) or [`netuids-for-hotkey`](/docs/query/netuids-for-hotkey). One hotkey can hold UIDs on multiple subnets simultaneously, but only one UID per subnet. ## 3. UID capacity and eviction [#3-uid-capacity-and-eviction] A subnet has a fixed number of UID slots: default 256, owner-trimmable down to a floor of 64. When the subnet is full, each new registration **evicts one existing neuron** — the one with the lowest pruning score that is not immune. The rules, as implemented in the chain's pruning logic: * The pruning score is **emission-based**. The chain does not distinguish miners from validators when pruning; whoever earns least is first out. * Immunity is `(current_block - registered_at) < immunity_period`. The chain default is 4096 blocks (about 13.7 hours at 12-second blocks), but real subnets often set it much higher — read the actual value with [`immunity-period`](/docs/query/immunity-period) before you plan around it. * If every neuron is still immune, the lowest-scoring immune neuron is pruned anyway; immunity is a preference, not a guarantee. * Ties on pruning score evict the **older** registration first. * The subnet owner's hotkey is permanently immune (bounded by an owner-immune limit, default 1 hotkey). Practical consequence: your immunity period is your runway. If the subnet's `immunity_period` is 4096 blocks, you have about half a day to start earning emissions before you're evictable. Eviction is mechanical, not a judgment. It costs you the UID and nothing else: your stake stays where it is, and on a collateral subnet your locked collateral stays attached to the hotkey and is credited when you [re-register](/docs/guides/mining/collateral#deregistration-and-re-registration). ## 4. Publish your endpoint [#4-publish-your-endpoint] Validators find you through the axon info stored on chain. [`serve-axon`](/docs/tx/serve-axon) writes your `ip:port`; it does **not** start a server — running the actual service is your job: ```bash btcli tx serve-axon --netuid 1 --ip 203.0.113.7 --port 8091 -w my_coldkey -H my_hotkey ``` Use [`serve-axon-tls`](/docs/tx/serve-axon-tls) if peers should verify a TLS certificate, and [`reset-axon`](/docs/tx/reset-axon) to stop advertising. Re-publishing is rate-limited by the chain. **Moving to another machine:** start the miner on the new machine and publish the new axon first, wait until the old machine stops receiving validator requests, then stop it. Validators take time to pick up an updated IP. All miner traffic comes through validators — only they can score you, so there is no reason to serve anyone else. Requests transit validators and miners with no confidentiality guarantee in either direction; subnets are not a channel for private data. ## 5. Watch your standing [#5-watch-your-standing] ```bash btcli query metagraph --netuid 1 --json # your rank, incentive, emission btcli query blocks-since-last-update --netuid 1 --uid 0 --json btcli query immunity-period --netuid 1 ``` Expect discovery lag. Emissions are credited at **tempo boundaries** (default tempo 360 blocks, about 72 minutes), and validators typically refresh their metagraph periodically rather than every block — a freshly registered miner can sit at zero for a while before validators start querying it. That lag is exactly what the immunity period is for. When the subnet runs commit-reveal, add the reveal period to every number you watch: validator scores on chain are stale by that window, so do **not** use your on-chain score as a liveness signal — by the time it drops, the failure is old and deregistration is likely already unavoidable. Monitor your own service directly. Longevity compounds. Validators' bonds toward a miner build up as an exponential moving average, so an established miner out-earns an identical newcomer until the newcomer's bonds mature — see [emissions](/docs/concepts/emissions) for the bond math. On collateral subnets, longevity also pays down your lock: every alpha of incentive you earn releases collateral back to withdrawable stake. Rewards accrue to the hotkey as stake. Have them auto-staked to a hotkey of your choice with [`set-auto-stake`](/docs/tx/set-auto-stake). ## Key hygiene [#key-hygiene] * Sign operational calls with the hotkey; keep the coldkey off the mining box entirely. Mining stacks pull in large untrusted dependency trees (ML frameworks, model code), and any of it could exfiltrate a key file — see [wallets](/docs/concepts/wallets) for the coldkey/hotkey split. Use a [proxy](/docs/concepts/advanced) if the box must submit coldkey-signed calls. * Create hotkeys on a trusted machine and transfer only the hotkey file (or mnemonic) to the miner. * If the hotkey is compromised, [`swap-hotkey`](/docs/tx/swap-hotkey) replaces it while keeping its registrations — and, on collateral subnets, its locked collateral, which follows the hotkey. # 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. Declared by the `Commitments` pallet; it classifies to the semantic code [`not_authorized`](/docs/errors/not-authorized). Declared at [`pallets/commitments/src/lib.rs#L121`](/code/pallets/commitments/src/lib.rs#L121). ## Remediation [#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`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/subtensor/src/macros/errors.rs#L333`](/code/pallets/subtensor/src/macros/errors.rs#L333). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain AccountRejectsLockedAlpha`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`already_exists`](/docs/errors/already-exists). Declared at [`pallets/subtensor/src/macros/errors.rs#L311`](/code/pallets/subtensor/src/macros/errors.rs#L311). ## Remediation [#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`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/subtensor/src/macros/errors.rs#L321`](/code/pallets/subtensor/src/macros/errors.rs#L321). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain ActivityCutoffFactorMilliOutOfBounds`. # 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 `. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/subtensor/src/macros/errors.rs#L196`](/code/pallets/subtensor/src/macros/errors.rs#L196). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain ActivityCutoffTooLow`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`rate_limited`](/docs/errors/rate-limited). Declared at [`pallets/subtensor/src/macros/errors.rs#L293`](/code/pallets/subtensor/src/macros/errors.rs#L293). ## Remediation [#remediation] Wait for the rate-limit window to pass, then retry The same explanation is available in the terminal: `btcli explain AddStakeBurnRateLimitExceeded`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`too_early`](/docs/errors/too-early). Declared at [`pallets/subtensor/src/macros/errors.rs#L243`](/code/pallets/subtensor/src/macros/errors.rs#L243). ## Remediation [#remediation] The required window has not opened yet; wait some blocks and retry The same explanation is available in the terminal: `btcli explain AdminActionProhibitedDuringWeightsWindow`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`too_early`](/docs/errors/too-early). Declared at [`pallets/subtensor/src/macros/errors.rs#L111`](/code/pallets/subtensor/src/macros/errors.rs#L111). ## Remediation [#remediation] The required window has not opened yet; wait some blocks and retry The same explanation is available in the terminal: `btcli explain AllNetworksInImmunity`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/subtensor/src/macros/errors.rs#L136`](/code/pallets/subtensor/src/macros/errors.rs#L136). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain AlphaHighTooLow`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/subtensor/src/macros/errors.rs#L138`](/code/pallets/subtensor/src/macros/errors.rs#L138). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain AlphaLowOutOfRange`. # 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. Declared by the `Multisig` pallet; it classifies to the semantic code [`already_exists`](/docs/errors/already-exists). ## Remediation [#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`. # 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. Declared by the `SafeMode` pallet; it classifies to the semantic code [`already_exists`](/docs/errors/already-exists). ## Remediation [#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`. # 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`. Declared by the `Crowdloan` pallet; it classifies to the semantic code [`already_exists`](/docs/errors/already-exists). Declared at [`pallets/crowdloan/src/lib.rs#L267`](/code/pallets/crowdloan/src/lib.rs#L267). ## Remediation [#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`. # 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. Declared by the `Crowdloan` pallet; it classifies to the semantic code [`already_exists`](/docs/errors/already-exists). Declared at [`pallets/crowdloan/src/lib.rs#L269`](/code/pallets/crowdloan/src/lib.rs#L269). ## Remediation [#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`. # 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. Declared by the `Preimage` pallet; it classifies to the semantic code [`already_exists`](/docs/errors/already-exists). ## Remediation [#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`. # 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. Declared by the `Multisig` pallet; it classifies to the semantic code [`already_exists`](/docs/errors/already-exists). ## Remediation [#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`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/subtensor/src/macros/errors.rs#L188`](/code/pallets/subtensor/src/macros/errors.rs#L188). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain AmountTooLow`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/subtensor/src/macros/errors.rs#L160`](/code/pallets/subtensor/src/macros/errors.rs#L160). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain AnnouncedColdkeyHashDoesNotMatch`. # 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. Declared by the `Proxy` pallet; it classifies to the semantic code [`internal`](/docs/errors/internal). Declared at [`pallets/proxy/src/lib.rs#L795`](/code/pallets/proxy/src/lib.rs#L795). ## Remediation [#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`. # 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. Declared by the `LimitOrders` pallet; it classifies to the semantic code [`internal`](/docs/errors/internal). Declared at [`pallets/limit-orders/src/lib.rs#L352`](/code/pallets/limit-orders/src/lib.rs#L352). ## Remediation [#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`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`already_exists`](/docs/errors/already-exists). Declared at [`pallets/subtensor/src/macros/errors.rs#L327`](/code/pallets/subtensor/src/macros/errors.rs#L327). ## Remediation [#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`. # 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. Declared by the `MevShield` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/shield/src/lib.rs#L234`](/code/pallets/shield/src/lib.rs#L234). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain BadEncKeyLen`. # 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. Declared by the `EVM` pallet; it classifies to the semantic code [`insufficient_balance`](/docs/errors/insufficient-balance). ## Remediation [#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`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`insufficient_balance`](/docs/errors/insufficient-balance). Declared at [`pallets/subtensor/src/macros/errors.rs#L43`](/code/pallets/subtensor/src/macros/errors.rs#L43). ## Remediation [#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`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`not_authorized`](/docs/errors/not-authorized). Declared at [`pallets/subtensor/src/macros/errors.rs#L239`](/code/pallets/subtensor/src/macros/errors.rs#L239). ## Remediation [#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`. # 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`. Declared by the `Crowdloan` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/crowdloan/src/lib.rs#L251`](/code/pallets/crowdloan/src/lib.rs#L251). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain BlockDurationTooLong`. # 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`. Declared by the `Crowdloan` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/crowdloan/src/lib.rs#L249`](/code/pallets/crowdloan/src/lib.rs#L249). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain BlockDurationTooShort`. # 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. Declared by the `AdminUtils` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). Declared at [`pallets/admin-utils/src/lib.rs#L149`](/code/pallets/admin-utils/src/lib.rs#L149). ## Remediation [#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`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`disabled`](/docs/errors/disabled). Declared at [`pallets/subtensor/src/macros/errors.rs#L198`](/code/pallets/subtensor/src/macros/errors.rs#L198). ## Remediation [#remediation] This call or feature is switched off on this network The same explanation is available in the terminal: `btcli explain CallDisabled`. # 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. Declared by the `System` pallet; it classifies to the semantic code [`not_authorized`](/docs/errors/not-authorized). ## Remediation [#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`. # 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. Declared by the `Crowdloan` pallet; it classifies to the semantic code [`not_found`](/docs/errors/not-found). Declared at [`pallets/crowdloan/src/lib.rs#L279`](/code/pallets/crowdloan/src/lib.rs#L279). ## Remediation [#remediation] The referenced object is not on-chain; check the identifier The same explanation is available in the terminal: `btcli explain CallUnavailable`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/subtensor/src/macros/errors.rs#L117`](/code/pallets/subtensor/src/macros/errors.rs#L117). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain CanNotSetRootNetworkWeights`. # 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. Declared by the `Contracts` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain CannotAddSelfAsDelegateDependency`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`insufficient_balance`](/docs/errors/insufficient-balance). Declared at [`pallets/subtensor/src/macros/errors.rs#L261`](/code/pallets/subtensor/src/macros/errors.rs#L261). ## Remediation [#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`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/subtensor/src/macros/errors.rs#L206`](/code/pallets/subtensor/src/macros/errors.rs#L206). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain CannotBurnOrRecycleOnRootSubnet`. # 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. Declared by the `Crowdloan` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/crowdloan/src/lib.rs#L247`](/code/pallets/crowdloan/src/lib.rs#L247). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain CannotEndInPast`. # 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. Declared by the `SafeMode` pallet; it classifies to the semantic code [`too_early`](/docs/errors/too-early). ## Remediation [#remediation] The required window has not opened yet; wait some blocks and retry The same explanation is available in the terminal: `btcli explain CannotReleaseYet`. # 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`. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`not_authorized`](/docs/errors/not-authorized). Declared at [`pallets/subtensor/src/macros/errors.rs#L313`](/code/pallets/subtensor/src/macros/errors.rs#L313). ## Remediation [#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`. # 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. Declared by the `Crowdloan` pallet; it classifies to the semantic code [`too_early`](/docs/errors/too-early). Declared at [`pallets/crowdloan/src/lib.rs#L275`](/code/pallets/crowdloan/src/lib.rs#L275). ## Remediation [#remediation] The required window has not opened yet; wait some blocks and retry The same explanation is available in the terminal: `btcli explain CapNotRaised`. # 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`. Declared by the `Crowdloan` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). Declared at [`pallets/crowdloan/src/lib.rs#L259`](/code/pallets/crowdloan/src/lib.rs#L259). ## Remediation [#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`. # 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. Declared by the `Crowdloan` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/crowdloan/src/lib.rs#L243`](/code/pallets/crowdloan/src/lib.rs#L243). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain CapTooLow`. # 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. Declared by the `LimitOrders` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/limit-orders/src/lib.rs#L346`](/code/pallets/limit-orders/src/lib.rs#L346). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain ChainIdMismatch`. # 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. Declared by the `Grandpa` pallet; it classifies to the semantic code [`already_exists`](/docs/errors/already-exists). ## Remediation [#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`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/subtensor/src/macros/errors.rs#L273`](/code/pallets/subtensor/src/macros/errors.rs#L273). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain ChildParentInconsistency`. # 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. Declared by the `Contracts` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain CodeInUse`. # 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. Declared by the `Contracts` pallet; it classifies to the semantic code [`not_found`](/docs/errors/not-found). ## Remediation [#remediation] The referenced object is not on-chain; check the identifier The same explanation is available in the terminal: `btcli explain CodeInfoNotFound`. # 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. Declared by the `Contracts` pallet; it classifies to the semantic code [`not_found`](/docs/errors/not-found). ## Remediation [#remediation] The referenced object is not on-chain; check the identifier The same explanation is available in the terminal: `btcli explain CodeNotFound`. # 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. Declared by the `Contracts` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain CodeRejected`. # 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. Declared by the `Contracts` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). ## Remediation [#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`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`already_exists`](/docs/errors/already-exists). Declared at [`pallets/subtensor/src/macros/errors.rs#L140`](/code/pallets/subtensor/src/macros/errors.rs#L140). ## Remediation [#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`. # ColdkeyCollateralIncomplete (/docs/errors/chain/ColdkeyCollateralIncomplete) A coldkey swap could not fully migrate miner collateral: after migrating every indexed collateral hotkey, the old coldkey's ColdkeyMinerCollateral aggregate was still non-zero. This is a fail-closed invariant — retry after investigating orphaned MinerCollateral rows for that coldkey, or contact runtime maintainers. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`internal`](/docs/errors/internal). Declared at [`pallets/subtensor/src/macros/errors.rs#L344`](/code/pallets/subtensor/src/macros/errors.rs#L344). ## Remediation [#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 ColdkeyCollateralIncomplete`. # ColdkeyCollateralPositionsFull (/docs/errors/chain/ColdkeyCollateralPositionsFull) This coldkey already has the maximum number of distinct hotkeys with miner collateral on the subnet. Drain or consolidate existing bonds before adding another collateral position. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). Declared at [`pallets/subtensor/src/macros/errors.rs#L348`](/code/pallets/subtensor/src/macros/errors.rs#L348). ## Remediation [#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 ColdkeyCollateralPositionsFull`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`already_exists`](/docs/errors/already-exists). Declared at [`pallets/subtensor/src/macros/errors.rs#L162`](/code/pallets/subtensor/src/macros/errors.rs#L162). ## Remediation [#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`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`already_exists`](/docs/errors/already-exists). Declared at [`pallets/subtensor/src/macros/errors.rs#L295`](/code/pallets/subtensor/src/macros/errors.rs#L295). ## Remediation [#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`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`not_found`](/docs/errors/not-found). Declared at [`pallets/subtensor/src/macros/errors.rs#L154`](/code/pallets/subtensor/src/macros/errors.rs#L154). ## Remediation [#remediation] The referenced object is not on-chain; check the identifier The same explanation is available in the terminal: `btcli explain ColdkeySwapAnnouncementNotFound`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`too_early`](/docs/errors/too-early). Declared at [`pallets/subtensor/src/macros/errors.rs#L299`](/code/pallets/subtensor/src/macros/errors.rs#L299). ## Remediation [#remediation] The required window has not opened yet; wait some blocks and retry The same explanation is available in the terminal: `btcli explain ColdkeySwapClearTooEarly`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`not_authorized`](/docs/errors/not-authorized). Declared at [`pallets/subtensor/src/macros/errors.rs#L297`](/code/pallets/subtensor/src/macros/errors.rs#L297). ## Remediation [#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`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`too_early`](/docs/errors/too-early). Declared at [`pallets/subtensor/src/macros/errors.rs#L158`](/code/pallets/subtensor/src/macros/errors.rs#L158). ## Remediation [#remediation] The required window has not opened yet; wait some blocks and retry The same explanation is available in the terminal: `btcli explain ColdkeySwapReannouncedTooEarly`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`too_early`](/docs/errors/too-early). Declared at [`pallets/subtensor/src/macros/errors.rs#L156`](/code/pallets/subtensor/src/macros/errors.rs#L156). ## Remediation [#remediation] The required window has not opened yet; wait some blocks and retry The same explanation is available in the terminal: `btcli explain ColdkeySwapTooEarly`. # CollateralDrainRatioOutOfBounds (/docs/errors/chain/CollateralDrainRatioOutOfBounds) `sudo_set_collateral_drain_ratio` was given a value that is zero, negative, or above `MaxCollateralDrainRatio` (10). The drain ratio must be strictly positive — with no other exit path for miner collateral, a ratio of zero would make locked collateral permanently unrecoverable. Pass a `drain_ratio` in the range (0, 10]. Declared by the `AdminUtils` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/admin-utils/src/lib.rs#L173`](/code/pallets/admin-utils/src/lib.rs#L173). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain CollateralDrainRatioOutOfBounds`. # CollateralLockShareTooHigh (/docs/errors/chain/CollateralLockShareTooHigh) `sudo_set_collateral_lock_share` was given a value above `MaxCollateralLockShare` (62258, \~95% of u16::MAX). The burned share of the registration price must stay strictly positive so re-registration always pays a nonzero, floating burn. Lower the `lock_share` argument to at most 62258. Declared by the `AdminUtils` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/admin-utils/src/lib.rs#L171`](/code/pallets/admin-utils/src/lib.rs#L171). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain CollateralLockShareTooHigh`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`disabled`](/docs/errors/disabled). Declared at [`pallets/subtensor/src/macros/errors.rs#L132`](/code/pallets/subtensor/src/macros/errors.rs#L132). ## Remediation [#remediation] This call or feature is switched off on this network The same explanation is available in the terminal: `btcli explain CommitRevealDisabled`. # 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`. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`disabled`](/docs/errors/disabled). Declared at [`pallets/subtensor/src/macros/errors.rs#L130`](/code/pallets/subtensor/src/macros/errors.rs#L130). ## Remediation [#remediation] This call or feature is switched off on this network The same explanation is available in the terminal: `btcli explain CommitRevealEnabled`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`rate_limited`](/docs/errors/rate-limited). Declared at [`pallets/subtensor/src/macros/errors.rs#L186`](/code/pallets/subtensor/src/macros/errors.rs#L186). ## Remediation [#remediation] Wait for the rate-limit window to pass, then retry The same explanation is available in the terminal: `btcli explain CommittingWeightsTooFast`. # 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. Declared by the `Contracts` pallet; it classifies to the semantic code [`not_found`](/docs/errors/not-found). ## Remediation [#remediation] The referenced object is not on-chain; check the identifier The same explanation is available in the terminal: `btcli explain ContractNotFound`. # 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. Declared by the `Contracts` pallet; it classifies to the semantic code [`internal`](/docs/errors/internal). ## Remediation [#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`. # 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. Declared by the `Contracts` pallet; it classifies to the semantic code [`internal`](/docs/errors/internal). ## Remediation [#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`. # 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. Declared by the `Crowdloan` pallet; it classifies to the semantic code [`expired`](/docs/errors/expired). Declared at [`pallets/crowdloan/src/lib.rs#L261`](/code/pallets/crowdloan/src/lib.rs#L261). ## Remediation [#remediation] The window has closed; restart the flow with fresh state The same explanation is available in the terminal: `btcli explain ContributionPeriodEnded`. # 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. Declared by the `Crowdloan` pallet; it classifies to the semantic code [`too_early`](/docs/errors/too-early). Declared at [`pallets/crowdloan/src/lib.rs#L271`](/code/pallets/crowdloan/src/lib.rs#L271). ## Remediation [#remediation] The required window has not opened yet; wait some blocks and retry The same explanation is available in the terminal: `btcli explain ContributionPeriodNotEnded`. # 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. Declared by the `Crowdloan` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/crowdloan/src/lib.rs#L263`](/code/pallets/crowdloan/src/lib.rs#L263). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain ContributionTooLow`. # 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. Declared by the `EVM` pallet; it classifies to the semantic code [`not_authorized`](/docs/errors/not-authorized). ## Remediation [#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`. # 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. Declared by the `SafeMode` pallet; it classifies to the semantic code [`internal`](/docs/errors/internal). ## Remediation [#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`. # 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. Declared by the `Balances` pallet; it classifies to the semantic code [`not_found`](/docs/errors/not-found). ## Remediation [#remediation] The referenced object is not on-chain; check the identifier The same explanation is available in the terminal: `btcli explain DeadAccount`. # 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. Declared by the `Contracts` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain DecodingFailed`. # 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. Declared by the `Contracts` pallet; it classifies to the semantic code [`already_exists`](/docs/errors/already-exists). ## Remediation [#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`. # 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. Declared by the `Contracts` pallet; it classifies to the semantic code [`not_found`](/docs/errors/not-found). ## Remediation [#remediation] The referenced object is not on-chain; check the identifier The same explanation is available in the terminal: `btcli explain DelegateDependencyNotFound`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/subtensor/src/macros/errors.rs#L123`](/code/pallets/subtensor/src/macros/errors.rs#L123). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain DelegateTakeTooHigh`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/subtensor/src/macros/errors.rs#L121`](/code/pallets/subtensor/src/macros/errors.rs#L121). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain DelegateTakeTooLow`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`rate_limited`](/docs/errors/rate-limited). Declared at [`pallets/subtensor/src/macros/errors.rs#L91`](/code/pallets/subtensor/src/macros/errors.rs#L91). ## Remediation [#remediation] Wait for the rate-limit window to pass, then retry The same explanation is available in the terminal: `btcli explain DelegateTxRateLimitExceeded`. # 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. Declared by the `Balances` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain DeltaZero`. # 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. Declared by the `Crowdloan` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/crowdloan/src/lib.rs#L283`](/code/pallets/crowdloan/src/lib.rs#L283). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain DepositCannotBeWithdrawn`. # 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. Declared by the `Crowdloan` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/crowdloan/src/lib.rs#L241`](/code/pallets/crowdloan/src/lib.rs#L241). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain DepositTooLow`. # 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`). Declared by the `SubtensorModule`, `AdminUtils`, `Swap` pallets; it classifies to the semantic code [`disabled`](/docs/errors/disabled). Declared at [`pallets/subtensor/src/macros/errors.rs#L287`](/code/pallets/subtensor/src/macros/errors.rs#L287), [`pallets/admin-utils/src/lib.rs#L169`](/code/pallets/admin-utils/src/lib.rs#L169), [`pallets/swap/src/pallet/mod.rs#L181`](/code/pallets/swap/src/pallet/mod.rs#L181). ## Remediation [#remediation] This call or feature is switched off on this network The same explanation is available in the terminal: `btcli explain Deprecated`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`disabled`](/docs/errors/disabled). Declared at [`pallets/subtensor/src/macros/errors.rs#L301`](/code/pallets/subtensor/src/macros/errors.rs#L301). ## Remediation [#remediation] This call or feature is switched off on this network The same explanation is available in the terminal: `btcli explain DisabledTemporarily`. # 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. Declared by the `Drand` pallet; it classifies to the semantic code [`internal`](/docs/errors/internal). Declared at [`pallets/drand/src/lib.rs#L264`](/code/pallets/drand/src/lib.rs#L264). ## Remediation [#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`. # 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. Declared by the `Proxy` pallet; it classifies to the semantic code [`already_exists`](/docs/errors/already-exists). Declared at [`pallets/proxy/src/lib.rs#L787`](/code/pallets/proxy/src/lib.rs#L787). ## Remediation [#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`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/subtensor/src/macros/errors.rs#L146`](/code/pallets/subtensor/src/macros/errors.rs#L146). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain DuplicateChild`. # 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. Declared by the `Contracts` pallet; it classifies to the semantic code [`already_exists`](/docs/errors/already-exists). ## Remediation [#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`. # 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. Declared by the `Grandpa` pallet; it classifies to the semantic code [`already_exists`](/docs/errors/already-exists). ## Remediation [#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`. # 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`. Declared by the `LimitOrders` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/limit-orders/src/lib.rs#L354`](/code/pallets/limit-orders/src/lib.rs#L354). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain DuplicateOrderInBatch`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/subtensor/src/macros/errors.rs#L53`](/code/pallets/subtensor/src/macros/errors.rs#L53). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain DuplicateUids`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`disabled`](/docs/errors/disabled). Declared at [`pallets/subtensor/src/macros/errors.rs#L331`](/code/pallets/subtensor/src/macros/errors.rs#L331). ## Remediation [#remediation] This call or feature is switched off on this network The same explanation is available in the terminal: `btcli explain DynamicTempoBlockedByCommitReveal`. # 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. Declared by the `SafeMode` pallet; it classifies to the semantic code [`already_exists`](/docs/errors/already-exists). ## Remediation [#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`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`already_exists`](/docs/errors/already-exists). Declared at [`pallets/subtensor/src/macros/errors.rs#L324`](/code/pallets/subtensor/src/macros/errors.rs#L324). ## Remediation [#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`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`rate_limited`](/docs/errors/rate-limited). Declared at [`pallets/subtensor/src/macros/errors.rs#L263`](/code/pallets/subtensor/src/macros/errors.rs#L263). ## Remediation [#remediation] Wait for the rate-limit window to pass, then retry The same explanation is available in the terminal: `btcli explain EvmKeyAssociateRateLimitExceeded`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). Declared at [`pallets/subtensor/src/macros/errors.rs#L265`](/code/pallets/subtensor/src/macros/errors.rs#L265). ## Remediation [#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`. # 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. Declared by the `Balances` pallet; it classifies to the semantic code [`insufficient_balance`](/docs/errors/insufficient-balance). ## Remediation [#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`. # 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. Declared by the `Balances` pallet; it classifies to the semantic code [`already_exists`](/docs/errors/already-exists). ## Remediation [#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`. # 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. Declared by the `SafeMode` pallet; it classifies to the semantic code [`already_exists`](/docs/errors/already-exists). ## Remediation [#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`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`not_authorized`](/docs/errors/not-authorized). Declared at [`pallets/subtensor/src/macros/errors.rs#L241`](/code/pallets/subtensor/src/macros/errors.rs#L241). ## Remediation [#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`. # 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. Declared by the `Balances` pallet; it classifies to the semantic code [`insufficient_balance`](/docs/errors/insufficient-balance). ## Remediation [#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`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`expired`](/docs/errors/expired). Declared at [`pallets/subtensor/src/macros/errors.rs#L180`](/code/pallets/subtensor/src/macros/errors.rs#L180). ## Remediation [#remediation] The window has closed; restart the flow with fresh state The same explanation is available in the terminal: `btcli explain ExpiredWeightCommit`. # 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. Declared by the `System` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain FailedToExtractRuntimeVersion`. # 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. Declared by the `Scheduler` pallet; it classifies to the semantic code [`internal`](/docs/errors/internal). ## Remediation [#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`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`disabled`](/docs/errors/disabled). Declared at [`pallets/subtensor/src/macros/errors.rs#L103`](/code/pallets/subtensor/src/macros/errors.rs#L103). ## Remediation [#remediation] This call or feature is switched off on this network The same explanation is available in the terminal: `btcli explain FaucetDisabled`. # 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. Declared by the `EVM` pallet; it classifies to the semantic code [`internal`](/docs/errors/internal). ## Remediation [#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`. # 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. Declared by the `Swap` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/swap/src/pallet/mod.rs#L145`](/code/pallets/swap/src/pallet/mod.rs#L145). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain FeeRateTooHigh`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`already_exists`](/docs/errors/already-exists). Declared at [`pallets/subtensor/src/macros/errors.rs#L200`](/code/pallets/subtensor/src/macros/errors.rs#L200). ## Remediation [#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`. # 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. Declared by the `EVM` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain GasLimitTooHigh`. # 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. Declared by the `EVM` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain GasLimitTooLow`. # 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. Declared by the `EVM` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain GasPriceTooLow`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`not_registered`](/docs/errors/not-registered). Declared at [`pallets/subtensor/src/macros/errors.rs#L21`](/code/pallets/subtensor/src/macros/errors.rs#L21). ## Remediation [#remediation] Register the hotkey on this subnet first with `btcli subnets register` The same explanation is available in the terminal: `btcli explain HotKeyAccountNotExists`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`already_exists`](/docs/errors/already-exists). Declared at [`pallets/subtensor/src/macros/errors.rs#L79`](/code/pallets/subtensor/src/macros/errors.rs#L79). ## Remediation [#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`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`already_exists`](/docs/errors/already-exists). Declared at [`pallets/subtensor/src/macros/errors.rs#L63`](/code/pallets/subtensor/src/macros/errors.rs#L63). ## Remediation [#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`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`not_registered`](/docs/errors/not-registered). Declared at [`pallets/subtensor/src/macros/errors.rs#L23`](/code/pallets/subtensor/src/macros/errors.rs#L23). ## Remediation [#remediation] Register the hotkey on this subnet first with `btcli subnets register` The same explanation is available in the terminal: `btcli explain HotKeyNotRegisteredInNetwork`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`not_registered`](/docs/errors/not-registered). Declared at [`pallets/subtensor/src/macros/errors.rs#L19`](/code/pallets/subtensor/src/macros/errors.rs#L19). ## Remediation [#remediation] Register the hotkey on this subnet first with `btcli subnets register` The same explanation is available in the terminal: `btcli explain HotKeyNotRegisteredInSubNet`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`rate_limited`](/docs/errors/rate-limited). Declared at [`pallets/subtensor/src/macros/errors.rs#L93`](/code/pallets/subtensor/src/macros/errors.rs#L93). ## Remediation [#remediation] Wait for the rate-limit window to pass, then retry The same explanation is available in the terminal: `btcli explain HotKeySetTxRateLimitExceeded`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`rate_limited`](/docs/errors/rate-limited). Declared at [`pallets/subtensor/src/macros/errors.rs#L214`](/code/pallets/subtensor/src/macros/errors.rs#L214). ## Remediation [#remediation] Wait for the rate-limit window to pass, then retry The same explanation is available in the terminal: `btcli explain HotKeySwapOnSubnetIntervalNotPassed`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/subtensor/src/macros/errors.rs#L249`](/code/pallets/subtensor/src/macros/errors.rs#L249). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain IncorrectCommitRevealVersion`. # 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`. Declared by the `LimitOrders` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/limit-orders/src/lib.rs#L342`](/code/pallets/limit-orders/src/lib.rs#L342). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain IncorrectPartialFillAmount`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/subtensor/src/macros/errors.rs#L83`](/code/pallets/subtensor/src/macros/errors.rs#L83). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain IncorrectWeightVersionKey`. # 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. Declared by the `Contracts` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain Indeterministic`. # 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. Declared by the `Contracts` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain InputForwarded`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/subtensor/src/macros/errors.rs#L184`](/code/pallets/subtensor/src/macros/errors.rs#L184). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain InputLengthsUnequal`. # 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). Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`insufficient_balance`](/docs/errors/insufficient-balance). Declared at [`pallets/subtensor/src/macros/errors.rs#L339`](/code/pallets/subtensor/src/macros/errors.rs#L339). ## Remediation [#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`. # 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. Declared by the `Balances`, `Crowdloan`, `Swap` pallets; it classifies to the semantic code [`insufficient_balance`](/docs/errors/insufficient-balance). Declared at [`pallets/crowdloan/src/lib.rs#L253`](/code/pallets/crowdloan/src/lib.rs#L253), [`pallets/swap/src/pallet/mod.rs#L157`](/code/pallets/swap/src/pallet/mod.rs#L157). ## Remediation [#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`. # 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. Declared by the `Swap` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/swap/src/pallet/mod.rs#L148`](/code/pallets/swap/src/pallet/mod.rs#L148). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain InsufficientInputAmount`. # 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. Declared by the `SubtensorModule`, `Swap` pallets; it classifies to the semantic code [`insufficient_liquidity`](/docs/errors/insufficient-liquidity). Declared at [`pallets/subtensor/src/macros/errors.rs#L190`](/code/pallets/subtensor/src/macros/errors.rs#L190), [`pallets/swap/src/pallet/mod.rs#L151`](/code/pallets/swap/src/pallet/mod.rs#L151). ## Remediation [#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`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`insufficient_balance`](/docs/errors/insufficient-balance). Declared at [`pallets/subtensor/src/macros/errors.rs#L307`](/code/pallets/subtensor/src/macros/errors.rs#L307). ## Remediation [#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`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`insufficient_balance`](/docs/errors/insufficient-balance). Declared at [`pallets/subtensor/src/macros/errors.rs#L223`](/code/pallets/subtensor/src/macros/errors.rs#L223). ## Remediation [#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`. # 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. Declared by the `Contracts` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain InvalidCallFlags`. # 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. Declared by the `EVM` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain InvalidChainId`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/subtensor/src/macros/errors.rs#L144`](/code/pallets/subtensor/src/macros/errors.rs#L144). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain InvalidChild`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/subtensor/src/macros/errors.rs#L166`](/code/pallets/subtensor/src/macros/errors.rs#L166). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain InvalidChildkeyTake`. # 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. Declared by the `Crowdloan` pallet; it classifies to the semantic code [`not_found`](/docs/errors/not-found). Declared at [`pallets/crowdloan/src/lib.rs#L257`](/code/pallets/crowdloan/src/lib.rs#L257). ## Remediation [#remediation] The referenced object is not on-chain; check the identifier The same explanation is available in the terminal: `btcli explain InvalidCrowdloanId`. # 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. Declared by the `Utility` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/utility/src/lib.rs#L171`](/code/pallets/utility/src/lib.rs#L171). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain InvalidDerivedAccount`. # 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). Declared by the `Proxy` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/proxy/src/lib.rs#L797`](/code/pallets/proxy/src/lib.rs#L797). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain InvalidDerivedAccountId`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/subtensor/src/macros/errors.rs#L72`](/code/pallets/subtensor/src/macros/errors.rs#L72). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain InvalidDifficulty`. # 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. Declared by the `Grandpa` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain InvalidEquivocationProof`. # 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. Declared by the `Crowdloan` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/crowdloan/src/lib.rs#L287`](/code/pallets/crowdloan/src/lib.rs#L287). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain InvalidFinalizationConfig`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/subtensor/src/macros/errors.rs#L170`](/code/pallets/subtensor/src/macros/errors.rs#L170). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain InvalidIdentity`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/subtensor/src/macros/errors.rs#L15`](/code/pallets/subtensor/src/macros/errors.rs#L15). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain InvalidIpAddress`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/subtensor/src/macros/errors.rs#L13`](/code/pallets/subtensor/src/macros/errors.rs#L13). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain InvalidIpType`. # 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. Declared by the `Grandpa` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain InvalidKeyOwnershipProof`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/subtensor/src/macros/errors.rs#L225`](/code/pallets/subtensor/src/macros/errors.rs#L225). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain InvalidLeaseBeneficiary`. # 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`. Declared by the `Swap` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/swap/src/pallet/mod.rs#L163`](/code/pallets/swap/src/pallet/mod.rs#L163). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain InvalidLiquidityValue`. # 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`. Declared by the `EVM` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain InvalidNonce`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/subtensor/src/macros/errors.rs#L275`](/code/pallets/subtensor/src/macros/errors.rs#L275). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain InvalidNumRootClaim`. # 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`. Declared by the `Crowdloan` pallet; it classifies to the semantic code [`not_authorized`](/docs/errors/not-authorized). Declared at [`pallets/crowdloan/src/lib.rs#L265`](/code/pallets/crowdloan/src/lib.rs#L265). ## Remediation [#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`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/subtensor/src/macros/errors.rs#L17`](/code/pallets/subtensor/src/macros/errors.rs#L17). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain InvalidPort`. # 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). Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/subtensor/src/macros/errors.rs#L210`](/code/pallets/subtensor/src/macros/errors.rs#L210). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain InvalidRecoveredPublicKey`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/subtensor/src/macros/errors.rs#L128`](/code/pallets/subtensor/src/macros/errors.rs#L128). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain InvalidRevealCommitHashNotMatch`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`expired`](/docs/errors/expired). Declared at [`pallets/subtensor/src/macros/errors.rs#L251`](/code/pallets/subtensor/src/macros/errors.rs#L251). ## Remediation [#remediation] The window has closed; restart the flow with fresh state The same explanation is available in the terminal: `btcli explain InvalidRevealRound`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/subtensor/src/macros/errors.rs#L277`](/code/pallets/subtensor/src/macros/errors.rs#L277). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain InvalidRootClaimThreshold`. # 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`. Declared by the `Drand` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/drand/src/lib.rs#L268`](/code/pallets/drand/src/lib.rs#L268). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain InvalidRoundNumber`. # 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. Declared by the `Contracts` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain InvalidSchedule`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/subtensor/src/macros/errors.rs#L74`](/code/pallets/subtensor/src/macros/errors.rs#L74). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain InvalidSeal`. # 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. Declared by the `Ethereum`, `EVM`, `LimitOrders` pallets; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/limit-orders/src/lib.rs#L318`](/code/pallets/limit-orders/src/lib.rs#L318). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain InvalidSignature`. # 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. Declared by the `System` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain InvalidSpecName`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/subtensor/src/macros/errors.rs#L279`](/code/pallets/subtensor/src/macros/errors.rs#L279). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain InvalidSubnetNumber`. # 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. Declared by the `Swap` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/swap/src/pallet/mod.rs#L160`](/code/pallets/swap/src/pallet/mod.rs#L160). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain InvalidTickRange`. # 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`). Declared by the `SubtensorModule`, `AdminUtils` pallets; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/subtensor/src/macros/errors.rs#L257`](/code/pallets/subtensor/src/macros/errors.rs#L257), [`pallets/admin-utils/src/lib.rs#L163`](/code/pallets/admin-utils/src/lib.rs#L163). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain InvalidValue`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/subtensor/src/macros/errors.rs#L285`](/code/pallets/subtensor/src/macros/errors.rs#L285). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain InvalidVotingPowerEmaAlpha`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/subtensor/src/macros/errors.rs#L70`](/code/pallets/subtensor/src/macros/errors.rs#L70). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain InvalidWorkBlock`. # 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. Declared by the `Balances` pallet; it classifies to the semantic code [`disabled`](/docs/errors/disabled). ## Remediation [#remediation] This call or feature is switched off on this network The same explanation is available in the terminal: `btcli explain IssuanceDeactivated`. # KeepStakeBlockedByCollateral (/docs/errors/chain/KeepStakeBlockedByCollateral) A hotkey swap with keep\_stake=true was refused because the old hotkey still has standing miner registration collateral. keep\_stake leaves stake on the old key while the UID moves, which would strand the bond. Retry with keep\_stake=false so collateral migrates with the UID; on-chain hotkey lineage maps track the rename for blacklist continuity. Or drain the bond through earned emission first. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`policy_violation`](/docs/errors/policy-violation). Declared at [`pallets/subtensor/src/macros/errors.rs#L219`](/code/pallets/subtensor/src/macros/errors.rs#L219). ## Remediation [#remediation] The action exceeds a configured safety policy The same explanation is available in the terminal: `btcli explain KeepStakeBlockedByCollateral`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/subtensor/src/macros/errors.rs#L227`](/code/pallets/subtensor/src/macros/errors.rs#L227). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain LeaseCannotEndInThePast`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`not_found`](/docs/errors/not-found). Declared at [`pallets/subtensor/src/macros/errors.rs#L231`](/code/pallets/subtensor/src/macros/errors.rs#L231). ## Remediation [#remediation] The referenced object is not on-chain; check the identifier The same explanation is available in the terminal: `btcli explain LeaseDoesNotExist`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/subtensor/src/macros/errors.rs#L233`](/code/pallets/subtensor/src/macros/errors.rs#L233). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain LeaseHasNoEndBlock`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`too_early`](/docs/errors/too-early). Declared at [`pallets/subtensor/src/macros/errors.rs#L235`](/code/pallets/subtensor/src/macros/errors.rs#L235). ## Remediation [#remediation] The required window has not opened yet; wait some blocks and retry The same explanation is available in the terminal: `btcli explain LeaseHasNotEnded`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`not_found`](/docs/errors/not-found). Declared at [`pallets/subtensor/src/macros/errors.rs#L229`](/code/pallets/subtensor/src/macros/errors.rs#L229). ## Remediation [#remediation] The referenced object is not on-chain; check the identifier The same explanation is available in the terminal: `btcli explain LeaseNetuidNotFound`. # 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. Declared by the `LimitOrders` pallet; it classifies to the semantic code [`disabled`](/docs/errors/disabled). Declared at [`pallets/limit-orders/src/lib.rs#L336`](/code/pallets/limit-orders/src/lib.rs#L336). ## Remediation [#remediation] This call or feature is switched off on this network The same explanation is available in the terminal: `btcli explain LimitOrdersDisabled`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`disabled`](/docs/errors/disabled). Declared at [`pallets/subtensor/src/macros/errors.rs#L134`](/code/pallets/subtensor/src/macros/errors.rs#L134). ## Remediation [#remediation] This call or feature is switched off on this network The same explanation is available in the terminal: `btcli explain LiquidAlphaDisabled`. # 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. Declared by the `Balances` pallet; it classifies to the semantic code [`insufficient_balance`](/docs/errors/insufficient-balance). ## Remediation [#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`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/subtensor/src/macros/errors.rs#L305`](/code/pallets/subtensor/src/macros/errors.rs#L305). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain LockHotkeyMismatch`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). Declared at [`pallets/subtensor/src/macros/errors.rs#L335`](/code/pallets/subtensor/src/macros/errors.rs#L335). ## Remediation [#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`. # 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. Declared by the `AdminUtils` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/admin-utils/src/lib.rs#L147`](/code/pallets/admin-utils/src/lib.rs#L147). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain MaxAllowedUIdsLessThanCurrentUIds`. # 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. Declared by the `AdminUtils` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/admin-utils/src/lib.rs#L161`](/code/pallets/admin-utils/src/lib.rs#L161). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain MaxAllowedUidsGreaterThanDefaultMaxAllowedUids`. # 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. Declared by the `AdminUtils` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/admin-utils/src/lib.rs#L159`](/code/pallets/admin-utils/src/lib.rs#L159). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain MaxAllowedUidsLessThanMinAllowedUids`. # 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. Declared by the `Contracts` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). ## Remediation [#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`. # 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`. Declared by the `Crowdloan` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). Declared at [`pallets/crowdloan/src/lib.rs#L289`](/code/pallets/crowdloan/src/lib.rs#L289). ## Remediation [#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`. # 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. Declared by the `Crowdloan` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). Declared at [`pallets/crowdloan/src/lib.rs#L285`](/code/pallets/crowdloan/src/lib.rs#L285). ## Remediation [#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`. # 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. Declared by the `Contracts` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). ## Remediation [#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`. # 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. Declared by the `AdminUtils` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/admin-utils/src/lib.rs#L145`](/code/pallets/admin-utils/src/lib.rs#L145). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain MaxValidatorsLargerThanMaxUIds`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). Declared at [`pallets/subtensor/src/macros/errors.rs#L77`](/code/pallets/subtensor/src/macros/errors.rs#L77). ## Remediation [#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`. # 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`. Declared by the `Multisig` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain MaxWeightTooLow`. # 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. Declared by the `Crowdloan` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/crowdloan/src/lib.rs#L291`](/code/pallets/crowdloan/src/lib.rs#L291). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain MaximumContributionTooLow`. # 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. Declared by the `SubtensorModule`, `Swap` pallets; it classifies to the semantic code [`subnet_not_exists`](/docs/errors/subnet-not-exists). Declared at [`pallets/subtensor/src/macros/errors.rs#L172`](/code/pallets/subtensor/src/macros/errors.rs#L172), [`pallets/swap/src/pallet/mod.rs#L169`](/code/pallets/swap/src/pallet/mod.rs#L169). ## Remediation [#remediation] Use an existing netuid; `btcli subnets list` shows valid ones The same explanation is available in the terminal: `btcli explain MechanismDoesNotExist`. # 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. Declared by the `Contracts` pallet; it classifies to the semantic code [`too_early`](/docs/errors/too-early). ## Remediation [#remediation] The required window has not opened yet; wait some blocks and retry The same explanation is available in the terminal: `btcli explain MigrationInProgress`. # 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. Declared by the `AdminUtils` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/admin-utils/src/lib.rs#L155`](/code/pallets/admin-utils/src/lib.rs#L155). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain MinAllowedUidsGreaterThanCurrentUids`. # 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. Declared by the `AdminUtils` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/admin-utils/src/lib.rs#L157`](/code/pallets/admin-utils/src/lib.rs#L157). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain MinAllowedUidsGreaterThanMaxAllowedUids`. # 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`. Declared by the `Crowdloan` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/crowdloan/src/lib.rs#L293`](/code/pallets/crowdloan/src/lib.rs#L293). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain MinimumContributionTooHigh`. # 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. Declared by the `Crowdloan` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/crowdloan/src/lib.rs#L245`](/code/pallets/crowdloan/src/lib.rs#L245). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain MinimumContributionTooLow`. # 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. Declared by the `Multisig` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain MinimumThreshold`. # 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. Declared by the `System` pallet; it classifies to the semantic code [`too_early`](/docs/errors/too-early). ## Remediation [#remediation] The required window has not opened yet; wait some blocks and retry The same explanation is available in the terminal: `btcli explain MultiBlockMigrationsOngoing`. # 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. Declared by the `Scheduler` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain Named`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`too_early`](/docs/errors/too-early). Declared at [`pallets/subtensor/src/macros/errors.rs#L202`](/code/pallets/subtensor/src/macros/errors.rs#L202). ## Remediation [#remediation] The required window has not opened yet; wait some blocks and retry The same explanation is available in the terminal: `btcli explain NeedWaitingMoreBlocksToStarCall`. # 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. Declared by the `AdminUtils` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/admin-utils/src/lib.rs#L151`](/code/pallets/admin-utils/src/lib.rs#L151). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain NegativeSigmoidSteepness`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`already_exists`](/docs/errors/already-exists). Declared at [`pallets/subtensor/src/macros/errors.rs#L291`](/code/pallets/subtensor/src/macros/errors.rs#L291). ## Remediation [#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`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`rate_limited`](/docs/errors/rate-limited). Declared at [`pallets/subtensor/src/macros/errors.rs#L89`](/code/pallets/subtensor/src/macros/errors.rs#L89). ## Remediation [#remediation] Wait for the rate-limit window to pass, then retry The same explanation is available in the terminal: `btcli explain NetworkTxRateLimitExceeded`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`not_authorized`](/docs/errors/not-authorized). Declared at [`pallets/subtensor/src/macros/errors.rs#L48`](/code/pallets/subtensor/src/macros/errors.rs#L48). ## Remediation [#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`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/subtensor/src/macros/errors.rs#L164`](/code/pallets/subtensor/src/macros/errors.rs#L164). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain NewColdKeyIsHotkey`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/subtensor/src/macros/errors.rs#L65`](/code/pallets/subtensor/src/macros/errors.rs#L65). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain NewHotKeyIsSameWithOld`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/subtensor/src/macros/errors.rs#L68`](/code/pallets/subtensor/src/macros/errors.rs#L68). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain NewHotKeyNotCleanForRootSwap`. # 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. Declared by the `Multisig` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain NoApprovalsNeeded`. # 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. Declared by the `Contracts` pallet; it classifies to the semantic code [`disabled`](/docs/errors/disabled). ## Remediation [#remediation] This call or feature is switched off on this network The same explanation is available in the terminal: `btcli explain NoChainExtension`. # 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. Declared by the `Crowdloan` pallet; it classifies to the semantic code [`not_found`](/docs/errors/not-found). Declared at [`pallets/crowdloan/src/lib.rs#L273`](/code/pallets/crowdloan/src/lib.rs#L273). ## Remediation [#remediation] The referenced object is not on-chain; check the identifier The same explanation is available in the terminal: `btcli explain NoContribution`. # 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. Declared by the `SafeMode` pallet; it classifies to the semantic code [`not_found`](/docs/errors/not-found). ## Remediation [#remediation] The referenced object is not on-chain; check the identifier The same explanation is available in the terminal: `btcli explain NoDeposit`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`not_found`](/docs/errors/not-found). Declared at [`pallets/subtensor/src/macros/errors.rs#L309`](/code/pallets/subtensor/src/macros/errors.rs#L309). ## Remediation [#remediation] The referenced object is not on-chain; check the identifier The same explanation is available in the terminal: `btcli explain NoExistingLock`. # 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. Declared by the `Contracts` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain NoMigrationPerformed`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). Declared at [`pallets/subtensor/src/macros/errors.rs#L119`](/code/pallets/subtensor/src/macros/errors.rs#L119). ## Remediation [#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`. # 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. Declared by the `Proxy` pallet; it classifies to the semantic code [`not_authorized`](/docs/errors/not-authorized). Declared at [`pallets/proxy/src/lib.rs#L789`](/code/pallets/proxy/src/lib.rs#L789). ## Remediation [#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`. # 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. Declared by the `Proxy` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/proxy/src/lib.rs#L793`](/code/pallets/proxy/src/lib.rs#L793). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain NoSelfProxy`. # 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`. Declared by the `Multisig` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain NoTimepoint`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`not_found`](/docs/errors/not-found). Declared at [`pallets/subtensor/src/macros/errors.rs#L126`](/code/pallets/subtensor/src/macros/errors.rs#L126). ## Remediation [#remediation] The referenced object is not on-chain; check the identifier The same explanation is available in the terminal: `btcli explain NoWeightsCommitFound`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`not_authorized`](/docs/errors/not-authorized). Declared at [`pallets/subtensor/src/macros/errors.rs#L26`](/code/pallets/subtensor/src/macros/errors.rs#L26). ## Remediation [#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`. # 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. Declared by the `System` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain NonDefaultComposite`. # 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. Declared by the `System` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain NonZeroRefCount`. # 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. Declared by the `Drand` pallet; it classifies to the semantic code [`not_found`](/docs/errors/not-found). Declared at [`pallets/drand/src/lib.rs#L260`](/code/pallets/drand/src/lib.rs#L260). ## Remediation [#remediation] The referenced object is not on-chain; check the identifier The same explanation is available in the terminal: `btcli explain NoneValue`. # 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. Declared by the `EVM` pallet; it classifies to the semantic code [`not_authorized`](/docs/errors/not-authorized). ## Remediation [#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`. # 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`. Declared by the `Preimage` pallet; it classifies to the semantic code [`not_authorized`](/docs/errors/not-authorized). ## Remediation [#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`. # 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. Declared by the `SafeMode` pallet; it classifies to the semantic code [`disabled`](/docs/errors/disabled). ## Remediation [#remediation] This call or feature is switched off on this network The same explanation is available in the terminal: `btcli explain NotConfigured`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`insufficient_liquidity`](/docs/errors/insufficient-liquidity). Declared at [`pallets/subtensor/src/macros/errors.rs#L204`](/code/pallets/subtensor/src/macros/errors.rs#L204). ## Remediation [#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`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`insufficient_balance`](/docs/errors/insufficient-balance). Declared at [`pallets/subtensor/src/macros/errors.rs#L142`](/code/pallets/subtensor/src/macros/errors.rs#L142). ## Remediation [#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`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`insufficient_balance`](/docs/errors/insufficient-balance). Declared at [`pallets/subtensor/src/macros/errors.rs#L113`](/code/pallets/subtensor/src/macros/errors.rs#L113). ## Remediation [#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`. # 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). Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`insufficient_balance`](/docs/errors/insufficient-balance). Declared at [`pallets/subtensor/src/macros/errors.rs#L40`](/code/pallets/subtensor/src/macros/errors.rs#L40). ## Remediation [#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`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`insufficient_balance`](/docs/errors/insufficient-balance). Declared at [`pallets/subtensor/src/macros/errors.rs#L29`](/code/pallets/subtensor/src/macros/errors.rs#L29). ## Remediation [#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`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`insufficient_balance`](/docs/errors/insufficient-balance). Declared at [`pallets/subtensor/src/macros/errors.rs#L37`](/code/pallets/subtensor/src/macros/errors.rs#L37). ## Remediation [#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`. # 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`. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`insufficient_balance`](/docs/errors/insufficient-balance). Declared at [`pallets/subtensor/src/macros/errors.rs#L35`](/code/pallets/subtensor/src/macros/errors.rs#L35). ## Remediation [#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`. # 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). Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`insufficient_balance`](/docs/errors/insufficient-balance). Declared at [`pallets/subtensor/src/macros/errors.rs#L32`](/code/pallets/subtensor/src/macros/errors.rs#L32). ## Remediation [#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`. # 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. Declared by the `Multisig`, `Scheduler`, `Proxy` pallets; it classifies to the semantic code [`not_found`](/docs/errors/not-found). Declared at [`pallets/proxy/src/lib.rs#L781`](/code/pallets/proxy/src/lib.rs#L781). ## Remediation [#remediation] The referenced object is not on-chain; check the identifier The same explanation is available in the terminal: `btcli explain NotFound`. # 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. Declared by the `Preimage` pallet; it classifies to the semantic code [`not_found`](/docs/errors/not-found). ## Remediation [#remediation] The referenced object is not on-chain; check the identifier The same explanation is available in the terminal: `btcli explain NotNoted`. # 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. Declared by the `Multisig` pallet; it classifies to the semantic code [`not_authorized`](/docs/errors/not-authorized). ## Remediation [#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`. # 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. Declared by the `AdminUtils` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/admin-utils/src/lib.rs#L165`](/code/pallets/admin-utils/src/lib.rs#L165). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain NotPermittedOnRootSubnet`. # 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. Declared by the `Proxy` pallet; it classifies to the semantic code [`not_authorized`](/docs/errors/not-authorized). Declared at [`pallets/proxy/src/lib.rs#L783`](/code/pallets/proxy/src/lib.rs#L783). ## Remediation [#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`. # 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. Declared by the `Crowdloan` pallet; it classifies to the semantic code [`too_early`](/docs/errors/too-early). Declared at [`pallets/crowdloan/src/lib.rs#L281`](/code/pallets/crowdloan/src/lib.rs#L281). ## Remediation [#remediation] The required window has not opened yet; wait some blocks and retry The same explanation is available in the terminal: `btcli explain NotReadyToDissolve`. # 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`. Declared by the `Preimage` pallet; it classifies to the semantic code [`not_found`](/docs/errors/not-found). ## Remediation [#remediation] The referenced object is not on-chain; check the identifier The same explanation is available in the terminal: `btcli explain NotRequested`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/subtensor/src/macros/errors.rs#L115`](/code/pallets/subtensor/src/macros/errors.rs#L115). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain NotRootSubnet`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`not_authorized`](/docs/errors/not-authorized). Declared at [`pallets/subtensor/src/macros/errors.rs#L105`](/code/pallets/subtensor/src/macros/errors.rs#L105). ## Remediation [#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`. # 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. Declared by the `System` pallet; it classifies to the semantic code [`not_found`](/docs/errors/not-found). ## Remediation [#remediation] The referenced object is not on-chain; check the identifier The same explanation is available in the terminal: `btcli explain NothingAuthorized`. # 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`. Declared by the `LimitOrders` pallet; it classifies to the semantic code [`already_exists`](/docs/errors/already-exists). Declared at [`pallets/limit-orders/src/lib.rs#L320`](/code/pallets/limit-orders/src/lib.rs#L320). ## Remediation [#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`. # 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. Declared by the `LimitOrders` pallet; it classifies to the semantic code [`expired`](/docs/errors/expired). Declared at [`pallets/limit-orders/src/lib.rs#L322`](/code/pallets/limit-orders/src/lib.rs#L322). ## Remediation [#remediation] The window has closed; restart the flow with fresh state The same explanation is available in the terminal: `btcli explain OrderCancelled`. # 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. Declared by the `LimitOrders` pallet; it classifies to the semantic code [`expired`](/docs/errors/expired). Declared at [`pallets/limit-orders/src/lib.rs#L324`](/code/pallets/limit-orders/src/lib.rs#L324). ## Remediation [#remediation] The window has closed; restart the flow with fresh state The same explanation is available in the terminal: `btcli explain OrderExpired`. # 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. Declared by the `LimitOrders` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/limit-orders/src/lib.rs#L334`](/code/pallets/limit-orders/src/lib.rs#L334). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain OrderNetUidMismatch`. # 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. Declared by the `Contracts` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain OutOfBounds`. # 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. Declared by the `Contracts` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). ## Remediation [#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`. # 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. Declared by the `Contracts` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). ## Remediation [#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`. # 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. Declared by the `Contracts` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain OutputBufferTooSmall`. # 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. Declared by the `SubtensorModule`, `Crowdloan` pallets; it classifies to the semantic code [`internal`](/docs/errors/internal). Declared at [`pallets/subtensor/src/macros/errors.rs#L237`](/code/pallets/subtensor/src/macros/errors.rs#L237), [`pallets/crowdloan/src/lib.rs#L255`](/code/pallets/crowdloan/src/lib.rs#L255). ## Remediation [#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`. # 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. Declared by the `AdminUtils` pallet; it classifies to the semantic code [`disabled`](/docs/errors/disabled). Declared at [`pallets/admin-utils/src/lib.rs#L167`](/code/pallets/admin-utils/src/lib.rs#L167). ## Remediation [#remediation] This call or feature is switched off on this network The same explanation is available in the terminal: `btcli explain POWRegistrationDisabled`. # 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. Declared by the `LimitOrders` pallet; it classifies to the semantic code [`not_registered`](/docs/errors/not-registered). Declared at [`pallets/limit-orders/src/lib.rs#L350`](/code/pallets/limit-orders/src/lib.rs#L350). ## Remediation [#remediation] Register the hotkey on this subnet first with `btcli subnets register` The same explanation is available in the terminal: `btcli explain PalletHotkeyNotRegistered`. # 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. Declared by the `LimitOrders` pallet; it classifies to the semantic code [`disabled`](/docs/errors/disabled). Declared at [`pallets/limit-orders/src/lib.rs#L340`](/code/pallets/limit-orders/src/lib.rs#L340). ## Remediation [#remediation] This call or feature is switched off on this network The same explanation is available in the terminal: `btcli explain PartialFillsNotEnabled`. # 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. Declared by the `Grandpa` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain PauseFailed`. # 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. Declared by the `EVM` pallet; it classifies to the semantic code [`internal`](/docs/errors/internal). ## Remediation [#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`. # 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. Declared by the `Ethereum` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain PreLogExists`. # 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. Declared by the `LimitOrders` pallet; it classifies to the semantic code [`too_early`](/docs/errors/too-early). Declared at [`pallets/limit-orders/src/lib.rs#L326`](/code/pallets/limit-orders/src/lib.rs#L326). ## Remediation [#remediation] The required window has not opened yet; wait some blocks and retry The same explanation is available in the terminal: `btcli explain PriceConditionNotMet`. # 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. Declared by the `Swap` pallet; it classifies to the semantic code [`insufficient_liquidity`](/docs/errors/insufficient-liquidity). Declared at [`pallets/swap/src/pallet/mod.rs#L154`](/code/pallets/swap/src/pallet/mod.rs#L154). ## Remediation [#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`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/subtensor/src/macros/errors.rs#L148`](/code/pallets/subtensor/src/macros/errors.rs#L148). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain ProportionOverflow`. # 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). Declared by the `Drand` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/drand/src/lib.rs#L270`](/code/pallets/drand/src/lib.rs#L270). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain PulseVerificationError`. # 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. Declared by the `Contracts` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). ## Remediation [#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`. # 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. Declared by the `Contracts` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain ReentranceDenied`. # 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. Declared by the `EVM` pallet; it classifies to the semantic code [`internal`](/docs/errors/internal). ## Remediation [#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`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/subtensor/src/macros/errors.rs#L107`](/code/pallets/subtensor/src/macros/errors.rs#L107). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain RegistrationNotPermittedOnRootSubnet`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). Declared at [`pallets/subtensor/src/macros/errors.rs#L303`](/code/pallets/subtensor/src/macros/errors.rs#L303). ## Remediation [#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`. # 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. Declared by the `LimitOrders` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/limit-orders/src/lib.rs#L338`](/code/pallets/limit-orders/src/lib.rs#L338). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain RelayerMissMatch`. # 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. Declared by the `LimitOrders` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/limit-orders/src/lib.rs#L344`](/code/pallets/limit-orders/src/lib.rs#L344). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain RelayerRequiredForPartialFill`. # 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. Declared by the `Preimage` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain Requested`. # 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. Declared by the `Sudo` pallet; it classifies to the semantic code [`not_authorized`](/docs/errors/not-authorized). ## Remediation [#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`. # 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. Declared by the `Scheduler` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain RescheduleNoChange`. # 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. Declared by the `Swap` pallet; it classifies to the semantic code [`insufficient_liquidity`](/docs/errors/insufficient-liquidity). Declared at [`pallets/swap/src/pallet/mod.rs#L175`](/code/pallets/swap/src/pallet/mod.rs#L175). ## Remediation [#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`. # 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. Declared by the `Swap` pallet; it classifies to the semantic code [`insufficient_liquidity`](/docs/errors/insufficient-liquidity). Declared at [`pallets/swap/src/pallet/mod.rs#L166`](/code/pallets/swap/src/pallet/mod.rs#L166). ## Remediation [#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`. # 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. Declared by the `Grandpa` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain ResumeFailed`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/subtensor/src/macros/errors.rs#L253`](/code/pallets/subtensor/src/macros/errors.rs#L253). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain RevealPeriodTooLarge`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/subtensor/src/macros/errors.rs#L255`](/code/pallets/subtensor/src/macros/errors.rs#L255). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain RevealPeriodTooSmall`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`too_early`](/docs/errors/too-early). Declared at [`pallets/subtensor/src/macros/errors.rs#L182`](/code/pallets/subtensor/src/macros/errors.rs#L182). ## Remediation [#remediation] The required window has not opened yet; wait some blocks and retry The same explanation is available in the terminal: `btcli explain RevealTooEarly`. # 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. Declared by the `LimitOrders` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/limit-orders/src/lib.rs#L332`](/code/pallets/limit-orders/src/lib.rs#L332). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain RootNetUidNotAllowed`. # 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`. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`subnet_not_exists`](/docs/errors/subnet-not-exists). Declared at [`pallets/subtensor/src/macros/errors.rs#L11`](/code/pallets/subtensor/src/macros/errors.rs#L11). ## Remediation [#remediation] Use an existing netuid; `btcli subnets list` shows valid ones The same explanation is available in the terminal: `btcli explain RootNetworkDoesNotExist`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`already_exists`](/docs/errors/already-exists). Declared at [`pallets/subtensor/src/macros/errors.rs#L267`](/code/pallets/subtensor/src/macros/errors.rs#L267). ## Remediation [#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`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/subtensor/src/macros/errors.rs#L221`](/code/pallets/subtensor/src/macros/errors.rs#L221). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain SameNetuid`. # 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. Declared by the `Multisig` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain SenderInSignatories`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`rate_limited`](/docs/errors/rate-limited). Declared at [`pallets/subtensor/src/macros/errors.rs#L85`](/code/pallets/subtensor/src/macros/errors.rs#L85). ## Remediation [#remediation] Wait for the rate-limit window to pass, then retry The same explanation is available in the terminal: `btcli explain ServingRateLimitExceeded`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`rate_limited`](/docs/errors/rate-limited). Declared at [`pallets/subtensor/src/macros/errors.rs#L81`](/code/pallets/subtensor/src/macros/errors.rs#L81). ## Remediation [#remediation] Wait for the rate-limit window to pass, then retry The same explanation is available in the terminal: `btcli explain SettingWeightsTooFast`. # 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. Declared by the `Multisig` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain SignatoriesOutOfOrder`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`insufficient_liquidity`](/docs/errors/insufficient-liquidity). Declared at [`pallets/subtensor/src/macros/errors.rs#L192`](/code/pallets/subtensor/src/macros/errors.rs#L192). ## Remediation [#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`. # 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. Declared by the `Commitments` pallet; it classifies to the semantic code [`rate_limited`](/docs/errors/rate-limited). Declared at [`pallets/commitments/src/lib.rs#L123`](/code/pallets/commitments/src/lib.rs#L123). ## Remediation [#remediation] Wait for the rate-limit window to pass, then retry The same explanation is available in the terminal: `btcli explain SpaceLimitExceeded`. # 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. Declared by the `System` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain SpecVersionNeedsToIncrease`. # 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`). Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`insufficient_balance`](/docs/errors/insufficient-balance). Declared at [`pallets/subtensor/src/macros/errors.rs#L109`](/code/pallets/subtensor/src/macros/errors.rs#L109). ## Remediation [#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`. # StakeUnavailable (/docs/errors/chain/StakeUnavailable) An unstake or same-subnet stake transfer would dip into stake that is still reserved: the requested alpha exceeds the coldkey's free balance on that subnet after subtracting conviction `Lock` and any miner registration collateral locked against hotkeys the coldkey owns. Check `Lock` and `MinerCollateral` for the netuid; only total stake minus those reservations can move. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`insufficient_balance`](/docs/errors/insufficient-balance). Declared at [`pallets/subtensor/src/macros/errors.rs#L174`](/code/pallets/subtensor/src/macros/errors.rs#L174). ## Remediation [#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`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`rate_limited`](/docs/errors/rate-limited). Declared at [`pallets/subtensor/src/macros/errors.rs#L95`](/code/pallets/subtensor/src/macros/errors.rs#L95). ## Remediation [#remediation] Wait for the rate-limit window to pass, then retry The same explanation is available in the terminal: `btcli explain StakingRateLimitExceeded`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`too_early`](/docs/errors/too-early). Declared at [`pallets/subtensor/src/macros/errors.rs#L337`](/code/pallets/subtensor/src/macros/errors.rs#L337). ## Remediation [#remediation] The required window has not opened yet; wait some blocks and retry The same explanation is available in the terminal: `btcli explain StartCallNotReady`. # 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. Declared by the `Contracts` pallet; it classifies to the semantic code [`not_authorized`](/docs/errors/not-authorized). ## Remediation [#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`. # 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`. Declared by the `Contracts` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). ## Remediation [#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`. # 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. Declared by the `Contracts` pallet; it classifies to the semantic code [`insufficient_balance`](/docs/errors/insufficient-balance). ## Remediation [#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`. # 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. Declared by the `Drand` pallet; it classifies to the semantic code [`internal`](/docs/errors/internal). Declared at [`pallets/drand/src/lib.rs#L262`](/code/pallets/drand/src/lib.rs#L262). ## Remediation [#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`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`disabled`](/docs/errors/disabled). Declared at [`pallets/subtensor/src/macros/errors.rs#L97`](/code/pallets/subtensor/src/macros/errors.rs#L97). ## Remediation [#remediation] This call or feature is switched off on this network The same explanation is available in the terminal: `btcli explain SubNetRegistrationDisabled`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`rate_limited`](/docs/errors/rate-limited). Declared at [`pallets/subtensor/src/macros/errors.rs#L289`](/code/pallets/subtensor/src/macros/errors.rs#L289). ## Remediation [#remediation] Wait for the rate-limit window to pass, then retry The same explanation is available in the terminal: `btcli explain SubnetBuybackRateLimitExceeded`. # 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. Declared by the `AdminUtils` pallet; it classifies to the semantic code [`subnet_not_exists`](/docs/errors/subnet-not-exists). Declared at [`pallets/admin-utils/src/lib.rs#L143`](/code/pallets/admin-utils/src/lib.rs#L143). ## Remediation [#remediation] Use an existing netuid; `btcli subnets list` shows valid ones The same explanation is available in the terminal: `btcli explain SubnetDoesNotExist`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). Declared at [`pallets/subtensor/src/macros/errors.rs#L259`](/code/pallets/subtensor/src/macros/errors.rs#L259). ## Remediation [#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`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`subnet_not_exists`](/docs/errors/subnet-not-exists). Declared at [`pallets/subtensor/src/macros/errors.rs#L176`](/code/pallets/subtensor/src/macros/errors.rs#L176). ## Remediation [#remediation] Use an existing netuid; `btcli subnets list` shows valid ones The same explanation is available in the terminal: `btcli explain SubnetNotExists`. # 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. Declared by the `SubtensorModule`, `Swap` pallets; it classifies to the semantic code [`subtoken_disabled`](/docs/errors/subtoken-disabled). Declared at [`pallets/subtensor/src/macros/errors.rs#L212`](/code/pallets/subtensor/src/macros/errors.rs#L212), [`pallets/swap/src/pallet/mod.rs#L172`](/code/pallets/swap/src/pallet/mod.rs#L172). ## Remediation [#remediation] The subnet is not active yet; wait for start\_call The same explanation is available in the terminal: `btcli explain SubtokenDisabled`. # 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. Declared by the `Swap` pallet; it classifies to the semantic code [`insufficient_liquidity`](/docs/errors/insufficient-liquidity). Declared at [`pallets/swap/src/pallet/mod.rs#L178`](/code/pallets/swap/src/pallet/mod.rs#L178). ## Remediation [#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`. # 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. Declared by the `LimitOrders` pallet; it classifies to the semantic code [`insufficient_liquidity`](/docs/errors/insufficient-liquidity). Declared at [`pallets/limit-orders/src/lib.rs#L330`](/code/pallets/limit-orders/src/lib.rs#L330). ## Remediation [#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`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`already_exists`](/docs/errors/already-exists). Declared at [`pallets/subtensor/src/macros/errors.rs#L247`](/code/pallets/subtensor/src/macros/errors.rs#L247). ## Remediation [#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`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`not_found`](/docs/errors/not-found). Declared at [`pallets/subtensor/src/macros/errors.rs#L245`](/code/pallets/subtensor/src/macros/errors.rs#L245). ## Remediation [#remediation] The referenced object is not on-chain; check the identifier The same explanation is available in the terminal: `btcli explain SymbolDoesNotExist`. # 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. Declared by the `Scheduler` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain TargetBlockNumberInPast`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/subtensor/src/macros/errors.rs#L319`](/code/pallets/subtensor/src/macros/errors.rs#L319). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain TempoOutOfBounds`. # 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. Declared by the `Contracts` pallet; it classifies to the semantic code [`internal`](/docs/errors/internal). ## Remediation [#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`. # 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. Declared by the `Contracts` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain TerminatedWhileReentrant`. # 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. Declared by the `Preimage` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). ## Remediation [#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`. # 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`. Declared by the `Preimage` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain TooFew`. # 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. Declared by the `Multisig` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain TooFewSignatories`. # 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`. Declared by the `Preimage`, `Proxy` pallets; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). Declared at [`pallets/proxy/src/lib.rs#L779`](/code/pallets/proxy/src/lib.rs#L779). ## Remediation [#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`. # 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. Declared by the `Utility` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). Declared at [`pallets/utility/src/lib.rs#L169`](/code/pallets/utility/src/lib.rs#L169). ## Remediation [#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`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). Declared at [`pallets/subtensor/src/macros/errors.rs#L150`](/code/pallets/subtensor/src/macros/errors.rs#L150). ## Remediation [#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`. # 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. Declared by the `Commitments` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). Declared at [`pallets/commitments/src/lib.rs#L119`](/code/pallets/commitments/src/lib.rs#L119). ## Remediation [#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`. # 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. Declared by the `Balances` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). ## Remediation [#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`. # 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. Declared by the `Balances` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). ## Remediation [#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`. # 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. Declared by the `MevShield` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). Declared at [`pallets/shield/src/lib.rs#L238`](/code/pallets/shield/src/lib.rs#L238). ## Remediation [#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`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`rate_limited`](/docs/errors/rate-limited). Declared at [`pallets/subtensor/src/macros/errors.rs#L61`](/code/pallets/subtensor/src/macros/errors.rs#L61). ## Remediation [#remediation] Wait for the rate-limit window to pass, then retry The same explanation is available in the terminal: `btcli explain TooManyRegistrationsThisBlock`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`rate_limited`](/docs/errors/rate-limited). Declared at [`pallets/subtensor/src/macros/errors.rs#L99`](/code/pallets/subtensor/src/macros/errors.rs#L99). ## Remediation [#remediation] Wait for the rate-limit window to pass, then retry The same explanation is available in the terminal: `btcli explain TooManyRegistrationsThisInterval`. # 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. Declared by the `Balances` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). ## Remediation [#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`. # 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. Declared by the `Multisig` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). ## Remediation [#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`. # 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. Declared by the `Contracts` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). ## Remediation [#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`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). Declared at [`pallets/subtensor/src/macros/errors.rs#L281`](/code/pallets/subtensor/src/macros/errors.rs#L281). ## Remediation [#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`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). Declared at [`pallets/subtensor/src/macros/errors.rs#L178`](/code/pallets/subtensor/src/macros/errors.rs#L178). ## Remediation [#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`. # 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. Declared by the `Grandpa` pallet; it classifies to the semantic code [`rate_limited`](/docs/errors/rate-limited). ## Remediation [#remediation] Wait for the rate-limit window to pass, then retry The same explanation is available in the terminal: `btcli explain TooSoon`. # 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. Declared by the `EVM` pallet; it classifies to the semantic code [`not_authorized`](/docs/errors/not-authorized). ## Remediation [#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`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`not_authorized`](/docs/errors/not-authorized). Declared at [`pallets/subtensor/src/macros/errors.rs#L101`](/code/pallets/subtensor/src/macros/errors.rs#L101). ## Remediation [#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`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`disabled`](/docs/errors/disabled). Declared at [`pallets/subtensor/src/macros/errors.rs#L194`](/code/pallets/subtensor/src/macros/errors.rs#L194). ## Remediation [#remediation] This call or feature is switched off on this network The same explanation is available in the terminal: `btcli explain TransferDisallowed`. # 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. Declared by the `Contracts` pallet; it classifies to the semantic code [`insufficient_balance`](/docs/errors/insufficient-balance). ## Remediation [#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`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). Declared at [`pallets/subtensor/src/macros/errors.rs#L271`](/code/pallets/subtensor/src/macros/errors.rs#L271). ## Remediation [#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`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`rate_limited`](/docs/errors/rate-limited). Declared at [`pallets/subtensor/src/macros/errors.rs#L168`](/code/pallets/subtensor/src/macros/errors.rs#L168). ## Remediation [#remediation] Wait for the rate-limit window to pass, then retry The same explanation is available in the terminal: `btcli explain TxChildkeyTakeRateLimitExceeded`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`rate_limited`](/docs/errors/rate-limited). Declared at [`pallets/subtensor/src/macros/errors.rs#L152`](/code/pallets/subtensor/src/macros/errors.rs#L152). ## Remediation [#remediation] Wait for the rate-limit window to pass, then retry The same explanation is available in the terminal: `btcli explain TxRateLimitExceeded`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`internal`](/docs/errors/internal). Declared at [`pallets/subtensor/src/macros/errors.rs#L269`](/code/pallets/subtensor/src/macros/errors.rs#L269). ## Remediation [#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`. # 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`). Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/subtensor/src/macros/errors.rs#L56`](/code/pallets/subtensor/src/macros/errors.rs#L56). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain UidVecContainInvalidOne`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). Declared at [`pallets/subtensor/src/macros/errors.rs#L87`](/code/pallets/subtensor/src/macros/errors.rs#L87). ## Remediation [#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`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/subtensor/src/macros/errors.rs#L208`](/code/pallets/subtensor/src/macros/errors.rs#L208). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain UnableToRecoverPublicKey`. # 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`. Declared by the `Proxy` pallet; it classifies to the semantic code [`too_early`](/docs/errors/too-early). Declared at [`pallets/proxy/src/lib.rs#L791`](/code/pallets/proxy/src/lib.rs#L791). ## Remediation [#remediation] The required window has not opened yet; wait some blocks and retry The same explanation is available in the terminal: `btcli explain Unannounced`. # 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`. Declared by the `System`, `LimitOrders` pallets; it classifies to the semantic code [`not_authorized`](/docs/errors/not-authorized). Declared at [`pallets/limit-orders/src/lib.rs#L328`](/code/pallets/limit-orders/src/lib.rs#L328). ## Remediation [#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`. # 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. Declared by the `EVM` pallet; it classifies to the semantic code [`internal`](/docs/errors/internal). ## Remediation [#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`. # 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`. Declared by the `Crowdloan` pallet; it classifies to the semantic code [`internal`](/docs/errors/internal). Declared at [`pallets/crowdloan/src/lib.rs#L277`](/code/pallets/crowdloan/src/lib.rs#L277). ## Remediation [#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`. # 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. Declared by the `Multisig` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain UnexpectedTimepoint`. # 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`. Declared by the `Commitments` pallet; it classifies to the semantic code [`internal`](/docs/errors/internal). Declared at [`pallets/commitments/src/lib.rs#L125`](/code/pallets/commitments/src/lib.rs#L125). ## Remediation [#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`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`insufficient_balance`](/docs/errors/insufficient-balance). Declared at [`pallets/subtensor/src/macros/errors.rs#L315`](/code/pallets/subtensor/src/macros/errors.rs#L315). ## Remediation [#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`. # 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. Declared by the `Proxy` pallet; it classifies to the semantic code [`not_authorized`](/docs/errors/not-authorized). Declared at [`pallets/proxy/src/lib.rs#L785`](/code/pallets/proxy/src/lib.rs#L785). ## Remediation [#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`. # 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. Declared by the `MevShield` pallet; it classifies to the semantic code [`internal`](/docs/errors/internal). Declared at [`pallets/shield/src/lib.rs#L236`](/code/pallets/shield/src/lib.rs#L236). ## Remediation [#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`. # 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`. Declared by the `Drand` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/drand/src/lib.rs#L266`](/code/pallets/drand/src/lib.rs#L266). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain UnverifiedPulse`. # 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. Declared by the `AdminUtils` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/admin-utils/src/lib.rs#L153`](/code/pallets/admin-utils/src/lib.rs#L153). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain ValueNotInBounds`. # 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. Declared by the `Contracts` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). ## Remediation [#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`. # 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. Declared by the `Balances` pallet; it classifies to the semantic code [`insufficient_balance`](/docs/errors/insufficient-balance). ## Remediation [#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`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`disabled`](/docs/errors/disabled). Declared at [`pallets/subtensor/src/macros/errors.rs#L283`](/code/pallets/subtensor/src/macros/errors.rs#L283). ## Remediation [#remediation] This call or feature is switched off on this network The same explanation is available in the terminal: `btcli explain VotingPowerTrackingNotEnabled`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`too_early`](/docs/errors/too-early). Declared at [`pallets/subtensor/src/macros/errors.rs#L317`](/code/pallets/subtensor/src/macros/errors.rs#L317). ## Remediation [#remediation] The required window has not opened yet; wait some blocks and retry The same explanation is available in the terminal: `btcli explain WaitingForDissolvedSubnetCleanup`. # 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. Declared by the `MevShield` pallet; it classifies to the semantic code [`limit_exceeded`](/docs/errors/limit-exceeded). Declared at [`pallets/shield/src/lib.rs#L240`](/code/pallets/shield/src/lib.rs#L240). ## Remediation [#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`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/subtensor/src/macros/errors.rs#L58`](/code/pallets/subtensor/src/macros/errors.rs#L58). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain WeightVecLengthIsLow`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/subtensor/src/macros/errors.rs#L51`](/code/pallets/subtensor/src/macros/errors.rs#L51). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain WeightVecNotEqualSize`. # 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. Declared by the `EVM` pallet; it classifies to the semantic code [`insufficient_balance`](/docs/errors/insufficient-balance). ## Remediation [#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`. # 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. Declared by the `Multisig` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain WrongTimepoint`. # 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. Declared by the `Contracts` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain XCMDecodeFailed`. # 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. Declared by the `SubtensorModule` pallet; it classifies to the semantic code [`insufficient_balance`](/docs/errors/insufficient-balance). Declared at [`pallets/subtensor/src/macros/errors.rs#L46`](/code/pallets/subtensor/src/macros/errors.rs#L46). ## Remediation [#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`. # 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. Declared by the `LimitOrders` pallet; it classifies to the semantic code [`invalid_argument`](/docs/errors/invalid-argument). Declared at [`pallets/limit-orders/src/lib.rs#L359`](/code/pallets/limit-orders/src/lib.rs#L359). ## Remediation [#remediation] Check the argument values against the operation schema The same explanation is available in the terminal: `btcli explain ZeroShareInBatch`. # Chain errors (/docs/errors/chain) 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. | | [`ColdkeyCollateralIncomplete`](/docs/errors/chain/ColdkeyCollateralIncomplete) | [`internal`](/docs/errors/internal) | A coldkey swap could not fully migrate miner collateral: after migrating every indexed collateral hotkey, the old coldkey's ColdkeyMinerCollateral aggregate was still non-zero. This is a fail-closed invariant — retry after investigating orphaned MinerCollateral rows for that coldkey, or contact runtime maintainers. | | [`ColdkeyCollateralPositionsFull`](/docs/errors/chain/ColdkeyCollateralPositionsFull) | [`limit_exceeded`](/docs/errors/limit-exceeded) | This coldkey already has the maximum number of distinct hotkeys with miner collateral on the subnet. Drain or consolidate existing bonds before adding another collateral position. | | [`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. | | [`CollateralDrainRatioOutOfBounds`](/docs/errors/chain/CollateralDrainRatioOutOfBounds) | [`invalid_argument`](/docs/errors/invalid-argument) | `sudo_set_collateral_drain_ratio` was given a value that is zero, negative, or above `MaxCollateralDrainRatio` (10). The drain ratio must be strictly positive — with no other exit path for miner collateral, a ratio of zero would make locked collateral permanently unrecoverable. Pass a `drain_ratio` in the range (0, 10]. | | [`CollateralLockShareTooHigh`](/docs/errors/chain/CollateralLockShareTooHigh) | [`invalid_argument`](/docs/errors/invalid-argument) | `sudo_set_collateral_lock_share` was given a value above `MaxCollateralLockShare` (62258, \~95% of u16::MAX). The burned share of the registration price must stay strictly positive so re-registration always pays a nonzero, floating burn. Lower the `lock_share` argument to at most 62258. | | [`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. | | [`KeepStakeBlockedByCollateral`](/docs/errors/chain/KeepStakeBlockedByCollateral) | [`policy_violation`](/docs/errors/policy-violation) | A hotkey swap with keep\_stake=true was refused because the old hotkey still has standing miner registration collateral. keep\_stake leaves stake on the old key while the UID moves, which would strand the bond. Retry with keep\_stake=false so collateral migrates with the UID; on-chain hotkey lineage maps track the rename for blacklist continuity. Or drain the bond through earned emission first. | | [`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 or same-subnet stake transfer would dip into stake that is still reserved: the requested alpha exceeds the coldkey's free balance on that subnet after subtracting conviction `Lock` and any miner registration collateral locked against hotkeys the coldkey owns. Check `Lock` and `MinerCollateral` for the netuid; only total stake minus those reservations can move. | | [`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. | Chain Rust source is browsable at /code/ (index: /code/index.json).