• ABOUT

  • WHITEPAPER

  • DOCS

  • DISCORD

  • WALLET

  • EXPLORE

The V438 Upgrade

Interfaces & Reliability · July 2026

Introduction

Spec 438 is a focused compatibility and correctness release after v437. It gives smart contracts a bounded way to inspect staking positions, lets Ledger-style signatures authorize limit orders, closes two small but important gaps in mechanism and epoch accounting, repairs testnet warp sync, and makes the release pipeline recover cleanly from a partial Python publication. There is no new token model or migration for ordinary wallets: v438 tightens the surfaces that applications, operators, and automation already use.

Staking data, directly from EVM

Contracts can now read a coldkey's non-zero alpha positions on one subnet through the staking precompile at 0x…0805. getStakeInfoForColdkeyAndNetuid accepts a coldkey, netuid, and up to 64 distinct candidate hotkeys, then returns only the candidates with stake. The caller supplies the search set deliberately: the precompile never walks the coldkey's unbounded historical hotkey index, so its work and gas remain proportional to the request.

IStaking.StakeInfo[] memory positions =
    staking.getStakeInfoForColdkeyAndNetuid(coldkey, netuid, hotkeys);

uint256 baseMinimum = staking.getDefaultMinStake();
EVM CONTRACTCOLDKEY · NETUID≤ 64 HOTKEYSSTATICCALLSTAKING PRECOMPILE0x…0805DISTINCT INPUTSBOUNDED READSNON-ZERO OUTPUTSSTAKEINFO[]HOTKEYALPHA STAKEGAS-BOUNDEDCALLER CHOOSES THE SEARCH SET · THE RUNTIME NEVER WALKS AN UNBOUNDED HISTORY

The contract chooses at most 64 distinct candidate hotkeys. The precompile charges for the bounded reads and returns only non-zero (hotkey, stake) pairs.

The companion getDefaultMinStake()view exposes the runtime's base staking minimum. It is intentionally a primitive, not a quote: operation fees, subnet price conversion, and full-unstake rules can still change what a particular transaction accepts. Both selectors are added to the Solidity interface, JSON ABI, Rust precompile, and gas-accounting tests, so Solidity and SDK callers share one contract.

Limit orders meet hardware wallets

A signed limit order can now use either sr25519 or ed25519 and can cover either the raw SCALE-encoded order or the Ledger-compatible <Bytes>blake2_256(order)</Bytes> payload produced bysignRaw. The order ID is unchanged: it remains the blake2-256 hash of the versioned order, so cancellation, replay protection, partial fills, and relayer restrictions behave exactly as before. ECDSA remains rejected because its account recovery model does not map to the 32-byte signer identity used by the pallet.

LIMIT ORDERSCALE BYTESDIRECTBLAKE2-256<BYTES>…</BYTES>LEDGER SIGNRAWRUNTIME VERIFIERSR25519 ✓ED25519 ✓ECDSA ✕SAME ORDER IDEXECUTABLE

Software keys can continue signing the order bytes directly. Ledger and compatible signers can sign the wrapped order hash; both paths verify against the same signer and derive the same on-chain order ID.

raw       = SCALE(versioned_order)
order_id  = blake2_256(raw)
wrapped   = "<Bytes>" ++ order_id ++ "</Bytes>"

accepted  = sr25519(raw | wrapped) or ed25519(raw | wrapped)
rejected  = ecdsa

Mechanism emission splits are exact

A custom mechanism split must now contain one entry for every active mechanism, and the entries must still sum to 65,535. Previously, a shorter vector passed validation and was padded with zeroes, which could silently starve trailing mechanisms while rounding residue was assigned to mechanism zero. Owners that want an even split can continue clearing the custom value; owners that set one explicitly must describe the full mechanism set.

ConfigurationBefore v438v438
No custom splitEven distributionEven distribution
Full vector, sum = 65,535AcceptedAccepted
Short vectorZero-paddedRejected
Wrong sumRejectedRejected

Epoch observability stays bounded

BlocksSinceLastStep is now capped at the subnet's tempo + 1. The scheduler's safety check uses that same subnet-specific tempo instead of a global maximum. Deferred epochs can still report the one-block-overdue state that triggers the fallback, but dashboards and clients no longer see a counter grow indefinitely during inconsistent or delayed state. The related epoch-status and next-epoch queries use the same model.

Testnet warp sync, repaired

Testnet nodes now start GRANDPA warp sync from two trusted, genesis-scoped authority checkpoints. The checkpoints are enabled only when the node sees the testnet genesis hash; mainnet and development chains retain their existing initial set-ID behavior. This repairs fast synchronization across historical authority-set changes without weakening checkpoint selection on any other network. The release also aligns the Polkadot SDK revision needed to verify those proofs and corrects dependency features that affected node log decoding.

Release train, recoverable

The mainnet watcher now tracks GitHub release completion and stable Python publication independently. A release can exist while a failed or partial PyPI upload is retried on the next watcher run; a provenance-bound completion marker is written only after every expected distribution is accepted. That removes the old failure mode where a successful GitHub release prevented the SDK publication from being retried.

Every merge to main also publishes the moving development image ghcr.io/raofoundation/subtensor:main. The deployed :latest tag remains tied to an executed mainnet release, while:devnet and :testnet continue following their network mirrors. Developers can therefore test merged code without mistaking it for the version running on mainnet. The complete promotion sequence is documented in the release process.

What to do

  • Node operators: wait for the on-chain spec_version to move to 438, then update to the matching release. Testnet operators should update promptly to pick up the warp-sync checkpoints.
  • EVM integrators: refresh stakingV2.abi and use a bounded, duplicate-free hotkey list when calling the new stake-position view.
  • Limit-order clients: raw sr25519 signatures remain valid. Ledger-style wrapped sr25519 and ed25519 signatures are now valid; do not emit ECDSA orders.
  • Subnet owners: when setting a custom mechanism emission split, send exactly one value per active mechanism and keep the total at 65,535.
  • Indexers: treat BlocksSinceLastStep as a bounded status counter, not an elapsed-time source beyond tempo + 1.

Signers: after the release train proposes, use btcli upgrade sign --url <v438 release URL> -w <wallet>.

Read the release process