From 1201a6aa74080187b38404b768662b08d1ea4fa7 Mon Sep 17 00:00:00 2001 From: UnarbosFour Date: Mon, 27 Jul 2026 18:56:28 -0400 Subject: [PATCH 1/8] Expand evm precompile maintenance skill with compatibility notes, include issue 2445 notes. --- .agents/skills/emv-maintainer/SKILL.md | 99 +++++ .../references/abi-versioning.md | 249 +++++++++++ .../references/coverage-and-testing.md | 267 ++++++++++++ .../references/event-subscriptions.md | 249 +++++++++++ docs/guides/evm/index.mdx | 10 + docs/guides/evm/meta.json | 2 + docs/guides/evm/precompile-design.mdx | 407 ++++++++++++++++++ .../evm/precompiles/account-balance.mdx | 20 + .../evm/precompiles/address-mapping.mdx | 20 + docs/guides/evm/precompiles/alpha.mdx | 40 ++ .../evm/precompiles/balance-transfer.mdx | 23 + .../evm/precompiles/configuration-events.mdx | 98 +++++ docs/guides/evm/precompiles/crowdloan.mdx | 37 ++ docs/guides/evm/precompiles/drand.mdx | 35 ++ docs/guides/evm/precompiles/index.mdx | 60 +++ docs/guides/evm/precompiles/leasing.mdx | 31 ++ docs/guides/evm/precompiles/meta.json | 31 ++ docs/guides/evm/precompiles/metagraph.mdx | 38 ++ docs/guides/evm/precompiles/neuron-events.mdx | 50 +++ docs/guides/evm/precompiles/neuron.mdx | 30 ++ docs/guides/evm/precompiles/proxy.mdx | 27 ++ docs/guides/evm/precompiles/registry.mdx | 39 ++ docs/guides/evm/precompiles/scheduler.mdx | 49 +++ .../guides/evm/precompiles/staking-events.mdx | 56 +++ docs/guides/evm/precompiles/staking-v1.mdx | 29 ++ docs/guides/evm/precompiles/staking-v2.mdx | 79 ++++ docs/guides/evm/precompiles/storage-query.mdx | 65 +++ docs/guides/evm/precompiles/subnet-events.mdx | 80 ++++ docs/guides/evm/precompiles/subnet.mdx | 93 ++++ docs/guides/evm/precompiles/timestamp.mdx | 29 ++ docs/guides/evm/precompiles/uid-lookup.mdx | 20 + docs/guides/evm/precompiles/voting-power.mdx | 26 ++ .../guides/evm/precompiles/weights-events.mdx | 47 ++ .../bittensor-website/src/components/copy.tsx | 22 + .../bittensor-website/src/components/mdx.tsx | 3 +- 35 files changed, 2459 insertions(+), 1 deletion(-) create mode 100644 .agents/skills/emv-maintainer/SKILL.md create mode 100644 .agents/skills/emv-maintainer/references/abi-versioning.md create mode 100644 .agents/skills/emv-maintainer/references/coverage-and-testing.md create mode 100644 .agents/skills/emv-maintainer/references/event-subscriptions.md create mode 100644 docs/guides/evm/precompile-design.mdx create mode 100644 docs/guides/evm/precompiles/account-balance.mdx create mode 100644 docs/guides/evm/precompiles/address-mapping.mdx create mode 100644 docs/guides/evm/precompiles/alpha.mdx create mode 100644 docs/guides/evm/precompiles/balance-transfer.mdx create mode 100644 docs/guides/evm/precompiles/configuration-events.mdx create mode 100644 docs/guides/evm/precompiles/crowdloan.mdx create mode 100644 docs/guides/evm/precompiles/drand.mdx create mode 100644 docs/guides/evm/precompiles/index.mdx create mode 100644 docs/guides/evm/precompiles/leasing.mdx create mode 100644 docs/guides/evm/precompiles/meta.json create mode 100644 docs/guides/evm/precompiles/metagraph.mdx create mode 100644 docs/guides/evm/precompiles/neuron-events.mdx create mode 100644 docs/guides/evm/precompiles/neuron.mdx create mode 100644 docs/guides/evm/precompiles/proxy.mdx create mode 100644 docs/guides/evm/precompiles/registry.mdx create mode 100644 docs/guides/evm/precompiles/scheduler.mdx create mode 100644 docs/guides/evm/precompiles/staking-events.mdx create mode 100644 docs/guides/evm/precompiles/staking-v1.mdx create mode 100644 docs/guides/evm/precompiles/staking-v2.mdx create mode 100644 docs/guides/evm/precompiles/storage-query.mdx create mode 100644 docs/guides/evm/precompiles/subnet-events.mdx create mode 100644 docs/guides/evm/precompiles/subnet.mdx create mode 100644 docs/guides/evm/precompiles/timestamp.mdx create mode 100644 docs/guides/evm/precompiles/uid-lookup.mdx create mode 100644 docs/guides/evm/precompiles/voting-power.mdx create mode 100644 docs/guides/evm/precompiles/weights-events.mdx diff --git a/.agents/skills/emv-maintainer/SKILL.md b/.agents/skills/emv-maintainer/SKILL.md new file mode 100644 index 0000000000..c26d65f9b7 --- /dev/null +++ b/.agents/skills/emv-maintainer/SKILL.md @@ -0,0 +1,99 @@ +--- +name: evm-maintainer +description: Maintain the EVM precompiles in backwards compatible way with API versioning. +--- + +# EVM Precompile Maintainer + +You are the maintainer of EVM precompiles. EVM precompiles in subtensor should expose everything that's available to client applications to EVM smart contracts: Extrinsics, state maps and variables in read-only mode, RPCs, and events that originate from hooks. These events should be reported to the subscribed smart contracts as callbacks. Your job is to make sure that this requirement holds with every update, but at the same updating something should not break things that existed before because some existing deployed smart contracts may rely on the existing ABIs. Read the notes below and then execute steps. + +## Reference routing + +- Before classifying or implementing any precompile change, including an + additive function, runtime adaptation, bug fix, deprecation, or disablement, + read [ABI versioning](references/abi-versioning.md). +- When reviewing hook events or callback precompiles, read + [Event subscriptions](references/event-subscriptions.md). +- Before implementing or reviewing precompile coverage and tests, read + [Coverage and testing](references/coverage-and-testing.md). + +## Backwards compatibility + +Treat every released precompile as a permanent public API. Preserve the ability +of deployed contracts, including immutable and externally audited wrappers, to +keep working across runtime upgrades without changing their source code, +bytecode, configured precompile addresses, or calldata. + +Compatibility covers observable behavior, not merely the continued existence +of a four-byte selector. Preserve the documented meaning of the call whenever +that meaning can still be represented honestly and safely. + +For each affected released function: + +1. Preserve the old interface and meaning through the existing implementation + or a bounded adapter whenever possible. +2. Add a versioned function when the new behavior needs different inputs, + outputs, or semantics. Keep the old address and selector routed. +3. Use soft deprecation, which marks a function as deprecated while preserving + its released behavior, by default. Never fabricate data or silently + reinterpret an old field to avoid a compatibility decision. +4. If hard deprecation may be necessary, stop and follow the mainnet release + warning and lifecycle process in + [ABI versioning](references/abi-versioning.md). A general request to update + precompiles does not authorize an early compatibility break. +5. Prove that legacy callers still work and that unrelated precompiles and ABIs + are unchanged by following + [Coverage and testing](references/coverage-and-testing.md). + +## Notes on coding precompiles + +- Never allow direct writing of state maps or variables to precompile callers. +- Keep every precompile path O(1) in CPU and memory. +- Follow [ABI versioning](references/abi-versioning.md) for every released + interface. +- Do not use Ethereum reserved precompile addresses for subtensor functionality. +- Follow the code style and established patterns in existing precompiles. +- Represent Substrate account IDs in EVM space as 32-byte public keys. +- Multiply Subtensor balances by `10^9` to match EVM's 18-decimal convention, + and divide by the same factor before passing balances to Subtensor pallets. +- Follow [Event subscriptions](references/event-subscriptions.md) for callback + interfaces, charging, bounds, and delivery. + +## Step 1 - Review current precompiles vs. subtensor functionality + +- All extrinsics should be exposed to precompile callers for the following pallets: + - subtensor + - admin-util + - balances + - proxy +- All runtime API RPCs for the subtensor pallet should be exposed as a callable precompile function with similar interface +- All events emitted from hooks (such as on_initialize or on_finalize) should be exposed as callbacks. + +Use [Coverage and testing](references/coverage-and-testing.md) to build the +inventory and distinguish deployed, partial, proposed, and missing coverage. + +## Step 2 — Determine the diff + +Determine the diff between current branch and the most recent main branch (may need to pull it locally if it is outdated). See how this diff affects EVM precompiles: + +- Does it remove or change any functions that precompiles rely on? Does it change function signatures or underlying functionality? +- Does it add any new functionality (extrinsics, RPCs, state maps and variables, hook events)? + +## Step 3 - Handle changed functions + +Apply the backwards-compatibility decision rule above and the detailed +[ABI versioning](references/abi-versioning.md) process. Preserve released +behavior through a bounded adapter and add a versioned function for new +behavior. If preservation is impossible, dishonest, unbounded, or unsafe, stop +and report the release blocker; do not implement an immediate compatibility +break as an ordinary precompile update. + +## Step 4 - Handle added functions + +Determine the category under which the new functionality needs to be added and add to the corresponding existing precompile. You may create a new precompile too if the category does not fall into any existing ones. + +## Step 5 - Update precompile documentation + +Update the Solidity interface, generated ABI, NatSpec, registry metadata, SDK +copies, and public precompile documentation together. Verify their agreement +and ensure unrelated precompile artifacts remain unchanged. diff --git a/.agents/skills/emv-maintainer/references/abi-versioning.md b/.agents/skills/emv-maintainer/references/abi-versioning.md new file mode 100644 index 0000000000..54037f4b1a --- /dev/null +++ b/.agents/skills/emv-maintainer/references/abi-versioning.md @@ -0,0 +1,249 @@ +# ABI versioning and lifecycle + +## Contents + +- [Establish the released baseline](#establish-the-released-baseline) +- [Preserve the external contract](#preserve-the-external-contract) +- [Reserve addresses and selectors](#reserve-addresses-and-selectors) +- [Version functions within a domain](#version-functions-within-a-domain) +- [Preserve old behavior through adapters](#preserve-old-behavior-through-adapters) +- [Classify changes](#classify-changes) +- [Apply the lifecycle model](#apply-the-lifecycle-model) +- [Stop an undeployed compatibility break](#stop-an-undeployed-compatibility-break) +- [Report lifecycle and availability](#report-lifecycle-and-availability) +- [Handle reversible disablement](#handle-reversible-disablement) + +## Establish the released baseline + +Before changing a precompile: + +1. Determine which addresses, selectors, Solidity interfaces, and ABI files + have been deployed or published for production use. Inspect release history + and the deployed runtime, not only the working tree. +2. Inspect `precompiles/src/lib.rs`, the Rust implementation, + `precompiles/src/solidity/*.sol`, generated `*.abi` files, tests, public + documentation, SDK copies, and known integration contracts. +3. Compare the branch with the relevant base and identify every runtime change + that affects inputs, outputs, state changes, errors, authorization, units, + value handling, or gas and weight requirements. +4. Treat uncertain production status as released until evidence establishes + otherwise. +5. Distinguish released interfaces from explicit proposals. Allow an + unassigned, unpublished proposal to change during design review; freeze its + address, selectors, and observable behavior once released. + +Do not infer compatibility from Rust names. Define the external contract as the +fixed EVM address plus accepted calldata, returned bytes, state effects, +authorization, charging, and success-or-revert behavior. + +## Preserve the external contract + +Preserve all observable properties of every released call: + +- address and selector handling; +- function name, input types, input order, and ABI encoding; +- return types, tuple and struct field order, and ABI encoding; +- documented meaning, units, precision, scaling, rounding, and defaults; +- view, state-changing, payable, and static-call behavior; +- treatment of attached EVM value; +- caller-to-Substrate account mapping and dispatched origin; +- authorization and proxy behavior; +- state transitions and atomicity; +- success-versus-revert behavior and documented error payloads; +- bounded-input and complexity guarantees; +- callback selectors, event-mask assignments, filters, charging, + auto-unsubscription, sequencing, and delivery guarantees. + +Return types do not contribute to a Solidity selector, but changing them under +an existing selector still breaks old callers because they decode the returned +bytes with the old ABI. + +Allow internal Rust names, storage layouts, hashers, intermediate types, and +algorithms to change only when the implementation adapts them back to the +released behavior. + +Allow runtime weight corrections, but preserve the complexity class and input +bounds. Do not introduce an unannounced increase large enough to make a +previously practical call unusable. Never replace bounded work with an +unbounded scan. + +## Reserve addresses and selectors + +Keep every released precompile address recognized by the precompile set. +Preserve compatible handling at that address: existing calldata must still +reach behavior that honors its released contract. The internal Rust type or +dispatch structure may change; the observable routing contract may not. + +Keep every released selector reserved permanently, including after hard +deprecation. Route a hard-deprecated selector to its descriptive error. Never +allow a different function to claim it. + +Before adding a function, calculate its selector from the canonical Solidity +signature and compare it with the complete selector set at the address. Reject +collisions even when the Solidity names differ. + +Treat a new function as additive only when: + +- its selector does not collide; +- old input and output encodings remain identical; +- unknown-selector and fallback behavior remain unchanged; +- old results and side effects remain unchanged; and +- no unrelated Solidity interface or ABI changes. + +## Version functions within a domain + +Prefer one fixed address for each coherent domain. Add versions at that address: + +```text +functionName +functionNameV2 +functionNameV3 +``` + +Keep every earlier version routed. Use a new address only for a genuinely +different domain with an independent responsibility and lifecycle. + +Continue supporting legacy addresses created under earlier per-contract +versioning. Do not use them as a precedent for creating a new address whenever +one function changes. + +Do not attempt a return-type-only overload. Because return types do not +distinguish selectors, use a versioned name or a genuinely distinct input +signature. + +When an audited integration expects a missing chain value or operation, prefer +adding the typed function it expects to the appropriate existing precompile. +Do not require changes to an audited wrapper when the precompile can satisfy +the wrapper's existing interface safely. + +## Preserve old behavior through adapters + +Adapt released calls to new runtime representations whenever the old result can +still be produced honestly with bounded, proportionate work: + +- Reconstruct an old aggregate when one stored value becomes several. +- Return the original tuple when a struct gains fields; expose the extended + tuple through a new version. +- Update Rust storage access when names, keys, hashers, or map shapes change. +- Supply the exact old default when an extrinsic gains an option; expose the + option through a new version. +- Derive the documented old result when the runtime replaces its computation. +- Preserve legacy units, precision, scaling, and rounding in the old function; + expose a corrected convention through a new version. + +Do not fabricate data to retain a byte shape. Do not reinterpret an old field +as a different concept. If an adapter cannot preserve the documented meaning, +make an explicit lifecycle decision. + +## Classify changes + +| Runtime change | Required treatment | +|---|---| +| Storage rename, hasher change, or map restructuring | Update the Rust implementation; preserve ABI and meaning. | +| Equivalent internal computation refactor | Keep the function and verify equivalent observable results. | +| Additional returned information | Keep the old subset; add a version for the richer result. | +| Input or return type/order change | Add a version with a new selector. | +| One concept splits into several | Reconstruct the old aggregate when honest; expose components through a version. | +| Extrinsic gains an option | Preserve the old default; expose the option through a version. | +| Entirely new operation or view | Add a selector to the appropriate domain. | +| Concept disappears without an honest representation | Reserve the selector and evaluate hard deprecation. | +| Bug fix changes observable semantics | Preserve the released behavior and add a corrected version unless retaining it is unsafe. | +| Urgent security or operational risk | Report the risk and consider whether reversible disablement should be recommended. | + +For a security-critical behavior that cannot remain callable, stop and report +the compatibility break. Do not silently change or delete the selector. + +## Apply the lifecycle model + +Keep function lifecycle separate from precompile availability: + +| Condition | Required call behavior | +|---|---| +| Active and enabled | Execute normally. | +| Soft-deprecated and enabled | Preserve the documented behavior and encoding. | +| Hard-deprecated and enabled | Keep routing the selector and return a descriptive precompile error. | +| Disabled | Return the precompile-disabled error regardless of function lifecycle. | + +Use soft deprecation by default. Preserve the call, mark the Solidity function +with `@deprecated`, and publish replacement metadata without adding +deprecation-only work to every invocation. + +Use hard deprecation only when old behavior cannot be represented honestly or +safely, for example because: + +- the underlying concept no longer exists and has no representation; +- the semantics changed beyond what the old return type can describe; or +- preservation requires fabricated data, dead state, unbounded work, or an + unacceptable security risk. + +Do not hard-deprecate because a replacement is newer, easier to maintain, or +more complete. First document why an adapter is impossible or disproportionate, +identify affected released functions and known callers, provide a replacement +when possible, and complete the agreed migration process. + +## Stop an undeployed compatibility break + +If the runtime change that makes old behavior impossible has not reached +mainnet, treat mainnet deployment as blocked by the compatibility break. Do not +interpret a request to update precompiles as authorization to deploy the break +or hard-deprecate affected functions immediately. + +Stop and give the developer this prominent warning: + +> **Mainnet compatibility warning:** This change would force hard deprecation +> of `` and break contracts that +> still call it. Do not deploy the incompatible runtime change to mainnet until +> `` is available, the old function has been soft-deprecated for +> the agreed migration window, and the phase-out criteria have been satisfied. + +State why an adapter cannot work, which released functions and known callers +are affected, what replacement is available or required, and which phase-out +steps remain. Continue only with non-breaking preparation such as adding the +replacement, tests, documentation, and lifecycle metadata. Preserve current +mainnet behavior throughout the migration window. Hard-deprecate only in the +later release that completes the planned phase-out. + +## Report lifecycle and availability + +Use this proposed registry shape as the compatibility target: + +```solidity +struct PrecompileStatus { + bool isDeprecated; + bool isDisabled; + address newPrecompile; + bytes4 newSelector; + string message; +} +``` + +Interpret `isDeprecated` as soft or hard function deprecation. Interpret +`isDisabled` as current unavailability through a reversible operational switch. +Use `newPrecompile` and `newSelector` for the recommended replacement; zero +replacement fields mean that none is available. Use `message` for +human-readable status or migration guidance. + +Do not infer deprecation from disablement. Do not clear deprecation when a +precompile is re-enabled. Do not describe the registry as callable until its +address and implementation are released. + +Keep registry metadata, Solidity NatSpec, public documentation, and call +behavior consistent. Prefer static registry queries over emitting a log on +every deprecated call. + +## Handle reversible disablement + +Treat disablement as an external, reversible operational action, not a normal +deprecation step. An agent may identify a risk, verify the mechanism, and +recommend that responsible decision-makers consider it. An agent cannot +perform or authorize the action. + +Require re-enablement to restore each function's previous active, +soft-deprecated, or hard-deprecated behavior. Never erase lifecycle metadata +when availability changes. + +Before recommending disablement, verify that the address routes through +`PrecompileExt::try_execute` and uses the intended `PrecompileEnum` entry. +Check whether multiple addresses share that entry and report the complete +effect of a toggle. Do not claim an address is toggleable merely because the +general mechanism exists. diff --git a/.agents/skills/emv-maintainer/references/coverage-and-testing.md b/.agents/skills/emv-maintainer/references/coverage-and-testing.md new file mode 100644 index 0000000000..1f1122fc64 --- /dev/null +++ b/.agents/skills/emv-maintainer/references/coverage-and-testing.md @@ -0,0 +1,267 @@ +# Precompile coverage and testing + +## Contents + +- [Define the coverage scope](#define-the-coverage-scope) +- [Build a coverage inventory](#build-a-coverage-inventory) +- [Cover extrinsics](#cover-extrinsics) +- [Cover state with typed views](#cover-state-with-typed-views) +- [Cover runtime APIs and public RPCs](#cover-runtime-apis-and-public-rpcs) +- [Cover events](#cover-events) +- [Add regression tests first](#add-regression-tests-first) +- [Test observable behavior](#test-observable-behavior) +- [Validate ABIs and routing](#validate-abis-and-routing) +- [Validate cost and bounds](#validate-cost-and-bounds) +- [Run repository checks](#run-repository-checks) +- [Report the result](#report-the-result) + +## Define the coverage scope + +Take the authoritative pallet and API scope from `SKILL.md`. Do not silently +expand or narrow it based on an older document. + +For each in-scope pallet, inspect: + +- every dispatchable extrinsic; +- every public state map and value; +- every publicly facing runtime API and RPC; +- every emitted event, including events originating in hooks and scheduled + work; and +- changes to types, guards, authorization, units, and error behavior used by + existing precompiles. + +Coverage means that Solidity contracts receive a typed equivalent of the +authorized client-facing functionality. It does not mean exposing raw pallet +storage, SCALE bytes, or Rust types. + +Distinguish deployed coverage from proposed coverage. Do not describe a +documented proposal, unassigned address, or Rust stub as callable. + +## Build a coverage inventory + +Create or update a working matrix with one row per source item: + +| Source | Kind | Public functionality | Precompile domain | Function or callback | Status | Evidence | +|---|---|---|---|---|---|---| +| Pallet and item | Extrinsic, state, runtime API, RPC, or event | Meaning exposed to clients | Existing or proposed address/domain | Canonical signature or callback | Covered, partial, missing, or excluded | Rust, Solidity, ABI, and test paths | + +For every partial, missing, or excluded row, state the exact reason. Do not +equate a similarly named function with coverage; compare parameters, returned +information, authorization, semantics, and failure behavior. + +Use the matrix to find both directions of drift: + +- runtime functionality with no typed EVM path; and +- precompile behavior whose runtime dependency changed or disappeared. + +Group additions by meaning under as few coherent contracts as reasonably +possible. Do not mirror pallet boundaries mechanically and do not create one +precompile per storage item. + +## Cover extrinsics + +Expose each authorized extrinsic through a typed state-changing function unless +an explicit scope decision excludes it. + +Preserve: + +- dispatched origin and caller mapping; +- authorization and proxy behavior; +- payable versus nonpayable behavior; +- attached-value conversion and handling; +- input validation and bounds; +- dispatch atomicity; +- runtime errors and EVM failure behavior; and +- gas and weight charging, including post-dispatch adjustment. + +Use `PrecompileHandleExt::try_dispatch_runtime_call` and established +precompile patterns where they apply. Do not bypass guards or create a direct +state-writing path that the pallet does not authorize. + +When an extrinsic changes, compare the old and new behavior rather than only +their Rust signatures. Follow [ABI versioning](abi-versioning.md) when an +existing function is affected. + +## Cover state with typed views + +Inventory every public state map and value in scope. Expose its meaningful +contents through typed view functions; never provide direct writable access to +storage. + +Let a view read one or more storage items when that is required to return the +meaningful value. Keep the mapping from source storage to typed functions +explicit in the coverage inventory so no item disappears behind an abstract +claim of domain coverage. + +Group related reads into coherent domain precompiles. Do not expose pallet +prefixes, storage keys, hashers, or SCALE encodings as the contract interface. + +For every view, specify and test: + +- key and account conversions; +- missing-state behavior; +- result types and tuple order; +- units, precision, scaling, and rounding; +- overflow and narrowing conversions; +- maximum input and output size; and +- the exact database reads charged. + +When storage changes internally, update the Rust adapter and prove that released +calldata still returns the released meaning. + +## Cover runtime APIs and public RPCs + +Inventory the publicly facing runtime APIs and RPCs in scope, including the +Subtensor runtime API surface required by `SKILL.md`. + +Expose typed functions with equivalent inputs and meaningful outputs. A +precompile may call the same underlying helpers rather than reproduce an RPC +transport detail. Preserve pagination, bounds, defaults, and absence semantics +that affect callers. + +Do not expose node-only behavior that cannot execute deterministically in the +runtime. When a public RPC composes runtime state, implement the deterministic +runtime-side result and document any transport-only behavior that has no EVM +equivalent. + +## Cover events + +Inspect event enums and active emission sites. Cover relevant hook-origin +events with subscription callbacks so contracts are not limited to their own +transaction receipts. + +Use [Event subscriptions](event-subscriptions.md) for domain grouping, +filtering, callback ABI, charging, queue bounds, and delivery semantics. + +Do not mark an enum-only placeholder as emitted coverage. Do not expose a raw +runtime event or an unbounded vector callback. + +## Add regression tests first + +For a bug fix, add a regression unit test that fails for the reported behavior +before implementing the fix. Confirm the failure is caused by the bug, then +apply the fix and confirm the same test passes. + +For an ABI-affecting runtime change, add a compatibility test that sends the +exact legacy calldata and decodes the result using the released ABI. A test +that only calls the new Rust helper or new selector does not prove backwards +compatibility. + +Keep precompile unit tests with the implementation's existing +`#[cfg(test)] mod tests` pattern and use `precompiles/src/mock.rs`. Reuse +`selector_u32`, `encode_with_selector`, `execute_returns`, +`execute_returns_raw`, and the established mock-state helpers where suitable. + +Name tests after observable behavior and the condition being protected. Avoid +tests that merely duplicate an implementation expression. + +## Test observable behavior + +Cover every affected path: + +- legacy success and return decoding; +- new selector success independently; +- invalid and boundary inputs; +- missing state; +- authorization and proxy origin; +- payable, nonpayable, attached-value, and static-call behavior; +- expected state transitions and rollback on failure; +- runtime dispatch errors and EVM errors; +- account and address conversion; +- TAO and Alpha unit conversion; +- precision, rounding, overflow, and narrowing; +- bounded collections and duplicate inputs; +- lifecycle status, hard-deprecation error, and disable/re-enable behavior when + applicable; and +- storage adapters against legacy and new state during migrations. + +Test both a representative normal case and the boundaries where conversion or +runtime semantics change. + +## Validate ABIs and routing + +Treat `precompiles/src/solidity/*.sol` and generated `*.abi` files as external +artifacts. Compare them with the relevant released or base-branch versions. + +Verify: + +1. Every old canonical signature and selector remains present. +2. Old input and output ABI encodings are unchanged. +3. Every new selector matches its canonical Solidity signature. +4. No selector collides with another selector at the address. +5. Only the intended Solidity interface and ABI gain the intended functions. +6. Unrelated precompile Solidity and ABI files are byte-for-byte unchanged. +7. The Rust macro signature, Solidity declaration, generated ABI, NatSpec, SDK + copies, registry metadata, and public documentation agree. +8. Every released address remains in `Precompiles::used_addresses()`. +9. `Precompiles::execute()` recognizes the address and routes it through the + intended availability control and compatible implementation. +10. Unknown-address and unknown-selector behavior remains unchanged. + +Do not hand-wave generated-file churn. Inspect each changed ABI entry and +remove unrelated regeneration changes. + +## Validate cost and bounds + +Keep every precompile path bounded in CPU, memory, storage access, and output +size. Record database reads and writes and dispatch weight through the existing +helpers. + +Test: + +- gas-limit rejection before overweight dispatch; +- post-dispatch charging and refund behavior when affected; +- the maximum accepted collection size; +- rejection just beyond the bound; +- proof-size-sensitive database access where relevant; +- callback gas and per-block delivery limits for subscriptions; and +- failure paths that could otherwise perform unpaid work. + +Do not accept a bounded input if processing it can trigger an unbounded runtime +scan. Document any change large enough to make a previously practical call +unusable even if its asymptotic complexity is unchanged. + +## Run repository checks + +Run the narrowest relevant unit test while iterating, then run the complete +precompile package tests: + +```sh +cargo test -p subtensor-precompiles +``` + +Check formatting: + +```sh +cargo fmt --all --check +``` + +Run Clippy for the package when practical: + +```sh +SKIP_WASM_BUILD=1 cargo clippy \ + -p subtensor-precompiles \ + --all-targets \ + --all-features \ + -- -D warnings +``` + +Escalate to workspace checks or affected pallet tests when shared runtime +types, dispatchables, mocks, or routing changed. Report any check that could not +run and the specific reason; do not imply success from an unexecuted check. + +## Report the result + +Summarize: + +- source functionality added, changed, or still missing; +- released addresses and selectors affected; +- adapters or new versions introduced; +- lifecycle or mainnet-release warnings; +- files and ABIs changed; +- evidence that unrelated precompiles and ABIs are unchanged; +- regression tests added and their before/after behavior; and +- commands run, results, and any remaining validation gaps. + +Do not claim completion while a required coverage row is unexplained or a +legacy caller test is missing. diff --git a/.agents/skills/emv-maintainer/references/event-subscriptions.md b/.agents/skills/emv-maintainer/references/event-subscriptions.md new file mode 100644 index 0000000000..47b4bee0e0 --- /dev/null +++ b/.agents/skills/emv-maintainer/references/event-subscriptions.md @@ -0,0 +1,249 @@ +# Event subscription precompiles + +## Contents + +- [Use typed domain precompiles](#use-typed-domain-precompiles) +- [Inventory reportable events](#inventory-reportable-events) +- [Use a common subscription interface](#use-a-common-subscription-interface) +- [Fund callback delivery](#fund-callback-delivery) +- [Keep event production bounded](#keep-event-production-bounded) +- [Define stable callback ABIs](#define-stable-callback-abis) +- [Normalize variable-length events](#normalize-variable-length-events) +- [Specify delivery semantics](#specify-delivery-semantics) +- [Protect execution](#protect-execution) +- [Test subscription behavior](#test-subscription-behavior) + +## Use typed domain precompiles + +Expose events from `SubtensorModule` and `AdminUtils` as typed Solidity +callbacks. Do not expose raw `RuntimeEvent`, pallet enum discriminants, or +SCALE-encoded payloads. + +Group callbacks by meaning under a small number of independently addressed +precompiles. Use the current proposed domains as the design baseline: + +- staking and economic flows; +- neurons, identities, relationships, and key rotation; +- weights and commit-reveal; +- subnet lifecycle, epochs, emissions, leases, and voting-power tracking; +- runtime and subnet configuration. + +Consult the corresponding pages under +`docs/guides/evm/precompiles/*-events.mdx` for the current proposed inventory. +Treat names, signatures, mask bits, and addresses as provisional until +released. After release, apply the ABI rules in +[ABI versioning](abi-versioning.md). + +Create another address only when an event family has a genuinely separate +domain and lifecycle. Do not create one precompile per pallet event. + +## Inventory reportable events + +Inspect both event enum definitions and every emission site. An enum variant +without an active emission site is not a live callback. Record it as a coverage +gap or future possibility, not as currently delivered behavior. + +For each emitted event: + +1. Record the source pallet, variant, fields, and emission sites. +2. Identify whether it originates from an extrinsic, scheduled operation, or + runtime hook. +3. Assign it to a meaningful event-precompile domain. +4. Define stable EVM field types and conversions. +5. Determine whether the source payload is bounded. +6. Define a recovery view when callbacks alone are not authoritative. +7. Add an event-mask bit without changing any released assignment. + +Prioritize hook-origin events because an interested contract cannot obtain them +from its own transaction receipt. Use the same subscription model for relevant +transaction and scheduled-operation events when this provides coherent domain +coverage. + +When a new source event starts being emitted, add a new typed callback and mask +bit. Do not change an existing callback to absorb different semantics. + +## Use a common subscription interface + +Use the same control shape for every event domain unless a documented reason +requires an additive version: + +```solidity +struct EventFilter { + uint256 eventMask; + uint16 netuid; + bytes32 accountId; + bool matchAnyNetuid; + bool matchAnyAccount; +} + +struct Subscription { + bool active; + EventFilter filter; + uint64 callbackGasLimit; + uint64 nextSequence; +} + +function subscribe( + EventFilter calldata filter, + uint64 callbackGasLimit +) external; + +function unsubscribe() external; + +function getSubscription( + address subscriber +) external view returns (Subscription memory); + +function minimumCallbackBalance( + uint64 callbackGasLimit +) external view returns (uint256); +``` + +Always make the caller the subscriber. Do not allow one address to subscribe or +unsubscribe another address. + +Store at most one fixed-size subscription per contract and event domain. Use a +fixed event mask plus at most one netuid and one account filter. Do not store or +iterate an arbitrary list of filters. + +Validate the mask, callback gas limit, filter flags, and minimum balance before +creating or replacing a subscription. Make subscription replacement atomic. + +## Fund callback delivery + +Charge callback attempts to the subscribing contract's own TAO balance. Require +enough balance at subscription time to fund the documented minimum number of +attempts at the selected callback gas limit. + +Charge a reverting callback for the work it consumed. Never let callback +failure revert the runtime operation that produced the source event. + +Automatically remove a subscription when its balance cannot fund the next +attempt. Define charging, rounding, and TAO-to-EVM unit conversion precisely. +Do not provide free delivery paths that allow subscription spam. + +Keep the minimum-balance calculation available as a typed view so a contract +can determine whether a subscription is fundable before submitting it. + +## Keep event production bounded + +Do not synchronously iterate all subscribers when an event is emitted. Append a +fixed-size typed report to a bounded queue in O(1), then process a bounded +amount of delivery work in later blocks. + +Advance delivery through bounded cursors. Cap: + +- queue capacity; +- work per block; +- callback gas; +- report size; +- subscription size; and +- the number of delivery attempts performed by one bounded work item. + +Do not copy or ABI-encode an unbounded vector while producing a report. If a +source event is variable-length, normalize it incrementally as described below. + +Define what happens when the queue reaches capacity. Never permit unbounded +runtime storage or memory growth. + +## Define stable callback ABIs + +Give every event a stable, event-specific receiver selector. Include +`uint64 sequence` and `uint64 sourceBlock` in every callback before the +event-specific fields. + +Use stable EVM representations: + +- Substrate account IDs and hashes: `bytes32`; +- EVM accounts: `address`; +- netuids and UIDs: `uint16` when the runtime domain fits; +- TAO and Alpha amounts: 18-decimal `uint256` values using the documented + `10^9` conversion factor; +- fixed-point values: an explicitly documented integer representation. + +Choose bounded representations for strings, identities, and other structured +values before release. Do not expose a Rust or SCALE representation as the ABI. + +After release: + +- reserve the precompile address and control selectors; +- reserve every event-mask bit; +- preserve callback names, parameters, order, types, and meaning; +- preserve filter, charging, sequencing, and delivery guarantees; and +- add a versioned callback when richer data is required. + +Do not add speculative fields to a callback merely because a future runtime +might produce them. Add another selector when the semantics become concrete. + +## Normalize variable-length events + +Convert every variable-length source event into bounded callbacks. Emit a +summary when useful, followed by one item callback per entry. Give related +callbacks the same source sequence and include item index and item count. + +For UID-indexed emission arrays, interpret the array index as the UID and +deliver one `(uid, amount)` callback per entry. For example, `[10, 20, 30]` +represents UIDs `0`, `1`, and `2`; do not treat an entry as an arbitrary UID +value. + +Apply the same approach to children lists, weight hashes, completed-netuid +batches, and similar collections. Use a stable typed representation for +per-item failures instead of SCALE-encoded `DispatchError`. + +Produce normalized items incrementally at the source. Do not first copy the +complete vector into a queued report. + +## Specify delivery semantics + +Treat callbacks as asynchronous, best-effort notifications. Do not promise that +a callback executes in the source event's block. + +Use a monotonically increasing source sequence and source block so receivers +can order reports and detect gaps. Define whether normalized items share one +source sequence and how item indices identify completeness. + +If bounded queue overwrite or another allowed failure drops a report, make the +gap observable through sequencing. Require authoritative recovery through the +corresponding typed view where contract logic needs exact current state. + +Document ordering across event domains only if the implementation guarantees +it. Require receivers to make callbacks idempotent and tolerate retries, +reordering outside documented guarantees, and sequence gaps. + +## Protect execution + +Apply reentrancy protection around delivery. Do not allow a callback to +recursively create unbounded callback work. + +Keep the source runtime operation independent of callback execution. Bound +callback gas and isolate callback failure. Validate that subscriber-controlled +code cannot stall block processing, retain an unpaid subscription, or make +another subscriber's delivery unbounded. + +Account for database reads, writes, queue operations, EVM execution, and failed +attempts. Use saturating arithmetic where appropriate and reject values that +cannot be converted safely. + +## Test subscription behavior + +Test at least: + +- self-subscription and self-unsubscription; +- attempts to manage another address; +- invalid masks, filters, and gas limits; +- insufficient initial balance; +- successful charging and delivery; +- reverting and out-of-gas callbacks; +- automatic unsubscription when payment fails; +- event filtering by mask, netuid, and account; +- monotonic sequencing and source-block reporting; +- queue capacity and observable gaps; +- bounded per-block work with many subscribers; +- reentrancy and recursive-work resistance; +- one-item normalization and item ordering; +- unit and account conversions; +- released callback selectors and event-mask assignments; and +- additive introduction of a new callback without changing old callbacks. + +Use [Coverage and testing](coverage-and-testing.md) for the general precompile +regression and ABI-diff requirements. diff --git a/docs/guides/evm/index.mdx b/docs/guides/evm/index.mdx index b7b591cb62..8944daa5d4 100644 --- a/docs/guides/evm/index.mdx +++ b/docs/guides/evm/index.mdx @@ -39,11 +39,21 @@ deeper concepts (address mappings, decimals, precompiles). End-to-end tutorials that build on the commands below: + + + A deployed contract may be immutable. Treat every released precompile address, + function signature, and selector as a permanent public API. + + +## Design goals + +The precompile layer is designed around five goals: + +1. **Contracts at rest keep working.** Runtime upgrades must not silently break + deployed contracts. +2. **Interfaces evolve additively.** Existing selectors remain reserved, and + richer behavior is introduced through new function versions. +3. **Deprecation is normally soft.** An old function continues to preserve its + original behavior whenever that behavior can still be represented safely. +4. **Status is discoverable.** Solidity interfaces and a registry should tell + developers when a function is deprecated, replaced, or temporarily disabled. +5. **The authorized Substrate API has full parity.** Every storage item and + extrinsic in scope has a typed precompile equivalent. + +## Fixed addresses and function selectors + +A precompile has a fixed EVM address for a domain such as staking, metagraph +data, or subnet operations. Solidity dispatches a call using the first four +bytes of the Keccak-256 hash of its canonical function signature. + +For example: + +```solidity +function getStake(uint16 netuid, uint16 uid) external view returns (uint64); +``` + +The selector belongs to that signature permanently once released. It must not +later be assigned different semantics, even if the original function is +hard-deprecated. Reusing a selector could make an old contract decode a +successful but unrelated result. + +The source-of-truth Solidity interfaces and generated ABIs live in +[`precompiles/src/solidity/`](https://github.com/RaoFoundation/subtensor/tree/main/precompiles/src/solidity). + +## Compatibility rules + +### Preserve released interfaces + +Do not remove or change a released function signature. A runtime implementation +may change internally to follow a new storage layout or computation, but the +observable result must retain the function's documented meaning. + +Changing any of these creates a different EVM interface: + +- function name or version suffix; +- parameter types or order; +- return types or order; +- mutability where it affects permitted calls; +- precompile address. + +### Version functions, not whole domains + +When a breaking return-type or parameter change is necessary, add a versioned +function at the same precompile address: + +```solidity +interface IMetagraph { + // Original selector remains supported. + function getStake( + uint16 netuid, + uint16 uid + ) external view returns (uint64); + + // New selector exposes the richer representation. + function getStakeV2( + uint16 netuid, + uint16 uid + ) external view returns (StakeInfo memory); +} +``` + +Use `functionName` for the initial version, followed by `functionNameV2`, +`functionNameV3`, and so on. Both selectors route independently, so adding a +version does not alter calls made by existing contracts. + +Creating a new domain address may still be appropriate when the functionality +is genuinely a different precompile, but it should not be the default +versioning mechanism. + +### Keep old semantics when possible + +Suppose `getStake` originally returned total stake, while a later runtime stores +self-stake and delegated stake separately. The original function can continue +returning their sum, while `getStakeV2` returns the breakdown. + +This is a soft deprecation: the old selector remains correct for callers that +depend on its original meaning. + +## Replace raw storage access with typed views + +Raw storage access couples a contract to pallet names, storage item names, +hashers, key shapes, and SCALE encodings. Any internal refactor can then make +the contract read an empty value or decode the wrong bytes without a useful +error. + +A typed view instead owns the encoding and decoding: + +```solidity +uint64 weight = IMetagraph(METAGRAPH_ADDRESS).getWeight(netuid, uid); +``` + +If the underlying storage map, key format, hasher, or value encoding changes, +the precompile implementation adapts while the Solidity interface remains +stable. A resulting Rust compilation failure provides a safety net that raw +storage queries do not. + +### Phasing out raw storage reads + +`StorageQueryPrecompile` at `0x…0807` exposes raw Substrate storage and is +inherently brittle. The intended migration is: + +1. Add a typed view for every storage item in the currently authorized pallets: + SubtensorModule, Balances, Proxy, Scheduler, Drand, Crowdloan, Sudo, + Multisig, Timestamp, and Swap. +2. Soft-deprecate raw storage access after that typed coverage exists. +3. Hard-deprecate it after a documented migration window. +4. Eventually disable it through an explicit root decision. + +Whether this 1:1 coverage should extend beyond the authorized pallets remains +an open design question. + +## Subscription-based event reporting + +Some Subtensor events are produced by runtime hooks rather than by the EVM +transaction that is interested in them. A transaction receipt therefore cannot +provide complete event coverage. Proposed event precompiles let a contract +subscribe itself and receive those events later as typed EVM callbacks. + +Event reporting is divided into dedicated domain precompiles for +[staking](/docs/guides/evm/precompiles/staking-events), +[neurons and keys](/docs/guides/evm/precompiles/neuron-events), +[weights](/docs/guides/evm/precompiles/weights-events), +[subnet lifecycle](/docs/guides/evm/precompiles/subnet-events), and +[runtime configuration](/docs/guides/evm/precompiles/configuration-events). +Each domain will have its own address. These precompiles report typed events +originating from `SubtensorModule` and `AdminUtils`; they do not expose raw +`RuntimeEvent` values or SCALE-encoded payloads. + +The callback inventories cover source variants with active emission sites in +the current runtime. An enum-only placeholder is not presented as a live +callback. If such a variant starts being emitted, its typed callback must be +added without changing the existing subscription or receiver selectors. + +### Subscription control + +Each event precompile should expose the same control shape: + +```solidity +struct EventFilter { + uint256 eventMask; + uint16 netuid; + bytes32 accountId; + bool matchAnyNetuid; + bool matchAnyAccount; +} + +struct Subscription { + bool active; + EventFilter filter; + uint64 callbackGasLimit; + uint64 nextSequence; +} + +function subscribe( + EventFilter calldata filter, + uint64 callbackGasLimit +) external; + +function unsubscribe() external; + +function getSubscription( + address subscriber +) external view returns (Subscription memory); + +function minimumCallbackBalance( + uint64 callbackGasLimit +) external view returns (uint256); +``` + +The caller is always the subscriber: a contract cannot subscribe or unsubscribe +another address. One fixed-size subscription per contract and domain keeps +lookup and update costs bounded. The event mask and optional single-netuid and +single-account filters let a subscriber narrow delivery without storing or +iterating an arbitrary filter list. + +`subscribe` succeeds only when the contract's own TAO balance can fund the +documented minimum number of callback attempts at its selected gas limit. Each +attempt is charged to that same balance. If the balance can no longer pay for +an attempt, the subscription is automatically removed. A reverting callback is +charged for the work it consumed and cannot revert the runtime operation that +produced the event. + +### Typed callbacks and delivery + +Every report has a stable, event-specific callback selector. Substrate account +IDs are represented as `bytes32`, and TAO and Alpha balances are multiplied by +`10^9` for EVM's 18-decimal convention. New source events add callback +selectors; released callbacks are never changed, removed, or reused. + +Callbacks are asynchronous notifications, not part of the transaction or hook +that produced the source event. Every callback includes a monotonically +increasing sequence and source block number so receivers can order deliveries +and detect a gap. A receiver must make its callback idempotent and must not +assume delivery in the source event's block. + +The runtime must not iterate every subscriber while emitting an event. Instead, +emission appends one fixed-size typed report to a bounded queue in O(1), and a +bounded amount of later block work advances a subscriber cursor one delivery at +a time. Callback gas is capped, delivery is protected against reentrancy, and a +callback cannot recursively create more callback work. + +Variable-length pallet events are normalized into bounded item callbacks. For +example, miner emissions are reported one UID at a time and a batch of weight +hashes is reported one hash at a time, with the same source sequence plus item +index and item count. The adapter must produce those items incrementally at the +source rather than copy or ABI-encode an unbounded vector. + +The queue has a fixed capacity so event reporting cannot grow runtime memory +without bound. If delivery falls behind far enough to overwrite an undelivered +report, the next successful callback exposes the sequence gap. Event callbacks +are therefore best-effort integration signals; contracts that require +authoritative recovery must use the corresponding typed view. + +## Function lifecycle + +Deprecation and disablement are different dimensions: + +- **Deprecation** communicates API evolution. It normally points callers toward + a replacement and is expected to remain part of the function's history. +- **Disablement** is an operational switch for an entire precompile. Root can + disable and later re-enable it through + `AdminUtils.sudo_toggle_evm_precompile`. + +| Lifecycle condition | Call behavior | +|---|---| +| Active and enabled | Executes normally | +| Soft-deprecated and enabled | Preserves its documented behavior | +| Hard-deprecated and enabled | Returns a descriptive precompile error | +| Disabled | Returns a precompile-disabled error regardless of function lifecycle | + +Soft deprecation is the default. Hard deprecation is reserved for cases where +the original behavior cannot be represented honestly or safely—for example, +when the underlying concept has been removed without a replacement. + +Disablement does not erase deprecation metadata. A soft-deprecated function can +also be disabled, and re-enabling its precompile restores its soft-deprecated +behavior. + +## Discovering status + +The proposed standalone registry precompile gives tooling and contracts one +place to inspect both API lifecycle and operational availability. + +Because the result covers both lifecycle and operational availability, it is +called `PrecompileStatus`: + +```solidity +interface IPrecompileRegistry { + struct PrecompileStatus { + bool isDeprecated; + bool isDisabled; + address newPrecompile; + bytes4 newSelector; + string message; + } + + function getPrecompileStatus( + address precompile, + bytes4 selector + ) external view returns (PrecompileStatus memory); +} +``` + +The fields have the following meaning: + +| Field | Meaning | +|---|---| +| `isDeprecated` | The function is soft- or hard-deprecated. | +| `isDisabled` | The containing precompile is currently disabled by Root; Root can re-enable it. | +| `newPrecompile` | Address of the recommended replacement, often the same address. | +| `newSelector` | Selector of the recommended replacement function. | +| `message` | Human-readable status or migration guidance. | + +Zero replacement fields mean that no replacement is available. Tooling should +not infer that `isDisabled` implies deprecation, or that re-enabling a +precompile clears `isDeprecated`. + +The registry avoids adding overhead to every deprecated call. Deployment tools, +frontends, and upgradeable contracts can query it when evaluating dependencies. + +Solidity interfaces should also carry NatSpec annotations: + +```solidity +interface IMetagraph { + /// @deprecated Use getStakeV2 instead. + function getStake( + uint16 netuid, + uint16 uid + ) external view returns (uint64); + + function getStakeV2( + uint16 netuid, + uint16 uid + ) external view returns (StakeInfo memory); +} +``` + +## Handling runtime changes + +### Additive representation changes + +Keep the original function returning the original subset, add a versioned +function for the extended result, and soft-deprecate the original if callers +should migrate. + +### Semantic refinements + +Adapt the original implementation to preserve its documented meaning. Add a new +version only when callers need a representation that the original return type +cannot express. + +### Storage and computation changes + +Change the precompile implementation without changing its interface. This +includes changing: + +- storage names, key shapes, or hashers; +- the number of storage items used; +- intermediate representations; +- the computation used to produce the exposed value. + +### Complete removal + +Keep the selector reserved and make the function return a descriptive error. +Mark it hard-deprecated and explain whether an alternative exists. Do not +delete the signature and do not reuse its selector. + +### Emergency disablement + +Root may disable a precompile with: + +```text +AdminUtils.sudo_toggle_evm_precompile(precompile_id, false) +``` + +and re-enable it with: + +```text +AdminUtils.sudo_toggle_evm_precompile(precompile_id, true) +``` + +This switch is reversible and applies to the precompile as a whole. It is not a +substitute for function-level lifecycle metadata or a normal deprecation +process. + +## Maintenance and testing requirements + +Every precompile change should verify: + +- all previously released selectors remain routed; +- existing function signatures and return encodings are unchanged; +- old semantics are preserved or explicitly hard-deprecated; +- new behavior uses a new versioned selector when necessary; +- Solidity interfaces, generated ABIs, SDK copies, and runtime implementations + agree; +- lifecycle registry metadata and NatSpec annotations agree; +- disable and re-enable behavior is covered for the affected precompile; +- no selector is reused. + +Typed views provide a compile-time safety advantage: when runtime types or +storage APIs change, the Rust implementation is more likely to stop compiling, +forcing maintainers to make an explicit compatibility decision. Coverage checks +should ensure that every storage item in the authorized pallets has a +corresponding view. + +Macro or code-generation support may eventually reduce boilerplate and validate +selector coverage, ABI synchronization, and registry entries. The compatibility +rules should remain explicit even if their enforcement becomes automated. + +## Summary + +Precompiles are a long-lived contract between Subtensor and deployed EVM code. +Keep addresses and released selectors stable, version functions additively, +preserve old semantics whenever possible, and replace raw storage access with +typed views that insulate callers from storage layouts. Use deprecation to guide +migration and reversible disablement to handle operational risk; report both +through a common status model without treating them as the same condition. diff --git a/docs/guides/evm/precompiles/account-balance.mdx b/docs/guides/evm/precompiles/account-balance.mdx new file mode 100644 index 0000000000..4fb1e89612 --- /dev/null +++ b/docs/guides/evm/precompiles/account-balance.mdx @@ -0,0 +1,20 @@ +--- +title: Account balance +description: Reference for the deployed BalancePrecompile. +--- + +| Property | Value | +|---|---| +| Rust implementation | `BalancePrecompile` | +| Solidity interface | `IBalance` | +| Address | `0x000000000000000000000000000000000000080e` | +| Status | Deployed | + +## Functions + +| Function | Mutability | +|---|---| +| `getFreeBalance(bytes32)` | `view` | + +Source: [`balance.sol`](https://github.com/RaoFoundation/subtensor/blob/main/precompiles/src/solidity/balance.sol) + diff --git a/docs/guides/evm/precompiles/address-mapping.mdx b/docs/guides/evm/precompiles/address-mapping.mdx new file mode 100644 index 0000000000..1561208e92 --- /dev/null +++ b/docs/guides/evm/precompiles/address-mapping.mdx @@ -0,0 +1,20 @@ +--- +title: Address mapping +description: Reference for the deployed AddressMappingPrecompile. +--- + +| Property | Value | +|---|---| +| Rust implementation | `AddressMappingPrecompile` | +| Solidity interface | `IAddressMapping` | +| Address | `0x000000000000000000000000000000000000080c` | +| Status | Deployed | + +## Functions + +| Function | Mutability | +|---|---| +| `addressMapping(address)` | `view` | + +Source: [`addressMapping.sol`](https://github.com/RaoFoundation/subtensor/blob/main/precompiles/src/solidity/addressMapping.sol) + diff --git a/docs/guides/evm/precompiles/alpha.mdx b/docs/guides/evm/precompiles/alpha.mdx new file mode 100644 index 0000000000..c45aba78f7 --- /dev/null +++ b/docs/guides/evm/precompiles/alpha.mdx @@ -0,0 +1,40 @@ +--- +title: Alpha +description: Reference for the deployed AlphaPrecompile. +--- + +| Property | Value | +|---|---| +| Rust implementation | `AlphaPrecompile` | +| Solidity interface | `IAlpha` | +| Address | `0x0000000000000000000000000000000000000808` | +| Status | Deployed | + +Provides typed views of subnet pools, prices, issuance, emissions, and simulated +swaps. All functions are `view`. + +## Functions + +```text +getAlphaPrice(uint16) +getMovingAlphaPrice(uint16) +getTaoInPool(uint16) +getAlphaInPool(uint16) +getAlphaOutPool(uint16) +getAlphaIssuance(uint16) +getTaoWeight() +simSwapTaoForAlpha(uint16,uint64) +simSwapAlphaForTao(uint16,uint64) +getSubnetMechanism(uint16) +getRootNetuid() +getEMAPriceHalvingBlocks(uint16) +getSubnetVolume(uint16) +getTaoInEmission(uint16) +getAlphaInEmission(uint16) +getAlphaOutEmission(uint16) +getSumAlphaPrice() +getCKBurn() +``` + +Source: [`alpha.sol`](https://github.com/RaoFoundation/subtensor/blob/main/precompiles/src/solidity/alpha.sol) + diff --git a/docs/guides/evm/precompiles/balance-transfer.mdx b/docs/guides/evm/precompiles/balance-transfer.mdx new file mode 100644 index 0000000000..71b3f4c1ce --- /dev/null +++ b/docs/guides/evm/precompiles/balance-transfer.mdx @@ -0,0 +1,23 @@ +--- +title: Balance transfer +description: Reference for the deployed BalanceTransferPrecompile. +--- + +| Property | Value | +|---|---| +| Rust implementation | `BalanceTransferPrecompile` | +| Solidity interface | `ISubtensorBalanceTransfer` | +| Address | `0x0000000000000000000000000000000000000800` | +| Status | Deployed | + +Transfers the EVM call value to the Substrate account supplied as a 32-byte +public key. + +## Functions + +| Function | Mutability | +|---|---| +| `transfer(bytes32)` | `payable` | + +Source: [`balanceTransfer.sol`](https://github.com/RaoFoundation/subtensor/blob/main/precompiles/src/solidity/balanceTransfer.sol) + diff --git a/docs/guides/evm/precompiles/configuration-events.mdx b/docs/guides/evm/precompiles/configuration-events.mdx new file mode 100644 index 0000000000..b3d1085b00 --- /dev/null +++ b/docs/guides/evm/precompiles/configuration-events.mdx @@ -0,0 +1,98 @@ +--- +title: Configuration events +description: Proposed subscription precompile for typed Subtensor and AdminUtils configuration callbacks. +--- + +| Property | Value | +|---|---| +| Proposed implementation | `ConfigurationEventsPrecompile` | +| Proposed Solidity interface | `IConfigurationEvents` | +| Callback receiver interface | `IConfigurationEventsReceiver` | +| Address | Dedicated address not assigned | +| Status | Proposed | + +This precompile reports runtime and subnet configuration changes emitted by +`SubtensorModule` and `AdminUtils`. It normalizes the two pallets into +meaningful typed callbacks while retaining the source pallet in callback +metadata when both pallets can describe the same setting. + +## Proposed Subtensor callbacks + +| Receiver function | Subtensor source event | +|---|---| +| `onActivityCutoffChanged(...)` | `ActivityCutoffSet` | +| `onActivityCutoffFactorChanged(...)` | `ActivityCutoffFactorMilliSet` | +| `onAdjustmentAlphaChanged(...)` | `AdjustmentAlphaSet` | +| `onAdjustmentIntervalChanged(...)` | `AdjustmentIntervalSet` | +| `onAdminFreezeWindowChanged(...)` | `AdminFreezeWindowSet` | +| `onBondsMovingAverageChanged(...)` | `BondsMovingAverageSet` | +| `onBondsPenaltyChanged(...)` | `BondsPenaltySet` | +| `onBondsResetOnSetChanged(...)` | `BondsResetOnSet` | +| `onColdkeySwapAnnouncementDelayChanged(...)` | `ColdkeySwapAnnouncementDelaySet` | +| `onColdkeySwapReannouncementDelayChanged(...)` | `ColdkeySwapReannouncementDelaySet` | +| `onDifficultyChanged(...)` | `DifficultySet` | +| `onDissolutionScheduleDurationChanged(...)` | `DissolveNetworkScheduleDurationSet` | +| `onImmunityPeriodChanged(...)` | `ImmunityPeriodSet` | +| `onKappaChanged(...)` | `KappaSet` | +| `onMaxAllowedUidsChanged(...)` | `MaxAllowedUidsSet` | +| `onMaxAllowedValidatorsChanged(...)` | `MaxAllowedValidatorsSet` | +| `onMaxBurnChanged(...)` | `MaxBurnSet` | +| `onMaxChildKeyTakeChanged(...)` | `MaxChildKeyTakeSet` | +| `onMaxDelegateTakeChanged(...)` | `MaxDelegateTakeSet` | +| `onMaxDifficultyChanged(...)` | `MaxDifficultySet` | +| `onMaxEpochsPerBlockChanged(...)` | `MaxEpochsPerBlockSet` | +| `onMaxRegistrationsPerBlockChanged(...)` | `MaxRegistrationsPerBlockSet` | +| `onMinAllowedUidsChanged(...)` | `MinAllowedUidsSet` | +| `onMinAllowedWeightChanged(...)` | `MinAllowedWeightSet` | +| `onMinBurnChanged(...)` | `MinBurnSet` | +| `onMinChildKeyTakeChanged(...)` | `MinChildKeyTakeSet` | +| `onMinChildKeyTakeForSubnetChanged(...)` | `MinChildKeyTakePerSubnetSet` | +| `onMinDelegateTakeChanged(...)` | `MinDelegateTakeSet` | +| `onMinDifficultyChanged(...)` | `MinDifficultySet` | +| `onMinNonImmuneUidsChanged(...)` | `MinNonImmuneUidsSet` | +| `onNetworkImmunityPeriodChanged(...)` | `NetworkImmunityPeriodSet` | +| `onNetworkLockCostReductionIntervalChanged(...)` | `NetworkLockCostReductionIntervalSet` | +| `onNetworkMinimumLockCostChanged(...)` | `NetworkMinLockCostSet` | +| `onNetworkRateLimitChanged(...)` | `NetworkRateLimitSet` | +| `onOwnerHyperparameterRateLimitChanged(...)` | `OwnerHyperparamRateLimitSet` | +| `onPowRegistrationAllowedChanged(...)` | `PowRegistrationAllowed` | +| `onRaoRecycledForRegistrationChanged(...)` | `RAORecycledForRegistrationSet` | +| `onRegistrationAllowedChanged(...)` | `RegistrationAllowed` | +| `onRegistrationsPerIntervalChanged(...)` | `RegistrationPerIntervalSet` | +| `onScalingLawPowerChanged(...)` | `ScalingLawPowerSet` | +| `onServingRateLimitChanged(...)` | `ServingRateLimitSet` | +| `onStakeThresholdChanged(...)` | `StakeThresholdSet` | +| `onStartCallDelayChanged(...)` | `StartCallDelaySet` | +| `onSubnetLimitChanged(...)` | `SubnetLimitSet` | +| `onSubnetOwnerCutChanged(...)` | `SubnetOwnerCutSet` | +| `onTempoChanged(...)` | `TempoSet` | +| `onTransferEnabledChanged(...)` | `TransferToggle` | +| `onChildKeyTakeRateLimitChanged(...)` | `TxChildKeyTakeRateLimitSet` | +| `onDelegateTakeRateLimitChanged(...)` | `TxDelegateTakeRateLimitSet` | +| `onTransactionRateLimitChanged(...)` | `TxRateLimitSet` | +| `onValidatorPruneLengthChanged(...)` | `ValidatorPruneLenSet` | +| `onWeightsRateLimitChanged(...)` | `WeightsSetRateLimitSet` | +| `onWeightsVersionKeyChanged(...)` | `WeightsVersionKeySet` | + +## Proposed AdminUtils callbacks + +| Receiver function | AdminUtils source event | +|---|---| +| `onPrecompileAvailabilityChanged(...)` | `PrecompileUpdated` | +| `onYuma3EnabledChanged(...)` | `Yuma3EnableToggled` | +| `onBondsResetEnabledChanged(...)` | `BondsResetToggled` | +| `onBurnHalfLifeChanged(...)` | `BurnHalfLifeSet` | +| `onBurnIncreaseMultiplierChanged(...)` | `BurnIncreaseMultSet` | +| `onSubnetEmissionEnabledChanged(...)` | `SubnetEmissionEnabledSet` | +| `onCollateralLockShareChanged(...)` | `CollateralLockShareSet` | +| `onCollateralDrainRatioChanged(...)` | `CollateralDrainRatioSet` | + +Every callback begins with `uint64 sequence`, `uint64 sourceBlock`, and a typed +source-pallet value, followed by the setting's typed fields. Account IDs use +`bytes32`, netuids use `uint16`, and fixed-point values use a documented stable +EVM representation. + +Subscription behavior is defined in +[Subscription-based event reporting](/docs/guides/evm/precompile-design#subscription-based-event-reporting). +The names and signatures are provisional and do not reserve selectors. + diff --git a/docs/guides/evm/precompiles/crowdloan.mdx b/docs/guides/evm/precompiles/crowdloan.mdx new file mode 100644 index 0000000000..abdce95497 --- /dev/null +++ b/docs/guides/evm/precompiles/crowdloan.mdx @@ -0,0 +1,37 @@ +--- +title: Crowdloan +description: Reference for the deployed CrowdloanPrecompile. +--- + +| Property | Value | +|---|---| +| Rust implementation | `CrowdloanPrecompile` | +| Solidity interface | `ICrowdloan` | +| Address | `0x0000000000000000000000000000000000000809` | +| Status | Deployed | + +## Views + +```text +getCrowdloan(uint32) +getContribution(uint32,bytes32) +``` + +## Operations + +All operations are `payable`: + +```text +create(uint64,uint64,uint64,uint32,address) +contribute(uint32,uint64) +withdraw(uint32) +finalize(uint32) +refund(uint32) +dissolve(uint32) +updateMinContribution(uint32,uint64) +updateEnd(uint32,uint32) +updateCap(uint32,uint64) +``` + +Source: [`crowdloan.sol`](https://github.com/RaoFoundation/subtensor/blob/main/precompiles/src/solidity/crowdloan.sol) + diff --git a/docs/guides/evm/precompiles/drand.mdx b/docs/guides/evm/precompiles/drand.mdx new file mode 100644 index 0000000000..9e8bf552f3 --- /dev/null +++ b/docs/guides/evm/precompiles/drand.mdx @@ -0,0 +1,35 @@ +--- +title: Drand +description: Proposed typed EVM interface for the Drand pallet. +--- + +| Property | Value | +|---|---| +| Proposed implementation | `DrandPrecompile` | +| Proposed Solidity interface | `IDrand` | +| Address | Not assigned | +| Status | Proposed | + +This precompile would expose typed beacon configuration and pulse data instead +of requiring callers to construct Drand storage keys and decode SCALE values. + +## Planned views + +| Function | Replaces | +|---|---| +| `getBeaconConfig()` | `Drand.BeaconConfig` | +| `getPulse(uint64 round)` | `Drand.Pulses` | +| `getStoredRoundRange()` | `Drand.OldestStoredRound` and `Drand.LastStoredRound` | +| `getNextUnsignedAt()` | `Drand.NextUnsignedAt` | +| `hasMigrationRun(bytes key)` | `Drand.HasMigrationRun` | + +## Planned operations + +```text +writePulse +setBeaconConfig +setOldestStoredRound +``` + +The runtime's existing signed, unsigned, and Root origin checks remain in force. +Names and signatures on this page are provisional and do not reserve selectors. diff --git a/docs/guides/evm/precompiles/index.mdx b/docs/guides/evm/precompiles/index.mdx new file mode 100644 index 0000000000..c0ed62162b --- /dev/null +++ b/docs/guides/evm/precompiles/index.mdx @@ -0,0 +1,60 @@ +--- +title: Precompiles +description: Addresses, implementations, and reference pages for Bittensor EVM precompiles. +--- + +Bittensor precompiles are fixed-address contracts implemented by the Subtensor +runtime. `Deployed` means that the address is registered in the current runtime; +it does not imply complete coverage of the underlying runtime domain. +`Proposed` precompiles have no assigned address or released selectors. + +## Ethereum and Frontier precompiles + +| Precompile | Address | Status | +|---|---|---| +| `ECRecover` | | Deployed | +| `Sha256` | | Deployed | +| `Ripemd160` | | Deployed | +| `Identity` | | Deployed | +| `Modexp` | | Deployed | +| `Dispatch` | | Deployed | +| `Bn128Mul` | | Deployed | +| `Bn128Pairing` | | Deployed | +| `Bn128Add` | | Deployed | +| `Sha3FIPS256` | | Deployed | +| `ECRecoverPublicKey` | | Deployed | +| `Ed25519Verify` | | Deployed | +| `Sr25519Verify` | | Deployed | + +## Bittensor precompiles + +| Precompile | Solidity interface | Details | +|---|---|---| +| [`BalanceTransferPrecompile`](/docs/guides/evm/precompiles/balance-transfer) | `ISubtensorBalanceTransfer` |
Deployed | +| [`StakingPrecompile`](/docs/guides/evm/precompiles/staking-v1) | `IStaking` V1 |
Deployed | +| [`MetagraphPrecompile`](/docs/guides/evm/precompiles/metagraph) | `IMetagraph` |
Deployed | +| [`SubnetPrecompile`](/docs/guides/evm/precompiles/subnet) | `ISubnet` |
Deployed | +| [`NeuronPrecompile`](/docs/guides/evm/precompiles/neuron) | `INeuron` |
Deployed | +| [`StakingPrecompileV2`](/docs/guides/evm/precompiles/staking-v2) | `IStaking` V2 |
Deployed | +| [`UidLookupPrecompile`](/docs/guides/evm/precompiles/uid-lookup) | `IUidLookup` |
Deployed | +| [`StorageQueryPrecompile`](/docs/guides/evm/precompiles/storage-query) | Selectorless |
Deployed · deprecation planned | +| [`AlphaPrecompile`](/docs/guides/evm/precompiles/alpha) | `IAlpha` |
Deployed | +| [`CrowdloanPrecompile`](/docs/guides/evm/precompiles/crowdloan) | `ICrowdloan` |
Deployed | +| [`LeasingPrecompile`](/docs/guides/evm/precompiles/leasing) | `ILeasing` |
Deployed | +| [`ProxyPrecompile`](/docs/guides/evm/precompiles/proxy) | `IProxy` |
Deployed | +| [`AddressMappingPrecompile`](/docs/guides/evm/precompiles/address-mapping) | `IAddressMapping` |
Deployed | +| [`VotingPowerPrecompile`](/docs/guides/evm/precompiles/voting-power) | `IVotingPower` |
Deployed | +| [`BalancePrecompile`](/docs/guides/evm/precompiles/account-balance) | `IBalance` |
Deployed | +| [`SchedulerPrecompile`](/docs/guides/evm/precompiles/scheduler) | `IScheduler` | Address not assigned
Proposed | +| [`DrandPrecompile`](/docs/guides/evm/precompiles/drand) | `IDrand` | Address not assigned
Proposed | +| [`TimestampPrecompile`](/docs/guides/evm/precompiles/timestamp) | `ITimestamp` | Address not assigned
Proposed | +| [`StakingEventsPrecompile`](/docs/guides/evm/precompiles/staking-events) | `IStakingEvents` | Dedicated address not assigned
Proposed | +| [`NeuronEventsPrecompile`](/docs/guides/evm/precompiles/neuron-events) | `INeuronEvents` | Dedicated address not assigned
Proposed | +| [`WeightsEventsPrecompile`](/docs/guides/evm/precompiles/weights-events) | `IWeightsEvents` | Dedicated address not assigned
Proposed | +| [`SubnetEventsPrecompile`](/docs/guides/evm/precompiles/subnet-events) | `ISubnetEvents` | Dedicated address not assigned
Proposed | +| [`ConfigurationEventsPrecompile`](/docs/guides/evm/precompiles/configuration-events) | `IConfigurationEvents` | Dedicated address not assigned
Proposed | +| [`PrecompileRegistry`](/docs/guides/evm/precompiles/registry) | `IPrecompileRegistry` | Address not assigned
Proposed | + +Released addresses and selectors remain reserved permanently. The compatibility +and lifecycle rules are documented in +[Precompile design and lifecycle](/docs/guides/evm/precompile-design). diff --git a/docs/guides/evm/precompiles/leasing.mdx b/docs/guides/evm/precompiles/leasing.mdx new file mode 100644 index 0000000000..e4a910e574 --- /dev/null +++ b/docs/guides/evm/precompiles/leasing.mdx @@ -0,0 +1,31 @@ +--- +title: Leasing +description: Reference for the deployed LeasingPrecompile. +--- + +| Property | Value | +|---|---| +| Rust implementation | `LeasingPrecompile` | +| Solidity interface | `ILeasing` | +| Address | `0x000000000000000000000000000000000000080a` | +| Status | Deployed | + +## Views + +```text +getLease(uint32) +getContributorShare(uint32,bytes32) +getLeaseIdForSubnet(uint16) +``` + +## Operations + +```text +createLeaseCrowdloan(uint64,uint64,uint64,uint32,uint8,bool,uint32) +terminateLease(uint32,bytes32) +``` + +Both operations are `payable`. + +Source: [`leasing.sol`](https://github.com/RaoFoundation/subtensor/blob/main/precompiles/src/solidity/leasing.sol) + diff --git a/docs/guides/evm/precompiles/meta.json b/docs/guides/evm/precompiles/meta.json new file mode 100644 index 0000000000..07144223c0 --- /dev/null +++ b/docs/guides/evm/precompiles/meta.json @@ -0,0 +1,31 @@ +{ + "title": "Precompiles", + "pages": [ + "index", + "balance-transfer", + "staking-v1", + "metagraph", + "subnet", + "neuron", + "staking-v2", + "uid-lookup", + "storage-query", + "alpha", + "crowdloan", + "leasing", + "proxy", + "address-mapping", + "voting-power", + "account-balance", + "---Proposed---", + "scheduler", + "drand", + "timestamp", + "staking-events", + "neuron-events", + "weights-events", + "subnet-events", + "configuration-events", + "registry" + ] +} diff --git a/docs/guides/evm/precompiles/metagraph.mdx b/docs/guides/evm/precompiles/metagraph.mdx new file mode 100644 index 0000000000..3b40492d35 --- /dev/null +++ b/docs/guides/evm/precompiles/metagraph.mdx @@ -0,0 +1,38 @@ +--- +title: Metagraph +description: Reference for the deployed MetagraphPrecompile. +--- + +| Property | Value | +|---|---| +| Rust implementation | `MetagraphPrecompile` | +| Solidity interface | `IMetagraph` | +| Address | `0x0000000000000000000000000000000000000802` | +| Status | Deployed | + +Provides typed views of per-neuron metagraph values. + +## Functions + +All functions are `view`: + +```text +getUidCount(uint16) +getStake(uint16,uint16) +getRank(uint16,uint16) +getTrust(uint16,uint16) +getConsensus(uint16,uint16) +getIncentive(uint16,uint16) +getDividends(uint16,uint16) +getEmission(uint16,uint16) +getVtrust(uint16,uint16) +getValidatorStatus(uint16,uint16) +getLastUpdate(uint16,uint16) +getIsActive(uint16,uint16) +getAxon(uint16,uint16) +getHotkey(uint16,uint16) +getColdkey(uint16,uint16) +``` + +Source: [`metagraph.sol`](https://github.com/RaoFoundation/subtensor/blob/main/precompiles/src/solidity/metagraph.sol) + diff --git a/docs/guides/evm/precompiles/neuron-events.mdx b/docs/guides/evm/precompiles/neuron-events.mdx new file mode 100644 index 0000000000..a66dc25a87 --- /dev/null +++ b/docs/guides/evm/precompiles/neuron-events.mdx @@ -0,0 +1,50 @@ +--- +title: Neuron and key events +description: Proposed subscription precompile for typed Subtensor neuron, identity, relationship, and key-rotation callbacks. +--- + +| Property | Value | +|---|---| +| Proposed implementation | `NeuronEventsPrecompile` | +| Proposed Solidity interface | `INeuronEvents` | +| Callback receiver interface | `INeuronEventsReceiver` | +| Address | Dedicated address not assigned | +| Status | Proposed | + +This precompile reports neuron registration and serving changes, hotkey and +coldkey rotations, identities, EVM-key associations, and child relationships +emitted by `SubtensorModule`. + +## Proposed callbacks + +| Receiver function | Subtensor source event | +|---|---| +| `onNeuronRegistered(...)` | `NeuronRegistered` | +| `onAxonServed(...)` | `AxonServed` | +| `onPrometheusServed(...)` | `PrometheusServed` | +| `onHotkeySwapped(...)` | `HotkeySwapped` | +| `onHotkeySwappedOnSubnet(...)` | `HotkeySwappedOnSubnet` | +| `onColdkeySwapAnnounced(...)` | `ColdkeySwapAnnounced` | +| `onColdkeySwapReset(...)` | `ColdkeySwapReset` | +| `onColdkeySwapped(...)` | `ColdkeySwapped` | +| `onColdkeySwapDisputed(...)` | `ColdkeySwapDisputed` | +| `onColdkeySwapCleared(...)` | `ColdkeySwapCleared` | +| `onChildrenScheduled(...)` | `SetChildrenScheduled` | +| `onChildScheduled(...)` | One item from `SetChildrenScheduled` | +| `onChildrenSet(...)` | `SetChildren` | +| `onChildSet(...)` | One item from `SetChildren` | +| `onChainIdentitySet(...)` | `ChainIdentitySet` | +| `onEvmKeyAssociated(...)` | `EvmKeyAssociated` | + +The schedule and children summary callbacks carry the hotkey, netuid, and item +count. Their item callbacks carry one child and proportion at a time, using a +shared source sequence, item index, and item count; no callback contains an +unbounded array. + +All callbacks also carry the source block. Account IDs and hashes use +`bytes32`, and the associated EVM key uses `address`. + +Subscription behavior is defined in +[Subscription-based event reporting](/docs/guides/evm/precompile-design#subscription-based-event-reporting). +The names and signatures are provisional and do not reserve selectors. + diff --git a/docs/guides/evm/precompiles/neuron.mdx b/docs/guides/evm/precompiles/neuron.mdx new file mode 100644 index 0000000000..c4940dacdd --- /dev/null +++ b/docs/guides/evm/precompiles/neuron.mdx @@ -0,0 +1,30 @@ +--- +title: Neuron +description: Reference for the deployed NeuronPrecompile. +--- + +| Property | Value | +|---|---| +| Rust implementation | `NeuronPrecompile` | +| Solidity interface | `INeuron` | +| Address | `0x0000000000000000000000000000000000000804` | +| Status | Deployed | + +Registers neurons, publishes serving endpoints, and submits weights. Every +function is `payable`. + +## Functions + +```text +burnedRegister(uint16,bytes32) +registerLimit(uint16,bytes32,uint64) +serveAxon(uint16,uint32,uint128,uint16,uint8,uint8,uint8,uint8) +serveAxonTls(uint16,uint32,uint128,uint16,uint8,uint8,uint8,uint8,bytes) +servePrometheus(uint16,uint32,uint128,uint16,uint8) +setWeights(uint16,uint16[],uint16[],uint64) +commitWeights(uint16,bytes32) +revealWeights(uint16,uint16[],uint16[],uint16[],uint64) +``` + +Source: [`neuron.sol`](https://github.com/RaoFoundation/subtensor/blob/main/precompiles/src/solidity/neuron.sol) + diff --git a/docs/guides/evm/precompiles/proxy.mdx b/docs/guides/evm/precompiles/proxy.mdx new file mode 100644 index 0000000000..5e3fdf71f2 --- /dev/null +++ b/docs/guides/evm/precompiles/proxy.mdx @@ -0,0 +1,27 @@ +--- +title: Proxy +description: Reference for the deployed ProxyPrecompile. +--- + +| Property | Value | +|---|---| +| Rust implementation | `ProxyPrecompile` | +| Solidity interface | `IProxy` | +| Address | `0x000000000000000000000000000000000000080b` | +| Status | Deployed | + +## Functions + +| Function | Mutability | +|---|---| +| `createPureProxy(uint8,uint32,uint16)` | nonpayable | +| `proxyCall(bytes32,uint8[],uint8[])` | nonpayable | +| `killPureProxy(bytes32,uint8,uint16,uint32,uint32)` | nonpayable | +| `addProxy(bytes32,uint8,uint32)` | nonpayable | +| `removeProxy(bytes32,uint8,uint32)` | nonpayable | +| `removeProxies()` | nonpayable | +| `pokeDeposit()` | nonpayable | +| `getProxies(bytes32)` | `view` | + +Source: [`proxy.sol`](https://github.com/RaoFoundation/subtensor/blob/main/precompiles/src/solidity/proxy.sol) + diff --git a/docs/guides/evm/precompiles/registry.mdx b/docs/guides/evm/precompiles/registry.mdx new file mode 100644 index 0000000000..8c7ad14b4d --- /dev/null +++ b/docs/guides/evm/precompiles/registry.mdx @@ -0,0 +1,39 @@ +--- +title: Precompile registry +description: Proposed registry for precompile lifecycle and availability. +--- + +| Property | Value | +|---|---| +| Proposed implementation | `PrecompileRegistry` | +| Proposed Solidity interface | `IPrecompileRegistry` | +| Address | Not assigned | +| Status | Proposed | + +The registry provides function-level lifecycle metadata and the current +operational availability of the containing precompile. + +## Proposed interface + +```solidity +interface IPrecompileRegistry { + struct PrecompileStatus { + bool isDeprecated; + bool isDisabled; + address newPrecompile; + bytes4 newSelector; + string message; + } + + function getPrecompileStatus( + address precompile, + bytes4 selector + ) external view returns (PrecompileStatus memory); +} +``` + +The lifecycle model is described in +[Precompile design and lifecycle](/docs/guides/evm/precompile-design#discovering-status). +The address and selector are not reserved until the interface is implemented +and released. + diff --git a/docs/guides/evm/precompiles/scheduler.mdx b/docs/guides/evm/precompiles/scheduler.mdx new file mode 100644 index 0000000000..64992b280c --- /dev/null +++ b/docs/guides/evm/precompiles/scheduler.mdx @@ -0,0 +1,49 @@ +--- +title: Scheduler +description: Proposed typed EVM interface for the Scheduler pallet. +--- + +| Property | Value | +|---|---| +| Proposed implementation | `SchedulerPrecompile` | +| Proposed Solidity interface | `IScheduler` | +| Address | Not assigned | +| Status | Proposed | + +This precompile would replace raw reads of Scheduler storage and expose the +Scheduler extrinsics through a stable EVM interface. + +## Planned views + +| Function | Replaces | +|---|---| +| `getIncompleteSince()` | `Scheduler.IncompleteSince` | +| `getScheduledCall(uint64 when,uint32 index)` | One entry of `Scheduler.Agenda` | +| `getScheduledCallCount(uint64 when)` | The bounded agenda length for a block | +| `getRetry(uint64 when,uint32 index)` | `Scheduler.Retries` | +| `getTaskAddress(bytes32 taskId)` | `Scheduler.Lookup` | + +Returning one agenda entry at a time keeps execution bounded and avoids an +unbounded array result. + +## Planned operations + +```text +schedule +cancel +scheduleNamed +cancelNamed +scheduleAfter +scheduleNamedAfter +setRetry +setRetryNamed +cancelRetry +cancelRetryNamed +``` + +Scheduled payloads must use a versioned, stable EVM call description. They must +not expose SCALE-encoded `RuntimeCall`, whose encoding can change after a +runtime upgrade. + +Names and signatures on this page are provisional and do not reserve selectors. + diff --git a/docs/guides/evm/precompiles/staking-events.mdx b/docs/guides/evm/precompiles/staking-events.mdx new file mode 100644 index 0000000000..209670e080 --- /dev/null +++ b/docs/guides/evm/precompiles/staking-events.mdx @@ -0,0 +1,56 @@ +--- +title: Staking events +description: Proposed subscription precompile for typed Subtensor staking, delegation, Alpha-flow, lock, and collateral callbacks. +--- + +| Property | Value | +|---|---| +| Proposed implementation | `StakingEventsPrecompile` | +| Proposed Solidity interface | `IStakingEvents` | +| Callback receiver interface | `IStakingEventsReceiver` | +| Address | Dedicated address not assigned | +| Status | Proposed | + +This precompile reports the economic and staking events emitted by +`SubtensorModule`. A contract subscribes itself through the common +[subscription interface](/docs/guides/evm/precompile-design#subscription-control) +and implements only the callbacks selected by its event mask. + +## Proposed callbacks + +| Receiver function | Subtensor source event | +|---|---| +| `onStakeAdded(...)` | `StakeAdded` | +| `onStakeRemoved(...)` | `StakeRemoved` | +| `onStakeMoved(...)` | `StakeMoved` | +| `onStakeTransferred(...)` | `StakeTransferred` | +| `onStakeAndHotkeyTransferred(...)` | `StakeAndHotkeyTransferred` | +| `onStakeSwapped(...)` | `StakeSwapped` | +| `onAlphaRecycled(...)` | `AlphaRecycled` | +| `onAlphaBurned(...)` | `AlphaBurned` | +| `onStakeBurned(...)` | `AddStakeBurn` | +| `onAutoStakeAdded(...)` | `AutoStakeAdded` | +| `onAutoStakeDestinationChanged(...)` | `AutoStakeDestinationSet` | +| `onStakeLocked(...)` | `StakeLocked` | +| `onLockMoved(...)` | `LockMoved` | +| `onCollateralLocked(...)` | `CollateralLocked` | +| `onMinimumCollateralChanged(...)` | `MinCollateralSet` | +| `onDelegateTakeIncreased(...)` | `TakeIncreased` | +| `onDelegateTakeDecreased(...)` | `TakeDecreased` | +| `onChildKeyTakeChanged(...)` | `ChildKeyTakeSet` | +| `onAutoParentDelegationChanged(...)` | `AutoParentDelegationEnabledSet` | +| `onRootClaimed(...)` | `RootClaimed` | +| `onRootClaimTypeChanged(...)` | `RootClaimTypeSet` | +| `onPerpetualLockChanged(...)` | `PerpetualLockUpdated` | +| `onLockedAlphaAcceptanceChanged(...)` | `RejectLockedAlphaUpdated` | +| `onFaucetFunded(...)` | `Faucet` | + +`onAutoStakeAdded` covers the current staking event emitted from a runtime hook. +The remaining callbacks also make transaction- and scheduled-operation events +available through the same receiver model. + +Each callback begins with `uint64 sequence` and `uint64 sourceBlock`, followed +by typed fields corresponding to the source event. Account IDs use `bytes32`; +TAO and Alpha amounts use 18-decimal `uint256` values. + +The names and signatures are provisional and do not reserve selectors. diff --git a/docs/guides/evm/precompiles/staking-v1.mdx b/docs/guides/evm/precompiles/staking-v1.mdx new file mode 100644 index 0000000000..b29cf3f203 --- /dev/null +++ b/docs/guides/evm/precompiles/staking-v1.mdx @@ -0,0 +1,29 @@ +--- +title: Staking V1 +description: Reference for the deployed legacy StakingPrecompile. +--- + +| Property | Value | +|---|---| +| Rust implementation | `StakingPrecompile` | +| Solidity interface | `IStaking` V1 | +| Address | `0x0000000000000000000000000000000000000801` | +| Status | Deployed | + +This legacy interface remains available for deployed callers. New staking +functionality belongs on [Staking V2](./staking-v2). + +## Functions + +| Function | Mutability | +|---|---| +| `addStake(bytes32,uint256)` | `payable` | +| `removeStake(bytes32,uint256,uint256)` | nonpayable | +| `getTotalColdkeyStake(bytes32)` | `view` | +| `getTotalHotkeyStake(bytes32)` | `view` | +| `addProxy(bytes32)` | nonpayable | +| `removeProxy(bytes32)` | nonpayable | +| `getStake(bytes32,bytes32,uint256)` | `view` | + +Source: [`staking.sol`](https://github.com/RaoFoundation/subtensor/blob/main/precompiles/src/solidity/staking.sol) + diff --git a/docs/guides/evm/precompiles/staking-v2.mdx b/docs/guides/evm/precompiles/staking-v2.mdx new file mode 100644 index 0000000000..1eea74daac --- /dev/null +++ b/docs/guides/evm/precompiles/staking-v2.mdx @@ -0,0 +1,79 @@ +--- +title: Staking V2 +description: Reference for the deployed StakingPrecompileV2. +--- + +| Property | Value | +|---|---| +| Rust implementation | `StakingPrecompileV2` | +| Solidity interface | `IStaking` V2 | +| Address | `0x0000000000000000000000000000000000000805` | +| Status | Deployed | + +This is the current staking interface. The V1 address remains available for +backward compatibility. + +## Stake operations + +```text +addStake(bytes32,uint256,uint256) +addStakeLimit(bytes32,uint256,uint256,bool,uint256) +removeStake(bytes32,uint256,uint256) +removeStakeLimit(bytes32,uint256,uint256,bool,uint256) +removeStakeFull(bytes32,uint256) +removeStakeFullLimit(bytes32,uint256,uint256) +moveStake(bytes32,bytes32,uint256,uint256,uint256) +transferStake(bytes32,bytes32,uint256,uint256,uint256) +burnAlpha(bytes32,uint256,uint256) +``` + +These functions are `payable`. + +## Stake views + +```text +getStake(bytes32,bytes32,uint256) +getStakeInfoForColdkeyAndNetuid(bytes32,uint256,bytes32[]) +getTotalColdkeyStake(bytes32) +getTotalColdkeyStakeOnSubnet(bytes32,uint256) +getTotalHotkeyStake(bytes32) +getAlphaStakedValidators(bytes32,uint256) +getTotalAlphaStaked(bytes32,uint256) +getNominatorMinRequiredStake() +getDefaultMinStake() +``` + +These functions are `view`. + +## Locks and account policy + +```text +lockStake(bytes32,uint256,uint256) +moveLock(bytes32,uint256) +setPerpetualLock(uint256,bool) +setRejectLockedAlpha(bool) +getColdkeyLock(bytes32,uint256) +getHotkeyLock(bytes32,uint256) +getHotkeyConvictions(uint256,bytes32[]) +getLockRates() +getRejectLockedAlpha(bytes32) +``` + +The `get` functions are `view`; the other functions are `payable`. + +## Proxies and stake allowances + +```text +addProxy(bytes32) +removeProxy(bytes32) +approve(address,uint256,uint256) +allowance(address,address,uint256) +increaseAllowance(address,uint256,uint256) +decreaseAllowance(address,uint256,uint256) +transferStakeFrom(address,address,bytes32,uint256,uint256,uint256) +``` + +`allowance` is `view`. Refer to the published ABI for the mutability and return +encoding of the allowance mutations. + +Source: [`stakingV2.sol`](https://github.com/RaoFoundation/subtensor/blob/main/precompiles/src/solidity/stakingV2.sol) diff --git a/docs/guides/evm/precompiles/storage-query.mdx b/docs/guides/evm/precompiles/storage-query.mdx new file mode 100644 index 0000000000..5dc7300588 --- /dev/null +++ b/docs/guides/evm/precompiles/storage-query.mdx @@ -0,0 +1,65 @@ +--- +title: Storage query +description: Reference for the deployed selectorless StorageQueryPrecompile. +--- + +| Property | Value | +|---|---| +| Rust implementation | `StorageQueryPrecompile` | +| Solidity interface | None | +| Address | `0x0000000000000000000000000000000000000807` | +| Status | Deployed · deprecation planned | + +This precompile has no named Solidity functions or four-byte function selector. +The complete call data is interpreted as a raw Substrate storage key. It returns +the stored SCALE-encoded bytes, or empty bytes when the key does not exist. + +Only keys whose first 16 bytes match an authorized pallet prefix are accepted: +SubtensorModule, Swap, Balances, Proxy, Scheduler, Drand, Crowdloan, Sudo, +Multisig, and Timestamp. + +Raw storage access is brittle because callers depend on runtime storage names, +hashers, key formats, and SCALE encodings. + +## Planned deprecation + + + Storage Query is still deployed and callable. Deprecation is planned, but it + does not begin until suitable typed replacement coverage is available. + + +The planned lifecycle is: + +1. Add typed views for all storage currently authorized through this + precompile. +2. Soft-deprecate Storage Query. Existing calls continue to execute identically + while the registry and documentation direct new callers to typed functions. +3. Allow a documented migration window for existing contracts and tooling. +4. Hard-deprecate Storage Query so calls return a descriptive precompile error. +5. Eventually disable the precompile through the existing Root-controlled + precompile switch. + +No migration-window length or activation block has been assigned. The general +lifecycle rules are described in +[Precompile design and lifecycle](/docs/guides/evm/precompile-design#phasing-out-raw-storage-reads). + +## Replacement destinations + +Typed coverage should be completed at existing domain addresses whenever a +compatible domain already exists. A new address is proposed only when no +existing precompile has a coherent responsibility for that state. + +| Authorized storage prefix | Typed replacement | +|---|---| +| `SubtensorModule` | Extend [Staking V2](./staking-v2), [Metagraph](./metagraph), [Subnet](./subnet), [Neuron](./neuron), [Alpha](./alpha), [Leasing](./leasing), [UID lookup](./uid-lookup), [Address mapping](./address-mapping), and [Voting power](./voting-power), according to the meaning of each value. | +| `Swap` | Extend [Alpha](./alpha) with typed liquidity, fee, balancer, reservoir, initialization, and migration-status views. | +| `Balances` | Extend [Account balance](./account-balance) with typed account, issuance, lock, reserve, hold, and freeze views. | +| `Proxy` | Extend [Proxy](./proxy) with typed announcement, last-call-result, and fee-payer views. | +| `Crowdloan` | Extend [Crowdloan](./crowdloan) with typed ID, contribution-limit, current-operation, and migration-status views. | +| `Scheduler` | Add the proposed [Scheduler](./scheduler) precompile. | +| `Drand` | Add the proposed [Drand](./drand) precompile. | +| `Sudo` | No dedicated EVM precompile is proposed; this access must be addressed explicitly before Storage Query is deprecated. | +| `Multisig` | No dedicated EVM precompile is proposed; this access must be addressed explicitly before Storage Query is deprecated. | +| `Timestamp` | Add the proposed [Timestamp](./timestamp) precompile for complete typed coverage; `getTimestamp()` is equivalent to the existing EVM `block.timestamp` value. | + +Source: [`storage_query.rs`](https://github.com/RaoFoundation/subtensor/blob/main/precompiles/src/storage_query.rs) diff --git a/docs/guides/evm/precompiles/subnet-events.mdx b/docs/guides/evm/precompiles/subnet-events.mdx new file mode 100644 index 0000000000..f1e8e5d8a4 --- /dev/null +++ b/docs/guides/evm/precompiles/subnet-events.mdx @@ -0,0 +1,80 @@ +--- +title: Subnet events +description: Proposed subscription precompile for typed Subtensor subnet lifecycle, lease, epoch, emission, and voting-power callbacks. +--- + +| Property | Value | +|---|---| +| Proposed implementation | `SubnetEventsPrecompile` | +| Proposed Solidity interface | `ISubnetEvents` | +| Callback receiver interface | `ISubnetEventsReceiver` | +| Address | Dedicated address not assigned | +| Status | Proposed | + +This precompile reports subnet creation and dissolution, ownership and identity +changes, leases, epoch execution, emissions, and voting-power tracking emitted +by `SubtensorModule`. + +## Proposed callbacks + +| Receiver function | Subtensor source | +|---|---| +| `onNetworkRegistrationQueued(...)` | `NetworkRegistrationQueued` | +| `onNetworkAdded(...)` | `NetworkAdded` | +| `onNetworkDissolutionScheduled(...)` | `DissolveNetworkScheduled` | +| `onNetworkRemoved(...)` | `NetworkRemoved` | +| `onNetworkDissolutionCleanupCompleted(...)` | `NetworkDissolveCleanupCompleted` | +| `onSubnetIdentitySet(...)` | `SubnetIdentitySet` | +| `onSubnetIdentityRemoved(...)` | `SubnetIdentityRemoved` | +| `onSubnetSymbolChanged(...)` | `SymbolUpdated` | +| `onSubnetOwnerHotkeyChanged(...)` | `SubnetOwnerHotkeySet` | +| `onSubnetOwnerChanged(...)` | `SubnetOwnerChanged` | +| `onFirstEmissionBlockSet(...)` | `FirstEmissionBlockNumberSet` | +| `onSubnetLeaseCreated(...)` | `SubnetLeaseCreated` | +| `onSubnetLeaseTerminated(...)` | `SubnetLeaseTerminated` | +| `onSubnetLeaseDividendDistributed(...)` | `SubnetLeaseDividendsDistributed` | +| `onEpochTriggered(...)` | `EpochTriggered` | +| `onEpochDeferred(...)` | `EpochDeferred` | +| `onEpochSkipped(...)` | `EpochSkipped` | +| `onUidEmissionCalculated(...)` | One callback per UID emission entry from `IncentiveAlphaEmittedToMiners` | +| `onVotingPowerTrackingEnabled(...)` | `VotingPowerTrackingEnabled` | +| `onVotingPowerTrackingDisableScheduled(...)` | `VotingPowerTrackingDisableScheduled` | +| `onVotingPowerTrackingDisabled(...)` | `VotingPowerTrackingDisabled` | +| `onVotingPowerEmaAlphaChanged(...)` | `VotingPowerEmaAlphaSet` | + +The current hook-origin callbacks are `onNetworkDissolutionCleanupCompleted`, +`onSubnetLeaseDividendDistributed`, `onEpochDeferred`, `onEpochSkipped`, +`onUidEmissionCalculated`, and `onVotingPowerTrackingDisabled`. + +### Per-UID emission calculation + +`IncentiveAlphaEmittedToMiners` contains an `emissions` array whose index is the +miner UID: `emissions[0]` is the Alpha emission for UID 0, `emissions[1]` is for +UID 1, and so on. The precompile does not pass this variable-length array to a +subscriber. It delivers one bounded callback for each `(uid, alpha)` entry: + +```solidity +function onUidEmissionCalculated( + uint64 sequence, + uint64 sourceBlock, + uint16 netuid, + uint16 uid, + uint16 uidCount, + uint256 alpha +) external; +``` + +For example, a source array of `[10, 20, 30]` produces callbacks for +`(uid=0, alpha=10)`, `(uid=1, alpha=20)`, and `(uid=2, alpha=30)`. All callbacks +from that source event share the same sequence and `uidCount`, allowing the +receiver to identify the complete set without accepting an unbounded argument. + +Subnet identity and symbol values must use bounded Solidity representations +chosen before the ABI is released. + +Every callback also carries the source block. Account IDs use `bytes32`, +netuids use `uint16`, and balances use 18-decimal `uint256` values. + +Subscription behavior is defined in +[Subscription-based event reporting](/docs/guides/evm/precompile-design#subscription-based-event-reporting). +The names and signatures are provisional and do not reserve selectors. diff --git a/docs/guides/evm/precompiles/subnet.mdx b/docs/guides/evm/precompiles/subnet.mdx new file mode 100644 index 0000000000..7b53a362ca --- /dev/null +++ b/docs/guides/evm/precompiles/subnet.mdx @@ -0,0 +1,93 @@ +--- +title: Subnet +description: Reference for the deployed SubnetPrecompile. +--- + +| Property | Value | +|---|---| +| Rust implementation | `SubnetPrecompile` | +| Solidity interface | `ISubnet` | +| Address | `0x0000000000000000000000000000000000000803` | +| Status | Deployed | + +Registers subnets and exposes selected subnet configuration. State-changing +functions are `payable`. + +## Registration + +`registerNetwork` has three overloads: + +```text +registerNetwork(bytes32) +registerNetwork(bytes32,string,string,string,string,string,string,string) +registerNetwork(bytes32,string,string,string,string,string,string,string,string) +``` + +## Views + +```text +getActivityCutoff(uint16) +getActivityCutoffFactor(uint16) +getAdjustmentAlpha(uint16) +getAlphaSigmoidSteepness(uint16) +getAlphaValues(uint16) +getBondsMovingAverage(uint16) +getBondsResetEnabled(uint16) +getCommitRevealWeightsEnabled(uint16) +getCommitRevealWeightsInterval(uint16) +getDifficulty(uint16) +getImmunityPeriod(uint16) +getKappa(uint16) +getLiquidAlphaEnabled(uint16) +getMaxBurn(uint16) +getMaxDifficulty(uint16) +getMaxWeightLimit(uint16) +getMinAllowedWeights(uint16) +getMinBurn(uint16) +getMinDifficulty(uint16) +getNetworkPowRegistrationAllowed(uint16) +getNetworkRegistrationAllowed(uint16) +getNetworkRegistrationBlock(uint16) +getOwnerCutAutoLockEnabled(uint16) +getRho(uint16) +getServingRateLimit(uint16) +getWeightsSetRateLimit(uint16) +getWeightsVersionKey(uint16) +getYuma3Enabled(uint16) +isSubnetDissolving(uint16) +``` + +## Configuration + +```text +setActivityCutoff(uint16,uint16) +setActivityCutoffFactor(uint16,uint32) +setAdjustmentAlpha(uint16,uint64) +setAlphaSigmoidSteepness(uint16,uint16) +setAlphaValues(uint16,uint16,uint16) +setBondsMovingAverage(uint16,uint64) +setBondsResetEnabled(uint16,bool) +setCommitRevealWeightsEnabled(uint16,bool) +setCommitRevealWeightsInterval(uint16,uint64) +setDifficulty(uint16,uint64) +setImmunityPeriod(uint16,uint16) +setKappa(uint16,uint16) +setLiquidAlphaEnabled(uint16,bool) +setMaxBurn(uint16,uint64) +setMaxDifficulty(uint16,uint64) +setMinAllowedWeights(uint16,uint16) +setMinBurn(uint16,uint64) +setMinDifficulty(uint16,uint64) +setNetworkPowRegistrationAllowed(uint16,bool) +setNetworkRegistrationAllowed(uint16,bool) +setOwnerCutAutoLockEnabled(uint16,bool) +setRho(uint16,uint16) +setServingRateLimit(uint16,uint64) +setWeightsSetRateLimit(uint16,uint64) +setWeightsVersionKey(uint16,uint64) +setYuma3Enabled(uint16,bool) +toggleTransfers(uint16,bool) +``` + +Source: [`subnet.sol`](https://github.com/RaoFoundation/subtensor/blob/main/precompiles/src/solidity/subnet.sol) + diff --git a/docs/guides/evm/precompiles/timestamp.mdx b/docs/guides/evm/precompiles/timestamp.mdx new file mode 100644 index 0000000000..97f0d78510 --- /dev/null +++ b/docs/guides/evm/precompiles/timestamp.mdx @@ -0,0 +1,29 @@ +--- +title: Timestamp +description: Proposed typed EVM interface for Timestamp pallet state. +--- + +| Property | Value | +|---|---| +| Proposed implementation | `TimestampPrecompile` | +| Proposed Solidity interface | `ITimestamp` | +| Address | Not assigned | +| Status | Proposed | + +## Planned views + +| Function | Replaces | +|---|---| +| `getTimestamp()` | `Timestamp.Now` | +| `wasUpdatedThisBlock()` | `Timestamp.DidUpdate` | + +`getTimestamp()` returns the same underlying time as the EVM +`block.timestamp` value. It exists here so every storage item authorized through +`StorageQueryPrecompile` has an explicit typed replacement. + +`Timestamp.set` is an inherent submitted by block production, not a public +user operation. The proposed precompile therefore exposes no state-changing +timestamp function. + +Names and signatures on this page are provisional and do not reserve selectors. + diff --git a/docs/guides/evm/precompiles/uid-lookup.mdx b/docs/guides/evm/precompiles/uid-lookup.mdx new file mode 100644 index 0000000000..163b6c4e83 --- /dev/null +++ b/docs/guides/evm/precompiles/uid-lookup.mdx @@ -0,0 +1,20 @@ +--- +title: UID lookup +description: Reference for the deployed UidLookupPrecompile. +--- + +| Property | Value | +|---|---| +| Rust implementation | `UidLookupPrecompile` | +| Solidity interface | `IUidLookup` | +| Address | `0x0000000000000000000000000000000000000806` | +| Status | Deployed | + +## Functions + +| Function | Mutability | +|---|---| +| `uidLookup(uint16,address,uint16)` | `view` | + +Source: [`uidLookup.sol`](https://github.com/RaoFoundation/subtensor/blob/main/precompiles/src/solidity/uidLookup.sol) + diff --git a/docs/guides/evm/precompiles/voting-power.mdx b/docs/guides/evm/precompiles/voting-power.mdx new file mode 100644 index 0000000000..ba2cdd3d8d --- /dev/null +++ b/docs/guides/evm/precompiles/voting-power.mdx @@ -0,0 +1,26 @@ +--- +title: Voting power +description: Reference for the deployed VotingPowerPrecompile. +--- + +| Property | Value | +|---|---| +| Rust implementation | `VotingPowerPrecompile` | +| Solidity interface | `IVotingPower` | +| Address | `0x000000000000000000000000000000000000080d` | +| Status | Deployed | + +All functions are `view`. + +## Functions + +```text +getVotingPower(uint16,bytes32) +isVotingPowerTrackingEnabled(uint16) +getVotingPowerDisableAtBlock(uint16) +getVotingPowerEmaAlpha(uint16) +getTotalVotingPower(uint16) +``` + +Source: [`votingPower.sol`](https://github.com/RaoFoundation/subtensor/blob/main/precompiles/src/solidity/votingPower.sol) + diff --git a/docs/guides/evm/precompiles/weights-events.mdx b/docs/guides/evm/precompiles/weights-events.mdx new file mode 100644 index 0000000000..6d1822d12c --- /dev/null +++ b/docs/guides/evm/precompiles/weights-events.mdx @@ -0,0 +1,47 @@ +--- +title: Weights events +description: Proposed subscription precompile for typed Subtensor weights, commit-reveal, and batch callbacks. +--- + +| Property | Value | +|---|---| +| Proposed implementation | `WeightsEventsPrecompile` | +| Proposed Solidity interface | `IWeightsEvents` | +| Callback receiver interface | `IWeightsEventsReceiver` | +| Address | Dedicated address not assigned | +| Status | Proposed | + +This precompile reports weight setting and each supported commit-reveal path +emitted by `SubtensorModule`, including reveals performed later by a runtime +hook. + +## Proposed callbacks + +| Receiver function | Subtensor source event | +|---|---| +| `onWeightsSet(...)` | `WeightsSet` | +| `onWeightsCommitted(...)` | `WeightsCommitted` | +| `onWeightsRevealed(...)` | `WeightsRevealed` | +| `onWeightBatchRevealItem(...)` | One hash from `WeightsBatchRevealed` | +| `onBatchWeightCompleted(...)` | One netuid from `BatchWeightsCompleted` | +| `onWeightBatchCompletedWithErrors(...)` | `BatchCompletedWithErrors` | +| `onWeightBatchItemFailed(...)` | `BatchWeightItemFailed` | +| `onTimelockedWeightsCommitted(...)` | `TimelockedWeightsCommitted` | +| `onTimelockedWeightsRevealed(...)` | `TimelockedWeightsRevealed` | +| `onCommitRevealPeriodsChanged(...)` | `CommitRevealPeriodsSet` | +| `onCommitRevealEnabledChanged(...)` | `CommitRevealEnabled` | +| `onCommitRevealVersionChanged(...)` | `CommitRevealVersionSet` | + +`onTimelockedWeightsRevealed` covers the current weights event emitted from a +runtime hook. + +Batch callbacks carry the source sequence, item index, and item count and +report one bounded item per invocation. Dispatch failures use a stable typed +error representation rather than SCALE-encoded `DispatchError`. + +Every callback also carries the source block. Hotkeys use `bytes32`, netuids +use `uint16`, and commitment hashes use `bytes32`. + +Subscription behavior is defined in +[Subscription-based event reporting](/docs/guides/evm/precompile-design#subscription-based-event-reporting). +The names and signatures are provisional and do not reserve selectors. diff --git a/website/apps/bittensor-website/src/components/copy.tsx b/website/apps/bittensor-website/src/components/copy.tsx index 1eb4b5b831..d23fce704c 100644 --- a/website/apps/bittensor-website/src/components/copy.tsx +++ b/website/apps/bittensor-website/src/components/copy.tsx @@ -40,6 +40,28 @@ export function CopyCodeButton() { ); } +/** Compact EVM address that copies the complete 20-byte value. */ +export function EvmAddress({ address }: { address: string }) { + const { copied, flash } = useCopied(); + const shortAddress = `${address.slice(0, 3)}...${address.slice(-4)}`; + + return ( + + ); +} + /** "Copy Markdown" — fetches the page's raw markdown and copies it. */ export function CopyMarkdownButton({ markdownUrl, diff --git a/website/apps/bittensor-website/src/components/mdx.tsx b/website/apps/bittensor-website/src/components/mdx.tsx index f7fcf9b4dd..754c125924 100644 --- a/website/apps/bittensor-website/src/components/mdx.tsx +++ b/website/apps/bittensor-website/src/components/mdx.tsx @@ -1,7 +1,7 @@ import Link from 'next/link'; import type { MDXComponents } from 'mdx/types'; import type { ComponentProps, ReactNode } from 'react'; -import { CopyCodeButton } from './copy'; +import { CopyCodeButton, EvmAddress } from './copy'; import { EvmAddressDomains } from './docs/evm-address-domains'; import { EvmMoneyFlows } from './docs/evm-money-flows'; import { ConvictionLockChart } from './docs/conviction-lock-chart'; @@ -130,6 +130,7 @@ export function getMDXComponents(components?: MDXComponents) { Cards, Card, Callout, + EvmAddress, TaoHalvingChart, SubnetEmissionShareChart, YumaConsensusDemo, From 5d10f594494bda96a7f75b30af0f6d5013f60c52 Mon Sep 17 00:00:00 2001 From: UnarbosFour Date: Tue, 28 Jul 2026 08:55:05 -0400 Subject: [PATCH 2/8] Add precompile coverage gaps for existing domains --- .agents/skills/emv-maintainer/SKILL.md | 5 ++ .../evm/precompiles/account-balance.mdx | 17 ++++- docs/guides/evm/precompiles/alpha.mdx | 26 ++++++- .../evm/precompiles/balance-transfer.mdx | 18 ++++- docs/guides/evm/precompiles/crowdloan.mdx | 12 ++- docs/guides/evm/precompiles/drand.mdx | 19 +++-- .../evm/precompiles/extrinsic-coverage.mdx | 75 +++++++++++++++++++ docs/guides/evm/precompiles/index.mdx | 5 ++ docs/guides/evm/precompiles/leasing.mdx | 10 ++- docs/guides/evm/precompiles/meta.json | 2 + docs/guides/evm/precompiles/neuron.mdx | 44 ++++++++++- docs/guides/evm/precompiles/proxy.mdx | 17 ++++- docs/guides/evm/precompiles/registry.mdx | 10 ++- .../evm/precompiles/runtime-configuration.mdx | 30 ++++++++ docs/guides/evm/precompiles/scheduler.mdx | 25 +++---- docs/guides/evm/precompiles/staking-v2.mdx | 46 ++++++++++++ docs/guides/evm/precompiles/subnet.mdx | 67 ++++++++++++++++- docs/guides/evm/precompiles/timestamp.mdx | 4 +- docs/guides/evm/precompiles/voting-power.mdx | 11 ++- 19 files changed, 408 insertions(+), 35 deletions(-) create mode 100644 docs/guides/evm/precompiles/extrinsic-coverage.mdx create mode 100644 docs/guides/evm/precompiles/runtime-configuration.mdx diff --git a/.agents/skills/emv-maintainer/SKILL.md b/.agents/skills/emv-maintainer/SKILL.md index c26d65f9b7..f59e2a83d8 100644 --- a/.agents/skills/emv-maintainer/SKILL.md +++ b/.agents/skills/emv-maintainer/SKILL.md @@ -66,6 +66,11 @@ For each affected released function: - admin-util - balances - proxy + - scheduler + - drand + - crowdloan + - timestamp + - swap - All runtime API RPCs for the subtensor pallet should be exposed as a callable precompile function with similar interface - All events emitted from hooks (such as on_initialize or on_finalize) should be exposed as callbacks. diff --git a/docs/guides/evm/precompiles/account-balance.mdx b/docs/guides/evm/precompiles/account-balance.mdx index 4fb1e89612..63c6edbe4d 100644 --- a/docs/guides/evm/precompiles/account-balance.mdx +++ b/docs/guides/evm/precompiles/account-balance.mdx @@ -16,5 +16,20 @@ description: Reference for the deployed BalancePrecompile. |---|---| | `getFreeBalance(bytes32)` | `view` | -Source: [`balance.sol`](https://github.com/RaoFoundation/subtensor/blob/main/precompiles/src/solidity/balance.sol) +## Proposed operations + +| Proposed function | Source extrinsic | +|---|---| +| `burnBalance` | `Balances.burn` | +| `forceUnreserve` | `Balances.force_unreserve` | +| `upgradeAccounts` | `Balances.upgrade_accounts` | +| `forceSetBalance` | `Balances.force_set_balance` | +| `forceAdjustTotalIssuance` | `Balances.force_adjust_total_issuance` | +| `setTotalIssuance` | `AdminUtils.sudo_set_total_issuance` | +`upgradeAccounts` must have an explicit fixed input bound. The implementation +must preserve all runtime authorization and issuance invariants. + +Proposed names and signatures do not reserve selectors. + +Source: [`balance.sol`](https://github.com/RaoFoundation/subtensor/blob/main/precompiles/src/solidity/balance.sol) diff --git a/docs/guides/evm/precompiles/alpha.mdx b/docs/guides/evm/precompiles/alpha.mdx index c45aba78f7..4cde04cdb5 100644 --- a/docs/guides/evm/precompiles/alpha.mdx +++ b/docs/guides/evm/precompiles/alpha.mdx @@ -36,5 +36,29 @@ getSumAlphaPrice() getCKBurn() ``` -Source: [`alpha.sol`](https://github.com/RaoFoundation/subtensor/blob/main/precompiles/src/solidity/alpha.sol) +## Proposed operations + +| Proposed function | Source extrinsic | +|---|---| +| `setSwapFeeRate` | `Swap.set_fee_rate` | +| `setRecycleOrBurn` | `AdminUtils.sudo_set_recycle_or_burn` | +| `setSubnetMovingAlpha` | `AdminUtils.sudo_set_subnet_moving_alpha` | +| `setEmaPriceHalvingPeriod` | `AdminUtils.sudo_set_ema_price_halving_period` | +| `setCkBurn` | `AdminUtils.sudo_set_ck_burn` | +| `setTaoFlowCutoff` | `AdminUtils.sudo_set_tao_flow_cutoff` | +| `setTaoFlowNormalizationExponent` | `AdminUtils.sudo_set_tao_flow_normalization_exponent` | +| `setTaoFlowSmoothingFactor` | `AdminUtils.sudo_set_tao_flow_smoothing_factor` | +| `setNetTaoFlowEnabled` | `AdminUtils.sudo_set_net_tao_flow_enabled` | +| `setBurnHalfLife` | `AdminUtils.sudo_set_burn_half_life` | +| `setBurnIncreaseMultiplier` | `AdminUtils.sudo_set_burn_increase_mult` | +| `setSubnetEmissionEnabled` | `AdminUtils.sudo_set_subnet_emission_enabled` | +| `setEmissionBarQuantile` | `AdminUtils.sudo_set_emission_bar_quantile` | +| `setEmissionGateExponent` | `AdminUtils.sudo_set_emission_gate_exponent` | +The five deprecated `Swap` liquidity extrinsics are intentionally not proposed; +they always return the pallet's `Deprecated` error. Runtime authorization +remains in force for every administrative operation. + +Proposed names and signatures do not reserve selectors. + +Source: [`alpha.sol`](https://github.com/RaoFoundation/subtensor/blob/main/precompiles/src/solidity/alpha.sol) diff --git a/docs/guides/evm/precompiles/balance-transfer.mdx b/docs/guides/evm/precompiles/balance-transfer.mdx index 71b3f4c1ce..0631247658 100644 --- a/docs/guides/evm/precompiles/balance-transfer.mdx +++ b/docs/guides/evm/precompiles/balance-transfer.mdx @@ -19,5 +19,21 @@ public key. |---|---| | `transfer(bytes32)` | `payable` | -Source: [`balanceTransfer.sol`](https://github.com/RaoFoundation/subtensor/blob/main/precompiles/src/solidity/balanceTransfer.sol) +## Proposed additions + +| Proposed function | Source extrinsic | +|---|---| +| `faucet` | `SubtensorModule.faucet` | +| `transferKeepAlive` | `Balances.transfer_keep_alive` | +| `transferAll` | `Balances.transfer_all` | +| `forceTransfer` | `Balances.force_transfer` | +The existing `transfer(bytes32)` semantically covers +`Balances.transfer_allow_death` by taking the amount from attached EVM value. +The proposed functions use explicit typed arguments where attached value does +not express the complete source operation. Runtime authorization remains in +force for `forceTransfer`. + +Proposed names and signatures do not reserve selectors. + +Source: [`balanceTransfer.sol`](https://github.com/RaoFoundation/subtensor/blob/main/precompiles/src/solidity/balanceTransfer.sol) diff --git a/docs/guides/evm/precompiles/crowdloan.mdx b/docs/guides/evm/precompiles/crowdloan.mdx index abdce95497..d563faf76e 100644 --- a/docs/guides/evm/precompiles/crowdloan.mdx +++ b/docs/guides/evm/precompiles/crowdloan.mdx @@ -33,5 +33,15 @@ updateEnd(uint32,uint32) updateCap(uint32,uint64) ``` -Source: [`crowdloan.sol`](https://github.com/RaoFoundation/subtensor/blob/main/precompiles/src/solidity/crowdloan.sol) +## Proposed addition + +| Proposed function | Source extrinsic | +|---|---| +| `setMaxContribution` | `Crowdloan.set_max_contribution` | +The typed interface must preserve the source call's optional value so the +creator can either set or clear the per-contributor maximum. + +The proposed name and signature do not reserve a selector. + +Source: [`crowdloan.sol`](https://github.com/RaoFoundation/subtensor/blob/main/precompiles/src/solidity/crowdloan.sol) diff --git a/docs/guides/evm/precompiles/drand.mdx b/docs/guides/evm/precompiles/drand.mdx index 9e8bf552f3..08b5b195a5 100644 --- a/docs/guides/evm/precompiles/drand.mdx +++ b/docs/guides/evm/precompiles/drand.mdx @@ -25,11 +25,14 @@ of requiring callers to construct Drand storage keys and decode SCALE values. ## Planned operations -```text -writePulse -setBeaconConfig -setOldestStoredRound -``` - -The runtime's existing signed, unsigned, and Root origin checks remain in force. -Names and signatures on this page are provisional and do not reserve selectors. +| Proposed function | Source extrinsic | +|---|---| +| `setBeaconConfig` | `Drand.set_beacon_config` | +| `setOldestStoredRound` | `Drand.set_oldest_stored_round` | + +`Drand.write_pulse` is not exposed. It is an unsigned offchain-worker +submission that requires `None` origin, which an EVM caller cannot satisfy +without changing the security model. + +The runtime's existing authorization checks remain in force. Names and +signatures on this page are provisional and do not reserve selectors. diff --git a/docs/guides/evm/precompiles/extrinsic-coverage.mdx b/docs/guides/evm/precompiles/extrinsic-coverage.mdx new file mode 100644 index 0000000000..4e9b58510c --- /dev/null +++ b/docs/guides/evm/precompiles/extrinsic-coverage.mdx @@ -0,0 +1,75 @@ +--- +title: Extrinsic coverage +description: Audit of typed EVM coverage for every extrinsic in the authorized runtime pallets. +--- + +This audit covers the runtime's `SubtensorModule`, `AdminUtils`, `Balances`, +`Proxy`, `Scheduler`, `Drand`, `Crowdloan`, `Timestamp`, and `Swap` pallets. +Sudo and Multisig extrinsics are intentionally outside typed precompile +coverage. + +Generic SCALE dispatch through the Frontier `Dispatch` precompile does not +count as typed coverage. A covered operation must have a stable Solidity +interface or an explicit proposed typed replacement. + +## Coverage summary + +| Pallet | Runtime extrinsics | Typed today | Proposed additions | Not exposed | +|---|---:|---:|---:|---:| +| `SubtensorModule` | 82 | 24 | 56 | 2 | +| `AdminUtils` | 86 | 24 | 62 | 0 | +| `Balances` | 9 | 1 | 8 | 0 | +| `Proxy` | 12 | 7 | 5 | 0 | +| `Scheduler` | 10 | 0 | 10 | 0 | +| `Drand` | 3 | 0 | 2 | 1 | +| `Crowdloan` | 10 | 9 | 1 | 0 | +| `Timestamp` | 1 | 0 | 0 | 1 | +| `Swap` | 6 | 0 | 1 | 5 | +| **Total** | **219** | **65** | **145** | **9** | + +`Typed today` counts semantic coverage, not only direct dispatch to the same +Rust call. For example, `registerNetwork(bytes32)` covers basic subnet +registration by dispatching `register_network_with_identity` with empty +identity fields. + +## Classification of proposed additions + +Each missing operation is listed on the page of its target precompile: + +| Target precompile | Missing extrinsics assigned | +|---|---:| +| [Subnet](/docs/guides/evm/precompiles/subnet) | 37 | +| [Staking V2](/docs/guides/evm/precompiles/staking-v2) | 29 | +| [Neuron](/docs/guides/evm/precompiles/neuron) | 27 | +| [Alpha](/docs/guides/evm/precompiles/alpha) | 14 | +| [Scheduler](/docs/guides/evm/precompiles/scheduler) | 10 | +| [Account balance](/docs/guides/evm/precompiles/account-balance) | 6 | +| [Proxy](/docs/guides/evm/precompiles/proxy) | 5 | +| [Balance transfer](/docs/guides/evm/precompiles/balance-transfer) | 4 | +| [Runtime configuration](/docs/guides/evm/precompiles/runtime-configuration) | 4 | +| [Voting power](/docs/guides/evm/precompiles/voting-power) | 3 | +| [Drand](/docs/guides/evm/precompiles/drand) | 2 | +| [Leasing](/docs/guides/evm/precompiles/leasing) | 2 | +| [Crowdloan](/docs/guides/evm/precompiles/crowdloan) | 1 | +| [Precompile registry](/docs/guides/evm/precompiles/registry) | 1 | + +Proposed function names do not reserve selectors. Their final parameter types, +bounds, authorization model, and return values must be specified before +implementation. + +## Extrinsics not exposed as EVM calls + +| Pallet extrinsic | Reason | +|---|---| +| `SubtensorModule.set_tempo` | Retained call-index compatibility entry point that succeeds without changing state. The real setting is `AdminUtils.sudo_set_tempo`, proposed as `SubnetPrecompile.setTempo`. | +| `SubtensorModule.set_activity_cutoff_factor` | Retained call-index compatibility entry point that succeeds without changing state. The active AdminUtils operation is already covered by `SubnetPrecompile.setActivityCutoffFactor`. | +| `Drand.write_pulse` | Unsigned offchain-worker submission requiring `None` origin. An EVM caller cannot satisfy that origin without changing its security model. | +| `Timestamp.set` | Block-production inherent requiring `None` origin. Contracts already receive the same time through `block.timestamp`. | +| `Swap.add_liquidity` | Permanently disabled pallet call that always returns `Deprecated`. | +| `Swap.remove_liquidity` | Permanently disabled pallet call that always returns `Deprecated`. | +| `Swap.modify_position` | Permanently disabled pallet call that always returns `Deprecated`. | +| `Swap.toggle_user_liquidity` | Permanently disabled pallet call that always returns `Deprecated`. | +| `Swap.disable_lp` | Permanently disabled pallet call that always returns `Deprecated`. | + +These exclusions preserve the existing runtime origin and lifecycle semantics; +they are not missing callable functionality. diff --git a/docs/guides/evm/precompiles/index.mdx b/docs/guides/evm/precompiles/index.mdx index c0ed62162b..817a3c5d18 100644 --- a/docs/guides/evm/precompiles/index.mdx +++ b/docs/guides/evm/precompiles/index.mdx @@ -8,6 +8,10 @@ runtime. `Deployed` means that the address is registered in the current runtime; it does not imply complete coverage of the underlying runtime domain. `Proposed` precompiles have no assigned address or released selectors. +The [extrinsic coverage audit](/docs/guides/evm/precompiles/extrinsic-coverage) +tracks every runtime extrinsic in scope and identifies its deployed, proposed, +or intentionally non-callable EVM treatment. + ## Ethereum and Frontier precompiles | Precompile | Address | Status | @@ -48,6 +52,7 @@ it does not imply complete coverage of the underlying runtime domain. | [`SchedulerPrecompile`](/docs/guides/evm/precompiles/scheduler) | `IScheduler` | Address not assigned
Proposed | | [`DrandPrecompile`](/docs/guides/evm/precompiles/drand) | `IDrand` | Address not assigned
Proposed | | [`TimestampPrecompile`](/docs/guides/evm/precompiles/timestamp) | `ITimestamp` | Address not assigned
Proposed | +| [`RuntimeConfigurationPrecompile`](/docs/guides/evm/precompiles/runtime-configuration) | `IRuntimeConfiguration` | Address not assigned
Proposed | | [`StakingEventsPrecompile`](/docs/guides/evm/precompiles/staking-events) | `IStakingEvents` | Dedicated address not assigned
Proposed | | [`NeuronEventsPrecompile`](/docs/guides/evm/precompiles/neuron-events) | `INeuronEvents` | Dedicated address not assigned
Proposed | | [`WeightsEventsPrecompile`](/docs/guides/evm/precompiles/weights-events) | `IWeightsEvents` | Dedicated address not assigned
Proposed | diff --git a/docs/guides/evm/precompiles/leasing.mdx b/docs/guides/evm/precompiles/leasing.mdx index e4a910e574..32f20af948 100644 --- a/docs/guides/evm/precompiles/leasing.mdx +++ b/docs/guides/evm/precompiles/leasing.mdx @@ -27,5 +27,13 @@ terminateLease(uint32,bytes32) Both operations are `payable`. -Source: [`leasing.sol`](https://github.com/RaoFoundation/subtensor/blob/main/precompiles/src/solidity/leasing.sol) +## Proposed additions + +| Proposed function | Source extrinsic | +|---|---| +| `startCall` | `SubtensorModule.start_call` | +| `setStartCallDelay` | `AdminUtils.sudo_set_start_call_delay` | +Proposed names and signatures do not reserve selectors. + +Source: [`leasing.sol`](https://github.com/RaoFoundation/subtensor/blob/main/precompiles/src/solidity/leasing.sol) diff --git a/docs/guides/evm/precompiles/meta.json b/docs/guides/evm/precompiles/meta.json index 07144223c0..9cb554aaf6 100644 --- a/docs/guides/evm/precompiles/meta.json +++ b/docs/guides/evm/precompiles/meta.json @@ -2,6 +2,7 @@ "title": "Precompiles", "pages": [ "index", + "extrinsic-coverage", "balance-transfer", "staking-v1", "metagraph", @@ -21,6 +22,7 @@ "scheduler", "drand", "timestamp", + "runtime-configuration", "staking-events", "neuron-events", "weights-events", diff --git a/docs/guides/evm/precompiles/neuron.mdx b/docs/guides/evm/precompiles/neuron.mdx index c4940dacdd..6cbe2643e4 100644 --- a/docs/guides/evm/precompiles/neuron.mdx +++ b/docs/guides/evm/precompiles/neuron.mdx @@ -26,5 +26,47 @@ commitWeights(uint16,bytes32) revealWeights(uint16,uint16[],uint16[],uint16[],uint64) ``` -Source: [`neuron.sol`](https://github.com/RaoFoundation/subtensor/blob/main/precompiles/src/solidity/neuron.sol) +## Proposed weight operations + +| Proposed function | Source extrinsic | +|---|---| +| `setMechanismWeights` | `SubtensorModule.set_mechanism_weights` | +| `batchSetWeights` | `SubtensorModule.batch_set_weights` | +| `commitMechanismWeights` | `SubtensorModule.commit_mechanism_weights` | +| `batchCommitWeights` | `SubtensorModule.batch_commit_weights` | +| `revealMechanismWeights` | `SubtensorModule.reveal_mechanism_weights` | +| `commitCrv3MechanismWeights` | `SubtensorModule.commit_crv3_mechanism_weights` | +| `batchRevealWeights` | `SubtensorModule.batch_reveal_weights` | +| `commitTimelockedWeights` | `SubtensorModule.commit_timelocked_weights` | +| `commitTimelockedMechanismWeights` | `SubtensorModule.commit_timelocked_mechanism_weights` | + +Every batch input must have an explicit fixed bound. Timelocked operations must +use typed Drand data rather than SCALE-encoded payloads. + +## Proposed registration and key operations +| Proposed function | Source extrinsic | +|---|---| +| `register` | `SubtensorModule.register` | +| `rootRegister` | `SubtensorModule.root_register` | +| `swapHotkey` | `SubtensorModule.swap_hotkey` | +| `swapHotkeyV2` | `SubtensorModule.swap_hotkey_v2` | +| `swapColdkey` | `SubtensorModule.swap_coldkey` | +| `scheduleColdkeySwap` | `SubtensorModule.schedule_swap_coldkey` | +| `setChildren` | `SubtensorModule.set_children` | +| `setIdentity` | `SubtensorModule.set_identity` | +| `tryAssociateHotkey` | `SubtensorModule.try_associate_hotkey` | +| `associateEvmKey` | `SubtensorModule.associate_evm_key` | +| `setPendingChildkeyCooldown` | `SubtensorModule.set_pending_childkey_cooldown` | +| `announceColdkeySwap` | `SubtensorModule.announce_coldkey_swap` | +| `executeAnnouncedColdkeySwap` | `SubtensorModule.swap_coldkey_announced` | +| `disputeColdkeySwap` | `SubtensorModule.dispute_coldkey_swap` | +| `resetColdkeySwap` | `SubtensorModule.reset_coldkey_swap` | +| `clearColdkeySwapAnnouncement` | `SubtensorModule.clear_coldkey_swap_announcement` | +| `setColdkeySwapAnnouncementDelay` | `AdminUtils.sudo_set_coldkey_swap_announcement_delay` | +| `setColdkeySwapReannouncementDelay` | `AdminUtils.sudo_set_coldkey_swap_reannouncement_delay` | + +Runtime authorization remains in force. Proposed names and signatures do not +reserve selectors. + +Source: [`neuron.sol`](https://github.com/RaoFoundation/subtensor/blob/main/precompiles/src/solidity/neuron.sol) diff --git a/docs/guides/evm/precompiles/proxy.mdx b/docs/guides/evm/precompiles/proxy.mdx index 5e3fdf71f2..21a87446b2 100644 --- a/docs/guides/evm/precompiles/proxy.mdx +++ b/docs/guides/evm/precompiles/proxy.mdx @@ -23,5 +23,20 @@ description: Reference for the deployed ProxyPrecompile. | `pokeDeposit()` | nonpayable | | `getProxies(bytes32)` | `view` | -Source: [`proxy.sol`](https://github.com/RaoFoundation/subtensor/blob/main/precompiles/src/solidity/proxy.sol) +## Proposed additions + +| Proposed function | Source extrinsic | +|---|---| +| `announce` | `Proxy.announce` | +| `removeAnnouncement` | `Proxy.remove_announcement` | +| `rejectAnnouncement` | `Proxy.reject_announcement` | +| `proxyAnnounced` | `Proxy.proxy_announced` | +| `setRealPaysFee` | `Proxy.set_real_pays_fee` | +`proxyAnnounced` must use the same versioned, stable EVM call description as +other typed proxy execution. A new interface must not introduce another +dependency on SCALE-encoded `RuntimeCall`. + +Proposed names and signatures do not reserve selectors. + +Source: [`proxy.sol`](https://github.com/RaoFoundation/subtensor/blob/main/precompiles/src/solidity/proxy.sol) diff --git a/docs/guides/evm/precompiles/registry.mdx b/docs/guides/evm/precompiles/registry.mdx index 8c7ad14b4d..721c930786 100644 --- a/docs/guides/evm/precompiles/registry.mdx +++ b/docs/guides/evm/precompiles/registry.mdx @@ -32,8 +32,16 @@ interface IPrecompileRegistry { } ``` +## Proposed operation + +| Proposed function | Source extrinsic | +|---|---| +| `setPrecompileEnabled` | `AdminUtils.sudo_toggle_evm_precompile` | + +This operation changes reversible availability; it does not change or erase a +function's deprecation lifecycle. Runtime authorization remains in force. + The lifecycle model is described in [Precompile design and lifecycle](/docs/guides/evm/precompile-design#discovering-status). The address and selector are not reserved until the interface is implemented and released. - diff --git a/docs/guides/evm/precompiles/runtime-configuration.mdx b/docs/guides/evm/precompiles/runtime-configuration.mdx new file mode 100644 index 0000000000..7c8ecff2c4 --- /dev/null +++ b/docs/guides/evm/precompiles/runtime-configuration.mdx @@ -0,0 +1,30 @@ +--- +title: Runtime configuration +description: Proposed typed EVM interface for global runtime configuration operations. +--- + +| Property | Value | +|---|---| +| Proposed implementation | `RuntimeConfigurationPrecompile` | +| Proposed Solidity interface | `IRuntimeConfiguration` | +| Address | Not assigned | +| Status | Proposed | + +This precompile groups the small set of global AdminUtils operations that do +not belong to a subnet, staking, Alpha, account-balance, or precompile-lifecycle +domain. + +## Planned operations + +| Proposed function | Source extrinsic | +|---|---| +| `swapAuthorities` | `AdminUtils.swap_authorities` | +| `setTransactionRateLimit` | `AdminUtils.sudo_set_tx_rate_limit` | +| `setEvmChainId` | `AdminUtils.sudo_set_evm_chain_id` | +| `scheduleGrandpaChange` | `AdminUtils.schedule_grandpa_change` | + +The runtime's authorization checks remain in force. The implementation must +define a typed, bounded authority representation and must not expose +SCALE-encoded runtime values. + +Names and signatures on this page are provisional and do not reserve selectors. diff --git a/docs/guides/evm/precompiles/scheduler.mdx b/docs/guides/evm/precompiles/scheduler.mdx index 64992b280c..94f1d8545d 100644 --- a/docs/guides/evm/precompiles/scheduler.mdx +++ b/docs/guides/evm/precompiles/scheduler.mdx @@ -28,22 +28,21 @@ unbounded array result. ## Planned operations -```text -schedule -cancel -scheduleNamed -cancelNamed -scheduleAfter -scheduleNamedAfter -setRetry -setRetryNamed -cancelRetry -cancelRetryNamed -``` +| Proposed function | Source extrinsic | +|---|---| +| `schedule` | `Scheduler.schedule` | +| `cancel` | `Scheduler.cancel` | +| `scheduleNamed` | `Scheduler.schedule_named` | +| `cancelNamed` | `Scheduler.cancel_named` | +| `scheduleAfter` | `Scheduler.schedule_after` | +| `scheduleNamedAfter` | `Scheduler.schedule_named_after` | +| `setRetry` | `Scheduler.set_retry` | +| `setRetryNamed` | `Scheduler.set_retry_named` | +| `cancelRetry` | `Scheduler.cancel_retry` | +| `cancelRetryNamed` | `Scheduler.cancel_retry_named` | Scheduled payloads must use a versioned, stable EVM call description. They must not expose SCALE-encoded `RuntimeCall`, whose encoding can change after a runtime upgrade. Names and signatures on this page are provisional and do not reserve selectors. - diff --git a/docs/guides/evm/precompiles/staking-v2.mdx b/docs/guides/evm/precompiles/staking-v2.mdx index 1eea74daac..334e76640d 100644 --- a/docs/guides/evm/precompiles/staking-v2.mdx +++ b/docs/guides/evm/precompiles/staking-v2.mdx @@ -76,4 +76,50 @@ transferStakeFrom(address,address,bytes32,uint256,uint256,uint256) `allowance` is `view`. Refer to the published ABI for the mutability and return encoding of the allowance mutations. +## Proposed Subtensor operations + +| Proposed function | Source extrinsic | +|---|---| +| `decreaseTake` | `SubtensorModule.decrease_take` | +| `increaseTake` | `SubtensorModule.increase_take` | +| `setChildkeyTake` | `SubtensorModule.set_childkey_take` | +| `setTxChildkeyTakeRateLimit` | `SubtensorModule.sudo_set_tx_childkey_take_rate_limit` | +| `setMinChildkeyTake` | `SubtensorModule.sudo_set_min_childkey_take` | +| `setMaxChildkeyTake` | `SubtensorModule.sudo_set_max_childkey_take` | +| `unstakeAll` | `SubtensorModule.unstake_all` | +| `unstakeAllAlpha` | `SubtensorModule.unstake_all_alpha` | +| `swapStake` | `SubtensorModule.swap_stake` | +| `swapStakeLimit` | `SubtensorModule.swap_stake_limit` | +| `recycleAlpha` | `SubtensorModule.recycle_alpha` | +| `setColdkeyAutoStakeHotkey` | `SubtensorModule.set_coldkey_auto_stake_hotkey` | +| `claimRoot` | `SubtensorModule.claim_root` | +| `setRootClaimType` | `SubtensorModule.set_root_claim_type` | +| `setNumRootClaims` | `SubtensorModule.sudo_set_num_root_claims` | +| `setRootClaimThreshold` | `SubtensorModule.sudo_set_root_claim_threshold` | +| `addStakeBurn` | `SubtensorModule.add_stake_burn` | +| `setAutoParentDelegationEnabled` | `SubtensorModule.set_auto_parent_delegation_enabled` | +| `transferStakeAndHotkey` | `SubtensorModule.transfer_stake_and_hotkey` | +| `addCollateral` | `SubtensorModule.add_collateral` | +| `setMinCollateral` | `SubtensorModule.set_min_collateral` | + +`recycleAlpha` is distinct from deployed `burnAlpha`: recycling reduces +`SubnetAlphaOut` and Alpha issuance, while burning does not reduce +`SubnetAlphaOut`. + +## Proposed AdminUtils operations + +| Proposed function | Source extrinsic | +|---|---| +| `setDefaultTake` | `AdminUtils.sudo_set_default_take` | +| `setStakeThreshold` | `AdminUtils.sudo_set_stake_threshold` | +| `setNominatorMinRequiredStake` | `AdminUtils.sudo_set_nominator_min_required_stake` | +| `setDelegateTakeRateLimit` | `AdminUtils.sudo_set_tx_delegate_take_rate_limit` | +| `setMinDelegateTake` | `AdminUtils.sudo_set_min_delegate_take` | +| `setMinChildkeyTakePerSubnet` | `AdminUtils.sudo_set_min_childkey_take_per_subnet` | +| `setCollateralLockShare` | `AdminUtils.sudo_set_collateral_lock_share` | +| `setCollateralDrainRatio` | `AdminUtils.sudo_set_collateral_drain_ratio` | + +Runtime authorization remains in force. Proposed names and signatures do not +reserve selectors. + Source: [`stakingV2.sol`](https://github.com/RaoFoundation/subtensor/blob/main/precompiles/src/solidity/stakingV2.sol) diff --git a/docs/guides/evm/precompiles/subnet.mdx b/docs/guides/evm/precompiles/subnet.mdx index 7b53a362ca..5817cc54ba 100644 --- a/docs/guides/evm/precompiles/subnet.mdx +++ b/docs/guides/evm/precompiles/subnet.mdx @@ -73,21 +73,80 @@ setDifficulty(uint16,uint64) setImmunityPeriod(uint16,uint16) setKappa(uint16,uint16) setLiquidAlphaEnabled(uint16,bool) -setMaxBurn(uint16,uint64) setMaxDifficulty(uint16,uint64) setMinAllowedWeights(uint16,uint16) -setMinBurn(uint16,uint64) setMinDifficulty(uint16,uint64) setNetworkPowRegistrationAllowed(uint16,bool) setNetworkRegistrationAllowed(uint16,bool) setOwnerCutAutoLockEnabled(uint16,bool) setRho(uint16,uint16) setServingRateLimit(uint16,uint64) -setWeightsSetRateLimit(uint16,uint64) setWeightsVersionKey(uint16,uint64) setYuma3Enabled(uint16,bool) toggleTransfers(uint16,bool) ``` -Source: [`subnet.sol`](https://github.com/RaoFoundation/subtensor/blob/main/precompiles/src/solidity/subnet.sol) +## Legacy no-op functions + +These released selectors remain routed but intentionally do not change state: + +```text +setWeightsSetRateLimit(uint16,uint64) +setMinBurn(uint16,uint64) +setMaxBurn(uint16,uint64) +``` + +Changing their behavior in place would break their released semantics. The +real AdminUtils operations therefore require the proposed V2 selectors below. + +## Proposed subnet operations + +| Proposed function | Source extrinsic | +|---|---| +| `dissolveNetwork` | `SubtensorModule.dissolve_network` | +| `setSubnetIdentity` | `SubtensorModule.set_subnet_identity` | +| `updateSubnetSymbol` | `SubtensorModule.update_symbol` | +| `rootDissolveNetwork` | `SubtensorModule.root_dissolve_network` | +| `triggerEpoch` | `SubtensorModule.trigger_epoch` | +## Proposed AdminUtils operations + +| Proposed function | Source extrinsic | +|---|---| +| `setAdjustmentInterval` | `AdminUtils.sudo_set_adjustment_interval` | +| `setAdminFreezeWindow` | `AdminUtils.sudo_set_admin_freeze_window` | +| `setBondsPenalty` | `AdminUtils.sudo_set_bonds_penalty` | +| `setCommitRevealVersion` | `AdminUtils.sudo_set_commit_reveal_version` | +| `setDissolveNetworkScheduleDuration` | `AdminUtils.sudo_set_dissolve_network_schedule_duration` | +| `setNetworkLockCostReductionInterval` | `AdminUtils.sudo_set_lock_reduction_interval` | +| `setMaxAllowedUids` | `AdminUtils.sudo_set_max_allowed_uids` | +| `setMaxAllowedValidators` | `AdminUtils.sudo_set_max_allowed_validators` | +| `setMaxBurnV2` | `AdminUtils.sudo_set_max_burn` | +| `setMaxEpochsPerBlock` | `AdminUtils.sudo_set_max_epochs_per_block` | +| `setMaxMechanismCount` | `AdminUtils.sudo_set_max_mechanism_count` | +| `setMaxRegistrationsPerBlock` | `AdminUtils.sudo_set_max_registrations_per_block` | +| `setMechanismCount` | `AdminUtils.sudo_set_mechanism_count` | +| `setMechanismEmissionSplit` | `AdminUtils.sudo_set_mechanism_emission_split` | +| `setMinAllowedUids` | `AdminUtils.sudo_set_min_allowed_uids` | +| `setMinBurnV2` | `AdminUtils.sudo_set_min_burn` | +| `setMinNonImmuneUids` | `AdminUtils.sudo_set_min_non_immune_uids` | +| `setNetworkImmunityPeriod` | `AdminUtils.sudo_set_network_immunity_period` | +| `setNetworkMinLockCost` | `AdminUtils.sudo_set_network_min_lock_cost` | +| `setNetworkRateLimit` | `AdminUtils.sudo_set_network_rate_limit` | +| `setOwnerCutEnabled` | `AdminUtils.sudo_set_owner_cut_enabled` | +| `setOwnerHyperparameterRateLimit` | `AdminUtils.sudo_set_owner_hparam_rate_limit` | +| `setOwnerImmuneNeuronLimit` | `AdminUtils.sudo_set_owner_immune_neuron_limit` | +| `setRaoRecycledForRegistration` | `AdminUtils.sudo_set_rao_recycled` | +| `setSubnetOwnerHotkey` | `AdminUtils.sudo_set_sn_owner_hotkey` | +| `setSubnetLimit` | `AdminUtils.sudo_set_subnet_limit` | +| `setSubnetOwnerCut` | `AdminUtils.sudo_set_subnet_owner_cut` | +| `setSubtokenEnabled` | `AdminUtils.sudo_set_subtoken_enabled` | +| `setTargetRegistrationsPerInterval` | `AdminUtils.sudo_set_target_registrations_per_interval` | +| `setTempo` | `AdminUtils.sudo_set_tempo` | +| `setWeightsSetRateLimitV2` | `AdminUtils.sudo_set_weights_set_rate_limit` | +| `trimToMaxAllowedUids` | `AdminUtils.sudo_trim_to_max_allowed_uids` | + +Runtime authorization remains in force. Proposed names and signatures do not +reserve selectors. + +Source: [`subnet.sol`](https://github.com/RaoFoundation/subtensor/blob/main/precompiles/src/solidity/subnet.sol) diff --git a/docs/guides/evm/precompiles/timestamp.mdx b/docs/guides/evm/precompiles/timestamp.mdx index 97f0d78510..8b6d6424d2 100644 --- a/docs/guides/evm/precompiles/timestamp.mdx +++ b/docs/guides/evm/precompiles/timestamp.mdx @@ -25,5 +25,7 @@ description: Proposed typed EVM interface for Timestamp pallet state. user operation. The proposed precompile therefore exposes no state-changing timestamp function. -Names and signatures on this page are provisional and do not reserve selectors. +See the complete classification in +[Extrinsic coverage](/docs/guides/evm/precompiles/extrinsic-coverage). +Names and signatures on this page are provisional and do not reserve selectors. diff --git a/docs/guides/evm/precompiles/voting-power.mdx b/docs/guides/evm/precompiles/voting-power.mdx index ba2cdd3d8d..a38f4d9d88 100644 --- a/docs/guides/evm/precompiles/voting-power.mdx +++ b/docs/guides/evm/precompiles/voting-power.mdx @@ -22,5 +22,14 @@ getVotingPowerEmaAlpha(uint16) getTotalVotingPower(uint16) ``` -Source: [`votingPower.sol`](https://github.com/RaoFoundation/subtensor/blob/main/precompiles/src/solidity/votingPower.sol) +## Proposed operations + +| Proposed function | Source extrinsic | +|---|---| +| `enableVotingPowerTracking` | `SubtensorModule.enable_voting_power_tracking` | +| `disableVotingPowerTracking` | `SubtensorModule.disable_voting_power_tracking` | +| `setVotingPowerEmaAlpha` | `SubtensorModule.sudo_set_voting_power_ema_alpha` | +Proposed names and signatures do not reserve selectors. + +Source: [`votingPower.sol`](https://github.com/RaoFoundation/subtensor/blob/main/precompiles/src/solidity/votingPower.sol) From 48445d7ffda3136a7a8ebbbb8fcef3a6dbebf841 Mon Sep 17 00:00:00 2001 From: UnarbosFour Date: Wed, 29 Jul 2026 16:57:40 -0400 Subject: [PATCH 3/8] Remove event reporting precompile suggestions --- .agents/skills/emv-maintainer/SKILL.md | 9 +- .../references/abi-versioning.md | 3 +- .../references/coverage-and-testing.md | 26 +- .../references/event-subscriptions.md | 249 ------------------ docs/guides/evm/precompile-design.mdx | 190 +++++++------ .../evm/precompiles/configuration-events.mdx | 98 ------- docs/guides/evm/precompiles/index.mdx | 9 +- docs/guides/evm/precompiles/meta.json | 5 - docs/guides/evm/precompiles/neuron-events.mdx | 50 ---- .../guides/evm/precompiles/staking-events.mdx | 56 ---- docs/guides/evm/precompiles/subnet-events.mdx | 80 ------ .../guides/evm/precompiles/weights-events.mdx | 47 ---- 12 files changed, 100 insertions(+), 722 deletions(-) delete mode 100644 .agents/skills/emv-maintainer/references/event-subscriptions.md delete mode 100644 docs/guides/evm/precompiles/configuration-events.mdx delete mode 100644 docs/guides/evm/precompiles/neuron-events.mdx delete mode 100644 docs/guides/evm/precompiles/staking-events.mdx delete mode 100644 docs/guides/evm/precompiles/subnet-events.mdx delete mode 100644 docs/guides/evm/precompiles/weights-events.mdx diff --git a/.agents/skills/emv-maintainer/SKILL.md b/.agents/skills/emv-maintainer/SKILL.md index f59e2a83d8..931b3ac8e1 100644 --- a/.agents/skills/emv-maintainer/SKILL.md +++ b/.agents/skills/emv-maintainer/SKILL.md @@ -5,15 +5,13 @@ description: Maintain the EVM precompiles in backwards compatible way with API v # EVM Precompile Maintainer -You are the maintainer of EVM precompiles. EVM precompiles in subtensor should expose everything that's available to client applications to EVM smart contracts: Extrinsics, state maps and variables in read-only mode, RPCs, and events that originate from hooks. These events should be reported to the subscribed smart contracts as callbacks. Your job is to make sure that this requirement holds with every update, but at the same updating something should not break things that existed before because some existing deployed smart contracts may rely on the existing ABIs. Read the notes below and then execute steps. +You are the maintainer of EVM precompiles. EVM precompiles in subtensor should expose the deterministic functionality available to client applications to EVM smart contracts: extrinsics, state maps and variables through typed read-only views, and runtime APIs/RPC results. Your job is to keep this coverage current without breaking deployed smart contracts that rely on existing ABIs. Read the notes below and then execute steps. ## Reference routing - Before classifying or implementing any precompile change, including an additive function, runtime adaptation, bug fix, deprecation, or disablement, read [ABI versioning](references/abi-versioning.md). -- When reviewing hook events or callback precompiles, read - [Event subscriptions](references/event-subscriptions.md). - Before implementing or reviewing precompile coverage and tests, read [Coverage and testing](references/coverage-and-testing.md). @@ -56,8 +54,6 @@ For each affected released function: - Represent Substrate account IDs in EVM space as 32-byte public keys. - Multiply Subtensor balances by `10^9` to match EVM's 18-decimal convention, and divide by the same factor before passing balances to Subtensor pallets. -- Follow [Event subscriptions](references/event-subscriptions.md) for callback - interfaces, charging, bounds, and delivery. ## Step 1 - Review current precompiles vs. subtensor functionality @@ -72,7 +68,6 @@ For each affected released function: - timestamp - swap - All runtime API RPCs for the subtensor pallet should be exposed as a callable precompile function with similar interface -- All events emitted from hooks (such as on_initialize or on_finalize) should be exposed as callbacks. Use [Coverage and testing](references/coverage-and-testing.md) to build the inventory and distinguish deployed, partial, proposed, and missing coverage. @@ -82,7 +77,7 @@ inventory and distinguish deployed, partial, proposed, and missing coverage. Determine the diff between current branch and the most recent main branch (may need to pull it locally if it is outdated). See how this diff affects EVM precompiles: - Does it remove or change any functions that precompiles rely on? Does it change function signatures or underlying functionality? -- Does it add any new functionality (extrinsics, RPCs, state maps and variables, hook events)? +- Does it add any new functionality (extrinsics, RPCs, state maps and variables)? ## Step 3 - Handle changed functions diff --git a/.agents/skills/emv-maintainer/references/abi-versioning.md b/.agents/skills/emv-maintainer/references/abi-versioning.md index 54037f4b1a..c4dafd1c0b 100644 --- a/.agents/skills/emv-maintainer/references/abi-versioning.md +++ b/.agents/skills/emv-maintainer/references/abi-versioning.md @@ -51,8 +51,7 @@ Preserve all observable properties of every released call: - state transitions and atomicity; - success-versus-revert behavior and documented error payloads; - bounded-input and complexity guarantees; -- callback selectors, event-mask assignments, filters, charging, - auto-unsubscription, sequencing, and delivery guarantees. +- lifecycle-status selectors and their documented availability guarantees. Return types do not contribute to a Solidity selector, but changing them under an existing selector still breaks old callers because they decode the returned diff --git a/.agents/skills/emv-maintainer/references/coverage-and-testing.md b/.agents/skills/emv-maintainer/references/coverage-and-testing.md index 1f1122fc64..902752a872 100644 --- a/.agents/skills/emv-maintainer/references/coverage-and-testing.md +++ b/.agents/skills/emv-maintainer/references/coverage-and-testing.md @@ -7,7 +7,6 @@ - [Cover extrinsics](#cover-extrinsics) - [Cover state with typed views](#cover-state-with-typed-views) - [Cover runtime APIs and public RPCs](#cover-runtime-apis-and-public-rpcs) -- [Cover events](#cover-events) - [Add regression tests first](#add-regression-tests-first) - [Test observable behavior](#test-observable-behavior) - [Validate ABIs and routing](#validate-abis-and-routing) @@ -25,14 +24,12 @@ For each in-scope pallet, inspect: - every dispatchable extrinsic; - every public state map and value; - every publicly facing runtime API and RPC; -- every emitted event, including events originating in hooks and scheduled - work; and - changes to types, guards, authorization, units, and error behavior used by existing precompiles. -Coverage means that Solidity contracts receive a typed equivalent of the -authorized client-facing functionality. It does not mean exposing raw pallet -storage, SCALE bytes, or Rust types. +Precompile coverage means that Solidity contracts receive a typed equivalent +of the authorized deterministic client-facing functionality. It does not mean +exposing raw pallet storage, SCALE bytes, or Rust types. Distinguish deployed coverage from proposed coverage. Do not describe a documented proposal, unassigned address, or Rust stub as callable. @@ -41,9 +38,9 @@ documented proposal, unassigned address, or Rust stub as callable. Create or update a working matrix with one row per source item: -| Source | Kind | Public functionality | Precompile domain | Function or callback | Status | Evidence | +| Source | Kind | Public functionality | Precompile domain | Function | Status | Evidence | |---|---|---|---|---|---|---| -| Pallet and item | Extrinsic, state, runtime API, RPC, or event | Meaning exposed to clients | Existing or proposed address/domain | Canonical signature or callback | Covered, partial, missing, or excluded | Rust, Solidity, ABI, and test paths | +| Pallet and item | Extrinsic, state, runtime API, or RPC | Meaning exposed to clients | Existing or proposed address/domain | Canonical signature | Covered, partial, missing, or excluded | Rust, Solidity, ABI, and test paths | For every partial, missing, or excluded row, state the exact reason. Do not equate a similarly named function with coverage; compare parameters, returned @@ -124,18 +121,6 @@ runtime. When a public RPC composes runtime state, implement the deterministic runtime-side result and document any transport-only behavior that has no EVM equivalent. -## Cover events - -Inspect event enums and active emission sites. Cover relevant hook-origin -events with subscription callbacks so contracts are not limited to their own -transaction receipts. - -Use [Event subscriptions](event-subscriptions.md) for domain grouping, -filtering, callback ABI, charging, queue bounds, and delivery semantics. - -Do not mark an enum-only placeholder as emitted coverage. Do not expose a raw -runtime event or an unbounded vector callback. - ## Add regression tests first For a bug fix, add a regression unit test that fails for the reported behavior @@ -214,7 +199,6 @@ Test: - the maximum accepted collection size; - rejection just beyond the bound; - proof-size-sensitive database access where relevant; -- callback gas and per-block delivery limits for subscriptions; and - failure paths that could otherwise perform unpaid work. Do not accept a bounded input if processing it can trigger an unbounded runtime diff --git a/.agents/skills/emv-maintainer/references/event-subscriptions.md b/.agents/skills/emv-maintainer/references/event-subscriptions.md deleted file mode 100644 index 47b4bee0e0..0000000000 --- a/.agents/skills/emv-maintainer/references/event-subscriptions.md +++ /dev/null @@ -1,249 +0,0 @@ -# Event subscription precompiles - -## Contents - -- [Use typed domain precompiles](#use-typed-domain-precompiles) -- [Inventory reportable events](#inventory-reportable-events) -- [Use a common subscription interface](#use-a-common-subscription-interface) -- [Fund callback delivery](#fund-callback-delivery) -- [Keep event production bounded](#keep-event-production-bounded) -- [Define stable callback ABIs](#define-stable-callback-abis) -- [Normalize variable-length events](#normalize-variable-length-events) -- [Specify delivery semantics](#specify-delivery-semantics) -- [Protect execution](#protect-execution) -- [Test subscription behavior](#test-subscription-behavior) - -## Use typed domain precompiles - -Expose events from `SubtensorModule` and `AdminUtils` as typed Solidity -callbacks. Do not expose raw `RuntimeEvent`, pallet enum discriminants, or -SCALE-encoded payloads. - -Group callbacks by meaning under a small number of independently addressed -precompiles. Use the current proposed domains as the design baseline: - -- staking and economic flows; -- neurons, identities, relationships, and key rotation; -- weights and commit-reveal; -- subnet lifecycle, epochs, emissions, leases, and voting-power tracking; -- runtime and subnet configuration. - -Consult the corresponding pages under -`docs/guides/evm/precompiles/*-events.mdx` for the current proposed inventory. -Treat names, signatures, mask bits, and addresses as provisional until -released. After release, apply the ABI rules in -[ABI versioning](abi-versioning.md). - -Create another address only when an event family has a genuinely separate -domain and lifecycle. Do not create one precompile per pallet event. - -## Inventory reportable events - -Inspect both event enum definitions and every emission site. An enum variant -without an active emission site is not a live callback. Record it as a coverage -gap or future possibility, not as currently delivered behavior. - -For each emitted event: - -1. Record the source pallet, variant, fields, and emission sites. -2. Identify whether it originates from an extrinsic, scheduled operation, or - runtime hook. -3. Assign it to a meaningful event-precompile domain. -4. Define stable EVM field types and conversions. -5. Determine whether the source payload is bounded. -6. Define a recovery view when callbacks alone are not authoritative. -7. Add an event-mask bit without changing any released assignment. - -Prioritize hook-origin events because an interested contract cannot obtain them -from its own transaction receipt. Use the same subscription model for relevant -transaction and scheduled-operation events when this provides coherent domain -coverage. - -When a new source event starts being emitted, add a new typed callback and mask -bit. Do not change an existing callback to absorb different semantics. - -## Use a common subscription interface - -Use the same control shape for every event domain unless a documented reason -requires an additive version: - -```solidity -struct EventFilter { - uint256 eventMask; - uint16 netuid; - bytes32 accountId; - bool matchAnyNetuid; - bool matchAnyAccount; -} - -struct Subscription { - bool active; - EventFilter filter; - uint64 callbackGasLimit; - uint64 nextSequence; -} - -function subscribe( - EventFilter calldata filter, - uint64 callbackGasLimit -) external; - -function unsubscribe() external; - -function getSubscription( - address subscriber -) external view returns (Subscription memory); - -function minimumCallbackBalance( - uint64 callbackGasLimit -) external view returns (uint256); -``` - -Always make the caller the subscriber. Do not allow one address to subscribe or -unsubscribe another address. - -Store at most one fixed-size subscription per contract and event domain. Use a -fixed event mask plus at most one netuid and one account filter. Do not store or -iterate an arbitrary list of filters. - -Validate the mask, callback gas limit, filter flags, and minimum balance before -creating or replacing a subscription. Make subscription replacement atomic. - -## Fund callback delivery - -Charge callback attempts to the subscribing contract's own TAO balance. Require -enough balance at subscription time to fund the documented minimum number of -attempts at the selected callback gas limit. - -Charge a reverting callback for the work it consumed. Never let callback -failure revert the runtime operation that produced the source event. - -Automatically remove a subscription when its balance cannot fund the next -attempt. Define charging, rounding, and TAO-to-EVM unit conversion precisely. -Do not provide free delivery paths that allow subscription spam. - -Keep the minimum-balance calculation available as a typed view so a contract -can determine whether a subscription is fundable before submitting it. - -## Keep event production bounded - -Do not synchronously iterate all subscribers when an event is emitted. Append a -fixed-size typed report to a bounded queue in O(1), then process a bounded -amount of delivery work in later blocks. - -Advance delivery through bounded cursors. Cap: - -- queue capacity; -- work per block; -- callback gas; -- report size; -- subscription size; and -- the number of delivery attempts performed by one bounded work item. - -Do not copy or ABI-encode an unbounded vector while producing a report. If a -source event is variable-length, normalize it incrementally as described below. - -Define what happens when the queue reaches capacity. Never permit unbounded -runtime storage or memory growth. - -## Define stable callback ABIs - -Give every event a stable, event-specific receiver selector. Include -`uint64 sequence` and `uint64 sourceBlock` in every callback before the -event-specific fields. - -Use stable EVM representations: - -- Substrate account IDs and hashes: `bytes32`; -- EVM accounts: `address`; -- netuids and UIDs: `uint16` when the runtime domain fits; -- TAO and Alpha amounts: 18-decimal `uint256` values using the documented - `10^9` conversion factor; -- fixed-point values: an explicitly documented integer representation. - -Choose bounded representations for strings, identities, and other structured -values before release. Do not expose a Rust or SCALE representation as the ABI. - -After release: - -- reserve the precompile address and control selectors; -- reserve every event-mask bit; -- preserve callback names, parameters, order, types, and meaning; -- preserve filter, charging, sequencing, and delivery guarantees; and -- add a versioned callback when richer data is required. - -Do not add speculative fields to a callback merely because a future runtime -might produce them. Add another selector when the semantics become concrete. - -## Normalize variable-length events - -Convert every variable-length source event into bounded callbacks. Emit a -summary when useful, followed by one item callback per entry. Give related -callbacks the same source sequence and include item index and item count. - -For UID-indexed emission arrays, interpret the array index as the UID and -deliver one `(uid, amount)` callback per entry. For example, `[10, 20, 30]` -represents UIDs `0`, `1`, and `2`; do not treat an entry as an arbitrary UID -value. - -Apply the same approach to children lists, weight hashes, completed-netuid -batches, and similar collections. Use a stable typed representation for -per-item failures instead of SCALE-encoded `DispatchError`. - -Produce normalized items incrementally at the source. Do not first copy the -complete vector into a queued report. - -## Specify delivery semantics - -Treat callbacks as asynchronous, best-effort notifications. Do not promise that -a callback executes in the source event's block. - -Use a monotonically increasing source sequence and source block so receivers -can order reports and detect gaps. Define whether normalized items share one -source sequence and how item indices identify completeness. - -If bounded queue overwrite or another allowed failure drops a report, make the -gap observable through sequencing. Require authoritative recovery through the -corresponding typed view where contract logic needs exact current state. - -Document ordering across event domains only if the implementation guarantees -it. Require receivers to make callbacks idempotent and tolerate retries, -reordering outside documented guarantees, and sequence gaps. - -## Protect execution - -Apply reentrancy protection around delivery. Do not allow a callback to -recursively create unbounded callback work. - -Keep the source runtime operation independent of callback execution. Bound -callback gas and isolate callback failure. Validate that subscriber-controlled -code cannot stall block processing, retain an unpaid subscription, or make -another subscriber's delivery unbounded. - -Account for database reads, writes, queue operations, EVM execution, and failed -attempts. Use saturating arithmetic where appropriate and reject values that -cannot be converted safely. - -## Test subscription behavior - -Test at least: - -- self-subscription and self-unsubscription; -- attempts to manage another address; -- invalid masks, filters, and gas limits; -- insufficient initial balance; -- successful charging and delivery; -- reverting and out-of-gas callbacks; -- automatic unsubscription when payment fails; -- event filtering by mask, netuid, and account; -- monotonic sequencing and source-block reporting; -- queue capacity and observable gaps; -- bounded per-block work with many subscribers; -- reentrancy and recursive-work resistance; -- one-item normalization and item ordering; -- unit and account conversions; -- released callback selectors and event-mask assignments; and -- additive introduction of a new callback without changing old callbacks. - -Use [Coverage and testing](coverage-and-testing.md) for the general precompile -regression and ABI-diff requirements. diff --git a/docs/guides/evm/precompile-design.mdx b/docs/guides/evm/precompile-design.mdx index a47c528866..66f59a8439 100644 --- a/docs/guides/evm/precompile-design.mdx +++ b/docs/guides/evm/precompile-design.mdx @@ -1,6 +1,6 @@ --- title: Precompile design and lifecycle -description: How Bittensor precompiles preserve deployed-contract compatibility, evolve function by function, and communicate deprecation or temporary disablement. +description: How Bittensor precompiles preserve compatibility and how projects relay selected Substrate events to EVM contracts. --- Bittensor precompiles are fixed-address EVM contracts implemented by the @@ -139,107 +139,93 @@ inherently brittle. The intended migration is: Whether this 1:1 coverage should extend beyond the authorized pallets remains an open design question. -## Subscription-based event reporting - -Some Subtensor events are produced by runtime hooks rather than by the EVM -transaction that is interested in them. A transaction receipt therefore cannot -provide complete event coverage. Proposed event precompiles let a contract -subscribe itself and receive those events later as typed EVM callbacks. - -Event reporting is divided into dedicated domain precompiles for -[staking](/docs/guides/evm/precompiles/staking-events), -[neurons and keys](/docs/guides/evm/precompiles/neuron-events), -[weights](/docs/guides/evm/precompiles/weights-events), -[subnet lifecycle](/docs/guides/evm/precompiles/subnet-events), and -[runtime configuration](/docs/guides/evm/precompiles/configuration-events). -Each domain will have its own address. These precompiles report typed events -originating from `SubtensorModule` and `AdminUtils`; they do not expose raw -`RuntimeEvent` values or SCALE-encoded payloads. - -The callback inventories cover source variants with active emission sites in -the current runtime. An enum-only placeholder is not presented as a live -callback. If such a variant starts being emitted, its typed callback must be -added without changing the existing subscription or receiver selectors. - -### Subscription control - -Each event precompile should expose the same control shape: - -```solidity -struct EventFilter { - uint256 eventMask; - uint16 netuid; - bytes32 accountId; - bool matchAnyNetuid; - bool matchAnyAccount; -} - -struct Subscription { - bool active; - EventFilter filter; - uint64 callbackGasLimit; - uint64 nextSequence; -} - -function subscribe( - EventFilter calldata filter, - uint64 callbackGasLimit -) external; - -function unsubscribe() external; - -function getSubscription( - address subscriber -) external view returns (Subscription memory); - -function minimumCallbackBalance( - uint64 callbackGasLimit -) external view returns (uint256); -``` - -The caller is always the subscriber: a contract cannot subscribe or unsubscribe -another address. One fixed-size subscription per contract and domain keeps -lookup and update costs bounded. The event mask and optional single-netuid and -single-account filters let a subscriber narrow delivery without storing or -iterating an arbitrary filter list. - -`subscribe` succeeds only when the contract's own TAO balance can fund the -documented minimum number of callback attempts at its selected gas limit. Each -attempt is charged to that same balance. If the balance can no longer pay for -an attempt, the subscription is automatically removed. A reverting callback is -charged for the work it consumed and cannot revert the runtime operation that -produced the event. - -### Typed callbacks and delivery - -Every report has a stable, event-specific callback selector. Substrate account -IDs are represented as `bytes32`, and TAO and Alpha balances are multiplied by -`10^9` for EVM's 18-decimal convention. New source events add callback -selectors; released callbacks are never changed, removed, or reused. - -Callbacks are asynchronous notifications, not part of the transaction or hook -that produced the source event. Every callback includes a monotonically -increasing sequence and source block number so receivers can order deliveries -and detect a gap. A receiver must make its callback idempotent and must not -assume delivery in the source event's block. - -The runtime must not iterate every subscriber while emitting an event. Instead, -emission appends one fixed-size typed report to a bounded queue in O(1), and a -bounded amount of later block work advances a subscriber cursor one delivery at -a time. Callback gas is capped, delivery is protected against reentrancy, and a -callback cannot recursively create more callback work. - -Variable-length pallet events are normalized into bounded item callbacks. For -example, miner emissions are reported one UID at a time and a batch of weight -hashes is reported one hash at a time, with the same source sequence plus item -index and item count. The adapter must produce those items incrementally at the -source rather than copy or ABI-encode an unbounded vector. - -The queue has a fixed capacity so event reporting cannot grow runtime memory -without bound. If delivery falls behind far enough to overwrite an undelivered -report, the next successful callback exposes the sequence gap. Event callbacks -are therefore best-effort integration signals; contracts that require -authoritative recovery must use the corresponding typed view. +## Project-scoped event relays + +Substrate events are already recorded in chain data. Reproducing the complete +event stream through protocol-level EVM callbacks would add another on-chain +copy together with subscription storage, delivery queues, and callback +execution. It would also force the runtime to support broad event delivery even +when an application needs only a small, highly filtered set of signals. + +Bittensor therefore does not propose event-reporting precompiles. A project +that needs proactive notifications in its EVM contracts should run an +off-chain relay tailored to that project's use cases. The relay watches +finalized Substrate events, performs application-specific filtering, +aggregation, and enrichment off chain, and submits only the reports that the +project's contracts can act on. + +Typed precompile views remain the authoritative way for contracts to read +current runtime state. Relayed reports are notifications under the trust and +availability model chosen by the project. + +### Relay flow + +A typical relay operates as follows: + +1. Relay nodes read finalized blocks and events from Substrate RPC endpoints or + an indexer. +2. Each node applies the project's filters and derives a canonical typed + report. +3. A configured signer quorum attests to the report. +4. A relayer submits the report and its authorization proof in an ordinary EVM + transaction. +5. The reporting contract verifies the report, rejects duplicates, and either + emits a typed EVM log, invokes a bounded set of subscribed receivers, or + records data for receivers to pull. + +A report should identify at least the source chain, finalized block hash and +number, source event position or another unique event identifier, schema +version, payload, and relay sequence or nonce. The signed message must be +domain-separated by chain ID, reporting-contract address, and schema version so +that it cannot be replayed on another chain, contract, or report type. + +Filtering belongs primarily in the relay. A subnet application might publish +only completed tempo summaries, material configuration changes, or aggregate +emission results instead of reproducing every underlying pallet event. + +### Subscription-capable reporting contracts + +A project can deploy a reporting contract that lets users or other contracts +register subscriptions and lets authorized relayers submit observed reports. +A subscription can select typed report kinds, project-specific filters, a +receiver, and a callback gas limit. The contract should make its payment, +retry, ordering, and removal rules explicit. + +Neither report submission nor callback delivery should iterate an unbounded +subscriber set. Limit each transaction to a fixed-size batch, let relayers +target matching subscribers explicitly, or let subscribers pull verified +reports. Catch callback failures so one receiver cannot revert delivery to +others, and require receiver callbacks to be idempotent. + +Every successful relay submission has an EVM transaction and receipt. Projects +must decide whether relayers fund these transactions, subscribers prepay for +delivery, or another project account subsidizes them. + +### Relayer trust and security + +A single relay signer is the simplest design but makes that signer a trusted +oracle. Projects that need stronger guarantees can use an independently +operated committee with an explicit `M-of-N` multisignature, a threshold +signature scheme, or another auditable quorum mechanism. The reporting +contract must define signer enrollment, quorum, key rotation, emergency +revocation, and version upgrades. + +Relay implementations should also: + +- wait for the documented source-chain finality condition; +- use deterministic report encoding and reject duplicate event identifiers; +- expose sequences or source positions so receivers can detect gaps; +- tolerate delayed, reordered, and repeated submissions; +- bound report size, callback gas, batch size, and retained on-chain history; +- separate observation from submission so any permitted party can submit a + valid quorum-authorized report; and +- provide a reconciliation path through typed precompile views when a report is + missing or disputed. + +Contracts must not treat relayed events as consensus-authenticated merely +because they describe on-chain activity. Their integrity depends on the relay +committee and verification rules, while their availability depends on relay +operators continuing to observe and submit reports. ## Function lifecycle diff --git a/docs/guides/evm/precompiles/configuration-events.mdx b/docs/guides/evm/precompiles/configuration-events.mdx deleted file mode 100644 index b3d1085b00..0000000000 --- a/docs/guides/evm/precompiles/configuration-events.mdx +++ /dev/null @@ -1,98 +0,0 @@ ---- -title: Configuration events -description: Proposed subscription precompile for typed Subtensor and AdminUtils configuration callbacks. ---- - -| Property | Value | -|---|---| -| Proposed implementation | `ConfigurationEventsPrecompile` | -| Proposed Solidity interface | `IConfigurationEvents` | -| Callback receiver interface | `IConfigurationEventsReceiver` | -| Address | Dedicated address not assigned | -| Status | Proposed | - -This precompile reports runtime and subnet configuration changes emitted by -`SubtensorModule` and `AdminUtils`. It normalizes the two pallets into -meaningful typed callbacks while retaining the source pallet in callback -metadata when both pallets can describe the same setting. - -## Proposed Subtensor callbacks - -| Receiver function | Subtensor source event | -|---|---| -| `onActivityCutoffChanged(...)` | `ActivityCutoffSet` | -| `onActivityCutoffFactorChanged(...)` | `ActivityCutoffFactorMilliSet` | -| `onAdjustmentAlphaChanged(...)` | `AdjustmentAlphaSet` | -| `onAdjustmentIntervalChanged(...)` | `AdjustmentIntervalSet` | -| `onAdminFreezeWindowChanged(...)` | `AdminFreezeWindowSet` | -| `onBondsMovingAverageChanged(...)` | `BondsMovingAverageSet` | -| `onBondsPenaltyChanged(...)` | `BondsPenaltySet` | -| `onBondsResetOnSetChanged(...)` | `BondsResetOnSet` | -| `onColdkeySwapAnnouncementDelayChanged(...)` | `ColdkeySwapAnnouncementDelaySet` | -| `onColdkeySwapReannouncementDelayChanged(...)` | `ColdkeySwapReannouncementDelaySet` | -| `onDifficultyChanged(...)` | `DifficultySet` | -| `onDissolutionScheduleDurationChanged(...)` | `DissolveNetworkScheduleDurationSet` | -| `onImmunityPeriodChanged(...)` | `ImmunityPeriodSet` | -| `onKappaChanged(...)` | `KappaSet` | -| `onMaxAllowedUidsChanged(...)` | `MaxAllowedUidsSet` | -| `onMaxAllowedValidatorsChanged(...)` | `MaxAllowedValidatorsSet` | -| `onMaxBurnChanged(...)` | `MaxBurnSet` | -| `onMaxChildKeyTakeChanged(...)` | `MaxChildKeyTakeSet` | -| `onMaxDelegateTakeChanged(...)` | `MaxDelegateTakeSet` | -| `onMaxDifficultyChanged(...)` | `MaxDifficultySet` | -| `onMaxEpochsPerBlockChanged(...)` | `MaxEpochsPerBlockSet` | -| `onMaxRegistrationsPerBlockChanged(...)` | `MaxRegistrationsPerBlockSet` | -| `onMinAllowedUidsChanged(...)` | `MinAllowedUidsSet` | -| `onMinAllowedWeightChanged(...)` | `MinAllowedWeightSet` | -| `onMinBurnChanged(...)` | `MinBurnSet` | -| `onMinChildKeyTakeChanged(...)` | `MinChildKeyTakeSet` | -| `onMinChildKeyTakeForSubnetChanged(...)` | `MinChildKeyTakePerSubnetSet` | -| `onMinDelegateTakeChanged(...)` | `MinDelegateTakeSet` | -| `onMinDifficultyChanged(...)` | `MinDifficultySet` | -| `onMinNonImmuneUidsChanged(...)` | `MinNonImmuneUidsSet` | -| `onNetworkImmunityPeriodChanged(...)` | `NetworkImmunityPeriodSet` | -| `onNetworkLockCostReductionIntervalChanged(...)` | `NetworkLockCostReductionIntervalSet` | -| `onNetworkMinimumLockCostChanged(...)` | `NetworkMinLockCostSet` | -| `onNetworkRateLimitChanged(...)` | `NetworkRateLimitSet` | -| `onOwnerHyperparameterRateLimitChanged(...)` | `OwnerHyperparamRateLimitSet` | -| `onPowRegistrationAllowedChanged(...)` | `PowRegistrationAllowed` | -| `onRaoRecycledForRegistrationChanged(...)` | `RAORecycledForRegistrationSet` | -| `onRegistrationAllowedChanged(...)` | `RegistrationAllowed` | -| `onRegistrationsPerIntervalChanged(...)` | `RegistrationPerIntervalSet` | -| `onScalingLawPowerChanged(...)` | `ScalingLawPowerSet` | -| `onServingRateLimitChanged(...)` | `ServingRateLimitSet` | -| `onStakeThresholdChanged(...)` | `StakeThresholdSet` | -| `onStartCallDelayChanged(...)` | `StartCallDelaySet` | -| `onSubnetLimitChanged(...)` | `SubnetLimitSet` | -| `onSubnetOwnerCutChanged(...)` | `SubnetOwnerCutSet` | -| `onTempoChanged(...)` | `TempoSet` | -| `onTransferEnabledChanged(...)` | `TransferToggle` | -| `onChildKeyTakeRateLimitChanged(...)` | `TxChildKeyTakeRateLimitSet` | -| `onDelegateTakeRateLimitChanged(...)` | `TxDelegateTakeRateLimitSet` | -| `onTransactionRateLimitChanged(...)` | `TxRateLimitSet` | -| `onValidatorPruneLengthChanged(...)` | `ValidatorPruneLenSet` | -| `onWeightsRateLimitChanged(...)` | `WeightsSetRateLimitSet` | -| `onWeightsVersionKeyChanged(...)` | `WeightsVersionKeySet` | - -## Proposed AdminUtils callbacks - -| Receiver function | AdminUtils source event | -|---|---| -| `onPrecompileAvailabilityChanged(...)` | `PrecompileUpdated` | -| `onYuma3EnabledChanged(...)` | `Yuma3EnableToggled` | -| `onBondsResetEnabledChanged(...)` | `BondsResetToggled` | -| `onBurnHalfLifeChanged(...)` | `BurnHalfLifeSet` | -| `onBurnIncreaseMultiplierChanged(...)` | `BurnIncreaseMultSet` | -| `onSubnetEmissionEnabledChanged(...)` | `SubnetEmissionEnabledSet` | -| `onCollateralLockShareChanged(...)` | `CollateralLockShareSet` | -| `onCollateralDrainRatioChanged(...)` | `CollateralDrainRatioSet` | - -Every callback begins with `uint64 sequence`, `uint64 sourceBlock`, and a typed -source-pallet value, followed by the setting's typed fields. Account IDs use -`bytes32`, netuids use `uint16`, and fixed-point values use a documented stable -EVM representation. - -Subscription behavior is defined in -[Subscription-based event reporting](/docs/guides/evm/precompile-design#subscription-based-event-reporting). -The names and signatures are provisional and do not reserve selectors. - diff --git a/docs/guides/evm/precompiles/index.mdx b/docs/guides/evm/precompiles/index.mdx index 817a3c5d18..84da100486 100644 --- a/docs/guides/evm/precompiles/index.mdx +++ b/docs/guides/evm/precompiles/index.mdx @@ -53,13 +53,12 @@ or intentionally non-callable EVM treatment. | [`DrandPrecompile`](/docs/guides/evm/precompiles/drand) | `IDrand` | Address not assigned
Proposed | | [`TimestampPrecompile`](/docs/guides/evm/precompiles/timestamp) | `ITimestamp` | Address not assigned
Proposed | | [`RuntimeConfigurationPrecompile`](/docs/guides/evm/precompiles/runtime-configuration) | `IRuntimeConfiguration` | Address not assigned
Proposed | -| [`StakingEventsPrecompile`](/docs/guides/evm/precompiles/staking-events) | `IStakingEvents` | Dedicated address not assigned
Proposed | -| [`NeuronEventsPrecompile`](/docs/guides/evm/precompiles/neuron-events) | `INeuronEvents` | Dedicated address not assigned
Proposed | -| [`WeightsEventsPrecompile`](/docs/guides/evm/precompiles/weights-events) | `IWeightsEvents` | Dedicated address not assigned
Proposed | -| [`SubnetEventsPrecompile`](/docs/guides/evm/precompiles/subnet-events) | `ISubnetEvents` | Dedicated address not assigned
Proposed | -| [`ConfigurationEventsPrecompile`](/docs/guides/evm/precompiles/configuration-events) | `IConfigurationEvents` | Dedicated address not assigned
Proposed | | [`PrecompileRegistry`](/docs/guides/evm/precompiles/registry) | `IPrecompileRegistry` | Address not assigned
Proposed | +Projects that need proactive event delivery should use +[project-scoped event relays](/docs/guides/evm/precompile-design#project-scoped-event-relays) +instead of protocol-level event-reporting precompiles. + Released addresses and selectors remain reserved permanently. The compatibility and lifecycle rules are documented in [Precompile design and lifecycle](/docs/guides/evm/precompile-design). diff --git a/docs/guides/evm/precompiles/meta.json b/docs/guides/evm/precompiles/meta.json index 9cb554aaf6..423e4fb969 100644 --- a/docs/guides/evm/precompiles/meta.json +++ b/docs/guides/evm/precompiles/meta.json @@ -23,11 +23,6 @@ "drand", "timestamp", "runtime-configuration", - "staking-events", - "neuron-events", - "weights-events", - "subnet-events", - "configuration-events", "registry" ] } diff --git a/docs/guides/evm/precompiles/neuron-events.mdx b/docs/guides/evm/precompiles/neuron-events.mdx deleted file mode 100644 index a66dc25a87..0000000000 --- a/docs/guides/evm/precompiles/neuron-events.mdx +++ /dev/null @@ -1,50 +0,0 @@ ---- -title: Neuron and key events -description: Proposed subscription precompile for typed Subtensor neuron, identity, relationship, and key-rotation callbacks. ---- - -| Property | Value | -|---|---| -| Proposed implementation | `NeuronEventsPrecompile` | -| Proposed Solidity interface | `INeuronEvents` | -| Callback receiver interface | `INeuronEventsReceiver` | -| Address | Dedicated address not assigned | -| Status | Proposed | - -This precompile reports neuron registration and serving changes, hotkey and -coldkey rotations, identities, EVM-key associations, and child relationships -emitted by `SubtensorModule`. - -## Proposed callbacks - -| Receiver function | Subtensor source event | -|---|---| -| `onNeuronRegistered(...)` | `NeuronRegistered` | -| `onAxonServed(...)` | `AxonServed` | -| `onPrometheusServed(...)` | `PrometheusServed` | -| `onHotkeySwapped(...)` | `HotkeySwapped` | -| `onHotkeySwappedOnSubnet(...)` | `HotkeySwappedOnSubnet` | -| `onColdkeySwapAnnounced(...)` | `ColdkeySwapAnnounced` | -| `onColdkeySwapReset(...)` | `ColdkeySwapReset` | -| `onColdkeySwapped(...)` | `ColdkeySwapped` | -| `onColdkeySwapDisputed(...)` | `ColdkeySwapDisputed` | -| `onColdkeySwapCleared(...)` | `ColdkeySwapCleared` | -| `onChildrenScheduled(...)` | `SetChildrenScheduled` | -| `onChildScheduled(...)` | One item from `SetChildrenScheduled` | -| `onChildrenSet(...)` | `SetChildren` | -| `onChildSet(...)` | One item from `SetChildren` | -| `onChainIdentitySet(...)` | `ChainIdentitySet` | -| `onEvmKeyAssociated(...)` | `EvmKeyAssociated` | - -The schedule and children summary callbacks carry the hotkey, netuid, and item -count. Their item callbacks carry one child and proportion at a time, using a -shared source sequence, item index, and item count; no callback contains an -unbounded array. - -All callbacks also carry the source block. Account IDs and hashes use -`bytes32`, and the associated EVM key uses `address`. - -Subscription behavior is defined in -[Subscription-based event reporting](/docs/guides/evm/precompile-design#subscription-based-event-reporting). -The names and signatures are provisional and do not reserve selectors. - diff --git a/docs/guides/evm/precompiles/staking-events.mdx b/docs/guides/evm/precompiles/staking-events.mdx deleted file mode 100644 index 209670e080..0000000000 --- a/docs/guides/evm/precompiles/staking-events.mdx +++ /dev/null @@ -1,56 +0,0 @@ ---- -title: Staking events -description: Proposed subscription precompile for typed Subtensor staking, delegation, Alpha-flow, lock, and collateral callbacks. ---- - -| Property | Value | -|---|---| -| Proposed implementation | `StakingEventsPrecompile` | -| Proposed Solidity interface | `IStakingEvents` | -| Callback receiver interface | `IStakingEventsReceiver` | -| Address | Dedicated address not assigned | -| Status | Proposed | - -This precompile reports the economic and staking events emitted by -`SubtensorModule`. A contract subscribes itself through the common -[subscription interface](/docs/guides/evm/precompile-design#subscription-control) -and implements only the callbacks selected by its event mask. - -## Proposed callbacks - -| Receiver function | Subtensor source event | -|---|---| -| `onStakeAdded(...)` | `StakeAdded` | -| `onStakeRemoved(...)` | `StakeRemoved` | -| `onStakeMoved(...)` | `StakeMoved` | -| `onStakeTransferred(...)` | `StakeTransferred` | -| `onStakeAndHotkeyTransferred(...)` | `StakeAndHotkeyTransferred` | -| `onStakeSwapped(...)` | `StakeSwapped` | -| `onAlphaRecycled(...)` | `AlphaRecycled` | -| `onAlphaBurned(...)` | `AlphaBurned` | -| `onStakeBurned(...)` | `AddStakeBurn` | -| `onAutoStakeAdded(...)` | `AutoStakeAdded` | -| `onAutoStakeDestinationChanged(...)` | `AutoStakeDestinationSet` | -| `onStakeLocked(...)` | `StakeLocked` | -| `onLockMoved(...)` | `LockMoved` | -| `onCollateralLocked(...)` | `CollateralLocked` | -| `onMinimumCollateralChanged(...)` | `MinCollateralSet` | -| `onDelegateTakeIncreased(...)` | `TakeIncreased` | -| `onDelegateTakeDecreased(...)` | `TakeDecreased` | -| `onChildKeyTakeChanged(...)` | `ChildKeyTakeSet` | -| `onAutoParentDelegationChanged(...)` | `AutoParentDelegationEnabledSet` | -| `onRootClaimed(...)` | `RootClaimed` | -| `onRootClaimTypeChanged(...)` | `RootClaimTypeSet` | -| `onPerpetualLockChanged(...)` | `PerpetualLockUpdated` | -| `onLockedAlphaAcceptanceChanged(...)` | `RejectLockedAlphaUpdated` | -| `onFaucetFunded(...)` | `Faucet` | - -`onAutoStakeAdded` covers the current staking event emitted from a runtime hook. -The remaining callbacks also make transaction- and scheduled-operation events -available through the same receiver model. - -Each callback begins with `uint64 sequence` and `uint64 sourceBlock`, followed -by typed fields corresponding to the source event. Account IDs use `bytes32`; -TAO and Alpha amounts use 18-decimal `uint256` values. - -The names and signatures are provisional and do not reserve selectors. diff --git a/docs/guides/evm/precompiles/subnet-events.mdx b/docs/guides/evm/precompiles/subnet-events.mdx deleted file mode 100644 index f1e8e5d8a4..0000000000 --- a/docs/guides/evm/precompiles/subnet-events.mdx +++ /dev/null @@ -1,80 +0,0 @@ ---- -title: Subnet events -description: Proposed subscription precompile for typed Subtensor subnet lifecycle, lease, epoch, emission, and voting-power callbacks. ---- - -| Property | Value | -|---|---| -| Proposed implementation | `SubnetEventsPrecompile` | -| Proposed Solidity interface | `ISubnetEvents` | -| Callback receiver interface | `ISubnetEventsReceiver` | -| Address | Dedicated address not assigned | -| Status | Proposed | - -This precompile reports subnet creation and dissolution, ownership and identity -changes, leases, epoch execution, emissions, and voting-power tracking emitted -by `SubtensorModule`. - -## Proposed callbacks - -| Receiver function | Subtensor source | -|---|---| -| `onNetworkRegistrationQueued(...)` | `NetworkRegistrationQueued` | -| `onNetworkAdded(...)` | `NetworkAdded` | -| `onNetworkDissolutionScheduled(...)` | `DissolveNetworkScheduled` | -| `onNetworkRemoved(...)` | `NetworkRemoved` | -| `onNetworkDissolutionCleanupCompleted(...)` | `NetworkDissolveCleanupCompleted` | -| `onSubnetIdentitySet(...)` | `SubnetIdentitySet` | -| `onSubnetIdentityRemoved(...)` | `SubnetIdentityRemoved` | -| `onSubnetSymbolChanged(...)` | `SymbolUpdated` | -| `onSubnetOwnerHotkeyChanged(...)` | `SubnetOwnerHotkeySet` | -| `onSubnetOwnerChanged(...)` | `SubnetOwnerChanged` | -| `onFirstEmissionBlockSet(...)` | `FirstEmissionBlockNumberSet` | -| `onSubnetLeaseCreated(...)` | `SubnetLeaseCreated` | -| `onSubnetLeaseTerminated(...)` | `SubnetLeaseTerminated` | -| `onSubnetLeaseDividendDistributed(...)` | `SubnetLeaseDividendsDistributed` | -| `onEpochTriggered(...)` | `EpochTriggered` | -| `onEpochDeferred(...)` | `EpochDeferred` | -| `onEpochSkipped(...)` | `EpochSkipped` | -| `onUidEmissionCalculated(...)` | One callback per UID emission entry from `IncentiveAlphaEmittedToMiners` | -| `onVotingPowerTrackingEnabled(...)` | `VotingPowerTrackingEnabled` | -| `onVotingPowerTrackingDisableScheduled(...)` | `VotingPowerTrackingDisableScheduled` | -| `onVotingPowerTrackingDisabled(...)` | `VotingPowerTrackingDisabled` | -| `onVotingPowerEmaAlphaChanged(...)` | `VotingPowerEmaAlphaSet` | - -The current hook-origin callbacks are `onNetworkDissolutionCleanupCompleted`, -`onSubnetLeaseDividendDistributed`, `onEpochDeferred`, `onEpochSkipped`, -`onUidEmissionCalculated`, and `onVotingPowerTrackingDisabled`. - -### Per-UID emission calculation - -`IncentiveAlphaEmittedToMiners` contains an `emissions` array whose index is the -miner UID: `emissions[0]` is the Alpha emission for UID 0, `emissions[1]` is for -UID 1, and so on. The precompile does not pass this variable-length array to a -subscriber. It delivers one bounded callback for each `(uid, alpha)` entry: - -```solidity -function onUidEmissionCalculated( - uint64 sequence, - uint64 sourceBlock, - uint16 netuid, - uint16 uid, - uint16 uidCount, - uint256 alpha -) external; -``` - -For example, a source array of `[10, 20, 30]` produces callbacks for -`(uid=0, alpha=10)`, `(uid=1, alpha=20)`, and `(uid=2, alpha=30)`. All callbacks -from that source event share the same sequence and `uidCount`, allowing the -receiver to identify the complete set without accepting an unbounded argument. - -Subnet identity and symbol values must use bounded Solidity representations -chosen before the ABI is released. - -Every callback also carries the source block. Account IDs use `bytes32`, -netuids use `uint16`, and balances use 18-decimal `uint256` values. - -Subscription behavior is defined in -[Subscription-based event reporting](/docs/guides/evm/precompile-design#subscription-based-event-reporting). -The names and signatures are provisional and do not reserve selectors. diff --git a/docs/guides/evm/precompiles/weights-events.mdx b/docs/guides/evm/precompiles/weights-events.mdx deleted file mode 100644 index 6d1822d12c..0000000000 --- a/docs/guides/evm/precompiles/weights-events.mdx +++ /dev/null @@ -1,47 +0,0 @@ ---- -title: Weights events -description: Proposed subscription precompile for typed Subtensor weights, commit-reveal, and batch callbacks. ---- - -| Property | Value | -|---|---| -| Proposed implementation | `WeightsEventsPrecompile` | -| Proposed Solidity interface | `IWeightsEvents` | -| Callback receiver interface | `IWeightsEventsReceiver` | -| Address | Dedicated address not assigned | -| Status | Proposed | - -This precompile reports weight setting and each supported commit-reveal path -emitted by `SubtensorModule`, including reveals performed later by a runtime -hook. - -## Proposed callbacks - -| Receiver function | Subtensor source event | -|---|---| -| `onWeightsSet(...)` | `WeightsSet` | -| `onWeightsCommitted(...)` | `WeightsCommitted` | -| `onWeightsRevealed(...)` | `WeightsRevealed` | -| `onWeightBatchRevealItem(...)` | One hash from `WeightsBatchRevealed` | -| `onBatchWeightCompleted(...)` | One netuid from `BatchWeightsCompleted` | -| `onWeightBatchCompletedWithErrors(...)` | `BatchCompletedWithErrors` | -| `onWeightBatchItemFailed(...)` | `BatchWeightItemFailed` | -| `onTimelockedWeightsCommitted(...)` | `TimelockedWeightsCommitted` | -| `onTimelockedWeightsRevealed(...)` | `TimelockedWeightsRevealed` | -| `onCommitRevealPeriodsChanged(...)` | `CommitRevealPeriodsSet` | -| `onCommitRevealEnabledChanged(...)` | `CommitRevealEnabled` | -| `onCommitRevealVersionChanged(...)` | `CommitRevealVersionSet` | - -`onTimelockedWeightsRevealed` covers the current weights event emitted from a -runtime hook. - -Batch callbacks carry the source sequence, item index, and item count and -report one bounded item per invocation. Dispatch failures use a stable typed -error representation rather than SCALE-encoded `DispatchError`. - -Every callback also carries the source block. Hotkeys use `bytes32`, netuids -use `uint16`, and commitment hashes use `bytes32`. - -Subscription behavior is defined in -[Subscription-based event reporting](/docs/guides/evm/precompile-design#subscription-based-event-reporting). -The names and signatures are provisional and do not reserve selectors. From 32980cdbe4ec8b3ef404c6e5032773cf6e533614 Mon Sep 17 00:00:00 2001 From: UnarbosFour Date: Wed, 29 Jul 2026 17:36:10 -0400 Subject: [PATCH 4/8] Require root priviledged calls to not be implemented in precompiles in evm maintainer skill --- .agents/skills/emv-maintainer/SKILL.md | 35 ++++++++- .../references/abi-versioning.md | 16 +++++ .../references/coverage-and-testing.md | 29 ++++++-- docs/guides/evm/precompile-design.mdx | 71 ++++++++++++++++++- .../evm/precompiles/account-balance.mdx | 8 +-- docs/guides/evm/precompiles/alpha.mdx | 17 ++--- .../evm/precompiles/balance-transfer.mdx | 7 +- docs/guides/evm/precompiles/drand.mdx | 24 +++---- .../evm/precompiles/extrinsic-coverage.mdx | 62 +++++++++------- docs/guides/evm/precompiles/index.mdx | 14 ++-- docs/guides/evm/precompiles/leasing.mdx | 5 +- docs/guides/evm/precompiles/neuron.mdx | 13 ++-- docs/guides/evm/precompiles/registry.mdx | 23 +++--- .../evm/precompiles/runtime-configuration.mdx | 33 ++++----- docs/guides/evm/precompiles/scheduler.mdx | 36 ++++------ docs/guides/evm/precompiles/staking-v2.mdx | 13 +--- docs/guides/evm/precompiles/subnet.mdx | 33 ++------- docs/guides/evm/precompiles/timestamp.mdx | 9 ++- docs/guides/evm/precompiles/voting-power.mdx | 6 +- 19 files changed, 274 insertions(+), 180 deletions(-) diff --git a/.agents/skills/emv-maintainer/SKILL.md b/.agents/skills/emv-maintainer/SKILL.md index 931b3ac8e1..bbd88d169e 100644 --- a/.agents/skills/emv-maintainer/SKILL.md +++ b/.agents/skills/emv-maintainer/SKILL.md @@ -45,11 +45,33 @@ For each affected released function: ## Notes on coding precompiles -- Never allow direct writing of state maps or variables to precompile callers. - Keep every precompile path O(1) in CPU and memory. +- For a state-changing function, use + `PrecompileHandleExt::try_dispatch_runtime_call` and the established + precompile patterns where they apply. Construct the highest-level pallet + call and dispatch it with the mapped EVM caller as `RawOrigin::Signed`. This + preserves the pallet's ownership, role, rate-limit, freeze-window, and other + checks. Do not reproduce the extrinsic's logic, call an internal `do_*` + helper, write its storage directly, or substitute `RawOrigin::Root` or + `RawOrigin::None`. +- Expose a state-changing extrinsic only when that highest-level pallet call + accepts a non-Root signed origin. +- An extrinsic that accepts either a signed authority, such as a subnet owner, + or Root may expose its signed path. Do not expose an extrinsic that is + Root-only or `None`-only unless a separately approved authorization design is + added to the runtime. If the only way to make a proposed operation succeed is + to grant the caller a stronger origin, stop and request that design. +- Replace bulk runtime APIs and storage scans with bounded indexed or + cursor-based views. Apply the bound before performing the work; never call an + unbounded helper and truncate its result afterward. - Follow [ABI versioning](references/abi-versioning.md) for every released interface. - Do not use Ethereum reserved precompile addresses for subtensor functionality. +- Assign new Bittensor domain precompiles sequentially from the next unused + Bittensor address. The current proposal reserves `0x080f` through `0x0813` + for Scheduler, Drand, Timestamp, Runtime Configuration, and the Precompile + Registry, respectively. Add routing and tests that lock every implemented + address and selector before release. - Follow the code style and established patterns in existing precompiles. - Represent Substrate account IDs in EVM space as 32-byte public keys. - Multiply Subtensor balances by `10^9` to match EVM's 18-decimal convention, @@ -57,7 +79,8 @@ For each affected released function: ## Step 1 - Review current precompiles vs. subtensor functionality -- All extrinsics should be exposed to precompile callers for the following pallets: +- All extrinsics that accept a non-Root signed origin should be exposed to + precompile callers for the following pallets: - subtensor - admin-util - balances @@ -67,7 +90,13 @@ For each affected released function: - crowdloan - timestamp - swap -- All runtime API RPCs for the subtensor pallet should be exposed as a callable precompile function with similar interface +- Root-only, `None`-only, inherent, disabled, and compatibility no-op + extrinsics must be inventoried and explicitly classified as not callable + through typed EVM precompiles. +- All deterministic runtime API RPC results for the subtensor pallet should be + exposed through typed precompile views. Preserve a similar interface when it + is already bounded; redesign bulk results as bounded indexed or cursor-based + views when it is not. Use [Coverage and testing](references/coverage-and-testing.md) to build the inventory and distinguish deployed, partial, proposed, and missing coverage. diff --git a/.agents/skills/emv-maintainer/references/abi-versioning.md b/.agents/skills/emv-maintainer/references/abi-versioning.md index c4dafd1c0b..03f6493f27 100644 --- a/.agents/skills/emv-maintainer/references/abi-versioning.md +++ b/.agents/skills/emv-maintainer/references/abi-versioning.md @@ -77,6 +77,22 @@ Keep every released selector reserved permanently, including after hard deprecation. Route a hard-deprecated selector to its descriptive error. Never allow a different function to claim it. +Assign a genuinely new Bittensor domain the next unused sequential Bittensor +address. The current proposal reserves: + +| Address | Domain | +|---|---| +| `0x080f` | Scheduler | +| `0x0810` | Drand | +| `0x0811` | Timestamp | +| `0x0812` | Runtime Configuration | +| `0x0813` | Precompile Registry | + +A documented reservation prevents another domain from taking the address but +does not make the precompile callable. When implementing a reserved address, +add exact-value tests for its index and full address, routing tests through the +precompile set, and selector tests for every function at that address. + Before adding a function, calculate its selector from the canonical Solidity signature and compare it with the complete selector set at the address. Reject collisions even when the Solidity names differ. diff --git a/.agents/skills/emv-maintainer/references/coverage-and-testing.md b/.agents/skills/emv-maintainer/references/coverage-and-testing.md index 902752a872..729f4ad07e 100644 --- a/.agents/skills/emv-maintainer/references/coverage-and-testing.md +++ b/.agents/skills/emv-maintainer/references/coverage-and-testing.md @@ -57,8 +57,11 @@ precompile per storage item. ## Cover extrinsics -Expose each authorized extrinsic through a typed state-changing function unless -an explicit scope decision excludes it. +Expose each extrinsic that accepts a non-Root signed origin through a typed +state-changing function unless an explicit scope decision excludes it. Calls +that accept either Root or a signed authority may expose only the signed path. +Classify Root-only and `None`-only calls as not EVM-callable; the existence of a +runtime extrinsic does not authorize a precompile to manufacture its origin. Preserve: @@ -71,9 +74,10 @@ Preserve: - runtime errors and EVM failure behavior; and - gas and weight charging, including post-dispatch adjustment. -Use `PrecompileHandleExt::try_dispatch_runtime_call` and established -precompile patterns where they apply. Do not bypass guards or create a direct -state-writing path that the pallet does not authorize. +Do not count a selector as coverage merely because it is routed. Test that a +mapped caller with the required signed authority can succeed and that a caller +without that authority fails without changing state. A selector that always +fails `BadOrigin` is not meaningful coverage. When an extrinsic changes, compare the old and new behavior rather than only their Rust signatures. Follow [ABI versioning](abi-versioning.md) when an @@ -116,6 +120,18 @@ precompile may call the same underlying helpers rather than reproduce an RPC transport detail. Preserve pagination, bounds, defaults, and absence semantics that affect callers. +Do not copy a bulk runtime API into Solidity when its work or result can grow +with chain state. Prefer one of these bounded shapes: + +- an indexed item view plus a bounded count; +- a cursor and caller-supplied limit capped by a fixed runtime maximum; or +- a fixed-size key batch whose maximum is part of the interface contract. + +Return the next cursor or an explicit completion indicator when callers need to +walk the complete collection. Charge for the maximum work actually permitted. +Apply limits before reading or constructing the collection; calling an +unbounded runtime helper and slicing its returned vector is still unbounded. + Do not expose node-only behavior that cannot execute deterministically in the runtime. When a public RPC composes runtime state, implement the deterministic runtime-side result and document any transport-only behavior that has no EVM @@ -182,6 +198,9 @@ Verify: 9. `Precompiles::execute()` recognizes the address and routes it through the intended availability control and compatible implementation. 10. Unknown-address and unknown-selector behavior remains unchanged. +11. Every new Bittensor domain uses the next reserved sequential address, and + address constants, `used_addresses()`, routing, documentation, Solidity + interfaces, and address-locking tests agree. Do not hand-wave generated-file churn. Inspect each changed ABI entry and remove unrelated regeneration changes. diff --git a/docs/guides/evm/precompile-design.mdx b/docs/guides/evm/precompile-design.mdx index 66f59a8439..c0e2e72ff5 100644 --- a/docs/guides/evm/precompile-design.mdx +++ b/docs/guides/evm/precompile-design.mdx @@ -29,8 +29,9 @@ The precompile layer is designed around five goals: original behavior whenever that behavior can still be represented safely. 4. **Status is discoverable.** Solidity interfaces and a registry should tell developers when a function is deprecated, replaced, or temporarily disabled. -5. **The authorized Substrate API has full parity.** Every storage item and - extrinsic in scope has a typed precompile equivalent. +5. **The signed, deterministic Substrate API has typed parity.** Storage and + runtime API results have bounded typed views, and extrinsics that accept a + non-Root signed origin have typed operations. ## Fixed addresses and function selectors @@ -97,6 +98,21 @@ Creating a new domain address may still be appropriate when the functionality is genuinely a different precompile, but it should not be the default versioning mechanism. +New Bittensor domain addresses are assigned sequentially from the next unused +Bittensor address. The currently proposed domains reserve: + +| Address | Domain | +|---|---| +| `0x000000000000000000000000000000000000080f` | Scheduler | +| `0x0000000000000000000000000000000000000810` | Drand | +| `0x0000000000000000000000000000000000000811` | Timestamp | +| `0x0000000000000000000000000000000000000812` | Runtime configuration | +| `0x0000000000000000000000000000000000000813` | Precompile registry | + +An address reservation does not make a proposed precompile callable. When an +implementation is added, routing and tests must lock the address and every +implemented selector before release. + ### Keep old semantics when possible Suppose `getStake` originally returned total stake, while a later runtime stores @@ -124,12 +140,56 @@ the precompile implementation adapts while the Solidity interface remains stable. A resulting Rust compilation failure provides a safety net that raw storage queries do not. +### Bound collection views + +Runtime APIs and storage collections that grow with chain state must not be +copied into a single Solidity function returning an unbounded array. Expose an +indexed item with a bounded count, or use a cursor and a caller-supplied limit +that is capped by a fixed runtime maximum. A fixed-size batch of explicit keys +is also suitable when callers already know which records they need. + +The bound must apply before storage is scanned or results are constructed. +Calling an unbounded runtime helper and truncating its result afterward does +not make the precompile bounded. Paginated views should return a next cursor or +completion indicator and define stable ordering, missing-item behavior, and +the maximum page size. + +### Preserve runtime authorization + +A state-changing precompile dispatches the highest-level pallet extrinsic with +the mapped EVM caller as a signed origin. The pallet then enforces the same +ownership, role, rate-limit, freeze-window, and validation checks that apply to +an ordinary signed Substrate transaction. + +An extrinsic that permits either Root or a non-Root signer, such as a subnet +owner, may expose its signed path. Root-only and `None`-only extrinsics are not +exposed through typed EVM functions. A precompile must never substitute Root, +invoke an internal state-changing helper, or reproduce the extrinsic logic to +bypass the top-level checks. + +### Read-only infrastructure views + +Read-only precompiles let contracts inspect deterministic consensus state +without receiving any authority to change it: + +- Scheduler views expose bounded task metadata so contracts can verify whether + and when runtime work is scheduled. +- Drand views expose beacon configuration, stored pulses, and round ranges for + contract logic that depends on the runtime's randomness state. +- Timestamp views replace raw reads of timestamp storage; `getTimestamp` + corresponds to the same underlying time represented by `block.timestamp`. +- Lifecycle views let contracts and tooling discover whether a selector is + deprecated, replaced, or currently unavailable. + +These views replace raw storage decoding or off-chain RPC composition. They do +not execute privileged extrinsics and do not provide a path to Root. + ### Phasing out raw storage reads `StorageQueryPrecompile` at `0x…0807` exposes raw Substrate storage and is inherently brittle. The intended migration is: -1. Add a typed view for every storage item in the currently authorized pallets: +1. Add a bounded typed view for every storage item in the currently authorized pallets: SubtensorModule, Balances, Proxy, Scheduler, Drand, Crowdloan, Sudo, Multisig, Timestamp, and Swap. 2. Soft-deprecate raw storage access after that typed coverage exists. @@ -371,6 +431,11 @@ Every precompile change should verify: agree; - lifecycle registry metadata and NatSpec annotations agree; - disable and re-enable behavior is covered for the affected precompile; +- state-changing functions dispatch the highest-level extrinsic as the mapped + signed caller and do not bypass its authorization checks; +- bulk views are bounded before they read or construct results; +- new domain addresses follow the documented sequential reservation and are + locked by routing tests; - no selector is reused. Typed views provide a compile-time safety advantage: when runtime types or diff --git a/docs/guides/evm/precompiles/account-balance.mdx b/docs/guides/evm/precompiles/account-balance.mdx index 63c6edbe4d..ac400bc3c1 100644 --- a/docs/guides/evm/precompiles/account-balance.mdx +++ b/docs/guides/evm/precompiles/account-balance.mdx @@ -21,14 +21,12 @@ description: Reference for the deployed BalancePrecompile. | Proposed function | Source extrinsic | |---|---| | `burnBalance` | `Balances.burn` | -| `forceUnreserve` | `Balances.force_unreserve` | | `upgradeAccounts` | `Balances.upgrade_accounts` | -| `forceSetBalance` | `Balances.force_set_balance` | -| `forceAdjustTotalIssuance` | `Balances.force_adjust_total_issuance` | -| `setTotalIssuance` | `AdminUtils.sudo_set_total_issuance` | `upgradeAccounts` must have an explicit fixed input bound. The implementation -must preserve all runtime authorization and issuance invariants. +must dispatch the highest-level Balances call as the mapped signer and preserve +all runtime authorization and issuance invariants. Force operations require +Root and are not exposed. Proposed names and signatures do not reserve selectors. diff --git a/docs/guides/evm/precompiles/alpha.mdx b/docs/guides/evm/precompiles/alpha.mdx index 4cde04cdb5..cb69de1c74 100644 --- a/docs/guides/evm/precompiles/alpha.mdx +++ b/docs/guides/evm/precompiles/alpha.mdx @@ -40,24 +40,15 @@ getCKBurn() | Proposed function | Source extrinsic | |---|---| -| `setSwapFeeRate` | `Swap.set_fee_rate` | | `setRecycleOrBurn` | `AdminUtils.sudo_set_recycle_or_burn` | -| `setSubnetMovingAlpha` | `AdminUtils.sudo_set_subnet_moving_alpha` | -| `setEmaPriceHalvingPeriod` | `AdminUtils.sudo_set_ema_price_halving_period` | -| `setCkBurn` | `AdminUtils.sudo_set_ck_burn` | -| `setTaoFlowCutoff` | `AdminUtils.sudo_set_tao_flow_cutoff` | -| `setTaoFlowNormalizationExponent` | `AdminUtils.sudo_set_tao_flow_normalization_exponent` | -| `setTaoFlowSmoothingFactor` | `AdminUtils.sudo_set_tao_flow_smoothing_factor` | -| `setNetTaoFlowEnabled` | `AdminUtils.sudo_set_net_tao_flow_enabled` | | `setBurnHalfLife` | `AdminUtils.sudo_set_burn_half_life` | | `setBurnIncreaseMultiplier` | `AdminUtils.sudo_set_burn_increase_mult` | -| `setSubnetEmissionEnabled` | `AdminUtils.sudo_set_subnet_emission_enabled` | -| `setEmissionBarQuantile` | `AdminUtils.sudo_set_emission_bar_quantile` | -| `setEmissionGateExponent` | `AdminUtils.sudo_set_emission_gate_exponent` | The five deprecated `Swap` liquidity extrinsics are intentionally not proposed; -they always return the pallet's `Deprecated` error. Runtime authorization -remains in force for every administrative operation. +they always return the pallet's `Deprecated` error. `Swap.set_fee_rate` and the +remaining Alpha-related AdminUtils calls require Root and are not exposed. +The three listed AdminUtils calls accept a signed subnet owner; only that +signed path is exposed and runtime authorization remains in force. Proposed names and signatures do not reserve selectors. diff --git a/docs/guides/evm/precompiles/balance-transfer.mdx b/docs/guides/evm/precompiles/balance-transfer.mdx index 0631247658..46d79d9f7b 100644 --- a/docs/guides/evm/precompiles/balance-transfer.mdx +++ b/docs/guides/evm/precompiles/balance-transfer.mdx @@ -23,16 +23,15 @@ public key. | Proposed function | Source extrinsic | |---|---| -| `faucet` | `SubtensorModule.faucet` | | `transferKeepAlive` | `Balances.transfer_keep_alive` | | `transferAll` | `Balances.transfer_all` | -| `forceTransfer` | `Balances.force_transfer` | The existing `transfer(bytes32)` semantically covers `Balances.transfer_allow_death` by taking the amount from attached EVM value. The proposed functions use explicit typed arguments where attached value does -not express the complete source operation. Runtime authorization remains in -force for `forceTransfer`. +not express the complete source operation. They dispatch the highest-level +Balances call as the mapped signer. `force_transfer` requires Root and the +feature-gated development faucet is not part of the production interface. Proposed names and signatures do not reserve selectors. diff --git a/docs/guides/evm/precompiles/drand.mdx b/docs/guides/evm/precompiles/drand.mdx index 08b5b195a5..970621c48b 100644 --- a/docs/guides/evm/precompiles/drand.mdx +++ b/docs/guides/evm/precompiles/drand.mdx @@ -7,11 +7,13 @@ description: Proposed typed EVM interface for the Drand pallet. |---|---| | Proposed implementation | `DrandPrecompile` | | Proposed Solidity interface | `IDrand` | -| Address | Not assigned | -| Status | Proposed | +| Reserved address | `0x0000000000000000000000000000000000000810` | +| Status | Proposed; not yet callable | This precompile would expose typed beacon configuration and pulse data instead of requiring callers to construct Drand storage keys and decode SCALE values. +Contracts can use the runtime's stored randomness state deterministically +without receiving permission to configure the beacon or submit pulses. ## Planned views @@ -23,16 +25,12 @@ of requiring callers to construct Drand storage keys and decode SCALE values. | `getNextUnsignedAt()` | `Drand.NextUnsignedAt` | | `hasMigrationRun(bytes key)` | `Drand.HasMigrationRun` | -## Planned operations +## State-changing operations -| Proposed function | Source extrinsic | -|---|---| -| `setBeaconConfig` | `Drand.set_beacon_config` | -| `setOldestStoredRound` | `Drand.set_oldest_stored_round` | - -`Drand.write_pulse` is not exposed. It is an unsigned offchain-worker -submission that requires `None` origin, which an EVM caller cannot satisfy -without changing the security model. +`Drand.set_beacon_config` and `Drand.set_oldest_stored_round` require Root, and +`Drand.write_pulse` requires `None` origin as an unsigned offchain-worker +submission. None is exposed as a typed EVM operation because doing so would +bypass the pallet's top-level origin checks. -The runtime's existing authorization checks remain in force. Names and -signatures on this page are provisional and do not reserve selectors. +The address is reserved for this domain. Names and signatures on this page are +provisional and do not reserve selectors. diff --git a/docs/guides/evm/precompiles/extrinsic-coverage.mdx b/docs/guides/evm/precompiles/extrinsic-coverage.mdx index 4e9b58510c..1688bf4d57 100644 --- a/docs/guides/evm/precompiles/extrinsic-coverage.mdx +++ b/docs/guides/evm/precompiles/extrinsic-coverage.mdx @@ -14,62 +14,74 @@ interface or an explicit proposed typed replacement. ## Coverage summary -| Pallet | Runtime extrinsics | Typed today | Proposed additions | Not exposed | +| Pallet | Runtime extrinsics | Typed today | Proposed signed additions | Not exposed | |---|---:|---:|---:|---:| -| `SubtensorModule` | 82 | 24 | 56 | 2 | -| `AdminUtils` | 86 | 24 | 62 | 0 | -| `Balances` | 9 | 1 | 8 | 0 | +| `SubtensorModule` | 82 | 24 | 44 | 14 | +| `AdminUtils` | 86 | 24 | 16 | 46 | +| `Balances` | 9 | 1 | 4 | 4 | | `Proxy` | 12 | 7 | 5 | 0 | -| `Scheduler` | 10 | 0 | 10 | 0 | -| `Drand` | 3 | 0 | 2 | 1 | +| `Scheduler` | 10 | 0 | 0 | 10 | +| `Drand` | 3 | 0 | 0 | 3 | | `Crowdloan` | 10 | 9 | 1 | 0 | | `Timestamp` | 1 | 0 | 0 | 1 | -| `Swap` | 6 | 0 | 1 | 5 | -| **Total** | **219** | **65** | **145** | **9** | +| `Swap` | 6 | 0 | 0 | 6 | +| **Total** | **219** | **65** | **70** | **84** | `Typed today` counts semantic coverage, not only direct dispatch to the same Rust call. For example, `registerNetwork(bytes32)` covers basic subnet registration by dispatching `register_network_with_identity` with empty identity fields. -## Classification of proposed additions +`Proposed signed additions` includes extrinsics whose highest-level pallet call +accepts a non-Root signed origin. If a call also accepts Root, only its signed +path is exposed: the mapped EVM caller is dispatched as `Signed`, and the +pallet performs its normal authorization checks. + +## Classification of proposed signed additions Each missing operation is listed on the page of its target precompile: | Target precompile | Missing extrinsics assigned | |---|---:| -| [Subnet](/docs/guides/evm/precompiles/subnet) | 37 | -| [Staking V2](/docs/guides/evm/precompiles/staking-v2) | 29 | -| [Neuron](/docs/guides/evm/precompiles/neuron) | 27 | -| [Alpha](/docs/guides/evm/precompiles/alpha) | 14 | -| [Scheduler](/docs/guides/evm/precompiles/scheduler) | 10 | -| [Account balance](/docs/guides/evm/precompiles/account-balance) | 6 | +| [Subnet](/docs/guides/evm/precompiles/subnet) | 13 | +| [Staking V2](/docs/guides/evm/precompiles/staking-v2) | 20 | +| [Neuron](/docs/guides/evm/precompiles/neuron) | 21 | +| [Alpha](/docs/guides/evm/precompiles/alpha) | 3 | +| [Account balance](/docs/guides/evm/precompiles/account-balance) | 2 | | [Proxy](/docs/guides/evm/precompiles/proxy) | 5 | -| [Balance transfer](/docs/guides/evm/precompiles/balance-transfer) | 4 | -| [Runtime configuration](/docs/guides/evm/precompiles/runtime-configuration) | 4 | -| [Voting power](/docs/guides/evm/precompiles/voting-power) | 3 | -| [Drand](/docs/guides/evm/precompiles/drand) | 2 | -| [Leasing](/docs/guides/evm/precompiles/leasing) | 2 | +| [Balance transfer](/docs/guides/evm/precompiles/balance-transfer) | 2 | +| [Voting power](/docs/guides/evm/precompiles/voting-power) | 2 | +| [Leasing](/docs/guides/evm/precompiles/leasing) | 1 | | [Crowdloan](/docs/guides/evm/precompiles/crowdloan) | 1 | -| [Precompile registry](/docs/guides/evm/precompiles/registry) | 1 | Proposed function names do not reserve selectors. Their final parameter types, -bounds, authorization model, and return values must be specified before -implementation. +bounds, and return values must be specified before implementation. Each +implementation must dispatch the highest-level pallet extrinsic as the mapped +signed caller rather than reproducing its logic. ## Extrinsics not exposed as EVM calls | Pallet extrinsic | Reason | |---|---| +| Root-only `SubtensorModule` extrinsics | `dissolve_network`, `root_dissolve_network`, `swap_coldkey`, `sudo_set_tx_childkey_take_rate_limit`, `sudo_set_min_childkey_take`, `sudo_set_max_childkey_take`, `set_pending_childkey_cooldown`, `reset_coldkey_swap`, `sudo_set_num_root_claims`, and `sudo_set_voting_power_ema_alpha` require Root. | +| `SubtensorModule.schedule_swap_coldkey` | Deprecated compatibility call that always returns `Deprecated`. | +| `SubtensorModule.faucet` | Build-feature-only development call; it is not part of the production runtime interface. | | `SubtensorModule.set_tempo` | Retained call-index compatibility entry point that succeeds without changing state. The real setting is `AdminUtils.sudo_set_tempo`, proposed as `SubnetPrecompile.setTempo`. | | `SubtensorModule.set_activity_cutoff_factor` | Retained call-index compatibility entry point that succeeds without changing state. The active AdminUtils operation is already covered by `SubnetPrecompile.setActivityCutoffFactor`. | +| Root-only `AdminUtils` extrinsics | Root-only administration is not delegated to EVM callers. Calls that also accept a signed subnet owner remain in the proposed signed additions on the domain pages. | +| `AdminUtils.sudo_set_total_issuance` | Deprecated call that always returns `Deprecated`. | +| Root-only `Balances` extrinsics | `force_unreserve`, `force_transfer`, `force_set_balance`, and `force_adjust_total_issuance` require Root. | +| All `Scheduler` extrinsics | `Scheduler.ScheduleOrigin` is configured as Root in the runtime. | | `Drand.write_pulse` | Unsigned offchain-worker submission requiring `None` origin. An EVM caller cannot satisfy that origin without changing its security model. | +| Drand configuration extrinsics | `set_beacon_config` and `set_oldest_stored_round` require Root. | | `Timestamp.set` | Block-production inherent requiring `None` origin. Contracts already receive the same time through `block.timestamp`. | +| `Swap.set_fee_rate` | Requires Root. | | `Swap.add_liquidity` | Permanently disabled pallet call that always returns `Deprecated`. | | `Swap.remove_liquidity` | Permanently disabled pallet call that always returns `Deprecated`. | | `Swap.modify_position` | Permanently disabled pallet call that always returns `Deprecated`. | | `Swap.toggle_user_liquidity` | Permanently disabled pallet call that always returns `Deprecated`. | | `Swap.disable_lp` | Permanently disabled pallet call that always returns `Deprecated`. | -These exclusions preserve the existing runtime origin and lifecycle semantics; -they are not missing callable functionality. +These exclusions preserve the existing runtime origin and lifecycle semantics. +A typed precompile must not manufacture Root or `None`, call an internal helper, +or write storage directly to make one of these operations callable. diff --git a/docs/guides/evm/precompiles/index.mdx b/docs/guides/evm/precompiles/index.mdx index 84da100486..902b925be8 100644 --- a/docs/guides/evm/precompiles/index.mdx +++ b/docs/guides/evm/precompiles/index.mdx @@ -6,7 +6,9 @@ description: Addresses, implementations, and reference pages for Bittensor EVM p Bittensor precompiles are fixed-address contracts implemented by the Subtensor runtime. `Deployed` means that the address is registered in the current runtime; it does not imply complete coverage of the underlying runtime domain. -`Proposed` precompiles have no assigned address or released selectors. +`Proposed` precompiles are not callable. Their documented addresses are +reserved for those domains, while their function selectors remain provisional +until the interfaces are implemented and released. The [extrinsic coverage audit](/docs/guides/evm/precompiles/extrinsic-coverage) tracks every runtime extrinsic in scope and identifies its deployed, proposed, @@ -49,11 +51,11 @@ or intentionally non-callable EVM treatment. | [`AddressMappingPrecompile`](/docs/guides/evm/precompiles/address-mapping) | `IAddressMapping` |
Deployed | | [`VotingPowerPrecompile`](/docs/guides/evm/precompiles/voting-power) | `IVotingPower` |
Deployed | | [`BalancePrecompile`](/docs/guides/evm/precompiles/account-balance) | `IBalance` |
Deployed | -| [`SchedulerPrecompile`](/docs/guides/evm/precompiles/scheduler) | `IScheduler` | Address not assigned
Proposed | -| [`DrandPrecompile`](/docs/guides/evm/precompiles/drand) | `IDrand` | Address not assigned
Proposed | -| [`TimestampPrecompile`](/docs/guides/evm/precompiles/timestamp) | `ITimestamp` | Address not assigned
Proposed | -| [`RuntimeConfigurationPrecompile`](/docs/guides/evm/precompiles/runtime-configuration) | `IRuntimeConfiguration` | Address not assigned
Proposed | -| [`PrecompileRegistry`](/docs/guides/evm/precompiles/registry) | `IPrecompileRegistry` | Address not assigned
Proposed | +| [`SchedulerPrecompile`](/docs/guides/evm/precompiles/scheduler) | `IScheduler` |
Proposed · address reserved | +| [`DrandPrecompile`](/docs/guides/evm/precompiles/drand) | `IDrand` |
Proposed · address reserved | +| [`TimestampPrecompile`](/docs/guides/evm/precompiles/timestamp) | `ITimestamp` |
Proposed · address reserved | +| [`RuntimeConfigurationPrecompile`](/docs/guides/evm/precompiles/runtime-configuration) | `IRuntimeConfiguration` |
Proposed · address reserved | +| [`PrecompileRegistry`](/docs/guides/evm/precompiles/registry) | `IPrecompileRegistry` |
Proposed · address reserved | Projects that need proactive event delivery should use [project-scoped event relays](/docs/guides/evm/precompile-design#project-scoped-event-relays) diff --git a/docs/guides/evm/precompiles/leasing.mdx b/docs/guides/evm/precompiles/leasing.mdx index 32f20af948..b211bc2a47 100644 --- a/docs/guides/evm/precompiles/leasing.mdx +++ b/docs/guides/evm/precompiles/leasing.mdx @@ -32,8 +32,9 @@ Both operations are `payable`. | Proposed function | Source extrinsic | |---|---| | `startCall` | `SubtensorModule.start_call` | -| `setStartCallDelay` | `AdminUtils.sudo_set_start_call_delay` | -Proposed names and signatures do not reserve selectors. +`startCall` accepts a signed subnet owner. The Root-only start-call delay +configuration is not exposed. The proposed name and signature do not reserve a +selector. Source: [`leasing.sol`](https://github.com/RaoFoundation/subtensor/blob/main/precompiles/src/solidity/leasing.sol) diff --git a/docs/guides/evm/precompiles/neuron.mdx b/docs/guides/evm/precompiles/neuron.mdx index 6cbe2643e4..47164e83aa 100644 --- a/docs/guides/evm/precompiles/neuron.mdx +++ b/docs/guides/evm/precompiles/neuron.mdx @@ -51,22 +51,19 @@ use typed Drand data rather than SCALE-encoded payloads. | `rootRegister` | `SubtensorModule.root_register` | | `swapHotkey` | `SubtensorModule.swap_hotkey` | | `swapHotkeyV2` | `SubtensorModule.swap_hotkey_v2` | -| `swapColdkey` | `SubtensorModule.swap_coldkey` | -| `scheduleColdkeySwap` | `SubtensorModule.schedule_swap_coldkey` | | `setChildren` | `SubtensorModule.set_children` | | `setIdentity` | `SubtensorModule.set_identity` | | `tryAssociateHotkey` | `SubtensorModule.try_associate_hotkey` | | `associateEvmKey` | `SubtensorModule.associate_evm_key` | -| `setPendingChildkeyCooldown` | `SubtensorModule.set_pending_childkey_cooldown` | | `announceColdkeySwap` | `SubtensorModule.announce_coldkey_swap` | | `executeAnnouncedColdkeySwap` | `SubtensorModule.swap_coldkey_announced` | | `disputeColdkeySwap` | `SubtensorModule.dispute_coldkey_swap` | -| `resetColdkeySwap` | `SubtensorModule.reset_coldkey_swap` | | `clearColdkeySwapAnnouncement` | `SubtensorModule.clear_coldkey_swap_announcement` | -| `setColdkeySwapAnnouncementDelay` | `AdminUtils.sudo_set_coldkey_swap_announcement_delay` | -| `setColdkeySwapReannouncementDelay` | `AdminUtils.sudo_set_coldkey_swap_reannouncement_delay` | -Runtime authorization remains in force. Proposed names and signatures do not -reserve selectors. +Every proposed operation accepts a non-Root signed origin. The precompile must +dispatch the highest-level extrinsic as the mapped caller so runtime +authorization remains in force. Root-only and deprecated compatibility calls +are classified in the [coverage audit](./extrinsic-coverage). Proposed names +and signatures do not reserve selectors. Source: [`neuron.sol`](https://github.com/RaoFoundation/subtensor/blob/main/precompiles/src/solidity/neuron.sol) diff --git a/docs/guides/evm/precompiles/registry.mdx b/docs/guides/evm/precompiles/registry.mdx index 721c930786..df0a4782a2 100644 --- a/docs/guides/evm/precompiles/registry.mdx +++ b/docs/guides/evm/precompiles/registry.mdx @@ -7,11 +7,13 @@ description: Proposed registry for precompile lifecycle and availability. |---|---| | Proposed implementation | `PrecompileRegistry` | | Proposed Solidity interface | `IPrecompileRegistry` | -| Address | Not assigned | -| Status | Proposed | +| Reserved address | `0x0000000000000000000000000000000000000813` | +| Status | Proposed; not yet callable | The registry provides function-level lifecycle metadata and the current -operational availability of the containing precompile. +operational availability of the containing precompile. Contracts, deployment +tools, and frontends can inspect whether a selector is deprecated, has a +replacement, or is currently unavailable without attempting the affected call. ## Proposed interface @@ -32,16 +34,11 @@ interface IPrecompileRegistry { } ``` -## Proposed operation - -| Proposed function | Source extrinsic | -|---|---| -| `setPrecompileEnabled` | `AdminUtils.sudo_toggle_evm_precompile` | - -This operation changes reversible availability; it does not change or erase a -function's deprecation lifecycle. Runtime authorization remains in force. +`AdminUtils.sudo_toggle_evm_precompile` is Root-only and is not exposed by this +precompile. The registry reports availability but does not grant callers +permission to change it. The lifecycle model is described in [Precompile design and lifecycle](/docs/guides/evm/precompile-design#discovering-status). -The address and selector are not reserved until the interface is implemented -and released. +The address is reserved for this domain. The proposed selector remains +provisional until the interface is implemented and released. diff --git a/docs/guides/evm/precompiles/runtime-configuration.mdx b/docs/guides/evm/precompiles/runtime-configuration.mdx index 7c8ecff2c4..1c5f6d688e 100644 --- a/docs/guides/evm/precompiles/runtime-configuration.mdx +++ b/docs/guides/evm/precompiles/runtime-configuration.mdx @@ -7,24 +7,25 @@ description: Proposed typed EVM interface for global runtime configuration opera |---|---| | Proposed implementation | `RuntimeConfigurationPrecompile` | | Proposed Solidity interface | `IRuntimeConfiguration` | -| Address | Not assigned | -| Status | Proposed | +| Reserved address | `0x0000000000000000000000000000000000000812` | +| Status | Proposed; not yet callable | -This precompile groups the small set of global AdminUtils operations that do -not belong to a subnet, staking, Alpha, account-balance, or precompile-lifecycle -domain. +This domain is reserved for bounded typed views of global runtime +configuration that do not belong to subnet, staking, Alpha, account-balance, +or precompile-lifecycle domains. -## Planned operations +## State-changing operations -| Proposed function | Source extrinsic | -|---|---| -| `swapAuthorities` | `AdminUtils.swap_authorities` | -| `setTransactionRateLimit` | `AdminUtils.sudo_set_tx_rate_limit` | -| `setEvmChainId` | `AdminUtils.sudo_set_evm_chain_id` | -| `scheduleGrandpaChange` | `AdminUtils.schedule_grandpa_change` | +The currently identified global configuration extrinsics are Root-only: + +```text +AdminUtils.swap_authorities +AdminUtils.sudo_set_tx_rate_limit +AdminUtils.sudo_set_evm_chain_id +AdminUtils.schedule_grandpa_change +``` -The runtime's authorization checks remain in force. The implementation must -define a typed, bounded authority representation and must not expose -SCALE-encoded runtime values. +They are not proposed as typed EVM operations. A future view must return a +typed, bounded representation and must not expose SCALE-encoded runtime values. -Names and signatures on this page are provisional and do not reserve selectors. +The address is reserved for this domain. No function selector is reserved. diff --git a/docs/guides/evm/precompiles/scheduler.mdx b/docs/guides/evm/precompiles/scheduler.mdx index 94f1d8545d..3e61d9646f 100644 --- a/docs/guides/evm/precompiles/scheduler.mdx +++ b/docs/guides/evm/precompiles/scheduler.mdx @@ -7,11 +7,13 @@ description: Proposed typed EVM interface for the Scheduler pallet. |---|---| | Proposed implementation | `SchedulerPrecompile` | | Proposed Solidity interface | `IScheduler` | -| Address | Not assigned | -| Status | Proposed | +| Reserved address | `0x000000000000000000000000000000000000080f` | +| Status | Proposed; not yet callable | -This precompile would replace raw reads of Scheduler storage and expose the -Scheduler extrinsics through a stable EVM interface. +This precompile would replace raw reads of Scheduler storage with a stable EVM +interface. It lets contracts inspect whether and when runtime work is +scheduled without decoding Scheduler storage or acquiring permission to modify +the schedule. ## Planned views @@ -26,23 +28,11 @@ Scheduler extrinsics through a stable EVM interface. Returning one agenda entry at a time keeps execution bounded and avoids an unbounded array result. -## Planned operations +## State-changing operations -| Proposed function | Source extrinsic | -|---|---| -| `schedule` | `Scheduler.schedule` | -| `cancel` | `Scheduler.cancel` | -| `scheduleNamed` | `Scheduler.schedule_named` | -| `cancelNamed` | `Scheduler.cancel_named` | -| `scheduleAfter` | `Scheduler.schedule_after` | -| `scheduleNamedAfter` | `Scheduler.schedule_named_after` | -| `setRetry` | `Scheduler.set_retry` | -| `setRetryNamed` | `Scheduler.set_retry_named` | -| `cancelRetry` | `Scheduler.cancel_retry` | -| `cancelRetryNamed` | `Scheduler.cancel_retry_named` | - -Scheduled payloads must use a versioned, stable EVM call description. They must -not expose SCALE-encoded `RuntimeCall`, whose encoding can change after a -runtime upgrade. - -Names and signatures on this page are provisional and do not reserve selectors. +The runtime configures `Scheduler.ScheduleOrigin` as Root. Scheduler extrinsics +therefore have no typed EVM operation: a precompile must not manufacture Root +or bypass the top-level Scheduler authorization check. + +The address is reserved for this domain. Names and signatures on this page are +provisional and do not reserve selectors. diff --git a/docs/guides/evm/precompiles/staking-v2.mdx b/docs/guides/evm/precompiles/staking-v2.mdx index 334e76640d..8db426ac92 100644 --- a/docs/guides/evm/precompiles/staking-v2.mdx +++ b/docs/guides/evm/precompiles/staking-v2.mdx @@ -83,9 +83,6 @@ encoding of the allowance mutations. | `decreaseTake` | `SubtensorModule.decrease_take` | | `increaseTake` | `SubtensorModule.increase_take` | | `setChildkeyTake` | `SubtensorModule.set_childkey_take` | -| `setTxChildkeyTakeRateLimit` | `SubtensorModule.sudo_set_tx_childkey_take_rate_limit` | -| `setMinChildkeyTake` | `SubtensorModule.sudo_set_min_childkey_take` | -| `setMaxChildkeyTake` | `SubtensorModule.sudo_set_max_childkey_take` | | `unstakeAll` | `SubtensorModule.unstake_all` | | `unstakeAllAlpha` | `SubtensorModule.unstake_all_alpha` | | `swapStake` | `SubtensorModule.swap_stake` | @@ -94,7 +91,6 @@ encoding of the allowance mutations. | `setColdkeyAutoStakeHotkey` | `SubtensorModule.set_coldkey_auto_stake_hotkey` | | `claimRoot` | `SubtensorModule.claim_root` | | `setRootClaimType` | `SubtensorModule.set_root_claim_type` | -| `setNumRootClaims` | `SubtensorModule.sudo_set_num_root_claims` | | `setRootClaimThreshold` | `SubtensorModule.sudo_set_root_claim_threshold` | | `addStakeBurn` | `SubtensorModule.add_stake_burn` | | `setAutoParentDelegationEnabled` | `SubtensorModule.set_auto_parent_delegation_enabled` | @@ -110,16 +106,13 @@ encoding of the allowance mutations. | Proposed function | Source extrinsic | |---|---| -| `setDefaultTake` | `AdminUtils.sudo_set_default_take` | -| `setStakeThreshold` | `AdminUtils.sudo_set_stake_threshold` | -| `setNominatorMinRequiredStake` | `AdminUtils.sudo_set_nominator_min_required_stake` | -| `setDelegateTakeRateLimit` | `AdminUtils.sudo_set_tx_delegate_take_rate_limit` | -| `setMinDelegateTake` | `AdminUtils.sudo_set_min_delegate_take` | | `setMinChildkeyTakePerSubnet` | `AdminUtils.sudo_set_min_childkey_take_per_subnet` | | `setCollateralLockShare` | `AdminUtils.sudo_set_collateral_lock_share` | | `setCollateralDrainRatio` | `AdminUtils.sudo_set_collateral_drain_ratio` | -Runtime authorization remains in force. Proposed names and signatures do not +The listed owner-or-Root calls expose only their signed subnet-owner path. +Every operation dispatches the highest-level extrinsic as the mapped caller so +runtime authorization remains in force. Proposed names and signatures do not reserve selectors. Source: [`stakingV2.sol`](https://github.com/RaoFoundation/subtensor/blob/main/precompiles/src/solidity/stakingV2.sol) diff --git a/docs/guides/evm/precompiles/subnet.mdx b/docs/guides/evm/precompiles/subnet.mdx index 5817cc54ba..95b3be9881 100644 --- a/docs/guides/evm/precompiles/subnet.mdx +++ b/docs/guides/evm/precompiles/subnet.mdx @@ -103,50 +103,31 @@ real AdminUtils operations therefore require the proposed V2 selectors below. | Proposed function | Source extrinsic | |---|---| -| `dissolveNetwork` | `SubtensorModule.dissolve_network` | | `setSubnetIdentity` | `SubtensorModule.set_subnet_identity` | | `updateSubnetSymbol` | `SubtensorModule.update_symbol` | -| `rootDissolveNetwork` | `SubtensorModule.root_dissolve_network` | | `triggerEpoch` | `SubtensorModule.trigger_epoch` | ## Proposed AdminUtils operations | Proposed function | Source extrinsic | |---|---| -| `setAdjustmentInterval` | `AdminUtils.sudo_set_adjustment_interval` | -| `setAdminFreezeWindow` | `AdminUtils.sudo_set_admin_freeze_window` | | `setBondsPenalty` | `AdminUtils.sudo_set_bonds_penalty` | -| `setCommitRevealVersion` | `AdminUtils.sudo_set_commit_reveal_version` | -| `setDissolveNetworkScheduleDuration` | `AdminUtils.sudo_set_dissolve_network_schedule_duration` | -| `setNetworkLockCostReductionInterval` | `AdminUtils.sudo_set_lock_reduction_interval` | | `setMaxAllowedUids` | `AdminUtils.sudo_set_max_allowed_uids` | -| `setMaxAllowedValidators` | `AdminUtils.sudo_set_max_allowed_validators` | | `setMaxBurnV2` | `AdminUtils.sudo_set_max_burn` | -| `setMaxEpochsPerBlock` | `AdminUtils.sudo_set_max_epochs_per_block` | -| `setMaxMechanismCount` | `AdminUtils.sudo_set_max_mechanism_count` | -| `setMaxRegistrationsPerBlock` | `AdminUtils.sudo_set_max_registrations_per_block` | | `setMechanismCount` | `AdminUtils.sudo_set_mechanism_count` | | `setMechanismEmissionSplit` | `AdminUtils.sudo_set_mechanism_emission_split` | -| `setMinAllowedUids` | `AdminUtils.sudo_set_min_allowed_uids` | | `setMinBurnV2` | `AdminUtils.sudo_set_min_burn` | -| `setMinNonImmuneUids` | `AdminUtils.sudo_set_min_non_immune_uids` | -| `setNetworkImmunityPeriod` | `AdminUtils.sudo_set_network_immunity_period` | -| `setNetworkMinLockCost` | `AdminUtils.sudo_set_network_min_lock_cost` | -| `setNetworkRateLimit` | `AdminUtils.sudo_set_network_rate_limit` | | `setOwnerCutEnabled` | `AdminUtils.sudo_set_owner_cut_enabled` | -| `setOwnerHyperparameterRateLimit` | `AdminUtils.sudo_set_owner_hparam_rate_limit` | | `setOwnerImmuneNeuronLimit` | `AdminUtils.sudo_set_owner_immune_neuron_limit` | -| `setRaoRecycledForRegistration` | `AdminUtils.sudo_set_rao_recycled` | -| `setSubnetOwnerHotkey` | `AdminUtils.sudo_set_sn_owner_hotkey` | -| `setSubnetLimit` | `AdminUtils.sudo_set_subnet_limit` | -| `setSubnetOwnerCut` | `AdminUtils.sudo_set_subnet_owner_cut` | -| `setSubtokenEnabled` | `AdminUtils.sudo_set_subtoken_enabled` | -| `setTargetRegistrationsPerInterval` | `AdminUtils.sudo_set_target_registrations_per_interval` | | `setTempo` | `AdminUtils.sudo_set_tempo` | -| `setWeightsSetRateLimitV2` | `AdminUtils.sudo_set_weights_set_rate_limit` | | `trimToMaxAllowedUids` | `AdminUtils.sudo_trim_to_max_allowed_uids` | -Runtime authorization remains in force. Proposed names and signatures do not -reserve selectors. +Each listed AdminUtils call accepts a signed subnet owner as well as Root. The +precompile exposes only the signed path and dispatches the highest-level +extrinsic so owner limits, freeze windows, and other runtime checks remain in +force. Root-only calls are classified as not EVM-callable in the +[coverage audit](./extrinsic-coverage). + +Proposed names and signatures do not reserve selectors. Source: [`subnet.sol`](https://github.com/RaoFoundation/subtensor/blob/main/precompiles/src/solidity/subnet.sol) diff --git a/docs/guides/evm/precompiles/timestamp.mdx b/docs/guides/evm/precompiles/timestamp.mdx index 8b6d6424d2..52cc225207 100644 --- a/docs/guides/evm/precompiles/timestamp.mdx +++ b/docs/guides/evm/precompiles/timestamp.mdx @@ -7,8 +7,8 @@ description: Proposed typed EVM interface for Timestamp pallet state. |---|---| | Proposed implementation | `TimestampPrecompile` | | Proposed Solidity interface | `ITimestamp` | -| Address | Not assigned | -| Status | Proposed | +| Reserved address | `0x0000000000000000000000000000000000000811` | +| Status | Proposed; not yet callable | ## Planned views @@ -20,6 +20,8 @@ description: Proposed typed EVM interface for Timestamp pallet state. `getTimestamp()` returns the same underlying time as the EVM `block.timestamp` value. It exists here so every storage item authorized through `StorageQueryPrecompile` has an explicit typed replacement. +`wasUpdatedThisBlock` provides the Timestamp pallet's update state without +requiring contracts to construct a storage key or decode SCALE. `Timestamp.set` is an inherent submitted by block production, not a public user operation. The proposed precompile therefore exposes no state-changing @@ -28,4 +30,5 @@ timestamp function. See the complete classification in [Extrinsic coverage](/docs/guides/evm/precompiles/extrinsic-coverage). -Names and signatures on this page are provisional and do not reserve selectors. +The address is reserved for this domain. Names and signatures on this page are +provisional and do not reserve selectors. diff --git a/docs/guides/evm/precompiles/voting-power.mdx b/docs/guides/evm/precompiles/voting-power.mdx index a38f4d9d88..1b2747f516 100644 --- a/docs/guides/evm/precompiles/voting-power.mdx +++ b/docs/guides/evm/precompiles/voting-power.mdx @@ -28,8 +28,10 @@ getTotalVotingPower(uint16) |---|---| | `enableVotingPowerTracking` | `SubtensorModule.enable_voting_power_tracking` | | `disableVotingPowerTracking` | `SubtensorModule.disable_voting_power_tracking` | -| `setVotingPowerEmaAlpha` | `SubtensorModule.sudo_set_voting_power_ema_alpha` | -Proposed names and signatures do not reserve selectors. +Both calls accept a signed subnet owner as well as Root. The precompile exposes +only the signed path and preserves the pallet's owner checks. The Root-only EMA +configuration call is not exposed. Proposed names and signatures do not +reserve selectors. Source: [`votingPower.sol`](https://github.com/RaoFoundation/subtensor/blob/main/precompiles/src/solidity/votingPower.sol) From 36dee7cb4037de517c08e7f5bd017bb969723174 Mon Sep 17 00:00:00 2001 From: UnarbosFour Date: Wed, 29 Jul 2026 18:20:20 -0400 Subject: [PATCH 5/8] Spec out the deprecation with locally defined precompile lifecycle rust annotations --- .agents/skills/emv-maintainer/SKILL.md | 13 +++- .../references/abi-versioning.md | 66 +++++++++++++++++-- .../references/coverage-and-testing.md | 3 + 3 files changed, 74 insertions(+), 8 deletions(-) diff --git a/.agents/skills/emv-maintainer/SKILL.md b/.agents/skills/emv-maintainer/SKILL.md index bbd88d169e..cf70ec198e 100644 --- a/.agents/skills/emv-maintainer/SKILL.md +++ b/.agents/skills/emv-maintainer/SKILL.md @@ -33,7 +33,12 @@ For each affected released function: 2. Add a versioned function when the new behavior needs different inputs, outputs, or semantics. Keep the old address and selector routed. 3. Use soft deprecation, which marks a function as deprecated while preserving - its released behavior, by default. Never fabricate data or silently + its released behavior, by default. Declare deprecation and replacement + metadata on the Rust precompile function with the lifecycle annotation + described in [ABI versioning](references/abi-versioning.md). Treat the + annotated Rust function as the source of truth and generate Solidity + lifecycle annotations and registry metadata from it; do not maintain + separate hand-written lifecycle data. Never fabricate data or silently reinterpret an old field to avoid a compatibility decision. 4. If hard deprecation may be necessary, stop and follow the mainnet release warning and lifecycle process in @@ -66,6 +71,12 @@ For each affected released function: unbounded helper and truncate its result afterward. - Follow [ABI versioning](references/abi-versioning.md) for every released interface. +- Treat repository-owned Rust function lifecycle annotations as the source of + truth for registry deprecation metadata, replacement selectors, migration + messages, and generated Solidity interfaces and `@custom:deprecated` + NatSpec. Do not hand-edit generated Solidity lifecycle data. Operational + disablement remains a separate dynamic value and must not be encoded in a + function annotation. - Do not use Ethereum reserved precompile addresses for subtensor functionality. - Assign new Bittensor domain precompiles sequentially from the next unused Bittensor address. The current proposal reserves `0x080f` through `0x0813` diff --git a/.agents/skills/emv-maintainer/references/abi-versioning.md b/.agents/skills/emv-maintainer/references/abi-versioning.md index 03f6493f27..ea753e0216 100644 --- a/.agents/skills/emv-maintainer/references/abi-versioning.md +++ b/.agents/skills/emv-maintainer/references/abi-versioning.md @@ -179,9 +179,10 @@ Keep function lifecycle separate from precompile availability: | Hard-deprecated and enabled | Keep routing the selector and return a descriptive precompile error. | | Disabled | Return the precompile-disabled error regardless of function lifecycle. | -Use soft deprecation by default. Preserve the call, mark the Solidity function -with `@deprecated`, and publish replacement metadata without adding -deprecation-only work to every invocation. +Use soft deprecation by default. Preserve the call, annotate the Rust +precompile function with its lifecycle metadata, generate the Solidity +`@custom:deprecated` NatSpec from that annotation, and publish replacement +metadata without adding deprecation-only work to every invocation. Use hard deprecation only when old behavior cannot be represented honestly or safely, for example because: @@ -232,19 +233,70 @@ struct PrecompileStatus { } ``` +### Function lifecycle annotations + +Declare deprecation metadata on the affected Rust precompile function with a +repository-owned annotation. The target syntax is: + +```rust +#[precompile_lifecycle::deprecated( + replace_with = "getStakeV2(uint16,uint16)", + message = "Use getStakeV2 for the current stake representation." +)] +#[precompile::public("getStake(uint16,uint16)")] +``` + +Do not use `#[precompile::deprecated]` unless the Frontier precompile macro +explicitly supports it: that namespace belongs to the Frontier macro. Do not +encode structured replacement metadata in Rust's built-in `#[deprecated]` +attribute, which does not provide `replace_with` and `message` fields. The +repository-owned annotation and its generator can be implemented in subtensor +without changing Frontier. + +The annotated Rust precompile function is the authoritative source for: + +- whether the function is deprecated; +- the canonical replacement signature used to derive `newSelector`; +- migration guidance returned in `message`; and +- the generated Solidity interface and its lifecycle NatSpec. + +The replacement precompile defaults to the containing precompile address. +Allow an explicit replacement address when migration genuinely crosses +domains. If no replacement exists, omit `replace_with`; the registry returns +zero replacement fields. A deprecated function without a useful human message +is incomplete metadata. + +Repository tooling should collect these Rust annotations and generate the +static registry metadata and Solidity interface lifecycle data. Generate +standards-compliant `@custom:deprecated` and, when applicable, +`@custom:replace-with` NatSpec; an additional `@notice` may make the warning +visible to clients that ignore custom tags. Do not duplicate the same +lifecycle data in a manually maintained Rust match, static table, Solidity +comment, or registry file, and do not hand-edit generated Solidity lifecycle +annotations. Build validation must fail when annotated Rust metadata, the +canonical replacement selector, generated Solidity and NatSpec, and public +documentation disagree. + +The annotation describes function lifecycle only. It does not perform +deprecation work on each invocation and does not control availability. +`isDisabled` remains a dynamic lookup of the containing precompile's +operational enablement state. + Interpret `isDeprecated` as soft or hard function deprecation. Interpret `isDisabled` as current unavailability through a reversible operational switch. Use `newPrecompile` and `newSelector` for the recommended replacement; zero replacement fields mean that none is available. Use `message` for human-readable status or migration guidance. -Do not infer deprecation from disablement. Do not clear deprecation when a +An active function has no deprecation annotation and therefore returns +`isDeprecated = false` with zero replacement fields and an empty message. Do +not infer deprecation from disablement. Do not clear deprecation when a precompile is re-enabled. Do not describe the registry as callable until its address and implementation are released. -Keep registry metadata, Solidity NatSpec, public documentation, and call -behavior consistent. Prefer static registry queries over emitting a log on -every deprecated call. +Keep generated registry metadata, Solidity NatSpec, public documentation, and +call behavior consistent. Prefer static registry queries over emitting a log +on every deprecated call. ## Handle reversible disablement diff --git a/.agents/skills/emv-maintainer/references/coverage-and-testing.md b/.agents/skills/emv-maintainer/references/coverage-and-testing.md index 729f4ad07e..f4b711c33b 100644 --- a/.agents/skills/emv-maintainer/references/coverage-and-testing.md +++ b/.agents/skills/emv-maintainer/references/coverage-and-testing.md @@ -194,6 +194,9 @@ Verify: 6. Unrelated precompile Solidity and ABI files are byte-for-byte unchanged. 7. The Rust macro signature, Solidity declaration, generated ABI, NatSpec, SDK copies, registry metadata, and public documentation agree. + For deprecated functions, verify that registry metadata and Solidity + `@custom:deprecated` NatSpec are generated from the Rust function lifecycle + annotation rather than duplicated manually. 8. Every released address remains in `Precompiles::used_addresses()`. 9. `Precompiles::execute()` recognizes the address and routes it through the intended availability control and compatible implementation. From 26d9925cd439acbc621580febbb9f2f89262c8d6 Mon Sep 17 00:00:00 2001 From: UnarbosFour Date: Thu, 30 Jul 2026 17:09:01 -0400 Subject: [PATCH 6/8] Add rules for read exposure of state variables and maps --- .agents/skills/emv-maintainer/SKILL.md | 19 +++++- .../references/coverage-and-testing.md | 12 +++- .../emv-maintainer/references/exceptions.md | 43 +++++++++++++ .../references/state-exposure.md | 61 +++++++++++++++++++ 4 files changed, 131 insertions(+), 4 deletions(-) create mode 100644 .agents/skills/emv-maintainer/references/exceptions.md create mode 100644 .agents/skills/emv-maintainer/references/state-exposure.md diff --git a/.agents/skills/emv-maintainer/SKILL.md b/.agents/skills/emv-maintainer/SKILL.md index cf70ec198e..f309a52c6d 100644 --- a/.agents/skills/emv-maintainer/SKILL.md +++ b/.agents/skills/emv-maintainer/SKILL.md @@ -14,6 +14,14 @@ You are the maintainer of EVM precompiles. EVM precompiles in subtensor should e read [ABI versioning](references/abi-versioning.md). - Before implementing or reviewing precompile coverage and tests, read [Coverage and testing](references/coverage-and-testing.md). +- Before classifying pallet state or adding, reviewing, or omitting a typed + state view, read [State exposure](references/state-exposure.md) and use its + direct, wrapped, and do-not-expose classifications. Do not override a + classification without an explicit human decision. +- Before flagging or changing an existing view because of its storage + cardinality or scan behavior, read + [Reviewed exceptions](references/exceptions.md). Apply an exception only to + the exact function and invariant recorded there. ## Backwards compatibility @@ -50,7 +58,9 @@ For each affected released function: ## Notes on coding precompiles -- Keep every precompile path O(1) in CPU and memory. +- Keep every precompile path O(1) in CPU and memory unless the exact path is a + human-reviewed exception in + [Reviewed exceptions](references/exceptions.md). - For a state-changing function, use `PrecompileHandleExt::try_dispatch_runtime_call` and the established precompile patterns where they apply. Construct the highest-level pallet @@ -68,7 +78,9 @@ For each affected released function: to grant the caller a stronger origin, stop and request that design. - Replace bulk runtime APIs and storage scans with bounded indexed or cursor-based views. Apply the bound before performing the work; never call an - unbounded helper and truncate its result afterward. + unbounded helper and truncate its result afterward. Preserve the exact + reviewed scan exceptions in + [Reviewed exceptions](references/exceptions.md). - Follow [ABI versioning](references/abi-versioning.md) for every released interface. - Treat repository-owned Rust function lifecycle annotations as the source of @@ -111,6 +123,9 @@ For each affected released function: Use [Coverage and testing](references/coverage-and-testing.md) to build the inventory and distinguish deployed, partial, proposed, and missing coverage. +Use [State exposure](references/state-exposure.md) to classify every state item +and [Reviewed exceptions](references/exceptions.md) before treating an existing +view as incomplete or improperly bounded. ## Step 2 — Determine the diff diff --git a/.agents/skills/emv-maintainer/references/coverage-and-testing.md b/.agents/skills/emv-maintainer/references/coverage-and-testing.md index f4b711c33b..b60feadb2d 100644 --- a/.agents/skills/emv-maintainer/references/coverage-and-testing.md +++ b/.agents/skills/emv-maintainer/references/coverage-and-testing.md @@ -89,6 +89,11 @@ Inventory every public state map and value in scope. Expose its meaningful contents through typed view functions; never provide direct writable access to storage. +Apply the classifications in [State exposure](state-exposure.md). Before +changing an existing view because its shape appears incomplete or unbounded, +check [Reviewed exceptions](exceptions.md). Treat exceptions as exact, +human-reviewed cases rather than patterns to extend by analogy. + Let a view read one or more storage items when that is required to return the meaningful value. Keep the mapping from source storage to typed functions explicit in the coverage inventory so no item disappears behind an abstract @@ -211,8 +216,11 @@ remove unrelated regeneration changes. ## Validate cost and bounds Keep every precompile path bounded in CPU, memory, storage access, and output -size. Record database reads and writes and dispatch weight through the existing -helpers. +size, except for the exact human-reviewed cases in +[Reviewed exceptions](exceptions.md). Record database reads and writes and +dispatch weight through the existing helpers. For an accepted scan exception, +test the protocol limit that makes the scan acceptable and charge for the +complete permitted scan. Test: diff --git a/.agents/skills/emv-maintainer/references/exceptions.md b/.agents/skills/emv-maintainer/references/exceptions.md new file mode 100644 index 0000000000..5da61b2489 --- /dev/null +++ b/.agents/skills/emv-maintainer/references/exceptions.md @@ -0,0 +1,43 @@ +# Reviewed precompile exceptions + +This file records narrow, human-reviewed exceptions to the general state +coverage and bounded-work rules. Apply an exception only to the exact function +and invariant described here. Do not infer that a similar storage shape or +collection is also exempt. + +When reviewing one of these functions, verify that its supporting invariant +still holds. If the runtime changes that invariant, stop treating the function +as an exception and reassess its interface, compatibility, cost, and tests. + +## `getColdkeyLock(bytes32,uint256)` + +`getColdkeyLock` returns the one individual lock for a `(coldkey, netuid)`. +Although `Lock` includes the target hotkey in its storage key and the +implementation locates the row with `iter_prefix(...).next()`, multiple lock +rows are not valid state for that pair: + +- `do_lock_stake` creates the lock when none exists and rejects a different + target hotkey with `LockHotkeyMismatch` when one already exists; +- `move_lock` moves the existing lock to a new target instead of creating a + second lock; and +- the lock is subnet-wide for the coldkey, while the hotkey identifies its + current target. + +The precompile therefore reflects the runtime design accurately and does not +need a paginated or hotkey-keyed replacement. Keep tests proving that a second +target is rejected and that moving a lock leaves exactly one row. + +This exception becomes invalid if any lock creation, transfer, migration, or +repair path permits multiple `Lock` rows for the same `(coldkey, netuid)`. + +## `getSumAlphaPrice()` + +`getSumAlphaPrice` may scan every subnet. Subnets are a protocol-limited, +scarce resource, and the function's meaningful result is the aggregate over +the complete set. A cursor would change that meaning and move composition to +the caller. + +Keep the complete scan, charge for all permitted subnet reads, and test it at +the configured subnet limit. This exception does not apply to collections +whose size grows with accounts, neurons, stakes, commitments, or other +user-created records. diff --git a/.agents/skills/emv-maintainer/references/state-exposure.md b/.agents/skills/emv-maintainer/references/state-exposure.md new file mode 100644 index 0000000000..f64e63ec46 --- /dev/null +++ b/.agents/skills/emv-maintainer/references/state-exposure.md @@ -0,0 +1,61 @@ +# Rules of exposing the state variables and maps + +This file lists concrete state variables and maps and classifies them as one of three classes: + +1. Safe to expose directly, as is, or +2. Need some type-safe wrapping, or +3. Internal, do not need to be exposed, or already known to be deprecated soon + +The class 1 state variables and maps are not anticipated to change anytime soon or change significantly. Also, even if they do, it is expected that their exposed values can be easily simulated or recalculated with no greater than O(1) complexity. + +The class 2 state variables and maps are not expected to stay for a long time, are temporary, or express complex formulas and need to be safely wrapped. + +## Safe to expose directly + +### Pallet subtensor + +- Delegation and childkeys: Delegates, ChildkeyTake, PendingChildKeys, ChildKeys, ParentKeys, PendingChildKeyCooldown, minimum/maximum delegate and childkey takes, and MinChildkeyTakePerSubnet. + +- Ownership and account relationships: OwnedHotkeys, AutoStakeDestination, AutoStakeDestinationColdkeys, HotkeySuccessor, HotkeyRoot, ColdkeySuccessor, ColdkeyRoot, coldkey-swap announcements/disputes/delays, and LastHotkeySwapOnNetuid. Owner is only indirectly available when the caller already knows a subnet UID, so arbitrary hotkey ownership is only partially covered. + +- Subnet identity and configuration: TokenSymbol, SubnetOwner, SubnetOwnerHotkey, Tempo, RecycleOrBurn, BondsPenalty, MaxAllowedUids, MaxAllowedValidators, AdjustmentInterval, TargetRegistrationsPerInterval, OwnerCutEnabled, ImmuneOwnerUidsLimit, MechanismCountCurrent, MechanismEmissionSplit, BurnHalfLife, BurnIncreaseMult, TransferToggle, MinAllowedUids, MinNonImmuneUids, and numerous global network limits. + +- Emission and economic accounting: BlockEmission, Subtensor TotalIssuance, TotalStake, AlphaDividendsPerSubnet, RootAlphaDividendsPerSubnet, LastHotkeyEmissionOnNetuid, SubnetMovingAlpha, RootProp, SubnetEmissionEnabled, SubnetExcessTao, SubnetRootSellTao, SubnetProtocolAlpha, flow/EMA maps, emission gate configuration, pending emission/cut maps, MinerBurned, and RAORecycledForRegistration. + +- Neuron state: Uids, IsNetworkMember, Weights, Bonds, BlockAtRegistration, NeuronCertificates, Prometheus, IdentitiesV2, SubnetIdentitiesV3, LoadedEmission, transaction-rate timestamps, and all weight-commit maps and versions. + +- Collateral and leasing: MinerCollateral, ColdkeyMinerCollateral, ColdkeyCollateralHotkeys, CollateralLockShare, CollateralDrainRatio, NextSubnetLeaseId, and AccumulatedLeaseDividends. + +- EVM associations: Forward view for AssociatedEvmAddress(netuid, uid). + +### Pallet balances + +TotalIssuance + +### Pallet Proxy + +proxy deposit, Announcements, LastCallResult, RealPaysFee + +### Pallet Swap + +FeeRate, SwapBalancer, BalancerTaoReservoir, BalancerAlphaReservoir, HasMigrationRun + +## Need some type-safe wrapping + +### Pallet Swap + +PalSwapInitialized and its successors should be exposed as just generic "IsSwapInitialized", non-specific to palswap / balancer. + +## Do not expose + +### Pallet subtensor + +- Root claims: RootClaimableThreshold, RootClaimable, RootClaimed, RootClaimType. + +### Pallet balances + +InactiveIssuance, the reserved, frozen, and flags portions of Account: Locks, Reserves, Holds, Freezes + +### Pallet swap + +ScrapReservoirAlpha \ No newline at end of file From 7f9ae800f9cfdcbcb16e4fc63eb67cbd1452a2fa Mon Sep 17 00:00:00 2001 From: UnarbosFour Date: Fri, 31 Jul 2026 11:06:19 -0400 Subject: [PATCH 7/8] Fix typo in path --- .agents/skills/{emv-maintainer => evm-maintainer}/SKILL.md | 0 .../references/abi-versioning.md | 0 .../references/coverage-and-testing.md | 0 .../{emv-maintainer => evm-maintainer}/references/exceptions.md | 0 .../references/state-exposure.md | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename .agents/skills/{emv-maintainer => evm-maintainer}/SKILL.md (100%) rename .agents/skills/{emv-maintainer => evm-maintainer}/references/abi-versioning.md (100%) rename .agents/skills/{emv-maintainer => evm-maintainer}/references/coverage-and-testing.md (100%) rename .agents/skills/{emv-maintainer => evm-maintainer}/references/exceptions.md (100%) rename .agents/skills/{emv-maintainer => evm-maintainer}/references/state-exposure.md (100%) diff --git a/.agents/skills/emv-maintainer/SKILL.md b/.agents/skills/evm-maintainer/SKILL.md similarity index 100% rename from .agents/skills/emv-maintainer/SKILL.md rename to .agents/skills/evm-maintainer/SKILL.md diff --git a/.agents/skills/emv-maintainer/references/abi-versioning.md b/.agents/skills/evm-maintainer/references/abi-versioning.md similarity index 100% rename from .agents/skills/emv-maintainer/references/abi-versioning.md rename to .agents/skills/evm-maintainer/references/abi-versioning.md diff --git a/.agents/skills/emv-maintainer/references/coverage-and-testing.md b/.agents/skills/evm-maintainer/references/coverage-and-testing.md similarity index 100% rename from .agents/skills/emv-maintainer/references/coverage-and-testing.md rename to .agents/skills/evm-maintainer/references/coverage-and-testing.md diff --git a/.agents/skills/emv-maintainer/references/exceptions.md b/.agents/skills/evm-maintainer/references/exceptions.md similarity index 100% rename from .agents/skills/emv-maintainer/references/exceptions.md rename to .agents/skills/evm-maintainer/references/exceptions.md diff --git a/.agents/skills/emv-maintainer/references/state-exposure.md b/.agents/skills/evm-maintainer/references/state-exposure.md similarity index 100% rename from .agents/skills/emv-maintainer/references/state-exposure.md rename to .agents/skills/evm-maintainer/references/state-exposure.md From 539f5d00b528ff652a43fa3bdf886cf6c5b240cc Mon Sep 17 00:00:00 2001 From: UnarbosFour Date: Fri, 31 Jul 2026 13:52:37 -0400 Subject: [PATCH 8/8] Add runtime constants to the precompile requirements --- .agents/skills/evm-maintainer/SKILL.md | 72 +++++++++++++------ .../references/abi-versioning.md | 9 ++- .../references/coverage-and-testing.md | 23 +++++- .../references/state-exposure.md | 30 ++++++-- 4 files changed, 105 insertions(+), 29 deletions(-) diff --git a/.agents/skills/evm-maintainer/SKILL.md b/.agents/skills/evm-maintainer/SKILL.md index f309a52c6d..555222f960 100644 --- a/.agents/skills/evm-maintainer/SKILL.md +++ b/.agents/skills/evm-maintainer/SKILL.md @@ -1,11 +1,16 @@ --- name: evm-maintainer -description: Maintain the EVM precompiles in backwards compatible way with API versioning. +description: Maintain backwards-compatible, versioned EVM precompiles that expose runtime extrinsics, state, constants, and APIs to Solidity. --- # EVM Precompile Maintainer -You are the maintainer of EVM precompiles. EVM precompiles in subtensor should expose the deterministic functionality available to client applications to EVM smart contracts: extrinsics, state maps and variables through typed read-only views, and runtime APIs/RPC results. Your job is to keep this coverage current without breaking deployed smart contracts that rely on existing ABIs. Read the notes below and then execute steps. +You are the maintainer of EVM precompiles. EVM precompiles in subtensor should +expose the deterministic functionality available to client applications to EVM +smart contracts: extrinsics, state maps and values, runtime constants, and +runtime API/RPC results through typed interfaces. Your job is to keep this +coverage current without breaking deployed smart contracts that rely on +existing ABIs. Read the notes below and then execute the workflow. ## Reference routing @@ -14,10 +19,11 @@ You are the maintainer of EVM precompiles. EVM precompiles in subtensor should e read [ABI versioning](references/abi-versioning.md). - Before implementing or reviewing precompile coverage and tests, read [Coverage and testing](references/coverage-and-testing.md). -- Before classifying pallet state or adding, reviewing, or omitting a typed - state view, read [State exposure](references/state-exposure.md) and use its - direct, wrapped, and do-not-expose classifications. Do not override a - classification without an explicit human decision. +- Before classifying pallet state or runtime constants, or adding, reviewing, + or omitting a typed view, read + [State exposure](references/state-exposure.md) and use its direct, wrapped, + and do-not-expose classifications. Do not override a classification without + an explicit human decision. - Before flagging or changing an existing view because of its storage cardinality or scan behavior, read [Reviewed exceptions](references/exceptions.md). Apply an exception only to @@ -34,7 +40,7 @@ Compatibility covers observable behavior, not merely the continued existence of a four-byte selector. Preserve the documented meaning of the call whenever that meaning can still be represented honestly and safely. -For each affected released function: +For each affected released function or view: 1. Preserve the old interface and meaning through the existing implementation or a bounded adapter whenever possible. @@ -56,6 +62,13 @@ For each affected released function: are unchanged by following [Coverage and testing](references/coverage-and-testing.md). +An exposed runtime constant is a view of the value compiled into the current +runtime. Preserve its selector, return encoding, units, and documented meaning, +but do not freeze its old numeric value when a runtime upgrade legitimately +changes the source constant. Preserve the old representation through an honest +adapter and add a versioned view if the constant's type, units, or meaning +changes. + ## Notes on coding precompiles - Keep every precompile path O(1) in CPU and memory unless the exact path is a @@ -81,6 +94,10 @@ For each affected released function: unbounded helper and truncate its result afterward. Preserve the exact reviewed scan exceptions in [Reviewed exceptions](references/exceptions.md). +- Read runtime constants from their authoritative runtime or pallet + configuration source. Never duplicate the literal value in precompile code. + Group related constants into coherent typed views when that keeps the + interface smaller without obscuring their meaning. - Follow [ABI versioning](references/abi-versioning.md) for every released interface. - Treat repository-owned Rust function lifecycle annotations as the source of @@ -100,10 +117,26 @@ For each affected released function: - Multiply Subtensor balances by `10^9` to match EVM's 18-decimal convention, and divide by the same factor before passing balances to Subtensor pallets. -## Step 1 - Review current precompiles vs. subtensor functionality +## Maintenance workflow + +Perform this workflow on: + +- Every change to subtensor Rust codebase +- When explicitly prompted + +## Step 1 — Determine the diff + +Determine the diff between current branch and the most recent main branch (may need to pull it locally if it is outdated). See how this diff affects EVM precompiles: + +- Does it remove or change any functions that precompiles rely on? Does it change function signatures or underlying functionality? +- Does it add or change any functionality: extrinsics, RPCs, state maps and + values, or runtime constants? + +## Step 2 - Review the diff in the context of current precompiles vs. subtensor functionality -- All extrinsics that accept a non-Root signed origin should be exposed to - precompile callers for the following pallets: +- All extrinsics that accept a non-Root signed origin, as well as all state + variables, maps, and constants should be exposed directly or through + type-safe readers to precompile callers for the following pallets: - subtensor - admin-util - balances @@ -124,17 +157,10 @@ For each affected released function: Use [Coverage and testing](references/coverage-and-testing.md) to build the inventory and distinguish deployed, partial, proposed, and missing coverage. Use [State exposure](references/state-exposure.md) to classify every state item -and [Reviewed exceptions](references/exceptions.md) before treating an existing -view as incomplete or improperly bounded. - -## Step 2 — Determine the diff - -Determine the diff between current branch and the most recent main branch (may need to pull it locally if it is outdated). See how this diff affects EVM precompiles: - -- Does it remove or change any functions that precompiles rely on? Does it change function signatures or underlying functionality? -- Does it add any new functionality (extrinsics, RPCs, state maps and variables)? +and runtime constant, and [Reviewed exceptions](references/exceptions.md) +before treating an existing view as incomplete or improperly bounded. -## Step 3 - Handle changed functions +## Step 3 - Handle changed functions, state variables and maps, and constants Apply the backwards-compatibility decision rule above and the detailed [ABI versioning](references/abi-versioning.md) process. Preserve released @@ -143,7 +169,7 @@ behavior. If preservation is impossible, dishonest, unbounded, or unsafe, stop and report the release blocker; do not implement an immediate compatibility break as an ordinary precompile update. -## Step 4 - Handle added functions +## Step 4 - Handle added functions, state variables and maps, and constants Determine the category under which the new functionality needs to be added and add to the corresponding existing precompile. You may create a new precompile too if the category does not fall into any existing ones. @@ -151,4 +177,6 @@ Determine the category under which the new functionality needs to be added and a Update the Solidity interface, generated ABI, NatSpec, registry metadata, SDK copies, and public precompile documentation together. Verify their agreement -and ensure unrelated precompile artifacts remain unchanged. +and ensure unrelated precompile artifacts remain unchanged. Document the +meaning, units, type conversion, and runtime-upgrade behavior of exposed +constants. diff --git a/.agents/skills/evm-maintainer/references/abi-versioning.md b/.agents/skills/evm-maintainer/references/abi-versioning.md index ea753e0216..98a5febd89 100644 --- a/.agents/skills/evm-maintainer/references/abi-versioning.md +++ b/.agents/skills/evm-maintainer/references/abi-versioning.md @@ -25,7 +25,7 @@ Before changing a precompile: documentation, SDK copies, and known integration contracts. 3. Compare the branch with the relevant base and identify every runtime change that affects inputs, outputs, state changes, errors, authorization, units, - value handling, or gas and weight requirements. + value handling, runtime constants, or gas and weight requirements. 4. Treat uncertain production status as released until evidence establishes otherwise. 5. Distinguish released interfaces from explicit proposals. Allow an @@ -44,6 +44,8 @@ Preserve all observable properties of every released call: - function name, input types, input order, and ABI encoding; - return types, tuple and struct field order, and ABI encoding; - documented meaning, units, precision, scaling, rounding, and defaults; +- whether a returned constant means the value compiled into the current + runtime or a value fixed by the released interface; - view, state-changing, payable, and static-call behavior; - treatment of attached EVM value; - caller-to-Substrate account mapping and dispatched origin; @@ -142,6 +144,8 @@ still be produced honestly with bounded, proportionate work: - Update Rust storage access when names, keys, hashers, or map shapes change. - Supply the exact old default when an extrinsic gains an option; expose the option through a new version. +- Follow a renamed or relocated runtime constant to its authoritative source + while preserving the released view's meaning, type, and units. - Derive the documented old result when the runtime replaces its computation. - Preserve legacy units, precision, scaling, and rounding in the old function; expose a corrected convention through a new version. @@ -160,6 +164,9 @@ make an explicit lifecycle decision. | Input or return type/order change | Add a version with a new selector. | | One concept splits into several | Reconstruct the old aggregate when honest; expose components through a version. | | Extrinsic gains an option | Preserve the old default; expose the option through a version. | +| Runtime constant is added | Add a typed view in the appropriate domain. | +| Current-runtime constant value changes | Keep the existing selector returning the new authoritative value when that is its documented meaning. | +| Runtime constant type, units, or meaning changes | Preserve the old representation through an honest adapter or add a versioned view. | | Entirely new operation or view | Add a selector to the appropriate domain. | | Concept disappears without an honest representation | Reserve the selector and evaluate hard deprecation. | | Bug fix changes observable semantics | Preserve the released behavior and add a corrected version unless retaining it is unsafe. | diff --git a/.agents/skills/evm-maintainer/references/coverage-and-testing.md b/.agents/skills/evm-maintainer/references/coverage-and-testing.md index b60feadb2d..83cab27509 100644 --- a/.agents/skills/evm-maintainer/references/coverage-and-testing.md +++ b/.agents/skills/evm-maintainer/references/coverage-and-testing.md @@ -6,6 +6,7 @@ - [Build a coverage inventory](#build-a-coverage-inventory) - [Cover extrinsics](#cover-extrinsics) - [Cover state with typed views](#cover-state-with-typed-views) +- [Cover runtime constants](#cover-runtime-constants) - [Cover runtime APIs and public RPCs](#cover-runtime-apis-and-public-rpcs) - [Add regression tests first](#add-regression-tests-first) - [Test observable behavior](#test-observable-behavior) @@ -23,6 +24,7 @@ For each in-scope pallet, inspect: - every dispatchable extrinsic; - every public state map and value; +- every public runtime constant; - every publicly facing runtime API and RPC; - changes to types, guards, authorization, units, and error behavior used by existing precompiles. @@ -40,7 +42,7 @@ Create or update a working matrix with one row per source item: | Source | Kind | Public functionality | Precompile domain | Function | Status | Evidence | |---|---|---|---|---|---|---| -| Pallet and item | Extrinsic, state, runtime API, or RPC | Meaning exposed to clients | Existing or proposed address/domain | Canonical signature | Covered, partial, missing, or excluded | Rust, Solidity, ABI, and test paths | +| Pallet and item | Extrinsic, state, constant, runtime API, or RPC | Meaning exposed to clients | Existing or proposed address/domain | Canonical signature | Covered, partial, missing, or excluded | Rust, Solidity, ABI, and test paths | For every partial, missing, or excluded row, state the exact reason. Do not equate a similarly named function with coverage; compare parameters, returned @@ -115,6 +117,23 @@ For every view, specify and test: When storage changes internally, update the Rust adapter and prove that released calldata still returns the released meaning. +## Cover runtime constants + +Inventory every public runtime constant in the in-scope pallet configuration +and expose its meaningful value through a typed view. Read the authoritative +`Get::get()`, associated constant, or equivalent runtime source; never repeat +its literal value in precompile code. + +Group related constants by contract use case when appropriate. Preserve each +constant's meaning, units, signedness, width, and overflow behavior. A constant +may change when a new runtime is compiled: when a released view promises the +current runtime value, test and document that behavior instead of treating the +old numeric value as ABI state. + +Do not expose generated weights, compiler/build constants, or private +implementation limits unless they are part of the pallet's deterministic +client-facing contract. + ## Cover runtime APIs and public RPCs Inventory the publicly facing runtime APIs and RPCs in scope, including the @@ -176,6 +195,7 @@ Cover every affected path: - account and address conversion; - TAO and Alpha unit conversion; - precision, rounding, overflow, and narrowing; +- runtime-constant source values, units, and conversion boundaries; - bounded collections and duplicate inputs; - lifecycle status, hard-deprecation error, and disable/re-enable behavior when applicable; and @@ -269,6 +289,7 @@ run and the specific reason; do not imply success from an unexecuted check. Summarize: - source functionality added, changed, or still missing; +- runtime constants added, changed, or still missing; - released addresses and selectors affected; - adapters or new versions introduced; - lifecycle or mainnet-release warnings; diff --git a/.agents/skills/evm-maintainer/references/state-exposure.md b/.agents/skills/evm-maintainer/references/state-exposure.md index f64e63ec46..a6146e2efc 100644 --- a/.agents/skills/evm-maintainer/references/state-exposure.md +++ b/.agents/skills/evm-maintainer/references/state-exposure.md @@ -1,14 +1,34 @@ -# Rules of exposing the state variables and maps +# Rules for exposing state and runtime constants -This file lists concrete state variables and maps and classifies them as one of three classes: +This file lists concrete state variables, maps, and runtime constants and +classifies them as one of three classes: 1. Safe to expose directly, as is, or 2. Need some type-safe wrapping, or 3. Internal, do not need to be exposed, or already known to be deprecated soon -The class 1 state variables and maps are not anticipated to change anytime soon or change significantly. Also, even if they do, it is expected that their exposed values can be easily simulated or recalculated with no greater than O(1) complexity. +The class 1 items are not anticipated to change significantly. Even if they do, +their exposed values should remain honestly reproducible with no greater than +O(1) complexity. -The class 2 state variables and maps are not expected to stay for a long time, are temporary, or express complex formulas and need to be safely wrapped. +The class 2 items are temporary, use unstable internal representations, or +express complex formulas and need to be safely wrapped. + +## Runtime constants + +Inventory every public runtime constant declared by or supplied to the +configuration of an in-scope pallet. Expose it directly or through a coherent +typed grouped view, reading the authoritative runtime source rather than +copying its literal value into the precompile. + +Preserve semantic types and units when converting Rust values to Solidity. +Treat fixed-point values, balances, block numbers, bounded sizes, and other +representation-specific constants as type-safe wrapping cases when their Rust +representation is not a suitable permanent ABI. + +This requirement covers deterministic client-facing runtime configuration. It +does not cover generated weights, compiler/build constants, or private +implementation details that are not part of the pallet's public behavior. ## Safe to expose directly @@ -58,4 +78,4 @@ InactiveIssuance, the reserved, frozen, and flags portions of Account: Locks, Re ### Pallet swap -ScrapReservoirAlpha \ No newline at end of file +ScrapReservoirAlpha