Conviction for Contracts
What shipped
Spec version 439 brings the chain's stake locks and miner conviction to EVM contracts. A contract can lock existing alpha, direct the resulting conviction at a hotkey, move that lock deliberately, choose whether the lock is perpetual or decaying, and read current coldkey or aggregate hotkey state without reconstructing runtime storage for itself. Subnet owners also gain contract-facing control over whether owner-cut emission is locked automatically. [source]
The release calls itself an interface release, and that distinction is worth holding onto while reading the rest of this page. It exposes the runtime's existing lock model through the canonical Solidity interfaces and Python SDK ABIs, while the semantics that decide outcomes — time-aware decay, conviction maturity, transfer policy, authorisation and cleanup — stay on-chain and unchanged. What contracts get is bounded calls with explicit gas accounting, not new economics. [source]
The source for this page is one document: the project's own note for the V439 upgrade, dated July 2026. [source] [source]
What it changes for the ecosystem
For anyone holding alpha, the mechanics this release exposes are worth knowing whether or not you ever write a contract. Locking turns part of a coldkey's existing alpha on a subnet into a subnet-wide unstaking floor: the stake itself does not move, the locked mass can live across several positions, and the hotkey named in the call is the one that receives the conviction. One coldkey has one lock target per subnet — repeating the call against that target tops the lock up, and changing the target requires a separate move. Locks decay by default; perpetual mode holds the locked mass fixed while conviction matures, and turning perpetual mode back off returns the lock to the runtime's ordinary decay curve, which the release's own sample notes is the only route back, since there is no separate direct-unlock call. [source] [source] [source]
The transfer and move rules are the part of this release that reaches past contract authors, because they decide whether an accumulated claim can change hands at all. Stake transfers and coldkey swaps can carry a proportional share of locked mass and conviction with them, so a position is not automatically stripped of its lock when it changes hands — but receiving coldkeys reject locked alpha by default, which the release frames as preventing an account from being handed stake it cannot immediately unstake, and a recipient has to opt in before such a transfer will land. The policy belongs to the receiving coldkey rather than the sending contract, and the note tells integrators to treat a rejection as a consent boundary instead of retrying through a different transfer path. Moving a lock has its own rule: the move preserves the current rolled locked mass, and conviction survives it when source and destination hotkeys share an owner but is reset when the destination belongs to someone else — which the release describes as stopping an established signal from being sold across owner boundaries. [source] [source] [source]
Subnet owners get one new switch. The subnet precompile can now report and set whether owner-cut emission is locked automatically. It is off by default; when it is on, new owner-cut emission is added to the subnet owner coldkey's lock, keeping an existing lock's target or otherwise making the subnet owner hotkey the target, and the setter preserves the runtime's existing root-or-owner authorisation. The release is careful about how it frames this: an opt-in compounding policy rather than a change to how owner cut is distributed, with subnets that leave the flag false behaving exactly as before, and the note repeats that the flag stays disabled until an owner or root turns it on explicitly. [source] [source] [source]
The instructions to builders are narrow. EVM integrators refresh both canonical ABIs before using the new selectors, since the staking methods live on the V2 interface. Contract developers decode conviction as unsigned Q64.64, treat query results as already rolled forward to the current block, and keep candidate batches distinct and at or below 64 hotkeys. Applications that move positions check the destination's locked-alpha policy before moving anything that may carry a lock. [source] [source] [source]
What to watch
Two properties of the new reads are worth holding onto, because both shape what a contract built on them can actually do. Every query is bounded: the conviction comparison call caps its input at 64 distinct hotkeys, rejects duplicates, keeps results aligned with the order supplied, and grows gas with that bounded list rather than with an unbounded metagraph scan — so a contract comparing a large candidate set has to batch it itself. And lock storage is lazy, advancing when it is touched rather than being written every block, so the views roll state forward in memory before returning it and every response describes the current block; a fully expired lock reports itself as not existing even when a stale storage row has not yet been cleaned up. [source] [source] [source]
Unverified The switch that lets owner-cut emission compound into conviction is the one to watch over time, because conviction is what decides who controls a subnet and the owner cut is the one income stream that arrives without competing for it. The release's framing — opt-in, and no change to how owner cut is distributed — is accurate about the emission itself, and it does not discuss what a steadily compounding owner position does to whether ownership stays contestable. Lesson 10's argument, that concentration rather than headcount decides outcomes, is the frame this belongs in; nothing in this note measures it, and nothing here has been checked against chain state. A smaller silence is worth naming alongside it: the note calls this a miner conviction system in its opening line and then never mentions miners again, describing every call in terms of coldkeys and hotkeys. What makes the conviction a miner's is not explained anywhere in this release.
How it actually works
Locking, and choosing a lock mode
lockStake turns part of the caller's existing alpha on a subnet into a subnet-wide unstaking floor. It does not move the stake, the locked alpha can live across several staking positions, and the supplied hotkey is the target that receives conviction. One coldkey has one lock target per subnet: repeating the call tops the lock up, and changing the target requires moveLock. The release's own sample shows both the lock and the mode switch. [source] [source] [source] [source]
IStaking staking = IStaking(0x0000000000000000000000000000000000000805);
staking.lockStake(hotkey, 25_000_000_000, netuid);
staking.setPerpetualLock(netuid, true);
// Resume normal lock decay; there is no separate direct-unlock call.
staking.setPerpetualLock(netuid, false);
Locks decay by default. Perpetual mode keeps the locked mass fixed while conviction matures; disabling it returns the lock to the runtime's ordinary decay curve. A move preserves the current rolled locked mass, and conviction is preserved when source and destination hotkeys share an owner and reset when they do not, which the release presents as stopping an established signal from being sold across owner boundaries. The global unlock and maturity timescales are available through getLockRates(). [source] [source] [source] [source]
Reads that roll state forward
Lock storage is lazy: it advances when touched rather than writing every block, so the new views roll state forward in memory before returning it. getColdkeyLock returns a coldkey's target hotkey, current locked mass, exact conviction and perpetual flag; getHotkeyLock combines the relevant perpetual and decaying aggregate buckets, and for a subnet owner hotkey also includes the owner-specific buckets. Every response describes the current block, and a fully expired lock reports exists = false even if a stale storage row has not yet been cleaned up. [source] [source]
getHotkeyConvictions(netuid, hotkeys) is the comparison call. Its input is capped at 64 distinct hotkeys, results stay aligned with the supplied order, duplicates are rejected, and each conviction is returned as exact unsigned Q64.64 bits — divide by 2^64 to express it in alpha rao. Gas grows with the bounded candidate list rather than with an unbounded metagraph scan. [source]
Locked-alpha transfer policy
Stake transfers and coldkey swaps can carry a proportional share of locked mass and conviction, and the receiving coldkey's policy decides whether they land: [source] [source] [source] [source]
- Default, reject: unlocked alpha accepted, locked alpha rejected. [source] [source] [source]
- Opted in via
setRejectLockedAlpha(false): unlocked alpha accepted, locked alpha accepted with proportional lock state. [source] [source] [source]
A contract can inspect the policy first with getRejectLockedAlpha(coldkey). The policy belongs to the receiving coldkey, not the sending contract, and the release tells integrators to treat a rejection as a consent boundary rather than retry through a different transfer path. [source] [source]
Owner-cut auto-lock
The subnet precompile at 0x…0803 adds getOwnerCutAutoLockEnabled and setOwnerCutAutoLockEnabled. Auto-locking is off by default. When enabled, new owner-cut emission is added to the subnet owner coldkey's lock: an existing lock keeps its current target, otherwise the subnet owner hotkey becomes the target. The setter preserves the runtime's existing root-or-subnet-owner authorisation, and the release states this is an opt-in compounding policy rather than a change to owner-cut distribution. The note's own sample reads the flag before setting it. [source] [source] [source] [source]
ISubnet subnet = ISubnet(0x0000000000000000000000000000000000000803);
if (!subnet.getOwnerCutAutoLockEnabled(netuid)) {
subnet.setOwnerCutAutoLockEnabled(netuid, true);
}
The contract surface
The release lists the methods it adds and what each is for: [source] [source] [source]
lockStake— lock existing subnet alpha and target a hotkey. [source] [source]moveLock— move the lock target with owner-aware conviction handling. [source] [source]setPerpetualLock— switch between fixed-mass and decaying lock modes. [source] [source]getColdkeyLockandgetHotkeyLock— read rolled individual or aggregate lock state. [source] [source]getHotkeyConvictions— read exact conviction for up to 64 distinct hotkeys. [source] [source]setRejectLockedAlpha— control whether the caller accepts transferred locked alpha. [source] [source]setOwnerCutAutoLockEnabled— opt subnet owner-cut emission into automatic locking. [source] [source]
The canonical staking and subnet ABIs, their Solidity interfaces and the Python SDK copies are synchronised in the release. Numeric inputs are range-checked before runtime dispatch, and state-changing calls execute as the precompile's mapped caller account. [source]