diff --git a/docs/guides/evm/precompile-design.mdx b/docs/guides/evm/precompile-design.mdx new file mode 100644 index 0000000000..a2a233c57f --- /dev/null +++ b/docs/guides/evm/precompile-design.mdx @@ -0,0 +1,458 @@ +--- +title: Precompile design and lifecycle +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 +Subtensor runtime. They give Solidity callers typed access to chain operations +and durable chain values without requiring contracts to understand Substrate +storage. + +This page defines the compatibility model that new and existing precompiles +should follow. It also describes the target lifecycle registry. The registry +interface shown below is a design contract; it is not yet available on chain. + + + 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 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 + +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. + +New Bittensor domain addresses are assigned sequentially from the next unused +Bittensor address. The latest deployed domains are: + +| Address | Domain | +|---|---| +| `0x000000000000000000000000000000000000080f` | Scheduler | +| `0x0000000000000000000000000000000000000810` | Drand | +| `0x0000000000000000000000000000000000000811` | Timestamp | +| `0x0000000000000000000000000000000000000812` | Runtime configuration | +| `0x0000000000000000000000000000000000000813` | Precompile registry | + +Documenting 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 +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. + +### 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 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. +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. + +## 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 + +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 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; +- 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 +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..478cbcf9f9 --- /dev/null +++ b/docs/guides/evm/precompiles/account-balance.mdx @@ -0,0 +1,33 @@ +--- +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` | +| `getTotalIssuance()` | `view` | + +## Added operations + +| Function | Source extrinsic | +|---|---| +| `burnBalance` | `Balances.burn` | +| `upgradeAccounts` | `Balances.upgrade_accounts` | + +`upgradeAccounts` has an explicit input bound of 64 accounts. Both operations +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. + + +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 new file mode 100644 index 0000000000..ae4cf01904 --- /dev/null +++ b/docs/guides/evm/precompiles/alpha.mdx @@ -0,0 +1,65 @@ +--- +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() +getEmissionAccounting(uint16,bytes32) +getSubnetEconomicState(uint16) +getSubnetFlowState(uint16) +getEmissionGateConfig() +getSwapState(uint16) +hasSwapMigrationRun(bytes) +``` + +Flow values use signed Solidity integers. Fixed-point economic values are +returned as their raw runtime bits. `getSwapState` includes the fee, +initialization status, balancer quote weight, and both protocol reservoirs; +the initialization flag is the generic swap-initialization view. + +## Added operations + +| Function | Source extrinsic | +|---|---| +| `setRecycleOrBurn` | `AdminUtils.sudo_set_recycle_or_burn` | +| `setBurnHalfLife` | `AdminUtils.sudo_set_burn_half_life` | +| `setBurnIncreaseMultiplier` | `AdminUtils.sudo_set_burn_increase_mult` | + +The five deprecated `Swap` liquidity extrinsics are intentionally not proposed; +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. + + +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..b94532ccf9 --- /dev/null +++ b/docs/guides/evm/precompiles/balance-transfer.mdx @@ -0,0 +1,37 @@ +--- +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` | + +## Added operations + +| Function | Source extrinsic | +|---|---| +| `transferKeepAlive` | `Balances.transfer_keep_alive` | +| `transferAll` | `Balances.transfer_all` | + +The existing `transfer(bytes32)` semantically covers +`Balances.transfer_allow_death` by taking the amount from attached EVM value. +The added functions use explicit typed arguments where attached value does +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. + + +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 new file mode 100644 index 0000000000..5684ff5a3f --- /dev/null +++ b/docs/guides/evm/precompiles/crowdloan.mdx @@ -0,0 +1,46 @@ +--- +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) +``` + +## Added operation + +| Function | Source extrinsic | +|---|---| +| `setMaxContribution` | `Crowdloan.set_max_contribution` | + +The typed interface preserves the source call's optional value so the creator +can either set or clear the per-contributor maximum. + + +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..ebfbf778af --- /dev/null +++ b/docs/guides/evm/precompiles/drand.mdx @@ -0,0 +1,38 @@ +--- +title: Drand +description: Typed EVM interface for stored Drand randomness. +--- + +| Property | Value | +|---|---| +| Implementation | `DrandPrecompile` | +| Solidity interface | `IDrand` | +| Address | `0x0000000000000000000000000000000000000810` | +| Status | Deployed | + +This precompile exposes 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. + +## 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` | + +## State-changing operations + +`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. + +`hasMigrationRun(bytes)` bounds the supplied key to 128 bytes before reading +storage. + +Source: [`drand.sol`](https://github.com/RaoFoundation/subtensor/blob/main/precompiles/src/solidity/drand.sol) diff --git a/docs/guides/evm/precompiles/extrinsic-coverage.mdx b/docs/guides/evm/precompiles/extrinsic-coverage.mdx new file mode 100644 index 0000000000..ce41488e86 --- /dev/null +++ b/docs/guides/evm/precompiles/extrinsic-coverage.mdx @@ -0,0 +1,86 @@ +--- +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 signed additions | Not exposed | +|---|---:|---:|---:|---:| +| `SubtensorModule` | 82 | 68 | 0 | 14 | +| `AdminUtils` | 86 | 40 | 0 | 46 | +| `Balances` | 9 | 5 | 0 | 4 | +| `Proxy` | 12 | 11 | 1 | 0 | +| `Scheduler` | 10 | 0 | 0 | 10 | +| `Drand` | 3 | 0 | 0 | 3 | +| `Crowdloan` | 10 | 10 | 0 | 0 | +| `Timestamp` | 1 | 0 | 0 | 1 | +| `Swap` | 6 | 0 | 0 | 6 | +| **Total** | **219** | **134** | **1** | **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. + +`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. + +## Signed additions + +Each implemented operation is listed on the page of its target precompile: + +| Target precompile | Missing extrinsics assigned | +|---|---:| +| [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) | 4 | +| [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 | + +The only remaining proposed signed operation is `Proxy.proxy_announced`. It +requires a stable, versioned EVM description of the proxied runtime call and +must not add another SCALE-encoded `RuntimeCall` interface. + +## 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 exposed as `SubnetPrecompile.setTempo` through `AdminUtils.sudo_set_tempo`. | +| `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. For calls that also accept a signed subnet owner, the domain precompile dispatches the highest-level call as the mapped EVM signer and preserves its authorization checks. | +| `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. +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 new file mode 100644 index 0000000000..6a264d9b98 --- /dev/null +++ b/docs/guides/evm/precompiles/index.mdx @@ -0,0 +1,66 @@ +--- +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 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, +or intentionally non-callable EVM treatment. + +## 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` |
Deployed | +| [`DrandPrecompile`](/docs/guides/evm/precompiles/drand) | `IDrand` |
Deployed | +| [`TimestampPrecompile`](/docs/guides/evm/precompiles/timestamp) | `ITimestamp` |
Deployed | +| [`RuntimeConfigurationPrecompile`](/docs/guides/evm/precompiles/runtime-configuration) | `IRuntimeConfiguration` |
Deployed | +| [`PrecompileRegistry`](/docs/guides/evm/precompiles/registry) | `IPrecompileRegistry` |
Deployed | + +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/leasing.mdx b/docs/guides/evm/precompiles/leasing.mdx new file mode 100644 index 0000000000..52694ee71f --- /dev/null +++ b/docs/guides/evm/precompiles/leasing.mdx @@ -0,0 +1,41 @@ +--- +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) +getNextLeaseId() +getAccumulatedLeaseDividends(uint32) +``` + +## Operations + +```text +createLeaseCrowdloan(uint64,uint64,uint64,uint32,uint8,bool,uint32) +terminateLease(uint32,bytes32) +``` + +Both operations are `payable`. + +## Added operation + +| Function | Source extrinsic | +|---|---| +| `startCall` | `SubtensorModule.start_call` | + +`startCall` accepts a signed subnet owner. The Root-only start-call delay +configuration is not exposed. + +Source: [`leasing.sol`](https://github.com/RaoFoundation/subtensor/blob/main/precompiles/src/solidity/leasing.sol) diff --git a/docs/guides/evm/precompiles/metagraph.mdx b/docs/guides/evm/precompiles/metagraph.mdx new file mode 100644 index 0000000000..f463c35454 --- /dev/null +++ b/docs/guides/evm/precompiles/metagraph.mdx @@ -0,0 +1,45 @@ +--- +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) +``` + +## Proposed bulk runtime API + +`SubnetInfoRuntimeApi.get_all_metagraphs` remains proposed. Its current result +can grow with the number and size of subnets, so no Solidity selector is +assigned in this change. Before implementation, it needs a bounded +cursor-based or indexed interface with stable typed metadata. The deployed +per-subnet and per-UID views above are unchanged. + +Source: [`metagraph.sol`](https://github.com/RaoFoundation/subtensor/blob/main/precompiles/src/solidity/metagraph.sol) diff --git a/docs/guides/evm/precompiles/neuron.mdx b/docs/guides/evm/precompiles/neuron.mdx new file mode 100644 index 0000000000..3a7cd165d0 --- /dev/null +++ b/docs/guides/evm/precompiles/neuron.mdx @@ -0,0 +1,105 @@ +--- +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) +``` + +## Added weight operations + +| 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` | + +Batch calls accept at most 16 outer items and at most 4,096 weight entries per +inner array. Timelocked commits and registration work are bounded to 5,000 and +64 bytes respectively; none of these functions accepts SCALE-encoded runtime +calls. + +## Added registration and key operations + +| Function | Source extrinsic | +|---|---| +| `register` | `SubtensorModule.register` | +| `rootRegister` | `SubtensorModule.root_register` | +| `swapHotkey` | `SubtensorModule.swap_hotkey` | +| `swapHotkeyV2` | `SubtensorModule.swap_hotkey_v2` | +| `setChildren` | `SubtensorModule.set_children` | +| `setIdentity` | `SubtensorModule.set_identity` | +| `tryAssociateHotkey` | `SubtensorModule.try_associate_hotkey` | +| `associateEvmKey` | `SubtensorModule.associate_evm_key` | +| `announceColdkeySwap` | `SubtensorModule.announce_coldkey_swap` | +| `executeAnnouncedColdkeySwap` | `SubtensorModule.swap_coldkey_announced` | +| `disputeColdkeySwap` | `SubtensorModule.dispute_coldkey_swap` | +| `clearColdkeySwapAnnouncement` | `SubtensorModule.clear_coldkey_swap_announcement` | + +Every added operation accepts a non-Root signed origin. The precompile +dispatches 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). + +## Typed state views + +```text +getUid(uint16,bytes32) +isNetworkMember(bytes32,uint16) +getWeights(uint16,uint16) +getBonds(uint16,uint16) +getBlockAtRegistration(uint16,uint16) +getNeuronCertificate(uint16,bytes32) +getPrometheus(uint16,bytes32) +getChainIdentity(bytes32) +getSubnetIdentity(uint16) +getLoadedEmission(uint16) +getTransactionKeyLastBlock(bytes32,uint16,uint16) +getLegacyTransactionRateBlocks(bytes32) +getWeightCommitCount(uint16,bytes32) +getWeightCommit(uint16,bytes32,uint32) +getTimelockedWeightCommitCount(uint16,uint64) +getTimelockedWeightCommit(uint16,uint64,uint32) +getLegacyTimelockedWeightCommitCount(uint8,uint16,uint64) +getLegacyTimelockedWeightCommit(uint8,uint16,uint64,uint32) +``` + +The indexed commit readers expose stable metadata. Timelocked ciphertext is +represented by its Keccak-256 hash and byte length instead of returning the +runtime's encrypted payload type. + +## Proposed bulk runtime API + +`NeuronInfoRuntimeApi.get_neurons` remains proposed. Its current result can +grow with subnet size, so no Solidity selector is assigned in this change. +Before implementation, it needs a bounded cursor-based or indexed interface +with a stable typed result. Existing single-neuron and per-field views remain +available through the deployed Neuron and Metagraph precompiles. + +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..0880a66b39 --- /dev/null +++ b/docs/guides/evm/precompiles/proxy.mdx @@ -0,0 +1,59 @@ +--- +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` | +| `getProxyDeposit(bytes32)` | `view` | +| `getAnnouncements(bytes32)` | `view` | +| `getLastCallResult(bytes32)` | `view` | +| `isRealPaysFee(bytes32,bytes32)` | `view` | + +`getLastCallResult` returns stable success/error metadata. Module errors expose +the pallet index and four error bytes. Error kinds are `1` Other, `2` +CannotLookup, `3` BadOrigin, `4` Module, `5` ConsumerRemaining, `6` +NoProviders, `7` TooManyConsumers, `8` Token, `9` Arithmetic, `10` +Transactional, `11` Exhausted, `12` Corruption, `13` Unavailable, `14` +RootNotAllowed, and `15` Trie. This avoids returning SCALE-encoded runtime +data. + +## Added operations + +| Function | Source extrinsic | +|---|---| +| `announce` | `Proxy.announce` | +| `removeAnnouncement` | `Proxy.remove_announcement` | +| `rejectAnnouncement` | `Proxy.reject_announcement` | +| `setRealPaysFee` | `Proxy.set_real_pays_fee` | + +## Proposed operation + +| Function | Source extrinsic | +|---|---| +| `proxyAnnounced` | `Proxy.proxy_announced` | + +`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`. + +No selector is reserved for `proxyAnnounced`. + +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..5a0ca38ef0 --- /dev/null +++ b/docs/guides/evm/precompiles/registry.mdx @@ -0,0 +1,44 @@ +--- +title: Precompile registry +description: Registry for precompile lifecycle and availability. +--- + +| Property | Value | +|---|---| +| Implementation | `PrecompileRegistry` | +| Solidity interface | `IPrecompileRegistry` | +| Address | `0x0000000000000000000000000000000000000813` | +| Status | Deployed | + +The registry provides function-level lifecycle metadata and the current +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. + +## 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); +} +``` + +`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). + +Source: [`registry.sol`](https://github.com/RaoFoundation/subtensor/blob/main/precompiles/src/solidity/registry.sol) diff --git a/docs/guides/evm/precompiles/runtime-configuration.mdx b/docs/guides/evm/precompiles/runtime-configuration.mdx new file mode 100644 index 0000000000..5817de694e --- /dev/null +++ b/docs/guides/evm/precompiles/runtime-configuration.mdx @@ -0,0 +1,66 @@ +--- +title: Runtime configuration +description: Typed EVM views for global runtime configuration. +--- + +| Property | Value | +|---|---| +| Implementation | `RuntimeConfigurationPrecompile` | +| Solidity interface | `IRuntimeConfiguration` | +| Address | `0x0000000000000000000000000000000000000812` | +| Status | Deployed | + +This domain contains bounded typed views of runtime configuration. The grouped +constant views read their values from the runtime and therefore reflect the +runtime version executing the call. + +## Views + +| Function | Meaning | +|---|---| +| `getEvmChainId()` | Current EVM chain identifier | +| `getTransactionRateLimit()` | Global Subtensor transaction rate limit | +| `getSubtensorEconomicConstants()` | Initial issuance, burn, stake, transfer, registration-lock, and key-swap balance constants | +| `getSubtensorSubnetConstants()` | Subnet size, tempo, immunity, activity, owner-cut, and epoch limits | +| `getSubtensorConsensusConstants()` | Initial weight, emission, Yuma, bonds, pruning, and TAO-weight configuration | +| `getSubtensorRegistrationConstants()` | Registration difficulty, adjustment, rate-limit, immunity, lock-reduction, and price-EMA configuration | +| `getSubtensorDelegationConstants()` | Initial delegate and childkey takes plus Liquid Alpha and Yuma feature defaults | +| `getSubtensorRateLimitConstants()` | Transaction, serving, EVM-association, swap, dissolution, start-call, and lease timing constants | +| `getSubtensorProtocolConstants()` | Fixed public protocol bounds, flags, voting-power timing, lock timing, and maximum TAO issuance | +| `getSubtensorSystemAccounts()` | Derived Subtensor pallet and burn accounts as `bytes32` | +| `getBalancesConstants()` | Existential deposit and lock, reserve, and freeze limits | +| `getProxyConstants()` | Proxy and announcement deposits and count limits | +| `getSchedulerConstants()` | Maximum scheduler weight and calls per block | +| `getDrandConstants()` | Quicknet chain hash, unsigned transaction configuration, and pulse-retention limits | +| `getCrowdloanConstants()` | Deposit, contribution, duration, contributor, refund, and pallet-account configuration | +| `getSwapConstants()` | Maximum fee rate, minimum liquidity and reserve, and derived protocol account | +| `getTimestampConstants()` | Minimum timestamp period | +| `getAdminConstants()` | Maximum authority count | + +## Constant representation + +All balance-valued constants are returned as `uint256` values using EVM's +18-decimal convention. Block numbers, durations, percentages, fixed-point +parts, and count limits retain the units stated by their Solidity output +names. `PalletId` configuration is wrapped as the derived 32-byte account that +contracts can use. + +A runtime upgrade may change a constant's returned value. The function +selector, output type and order, units, and documented meaning remain the +compatibility contract. + +## State-changing operations + +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 +``` + +They are not exposed as typed EVM operations. A future view must return a +typed, bounded representation and must not expose SCALE-encoded runtime values. + +Source: [`runtimeConfiguration.sol`](https://github.com/RaoFoundation/subtensor/blob/main/precompiles/src/solidity/runtimeConfiguration.sol) diff --git a/docs/guides/evm/precompiles/scheduler.mdx b/docs/guides/evm/precompiles/scheduler.mdx new file mode 100644 index 0000000000..662c8c6b95 --- /dev/null +++ b/docs/guides/evm/precompiles/scheduler.mdx @@ -0,0 +1,40 @@ +--- +title: Scheduler +description: Typed EVM interface for Scheduler metadata. +--- + +| Property | Value | +|---|---| +| Implementation | `SchedulerPrecompile` | +| Solidity interface | `IScheduler` | +| Address | `0x000000000000000000000000000000000000080f` | +| Status | Deployed | + +This precompile replaces 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. + +## 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. + +## State-changing operations + +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 returned agenda entry contains stable metadata rather than a SCALE-encoded +runtime call or origin. + +Source: [`scheduler.sol`](https://github.com/RaoFoundation/subtensor/blob/main/precompiles/src/solidity/scheduler.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..e57ed90de4 --- /dev/null +++ b/docs/guides/evm/precompiles/staking-v2.mdx @@ -0,0 +1,162 @@ +--- +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`. + +## Relationship and ownership views + +```text +getDelegate(bytes32) +getChildkeyTake(bytes32,uint16) +getPendingChildKeys(bytes32,uint16) +getChildKeys(bytes32,uint16) +getParentKeys(bytes32,uint16) +getPendingChildKeyCooldown() +getTakeLimits() +getMinChildkeyTakePerSubnet(uint16) +getHotkeyOwner(bytes32) +getOwnedHotkeys(bytes32) +getAutoStakeDestination(bytes32,uint16) +getAutoStakeDestinationColdkeys(bytes32,uint16) +getHotkeySuccessor(bytes32,uint16) +getHotkeyRoot(bytes32,uint16) +getColdkeySuccessor(bytes32) +getColdkeyRoot(bytes32) +getColdkeySwapStatus(bytes32) +getColdkeySwapDelays() +getLastHotkeySwapOnSubnet(bytes32,uint16) +``` + +Optional relationships return an explicit `exists` flag. Child and parent +links return typed `(proportion, account)` entries. + +## Accounting and collateral views + +```text +getStakeAccounting() +getMinerCollateral(uint16,bytes32,bytes32) +getColdkeyCollateral(uint16,bytes32) +getCollateralConfig(uint16) +``` + +Fixed-point collateral ratios are returned as their raw `U64F64` bits. + +## 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. + +## Added Subtensor operations + +| Function | Source extrinsic | +|---|---| +| `decreaseTake` | `SubtensorModule.decrease_take` | +| `increaseTake` | `SubtensorModule.increase_take` | +| `setChildkeyTake` | `SubtensorModule.set_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` | +| `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`. + +## Added AdminUtils operations + +| Function | Source extrinsic | +|---|---| +| `setMinChildkeyTakePerSubnet` | `AdminUtils.sudo_set_min_childkey_take_per_subnet` | +| `setCollateralLockShare` | `AdminUtils.sudo_set_collateral_lock_share` | +| `setCollateralDrainRatio` | `AdminUtils.sudo_set_collateral_drain_ratio` | + +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 bulk runtime API + +`DelegateInfoRuntimeApi.get_delegates` remains proposed. Its current result can +grow with chain state, so no Solidity selector is assigned in this change. +Before implementation, it needs a bounded cursor-based or indexed interface +whose stable return type is independent of the runtime's SCALE representation. + +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..dd3636ed4d --- /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` | Use the typed [Scheduler](./scheduler) precompile. | +| `Drand` | Use the typed [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` | Use the typed [Timestamp](./timestamp) precompile; `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.mdx b/docs/guides/evm/precompiles/subnet.mdx new file mode 100644 index 0000000000..0caa4dd9f6 --- /dev/null +++ b/docs/guides/evm/precompiles/subnet.mdx @@ -0,0 +1,188 @@ +--- +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) +getRegisteredSubnetCounter(uint16) +getOwnerCutAutoLockEnabled(uint16) +getRho(uint16) +getServingRateLimit(uint16) +getWeightsSetRateLimit(uint16) +getWeightsVersionKey(uint16) +getYuma3Enabled(uint16) +isSubnetDissolving(uint16) +getSubnetDissolutionStatus(uint16) +getSubnetMetadata(uint16) +getSubnetCapacityConfig(uint16) +getMechanismEmissionSplit(uint16) +getBurnConfig(uint16) +getGlobalNetworkLimits() +getGlobalRateLimits() +getGlobalProtocolConfig() +``` + +The grouped configuration views return stable typed fields rather than raw +storage encodings. Fixed-point burn multipliers are returned as raw `U64F64` +bits. + +`getRegisteredSubnetCounter` returns a monotonic generation number for a +netuid. It increments on every successful registration, allowing a contract to +distinguish a reused netuid even when it did not retain the previous +registration block. + +`getSubnetDissolutionStatus` returns `(isDissolving, cleanupInProgress, +cleanupPhase)`. Phase `0` means that detailed cleanup has not started. Active +cleanup uses stable, append-only phase codes: + +| Code | Cleanup work | +|---:|---| +| 1 | Root claimable dividends | +| 2 | Root claimed dividends | +| 3 | Calculate stake value | +| 4 | Settle stakes | +| 5 | Clear alpha | +| 6 | Clear hotkey totals | +| 7 | Clear stake locks | +| 8 | Clear decaying stake locks | +| 9 | Finish stake cleanup | +| 10 | Clear protocol liquidity | +| 11 | Purge subnet commitments | +| 12 | Clear network membership | +| 13 | Clear network parameters | +| 14 | Clear network maps | +| 15 | Update root weights | +| 16 | Clear childkey takes | +| 17 | Clear childkeys | +| 18 | Clear parentkeys | +| 19 | Clear last hotkey emissions | +| 20 | Clear last-epoch hotkey alpha | +| 21 | Clear transaction rate-limit records | +| 22 | Clear network locks | +| 23 | Clear decaying network locks | + +The runtime does not currently store a per-subnet future dissolution block. +The authorized dissolution calls execute dissolution immediately, while the +generic scheduler stores calls by agenda position rather than maintaining a +typed netuid-to-dissolution lookup. Consequently, there is no truthful, +bounded per-subnet scheduled-block view to expose. + +## 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) +setMaxDifficulty(uint16,uint64) +setMinAllowedWeights(uint16,uint16) +setMinDifficulty(uint16,uint64) +setNetworkPowRegistrationAllowed(uint16,bool) +setNetworkRegistrationAllowed(uint16,bool) +setOwnerCutAutoLockEnabled(uint16,bool) +setRho(uint16,uint16) +setServingRateLimit(uint16,uint64) +setWeightsVersionKey(uint16,uint64) +setYuma3Enabled(uint16,bool) +toggleTransfers(uint16,bool) +``` + +## 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 use the V2 selectors below. + +## Added subnet operations + +| Function | Source extrinsic | +|---|---| +| `setSubnetIdentity` | `SubtensorModule.set_subnet_identity` | +| `updateSubnetSymbol` | `SubtensorModule.update_symbol` | +| `triggerEpoch` | `SubtensorModule.trigger_epoch` | + +## Added AdminUtils operations + +| Function | Source extrinsic | +|---|---| +| `setBondsPenalty` | `AdminUtils.sudo_set_bonds_penalty` | +| `setMaxAllowedUids` | `AdminUtils.sudo_set_max_allowed_uids` | +| `setMaxBurnV2` | `AdminUtils.sudo_set_max_burn` | +| `setMechanismCount` | `AdminUtils.sudo_set_mechanism_count` | +| `setMechanismEmissionSplit` | `AdminUtils.sudo_set_mechanism_emission_split` | +| `setMinBurnV2` | `AdminUtils.sudo_set_min_burn` | +| `setOwnerCutEnabled` | `AdminUtils.sudo_set_owner_cut_enabled` | +| `setOwnerImmuneNeuronLimit` | `AdminUtils.sudo_set_owner_immune_neuron_limit` | +| `setTempo` | `AdminUtils.sudo_set_tempo` | +| `trimToMaxAllowedUids` | `AdminUtils.sudo_trim_to_max_allowed_uids` | + +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). + +These functions dispatch the listed highest-level extrinsics as the mapped +signed caller. + +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..5c78dfffee --- /dev/null +++ b/docs/guides/evm/precompiles/timestamp.mdx @@ -0,0 +1,33 @@ +--- +title: Timestamp +description: Typed EVM interface for Timestamp pallet state. +--- + +| Property | Value | +|---|---| +| Implementation | `TimestampPrecompile` | +| Solidity interface | `ITimestamp` | +| Address | `0x0000000000000000000000000000000000000811` | +| Status | Deployed | + +## 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. +`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 precompile therefore exposes no state-changing +timestamp function. + +See the complete classification in +[Extrinsic coverage](/docs/guides/evm/precompiles/extrinsic-coverage). + +Source: [`timestamp.sol`](https://github.com/RaoFoundation/subtensor/blob/main/precompiles/src/solidity/timestamp.sol) diff --git a/docs/guides/evm/precompiles/uid-lookup.mdx b/docs/guides/evm/precompiles/uid-lookup.mdx new file mode 100644 index 0000000000..a34bf4a19f --- /dev/null +++ b/docs/guides/evm/precompiles/uid-lookup.mdx @@ -0,0 +1,24 @@ +--- +title: UID lookup +description: Reference for the deployed UidLookupPrecompile. +--- + +| Property | Value | +|---|---| +| Rust implementation | `UidLookupPrecompile` | +| Solidity interface | `IUidLookup` | +| Address | `0x0000000000000000000000000000000000000806` | +| Status | Deployed | + +## Views + +```text +uidLookup(uint16,address,uint16) +getAssociatedEvmAddress(uint16,uint16) +``` + +`uidLookup` returns the bounded reverse association list for an EVM address. +`getAssociatedEvmAddress` returns the forward address and the block at which +ownership was last proved, with an explicit `exists` flag. + +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..6fda95fe61 --- /dev/null +++ b/docs/guides/evm/precompiles/voting-power.mdx @@ -0,0 +1,41 @@ +--- +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) +``` + +`getTotalVotingPower` reads a maintained per-subnet aggregate. A one-time +runtime migration initializes the aggregate from existing validator entries; +normal epoch updates, removals, swaps, and tracking disablement keep it in +sync without a precompile-side map scan. + +## Added operations + +| Function | Source extrinsic | +|---|---| +| `enableVotingPowerTracking` | `SubtensorModule.enable_voting_power_tracking` | +| `disableVotingPowerTracking` | `SubtensorModule.disable_voting_power_tracking` | + +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. + +Source: [`votingPower.sol`](https://github.com/RaoFoundation/subtensor/blob/main/precompiles/src/solidity/votingPower.sol) diff --git a/pallets/admin-utils/src/lib.rs b/pallets/admin-utils/src/lib.rs index 25877b7820..7aa15c2a75 100644 --- a/pallets/admin-utils/src/lib.rs +++ b/pallets/admin-utils/src/lib.rs @@ -203,6 +203,16 @@ pub mod pallet { VotingPower, /// Account balance precompile AccountBalance, + /// Scheduler metadata precompile + Scheduler, + /// Drand metadata precompile + Drand, + /// Timestamp metadata precompile + Timestamp, + /// Global runtime configuration metadata precompile + RuntimeConfiguration, + /// Precompile lifecycle and availability registry + PrecompileRegistry, } #[pallet::type_value] diff --git a/pallets/drand/src/lib.rs b/pallets/drand/src/lib.rs index f92cc09236..90d50c5938 100644 --- a/pallets/drand/src/lib.rs +++ b/pallets/drand/src/lib.rs @@ -451,6 +451,13 @@ pub mod pallet { } } +impl Pallet { + /// Return the block at which the next unsigned pulse submission is accepted. + pub fn next_unsigned_at() -> BlockNumberFor { + NextUnsignedAt::::get() + } +} + impl Pallet { /// fetch the latest public pulse from the configured drand beacon /// then send a signed transaction to include it on-chain diff --git a/pallets/proxy/src/lib.rs b/pallets/proxy/src/lib.rs index 1fca855327..4e5b781555 100644 --- a/pallets/proxy/src/lib.rs +++ b/pallets/proxy/src/lib.rs @@ -93,6 +93,20 @@ pub struct Announcement { height: BlockNumber, } +impl Announcement { + pub fn real(&self) -> &AccountId { + &self.real + } + + pub fn call_hash(&self) -> &Hash { + &self.call_hash + } + + pub fn height(&self) -> &BlockNumber { + &self.height + } +} + /// The type of deposit #[derive( Encode, diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 3cf4318e9d..957df5ea93 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -2410,6 +2410,12 @@ pub mod pallet { pub type VotingPower = StorageDoubleMap<_, Identity, NetUid, Blake2_128Concat, T::AccountId, u64, ValueQuery>; + #[pallet::storage] + /// MAP ( netuid ) --> total_voting_power | Sum of all validator voting-power + /// entries on the subnet. Kept in sync with `VotingPower` so consumers can + /// read the aggregate without iterating the complete validator map. + pub type TotalVotingPower = StorageMap<_, Identity, NetUid, u64, ValueQuery>; + #[pallet::storage] /// MAP ( netuid ) --> bool | Whether voting power tracking is enabled for this subnet. /// When enabled, VotingPower EMA is updated every epoch. Default is false. diff --git a/pallets/subtensor/src/macros/hooks.rs b/pallets/subtensor/src/macros/hooks.rs index 6d3692d9a2..97eb5125b8 100644 --- a/pallets/subtensor/src/macros/hooks.rs +++ b/pallets/subtensor/src/macros/hooks.rs @@ -183,7 +183,9 @@ mod hooks { // Remove orphan SubnetIdentitiesV3 entries left for recycled netuids. .saturating_add(migrations::migrate_clear_orphan_subnet_identities_v3::migrate_clear_orphan_subnet_identities_v3::()) // Backfill ColdkeyCollateralHotkeys from standing MinerCollateral rows. - .saturating_add(migrations::migrate_coldkey_collateral_hotkeys::migrate_coldkey_collateral_hotkeys::()); + .saturating_add(migrations::migrate_coldkey_collateral_hotkeys::migrate_coldkey_collateral_hotkeys::()) + // Backfill the O(1) aggregate used by the voting-power precompile. + .saturating_add(migrations::migrate_total_voting_power::migrate_total_voting_power::()); weight } diff --git a/pallets/subtensor/src/migrations/migrate_total_voting_power.rs b/pallets/subtensor/src/migrations/migrate_total_voting_power.rs new file mode 100644 index 0000000000..2fa5343846 --- /dev/null +++ b/pallets/subtensor/src/migrations/migrate_total_voting_power.rs @@ -0,0 +1,68 @@ +use crate::{Config, HasMigrationRun, TotalVotingPower, VotingPower}; +use alloc::collections::BTreeMap; +use frame_support::{traits::Get, weights::Weight}; +use subtensor_runtime_common::NetUid; + +const MIGRATION_NAME: &[u8] = b"migrate_total_voting_power"; + +/// Backfill the per-subnet voting-power aggregate from the existing +/// `VotingPower` entries. The full scan happens once during the runtime +/// upgrade; subsequent reads use `TotalVotingPower` in O(1). +pub fn migrate_total_voting_power() -> Weight { + let migration_name = MIGRATION_NAME.to_vec(); + let mut reads = 1u64; + + if HasMigrationRun::::get(&migration_name) { + return T::DbWeight::get().reads(reads); + } + + let mut totals = BTreeMap::::new(); + for (netuid, _, voting_power) in VotingPower::::iter() { + reads = reads.saturating_add(1); + totals + .entry(netuid) + .and_modify(|total| *total = total.saturating_add(voting_power)) + .or_insert(voting_power); + } + + let mut writes = 1u64; + for (netuid, total) in totals { + TotalVotingPower::::insert(netuid, total); + writes = writes.saturating_add(1); + } + + HasMigrationRun::::insert(&migration_name, true); + T::DbWeight::get().reads_writes(reads, writes) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{tests::mock::*, *}; + use sp_core::U256; + + #[test] + fn migration_backfills_total_voting_power_once() { + new_test_ext(1).execute_with(|| { + let first_netuid = NetUid::from(1); + let second_netuid = NetUid::from(2); + VotingPower::::insert(first_netuid, U256::from(1), 10); + VotingPower::::insert(first_netuid, U256::from(2), 20); + VotingPower::::insert(second_netuid, U256::from(3), 7); + + let weight = migrate_total_voting_power::(); + + assert_eq!(TotalVotingPower::::get(first_netuid), 30); + assert_eq!(TotalVotingPower::::get(second_netuid), 7); + assert!(HasMigrationRun::::get(MIGRATION_NAME.to_vec())); + assert_eq!( + weight, + ::DbWeight::get().reads_writes(4, 3) + ); + + VotingPower::::insert(first_netuid, U256::from(4), 100); + migrate_total_voting_power::(); + assert_eq!(TotalVotingPower::::get(first_netuid), 30); + }); + } +} diff --git a/pallets/subtensor/src/migrations/mod.rs b/pallets/subtensor/src/migrations/mod.rs index 63a7ec4439..2cf4955e10 100644 --- a/pallets/subtensor/src/migrations/mod.rs +++ b/pallets/subtensor/src/migrations/mod.rs @@ -73,6 +73,7 @@ pub mod migrate_subnet_volume; pub mod migrate_tao_in_refund_deployment_block; pub mod migrate_to_v1_separate_emission; pub mod migrate_to_v2_fixed_total_stake; +pub mod migrate_total_voting_power; pub mod migrate_transfer_ownership_to_foundation; pub mod migrate_upgrade_revealed_commitments; diff --git a/pallets/subtensor/src/tests/voting_power.rs b/pallets/subtensor/src/tests/voting_power.rs index 9af3639b99..8a8f8c0664 100644 --- a/pallets/subtensor/src/tests/voting_power.rs +++ b/pallets/subtensor/src/tests/voting_power.rs @@ -354,6 +354,42 @@ fn test_voting_power_ema_calculation() { }); } +#[test] +fn test_total_voting_power_tracks_updates_removals_and_swaps() { + new_test_ext(1).execute_with(|| { + let f = VotingPowerTestFixture::new(); + f.setup_full(); + f.run_epochs(1); + + let voting_power = f.get_voting_power(); + assert!(voting_power > 0); + assert_eq!(TotalVotingPower::::get(f.netuid), voting_power); + + let replacement = U256::from(99); + SubtensorModule::swap_voting_power_for_hotkey(&f.hotkey, &replacement, f.netuid); + assert_eq!( + VotingPower::::get(f.netuid, replacement), + voting_power + ); + assert_eq!(TotalVotingPower::::get(f.netuid), voting_power); + + ValidatorPermit::::insert(f.netuid, vec![false]); + let mut output = BTreeMap::new(); + output.insert( + replacement, + EpochTerms { + uid: 0, + new_validator_permit: false, + ..Default::default() + }, + ); + SubtensorModule::update_voting_power_for_subnet(f.netuid, &output); + + assert_eq!(VotingPower::::get(f.netuid, replacement), 0); + assert_eq!(TotalVotingPower::::get(f.netuid), 0); + }); +} + // SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::voting_power::test_voting_power_cleared_when_deregistered --exact --nocapture #[test] fn test_voting_power_cleared_when_deregistered() { diff --git a/pallets/subtensor/src/utils/voting_power.rs b/pallets/subtensor/src/utils/voting_power.rs index 11d8880b97..6af558c071 100644 --- a/pallets/subtensor/src/utils/voting_power.rs +++ b/pallets/subtensor/src/utils/voting_power.rs @@ -37,6 +37,11 @@ impl Pallet { VotingPowerEmaAlpha::::get(netuid) } + /// Get the maintained sum of voting power for all validators on a subnet. + pub fn get_total_voting_power(netuid: NetUid) -> u64 { + TotalVotingPower::::get(netuid) + } + // ======================== // === Extrinsic Handlers === // ======================== @@ -148,7 +153,7 @@ impl Pallet { Self::update_voting_power_for_hotkey(netuid, hotkey, terms.stake, alpha, min_stake); } else { // Miner without vpermit - remove any existing voting power - VotingPower::::remove(netuid, hotkey); + Self::remove_voting_power(netuid, hotkey); } } @@ -174,7 +179,7 @@ impl Pallet { // Remove voting power for deregistered hotkeys for hotkey in hotkeys_to_remove { - VotingPower::::remove(netuid, &hotkey); + Self::remove_voting_power(netuid, &hotkey); log::trace!( "VotingPower removed for deregistered hotkey {hotkey:?} on netuid {netuid:?}" ); @@ -201,13 +206,13 @@ impl Pallet { // This allows new validators to build up voting power from 0 without being removed. if new_ema < min_stake && previous_ema >= min_stake { // Was above threshold, now decayed below - remove - VotingPower::::remove(netuid, hotkey); + Self::remove_voting_power(netuid, hotkey); log::trace!( "VotingPower removed for hotkey {hotkey:?} on netuid {netuid:?} (decayed below removal threshold: {new_ema:?} < {min_stake:?})" ); } else if new_ema > 0 { // Update voting power (building up or maintaining) - VotingPower::::insert(netuid, hotkey, new_ema); + Self::set_voting_power(netuid, hotkey, previous_ema, new_ema); log::trace!( "VotingPower updated for hotkey {hotkey:?} on netuid {netuid:?}: {previous_ema:?} -> {new_ema:?}" ); @@ -238,11 +243,39 @@ impl Pallet { result.min(u64::MAX as u128) as u64 } + /// Store one validator's voting power and update the subnet aggregate by + /// the same delta. + fn set_voting_power( + netuid: NetUid, + hotkey: &T::AccountId, + previous_voting_power: u64, + new_voting_power: u64, + ) { + VotingPower::::insert(netuid, hotkey, new_voting_power); + TotalVotingPower::::mutate(netuid, |total| { + *total = total + .saturating_sub(previous_voting_power) + .saturating_add(new_voting_power); + }); + } + + /// Remove one validator's voting power and subtract it from the subnet + /// aggregate. + fn remove_voting_power(netuid: NetUid, hotkey: &T::AccountId) { + let removed = VotingPower::::take(netuid, hotkey); + if removed > 0 { + TotalVotingPower::::mutate(netuid, |total| { + *total = total.saturating_sub(removed); + }); + } + } + /// Finalize the disabling of voting power tracking. /// Clears all VotingPower entries for the subnet. fn finalize_voting_power_disable(netuid: NetUid) { // Clear all VotingPower entries for this subnet let _ = VotingPower::::clear_prefix(netuid, u32::MAX, None); + TotalVotingPower::::remove(netuid); // Disable tracking VotingPowerTrackingEnabled::::insert(netuid, false); diff --git a/precompiles/Cargo.toml b/precompiles/Cargo.toml index 4c1b924ed9..8f2cd1f55f 100644 --- a/precompiles/Cargo.toml +++ b/precompiles/Cargo.toml @@ -39,7 +39,11 @@ pallet-subtensor-swap.workspace = true pallet-admin-utils.workspace = true subtensor-swap-interface.workspace = true pallet-crowdloan.workspace = true +pallet-drand.workspace = true +pallet-evm-chain-id.workspace = true pallet-shield.workspace = true +pallet-scheduler.workspace = true +pallet-timestamp.workspace = true [lints] workspace = true @@ -103,9 +107,5 @@ runtime-benchmarks = [ ] [dev-dependencies] -pallet-drand = { workspace = true, features = ["std"] } -pallet-evm-chain-id = { workspace = true, features = ["std"] } pallet-preimage = { workspace = true, features = ["std"] } -pallet-scheduler = { workspace = true, features = ["std"] } -pallet-timestamp = { workspace = true, features = ["std"] } precompile-utils = { workspace = true, features = ["std", "testing"] } diff --git a/precompiles/src/alpha.rs b/precompiles/src/alpha.rs index 9840c42575..62b70272d4 100644 --- a/precompiles/src/alpha.rs +++ b/precompiles/src/alpha.rs @@ -2,9 +2,18 @@ use core::marker::PhantomData; use crate::PrecompileExt; use fp_evm::{ExitError, PrecompileFailure}; -use pallet_evm::{BalanceConverter, PrecompileHandle, SubstrateBalance}; -use precompile_utils::EvmResult; -use sp_runtime::{SaturatedConversion, Vec}; +use frame_support::{ + BoundedVec, + dispatch::{DispatchInfo, GetDispatchInfo, PostDispatchInfo}, + traits::{ConstU32, IsSubType}, +}; +use frame_system::RawOrigin; +use pallet_evm::{AddressMapping, BalanceConverter, PrecompileHandle, SubstrateBalance}; +use precompile_utils::{EvmResult, prelude::BoundedBytes}; +use sp_runtime::{ + SaturatedConversion, Vec, + traits::{AsSystemOriginSigner, Dispatchable}, +}; use crate::PrecompileHandleExt; use sp_core::U256; @@ -18,8 +27,24 @@ where R: frame_system::Config + pallet_subtensor::Config + pallet_subtensor_swap::Config - + pallet_evm::Config, + + pallet_evm::Config + + pallet_admin_utils::Config + + pallet_balances::Config + + pallet_shield::Config + + pallet_subtensor_proxy::Config + + Send + + Sync + + scale_info::TypeInfo, R::AccountId: From<[u8; 32]>, + ::RuntimeOrigin: AsSystemOriginSigner + Clone, + ::RuntimeCall: From> + + GetDispatchInfo + + Dispatchable + + IsSubType> + + IsSubType> + + IsSubType> + + IsSubType>, + ::AddressMapping: AddressMapping, { const INDEX: u64 = 2056; } @@ -30,7 +55,24 @@ where R: frame_system::Config + pallet_subtensor::Config + pallet_subtensor_swap::Config - + pallet_evm::Config, + + pallet_evm::Config + + pallet_admin_utils::Config + + pallet_balances::Config + + pallet_shield::Config + + pallet_subtensor_proxy::Config + + Send + + Sync + + scale_info::TypeInfo, + R::AccountId: From<[u8; 32]>, + ::RuntimeOrigin: AsSystemOriginSigner + Clone, + ::RuntimeCall: From> + + GetDispatchInfo + + Dispatchable + + IsSubType> + + IsSubType> + + IsSubType> + + IsSubType>, + ::AddressMapping: AddressMapping, { #[precompile::public("getAlphaPrice(uint16)")] #[precompile::view] @@ -251,6 +293,183 @@ where Ok(price_eth) } + + #[precompile::public("setRecycleOrBurn(uint16,uint8)")] + fn set_recycle_or_burn( + handle: &mut impl PrecompileHandle, + netuid: u16, + mode: u8, + ) -> EvmResult<()> { + let recycle_or_burn = match mode { + 0 => pallet_subtensor::RecycleOrBurnEnum::Burn, + 1 => pallet_subtensor::RecycleOrBurnEnum::Recycle, + _ => { + return Err(PrecompileFailure::Error { + exit_status: ExitError::Other("invalid recycle-or-burn mode".into()), + }); + } + }; + let caller = handle.caller_account_id::(); + let call = pallet_admin_utils::Call::::sudo_set_recycle_or_burn { + netuid: NetUid::from(netuid), + recycle_or_burn, + }; + handle.try_dispatch_runtime_call::(call, RawOrigin::Signed(caller)) + } + + #[precompile::public("setBurnHalfLife(uint16,uint16)")] + fn set_burn_half_life( + handle: &mut impl PrecompileHandle, + netuid: u16, + burn_half_life: u16, + ) -> EvmResult<()> { + let caller = handle.caller_account_id::(); + let call = pallet_admin_utils::Call::::sudo_set_burn_half_life { + netuid: NetUid::from(netuid), + burn_half_life, + }; + handle.try_dispatch_runtime_call::(call, RawOrigin::Signed(caller)) + } + + #[precompile::public("setBurnIncreaseMultiplier(uint16,uint128)")] + fn set_burn_increase_multiplier( + handle: &mut impl PrecompileHandle, + netuid: u16, + raw_multiplier: u128, + ) -> EvmResult<()> { + let caller = handle.caller_account_id::(); + let call = pallet_admin_utils::Call::::sudo_set_burn_increase_mult { + netuid: NetUid::from(netuid), + burn_increase_mult: U64F64::from_bits(raw_multiplier), + }; + handle.try_dispatch_runtime_call::(call, RawOrigin::Signed(caller)) + } + + #[precompile::public("getEmissionAccounting(uint16,bytes32)")] + #[precompile::view] + fn get_emission_accounting( + handle: &mut impl PrecompileHandle, + netuid: u16, + hotkey: sp_core::H256, + ) -> EvmResult<(u64, u64, u64, u64, u64, u64, u64, u128, u64)> { + handle.record_db_reads::(9)?; + let netuid = NetUid::from(netuid); + let hotkey = R::AccountId::from(hotkey.0); + Ok(( + pallet_subtensor::AlphaDividendsPerSubnet::::get(netuid, &hotkey).to_u64(), + pallet_subtensor::RootAlphaDividendsPerSubnet::::get(netuid, &hotkey).to_u64(), + pallet_subtensor::LastHotkeyEmissionOnNetuid::::get(&hotkey, netuid).to_u64(), + pallet_subtensor::PendingServerEmission::::get(netuid).to_u64(), + pallet_subtensor::PendingValidatorEmission::::get(netuid).to_u64(), + pallet_subtensor::PendingRootAlphaDivs::::get(netuid).to_u64(), + pallet_subtensor::PendingOwnerCut::::get(netuid).to_u64(), + pallet_subtensor::MinerBurned::::get(netuid).to_bits(), + pallet_subtensor::RAORecycledForRegistration::::get(netuid).to_u64(), + )) + } + + #[precompile::public("getSubnetEconomicState(uint16)")] + #[precompile::view] + fn get_subnet_economic_state( + handle: &mut impl PrecompileHandle, + netuid: u16, + ) -> EvmResult<(bool, u128, u64, u64, u64)> { + handle.record_db_reads::(5)?; + let netuid = NetUid::from(netuid); + Ok(( + pallet_subtensor::SubnetEmissionEnabled::::get(netuid), + pallet_subtensor::RootProp::::get(netuid).to_bits(), + pallet_subtensor::SubnetExcessTao::::get(netuid).to_u64(), + pallet_subtensor::SubnetRootSellTao::::get(netuid).to_u64(), + pallet_subtensor::SubnetProtocolAlpha::::get(netuid).to_u64(), + )) + } + + #[precompile::public("getSubnetFlowState(uint16)")] + #[precompile::view] + fn get_subnet_flow_state( + handle: &mut impl PrecompileHandle, + netuid: u16, + ) -> EvmResult<(U256, bool, u64, U256, U256, bool, u64, U256)> { + handle.record_db_reads::(4)?; + let netuid = NetUid::from(netuid); + let tao_ema = pallet_subtensor::SubnetEmaTaoFlow::::get(netuid); + let protocol_ema = pallet_subtensor::SubnetEmaProtocolFlow::::get(netuid); + Ok(( + signed_i64_word(pallet_subtensor::SubnetTaoFlow::::get(netuid)), + tao_ema.is_some(), + tao_ema.map(|(block, _)| block).unwrap_or(0), + signed_i128_word(tao_ema.map(|(_, value)| value.to_bits()).unwrap_or(0)), + signed_i64_word(pallet_subtensor::SubnetProtocolFlow::::get(netuid)), + protocol_ema.is_some(), + protocol_ema.map(|(block, _)| block).unwrap_or(0), + signed_i128_word(protocol_ema.map(|(_, value)| value.to_bits()).unwrap_or(0)), + )) + } + + #[precompile::public("getEmissionGateConfig()")] + #[precompile::view] + fn get_emission_gate_config( + handle: &mut impl PrecompileHandle, + ) -> EvmResult<(u64, U256, bool, U256, u128, u128, u128, u128, u64)> { + handle.record_db_reads::(9)?; + Ok(( + #[allow(deprecated)] + pallet_subtensor::BlockEmission::::get(), + signed_i128_word(pallet_subtensor::SubnetMovingAlpha::::get().to_bits()), + pallet_subtensor::NetTaoFlowEnabled::::get(), + signed_i128_word(pallet_subtensor::TaoFlowCutoff::::get().to_bits()), + pallet_subtensor::FlowNormExponent::::get().to_bits(), + pallet_subtensor::EmissionBarQuantile::::get().to_bits(), + pallet_subtensor::EmissionGateExponent::::get().to_bits(), + pallet_subtensor::EmissionGateBar::::get().to_bits(), + pallet_subtensor::FlowEmaSmoothingFactor::::get(), + )) + } + + #[precompile::public("getSwapState(uint16)")] + #[precompile::view] + fn get_swap_state( + handle: &mut impl PrecompileHandle, + netuid: u16, + ) -> EvmResult<(u16, bool, u64, u64, u64)> { + handle.record_db_reads::(5)?; + let netuid = NetUid::from(netuid); + Ok(( + pallet_subtensor_swap::FeeRate::::get(netuid), + pallet_subtensor_swap::PalSwapInitialized::::get(netuid), + pallet_subtensor_swap::SwapBalancer::::get(netuid) + .get_quote_weight() + .deconstruct(), + pallet_subtensor_swap::BalancerTaoReservoir::::get(netuid).to_u64(), + pallet_subtensor_swap::BalancerAlphaReservoir::::get(netuid).to_u64(), + )) + } + + #[precompile::public("hasSwapMigrationRun(bytes)")] + #[precompile::view] + fn has_swap_migration_run( + handle: &mut impl PrecompileHandle, + migration_name: BoundedBytes>, + ) -> EvmResult { + handle.record_db_reads::(1)?; + let migration_name = BoundedVec::>::truncate_from(migration_name.into()); + Ok(pallet_subtensor_swap::HasMigrationRun::::get( + migration_name, + )) + } +} + +fn signed_i64_word(value: i64) -> U256 { + let mut encoded = [if value.is_negative() { 0xff } else { 0 }; 32]; + encoded[24..].copy_from_slice(&value.to_be_bytes()); + U256::from_big_endian(&encoded) +} + +fn signed_i128_word(value: i128) -> U256 { + let mut encoded = [if value.is_negative() { 0xff } else { 0 }; 32]; + encoded[16..].copy_from_slice(&value.to_be_bytes()); + U256::from_big_endian(&encoded) } #[cfg(test)] @@ -613,4 +832,135 @@ mod tests { ); }); } + + #[test] + fn alpha_state_views_return_typed_runtime_state() { + new_test_ext().execute_with(|| { + let precompiles = precompiles::>(); + let caller = addr_from_index(1); + let address = addr_from_index(AlphaPrecompile::::INDEX); + let netuid = NetUid::from(DYNAMIC_NETUID_U16); + let hotkey = sp_core::H256::repeat_byte(0x41); + + assert_view( + &precompiles, + caller, + address, + "getEmissionAccounting(uint16,bytes32)", + (DYNAMIC_NETUID_U16, hotkey), + ( + 0_u64, 0_u64, 0_u64, 0_u64, 0_u64, 0_u64, 0_u64, 0_u128, 0_u64, + ), + ); + + assert_view( + &precompiles, + caller, + address, + "getSubnetEconomicState(uint16)", + (DYNAMIC_NETUID_U16,), + ( + pallet_subtensor::SubnetEmissionEnabled::::get(netuid), + pallet_subtensor::RootProp::::get(netuid).to_bits(), + pallet_subtensor::SubnetExcessTao::::get(netuid).to_u64(), + pallet_subtensor::SubnetRootSellTao::::get(netuid).to_u64(), + pallet_subtensor::SubnetProtocolAlpha::::get(netuid).to_u64(), + ), + ); + + let tao_ema = pallet_subtensor::SubnetEmaTaoFlow::::get(netuid); + let protocol_ema = pallet_subtensor::SubnetEmaProtocolFlow::::get(netuid); + assert_view( + &precompiles, + caller, + address, + "getSubnetFlowState(uint16)", + (DYNAMIC_NETUID_U16,), + ( + signed_i64_word(pallet_subtensor::SubnetTaoFlow::::get(netuid)), + tao_ema.is_some(), + tao_ema.map(|(block, _)| block).unwrap_or(0), + signed_i128_word(tao_ema.map(|(_, value)| value.to_bits()).unwrap_or(0)), + signed_i64_word(pallet_subtensor::SubnetProtocolFlow::::get(netuid)), + protocol_ema.is_some(), + protocol_ema.map(|(block, _)| block).unwrap_or(0), + signed_i128_word(protocol_ema.map(|(_, value)| value.to_bits()).unwrap_or(0)), + ), + ); + + #[allow(deprecated)] + let emission_gate = ( + pallet_subtensor::BlockEmission::::get(), + signed_i128_word(pallet_subtensor::SubnetMovingAlpha::::get().to_bits()), + pallet_subtensor::NetTaoFlowEnabled::::get(), + signed_i128_word(pallet_subtensor::TaoFlowCutoff::::get().to_bits()), + pallet_subtensor::FlowNormExponent::::get().to_bits(), + pallet_subtensor::EmissionBarQuantile::::get().to_bits(), + pallet_subtensor::EmissionGateExponent::::get().to_bits(), + pallet_subtensor::EmissionGateBar::::get().to_bits(), + pallet_subtensor::FlowEmaSmoothingFactor::::get(), + ); + assert_view( + &precompiles, + caller, + address, + "getEmissionGateConfig()", + (), + emission_gate, + ); + + let balancer = pallet_subtensor_swap::SwapBalancer::::get(netuid); + assert_view( + &precompiles, + caller, + address, + "getSwapState(uint16)", + (DYNAMIC_NETUID_U16,), + ( + pallet_subtensor_swap::FeeRate::::get(netuid), + pallet_subtensor_swap::PalSwapInitialized::::get(netuid), + balancer.get_quote_weight().deconstruct(), + pallet_subtensor_swap::BalancerTaoReservoir::::get(netuid).to_u64(), + pallet_subtensor_swap::BalancerAlphaReservoir::::get(netuid).to_u64(), + ), + ); + + let migration_name = b"reader-test".to_vec(); + pallet_subtensor_swap::HasMigrationRun::::insert( + BoundedVec::truncate_from(migration_name.clone()), + true, + ); + assert_view( + &precompiles, + caller, + address, + "hasSwapMigrationRun(bytes)", + (BoundedBytes::>::from(migration_name),), + true, + ); + }); + } + + fn assert_view( + precompiles: &impl pallet_evm::PrecompileSet, + caller: sp_core::H160, + address: sp_core::H160, + signature: &str, + args: Args, + expected: Output, + ) where + Args: precompile_utils::solidity::Codec, + Output: precompile_utils::solidity::Codec, + { + use precompile_utils::testing::PrecompileTesterExt; + + precompiles + .prepare_test( + caller, + address, + encode_with_selector(selector_u32(signature), args), + ) + .with_static_call(true) + .execute_returns(expected); + } } diff --git a/precompiles/src/balance.rs b/precompiles/src/balance.rs index 36b80489e3..af990b40ad 100644 --- a/precompiles/src/balance.rs +++ b/precompiles/src/balance.rs @@ -1,8 +1,14 @@ use core::marker::PhantomData; -use pallet_evm::PrecompileHandle; -use precompile_utils::EvmResult; +use frame_support::{ + dispatch::{DispatchInfo, GetDispatchInfo, PostDispatchInfo}, + traits::{ConstU32, IsSubType}, +}; +use frame_system::RawOrigin; +use pallet_evm::{AddressMapping, PrecompileHandle}; +use precompile_utils::{EvmResult, prelude::BoundedVec}; use sp_core::{H256, U256}; +use sp_runtime::traits::{AsSystemOriginSigner, Dispatchable}; use crate::PrecompileExt; use crate::PrecompileHandleExt; @@ -11,9 +17,26 @@ pub struct BalancePrecompile(PhantomData); impl PrecompileExt for BalancePrecompile where - R: frame_system::Config + pallet_balances::Config + pallet_evm::Config, + R: frame_system::Config + + pallet_balances::Config + + pallet_evm::Config + + pallet_subtensor::Config + + pallet_shield::Config + + pallet_subtensor_proxy::Config + + Send + + Sync + + scale_info::TypeInfo, R::AccountId: From<[u8; 32]>, - ::Balance: Into, + ::RuntimeOrigin: AsSystemOriginSigner + Clone, + ::RuntimeCall: From> + + GetDispatchInfo + + Dispatchable + + IsSubType> + + IsSubType> + + IsSubType> + + IsSubType>, + ::Balance: Into + TryFrom, + ::AddressMapping: AddressMapping, { const INDEX: u64 = 2062; } @@ -21,9 +44,26 @@ where #[precompile_utils::precompile] impl BalancePrecompile where - R: frame_system::Config + pallet_balances::Config + pallet_evm::Config, + R: frame_system::Config + + pallet_balances::Config + + pallet_evm::Config + + pallet_subtensor::Config + + pallet_shield::Config + + pallet_subtensor_proxy::Config + + Send + + Sync + + scale_info::TypeInfo, R::AccountId: From<[u8; 32]>, - ::Balance: Into, + ::RuntimeOrigin: AsSystemOriginSigner + Clone, + ::RuntimeCall: From> + + GetDispatchInfo + + Dispatchable + + IsSubType> + + IsSubType> + + IsSubType> + + IsSubType>, + ::Balance: Into + TryFrom, + ::AddressMapping: AddressMapping, { #[precompile::public("getFreeBalance(bytes32)")] #[precompile::view] @@ -32,6 +72,47 @@ where let coldkey = R::AccountId::from(coldkey.0); Ok(pallet_balances::Pallet::::free_balance(&coldkey).into()) } + + #[precompile::public("getTotalIssuance()")] + #[precompile::view] + fn get_total_issuance(handle: &mut impl PrecompileHandle) -> EvmResult { + handle.record_db_reads::(1)?; + Ok(pallet_balances::Pallet::::total_issuance().into()) + } + + #[precompile::public("burnBalance(uint256,bool)")] + fn burn_balance( + handle: &mut impl PrecompileHandle, + amount: U256, + keep_alive: bool, + ) -> EvmResult<()> { + let caller = handle.caller_account_id::(); + let call = pallet_balances::Call::::burn { + value: amount + .try_into() + .map_err(|_| fp_evm::PrecompileFailure::Error { + exit_status: fp_evm::ExitError::Other( + "balance amount does not fit runtime".into(), + ), + })?, + keep_alive, + }; + handle.try_dispatch_runtime_call::(call, RawOrigin::Signed(caller)) + } + + #[precompile::public("upgradeAccounts(bytes32[])")] + fn upgrade_accounts( + handle: &mut impl PrecompileHandle, + accounts: BoundedVec>, + ) -> EvmResult<()> { + let caller = handle.caller_account_id::(); + let who = Vec::::from(accounts) + .into_iter() + .map(|account| R::AccountId::from(account.0)) + .collect(); + let call = pallet_balances::Call::::upgrade_accounts { who }; + handle.try_dispatch_runtime_call::(call, RawOrigin::Signed(caller)) + } } #[cfg(test)] @@ -87,6 +168,17 @@ mod tests { .with_static_call(true) .expect_cost(RuntimeHelper::::db_read_gas_cost()) .execute_returns_raw(abi_word(U256::from(amount))); + + let total_issuance: U256 = pallet_balances::Pallet::::total_issuance().into(); + precompiles::>() + .prepare_test( + caller, + addr_from_index(BalancePrecompile::::INDEX), + selector_u32("getTotalIssuance()").to_be_bytes().to_vec(), + ) + .with_static_call(true) + .expect_cost(RuntimeHelper::::db_read_gas_cost()) + .execute_returns(total_issuance); }); } diff --git a/precompiles/src/balance_transfer.rs b/precompiles/src/balance_transfer.rs index d8d10970a3..5bf66af47c 100644 --- a/precompiles/src/balance_transfer.rs +++ b/precompiles/src/balance_transfer.rs @@ -3,7 +3,7 @@ use core::marker::PhantomData; use frame_support::dispatch::{DispatchInfo, GetDispatchInfo, PostDispatchInfo}; use frame_support::traits::IsSubType; use frame_system::RawOrigin; -use pallet_evm::PrecompileHandle; +use pallet_evm::{AddressMapping, PrecompileHandle}; use precompile_utils::EvmResult; use sp_core::{H256, U256}; use sp_runtime::traits::{AsSystemOriginSigner, Dispatchable, StaticLookup, UniqueSaturatedInto}; @@ -36,6 +36,7 @@ where + Dispatchable, <::Lookup as StaticLookup>::Source: From, ::Balance: TryFrom, + ::AddressMapping: AddressMapping, { const INDEX: u64 = 2048; } @@ -65,6 +66,7 @@ where + Dispatchable, <::Lookup as StaticLookup>::Source: From, ::Balance: TryFrom, + ::AddressMapping: AddressMapping, { #[precompile::public("transfer(bytes32)")] #[precompile::payable] @@ -84,4 +86,76 @@ where handle.try_dispatch_runtime_call::(call, RawOrigin::Signed(Self::account_id())) } + + #[precompile::public("transferKeepAlive(bytes32,uint256)")] + fn transfer_keep_alive( + handle: &mut impl PrecompileHandle, + address: H256, + amount: U256, + ) -> EvmResult<()> { + let caller = handle.caller_account_id::(); + let call = pallet_balances::Call::::transfer_keep_alive { + dest: R::AccountId::from(address.0).into(), + value: amount + .try_into() + .map_err(|_| fp_evm::PrecompileFailure::Error { + exit_status: fp_evm::ExitError::Other( + "balance amount does not fit runtime".into(), + ), + })?, + }; + handle.try_dispatch_runtime_call::(call, RawOrigin::Signed(caller)) + } + + #[precompile::public("transferAll(bytes32,bool)")] + fn transfer_all( + handle: &mut impl PrecompileHandle, + address: H256, + keep_alive: bool, + ) -> EvmResult<()> { + let caller = handle.caller_account_id::(); + let call = pallet_balances::Call::::transfer_all { + dest: R::AccountId::from(address.0).into(), + keep_alive, + }; + handle.try_dispatch_runtime_call::(call, RawOrigin::Signed(caller)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::mock::{ + AccountId, Runtime, addr_from_index, fund_account, mapped_account, new_test_ext, + precompiles, selector_u32, + }; + use precompile_utils::{solidity::encode_with_selector, testing::PrecompileTesterExt}; + + #[test] + fn transfer_keep_alive_dispatches_as_mapped_caller() { + new_test_ext().execute_with(|| { + let caller = addr_from_index(0x8100); + let caller_account = mapped_account(caller); + let destination = RUNTIME_DESTINATION; + fund_account(&caller_account, 1_000); + + precompiles::>() + .prepare_test( + caller, + addr_from_index(BalanceTransferPrecompile::::INDEX), + encode_with_selector( + selector_u32("transferKeepAlive(bytes32,uint256)"), + (destination, U256::from(100u64)), + ), + ) + .execute_returns(()); + + assert_eq!( + pallet_balances::Pallet::::free_balance(AccountId::from(destination.0)), + 100u64.into() + ); + }); + } + + const RUNTIME_DESTINATION: H256 = H256([0x44; 32]); } diff --git a/precompiles/src/crowdloan.rs b/precompiles/src/crowdloan.rs index 1c66d941ca..a5152d14c6 100644 --- a/precompiles/src/crowdloan.rs +++ b/precompiles/src/crowdloan.rs @@ -244,6 +244,21 @@ where handle.try_dispatch_runtime_call::(call, RawOrigin::Signed(account_id)) } + + #[precompile::public("setMaxContribution(uint32,bool,uint64)")] + fn set_max_contribution( + handle: &mut impl PrecompileHandle, + crowdloan_id: u32, + has_max_contribution: bool, + max_contribution: u64, + ) -> EvmResult<()> { + let account_id = handle.caller_account_id::(); + let call = pallet_crowdloan::Call::::set_max_contribution { + crowdloan_id, + new_max_contribution: has_max_contribution.then_some(max_contribution.into()), + }; + handle.try_dispatch_runtime_call::(call, RawOrigin::Signed(account_id)) + } } #[derive(Codec)] diff --git a/precompiles/src/drand.rs b/precompiles/src/drand.rs new file mode 100644 index 0000000000..ee98d5e821 --- /dev/null +++ b/precompiles/src/drand.rs @@ -0,0 +1,185 @@ +use core::marker::PhantomData; + +use alloc::vec::Vec; +use fp_evm::{ExitError, PrecompileFailure}; +use frame_support::BoundedVec; +use pallet_evm::PrecompileHandle; +use precompile_utils::{ + EvmResult, + prelude::{BoundedBytes, UnboundedBytes}, +}; +use sp_core::ConstU32; + +use crate::{PrecompileExt, PrecompileHandleExt}; + +type BeaconConfiguration = ( + UnboundedBytes, + u32, + u32, + UnboundedBytes, + UnboundedBytes, + UnboundedBytes, + UnboundedBytes, +); + +pub struct DrandPrecompile(PhantomData); + +impl PrecompileExt for DrandPrecompile +where + R: frame_system::Config + pallet_evm::Config + pallet_drand::Config, + R::AccountId: From<[u8; 32]>, + frame_system::pallet_prelude::BlockNumberFor: TryInto, +{ + const INDEX: u64 = 2064; +} + +#[precompile_utils::precompile] +impl DrandPrecompile +where + R: frame_system::Config + pallet_evm::Config + pallet_drand::Config, + R::AccountId: From<[u8; 32]>, + frame_system::pallet_prelude::BlockNumberFor: TryInto, +{ + #[precompile::public("getBeaconConfig()")] + #[precompile::view] + fn get_beacon_config(handle: &mut impl PrecompileHandle) -> EvmResult { + handle.record_db_reads::(1)?; + let config = pallet_drand::BeaconConfig::::get(); + Ok(( + config.public_key.into_inner().into(), + config.period, + config.genesis_time, + config.hash.into_inner().into(), + config.group_hash.into_inner().into(), + config.scheme_id.into_inner().into(), + config.metadata.beacon_id.into_inner().into(), + )) + } + + #[precompile::public("getPulse(uint64)")] + #[precompile::view] + fn get_pulse( + handle: &mut impl PrecompileHandle, + round: u64, + ) -> EvmResult<(bool, u64, UnboundedBytes, UnboundedBytes)> { + handle.record_db_reads::(1)?; + match pallet_drand::Pulses::::get(round) { + Some(pulse) => Ok(( + true, + pulse.round, + pulse.randomness.into_inner().into(), + pulse.signature.into_inner().into(), + )), + None => Ok(( + false, + round, + UnboundedBytes::default(), + UnboundedBytes::default(), + )), + } + } + + #[precompile::public("getStoredRoundRange()")] + #[precompile::view] + fn get_stored_round_range(handle: &mut impl PrecompileHandle) -> EvmResult<(u64, u64)> { + handle.record_db_reads::(2)?; + Ok(( + pallet_drand::OldestStoredRound::::get(), + pallet_drand::LastStoredRound::::get(), + )) + } + + #[precompile::public("getNextUnsignedAt()")] + #[precompile::view] + fn get_next_unsigned_at(handle: &mut impl PrecompileHandle) -> EvmResult { + handle.record_db_reads::(1)?; + pallet_drand::Pallet::::next_unsigned_at() + .try_into() + .map_err(|_| conversion_error("drand next unsigned block")) + } + + #[precompile::public("hasMigrationRun(bytes)")] + #[precompile::view] + fn has_migration_run( + handle: &mut impl PrecompileHandle, + key: BoundedBytes>, + ) -> EvmResult { + handle.record_db_reads::(1)?; + let key = BoundedVec::>::try_from(Vec::::from(key)) + .map_err(|_| conversion_error("drand migration key"))?; + Ok(pallet_drand::HasMigrationRun::::get(key)) + } +} + +fn conversion_error(field: &'static str) -> PrecompileFailure { + PrecompileFailure::Error { + exit_status: ExitError::Other(field.into()), + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::mock::{Runtime, addr_from_index, new_test_ext, precompiles, selector_u32}; + use precompile_utils::{ + prelude::RuntimeHelper, + solidity::{encode_return_value, encode_with_selector}, + testing::PrecompileTesterExt, + }; + + #[test] + fn address_selectors_and_empty_state_are_stable() { + new_test_ext().execute_with(|| { + assert_eq!(DrandPrecompile::::INDEX, 2064); + let precompiles = precompiles::>(); + let caller = addr_from_index(1); + let address = addr_from_index(2064); + let read_cost = RuntimeHelper::::db_read_gas_cost(); + + precompiles + .prepare_test( + caller, + address, + encode_with_selector(selector_u32("getPulse(uint64)"), (42u64,)), + ) + .with_static_call(true) + .expect_cost(read_cost) + .execute_returns_raw(encode_return_value(( + false, + 42u64, + UnboundedBytes::default(), + UnboundedBytes::default(), + ))); + precompiles + .prepare_test( + caller, + address, + encode_with_selector(selector_u32("getStoredRoundRange()"), ()), + ) + .with_static_call(true) + .expect_cost(read_cost.saturating_mul(2)) + .execute_returns_raw(encode_return_value((0u64, 0u64))); + precompiles + .prepare_test( + caller, + address, + encode_with_selector(selector_u32("getNextUnsignedAt()"), ()), + ) + .with_static_call(true) + .expect_cost(read_cost) + .execute_returns_raw(encode_return_value(0u64)); + precompiles + .prepare_test( + caller, + address, + encode_with_selector( + selector_u32("hasMigrationRun(bytes)"), + (BoundedBytes::>::from(Vec::::new()),), + ), + ) + .with_static_call(true) + .expect_cost(read_cost) + .execute_returns_raw(encode_return_value(false)); + }); + } +} diff --git a/precompiles/src/leasing.rs b/precompiles/src/leasing.rs index 5ebf03cb3c..4dcb371ca2 100644 --- a/precompiles/src/leasing.rs +++ b/precompiles/src/leasing.rs @@ -122,6 +122,23 @@ where Ok(lease_id.into()) } + #[precompile::public("getNextLeaseId()")] + #[precompile::view] + fn get_next_lease_id(handle: &mut impl PrecompileHandle) -> EvmResult { + handle.record_db_reads::(1)?; + Ok(pallet_subtensor::NextSubnetLeaseId::::get()) + } + + #[precompile::public("getAccumulatedLeaseDividends(uint32)")] + #[precompile::view] + fn get_accumulated_lease_dividends( + handle: &mut impl PrecompileHandle, + lease_id: u32, + ) -> EvmResult { + handle.record_db_reads::(1)?; + Ok(pallet_subtensor::AccumulatedLeaseDividends::::get(lease_id).into()) + } + #[precompile::public("createLeaseCrowdloan(uint64,uint64,uint64,uint32,uint8,bool,uint32)")] #[precompile::payable] #[allow(clippy::too_many_arguments)] @@ -177,6 +194,15 @@ where handle.try_dispatch_runtime_call::(call, RawOrigin::Signed(who)) } + + #[precompile::public("startCall(uint16)")] + fn start_call(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult<()> { + let who = handle.caller_account_id::(); + let call = pallet_subtensor::Call::::start_call { + netuid: NetUid::from(netuid), + }; + handle.try_dispatch_runtime_call::(call, RawOrigin::Signed(who)) + } } #[derive(Codec)] @@ -325,6 +351,32 @@ mod tests { get_lease(caller, lease_id, expected_lease_info(lease_id)); let precompile_addr = addr_from_index(LeasingPrecompile::::INDEX); + precompiles::>() + .prepare_test( + caller, + precompile_addr, + selector_u32("getNextLeaseId()").to_be_bytes().to_vec(), + ) + .with_static_call(true) + .execute_returns(pallet_subtensor::NextSubnetLeaseId::::get()); + + let accumulated_dividends = 321_u64; + pallet_subtensor::AccumulatedLeaseDividends::::insert( + lease_id, + subtensor_runtime_common::AlphaBalance::from(accumulated_dividends), + ); + precompiles::>() + .prepare_test( + caller, + precompile_addr, + encode_with_selector( + selector_u32("getAccumulatedLeaseDividends(uint32)"), + (lease_id,), + ), + ) + .with_static_call(true) + .execute_returns(accumulated_dividends); + precompiles::>() .prepare_test( caller, diff --git a/precompiles/src/lib.rs b/precompiles/src/lib.rs index d70c3b5eea..c67ef77b1a 100644 --- a/precompiles/src/lib.rs +++ b/precompiles/src/lib.rs @@ -10,6 +10,7 @@ pub use alpha::AlphaPrecompile; pub use balance::BalancePrecompile; pub use balance_transfer::BalanceTransferPrecompile; pub use crowdloan::CrowdloanPrecompile; +pub use drand::DrandPrecompile; pub use ed25519::Ed25519Verify; pub use extensions::PrecompileExt; use fp_evm::{ExitError, PrecompileFailure}; @@ -33,6 +34,9 @@ use pallet_evm_precompile_sha3fips::Sha3FIPS256; use pallet_evm_precompile_simple::{ECRecover, ECRecoverPublicKey, Identity, Ripemd160, Sha256}; use pallet_subtensor_proxy as pallet_proxy; pub use proxy::ProxyPrecompile; +pub use registry::PrecompileRegistry; +pub use runtime_configuration::RuntimeConfigurationPrecompile; +pub use scheduler::SchedulerPrecompile; use sp_core::{H160, U256, crypto::ByteArray}; use sp_runtime::traits::{AsSystemOriginSigner, Dispatchable, StaticLookup}; pub use sr25519::Sr25519Verify; @@ -40,6 +44,7 @@ pub use staking::{StakingPrecompile, StakingPrecompileV2}; pub use storage_query::StorageQueryPrecompile; pub use subnet::SubnetPrecompile; use subtensor_runtime_common::ProxyType; +pub use timestamp::TimestampPrecompile; pub use uid_lookup::UidLookupPrecompile; pub use voting_power::VotingPowerPrecompile; @@ -48,16 +53,21 @@ mod alpha; mod balance; mod balance_transfer; mod crowdloan; +mod drand; mod ed25519; mod extensions; mod leasing; mod metagraph; mod neuron; mod proxy; +mod registry; +mod runtime_configuration; +mod scheduler; mod sr25519; mod staking; mod storage_query; mod subnet; +mod timestamp; mod uid_lookup; mod voting_power; @@ -76,12 +86,19 @@ where + pallet_subtensor_swap::Config + pallet_proxy::Config + pallet_crowdloan::Config + + pallet_drand::Config + + pallet_evm_chain_id::Config + + pallet_scheduler::Config + pallet_shield::Config + pallet_subtensor_proxy::Config + + pallet_timestamp::Config + Send + Sync + scale_info::TypeInfo, R::AccountId: From<[u8; 32]> + ByteArray + Into<[u8; 32]>, + R::Hash: AsRef<[u8]>, + ::Moment: TryInto, + pallet_scheduler::BlockNumberFor: TryFrom + TryInto, ::RuntimeOrigin: AsSystemOriginSigner + Clone, ::RuntimeCall: From> + From> @@ -96,6 +113,7 @@ where + IsSubType>, ::AddressMapping: AddressMapping, ::Balance: Into + TryFrom, + runtime_configuration::ProxyBalanceOf: Into, <::Lookup as StaticLookup>::Source: From, { fn default() -> Self { @@ -113,12 +131,19 @@ where + pallet_subtensor_swap::Config + pallet_proxy::Config + pallet_crowdloan::Config + + pallet_drand::Config + + pallet_evm_chain_id::Config + + pallet_scheduler::Config + pallet_shield::Config + pallet_subtensor_proxy::Config + + pallet_timestamp::Config + Send + Sync + scale_info::TypeInfo, R::AccountId: From<[u8; 32]> + ByteArray + Into<[u8; 32]>, + R::Hash: AsRef<[u8]>, + ::Moment: TryInto, + pallet_scheduler::BlockNumberFor: TryFrom + TryInto, ::RuntimeOrigin: AsSystemOriginSigner + Clone, ::RuntimeCall: From> + From> @@ -133,13 +158,14 @@ where + IsSubType>, ::AddressMapping: AddressMapping, ::Balance: Into + TryFrom, + runtime_configuration::ProxyBalanceOf: Into, <::Lookup as StaticLookup>::Source: From, { pub fn new() -> Self { Self(Default::default()) } - pub fn used_addresses() -> [H160; 28] { + pub fn used_addresses() -> [H160; 33] { [ hash(1), hash(2), @@ -169,6 +195,11 @@ where hash(ProxyPrecompile::::INDEX), hash(AddressMappingPrecompile::::INDEX), hash(BalancePrecompile::::INDEX), + hash(SchedulerPrecompile::::INDEX), + hash(DrandPrecompile::::INDEX), + hash(TimestampPrecompile::::INDEX), + hash(RuntimeConfigurationPrecompile::::INDEX), + hash(PrecompileRegistry::::INDEX), ] } } @@ -182,12 +213,19 @@ where + pallet_subtensor_swap::Config + pallet_proxy::Config + pallet_crowdloan::Config + + pallet_drand::Config + + pallet_evm_chain_id::Config + + pallet_scheduler::Config + pallet_shield::Config + pallet_subtensor_proxy::Config + + pallet_timestamp::Config + Send + Sync + scale_info::TypeInfo, R::AccountId: From<[u8; 32]> + ByteArray + Into<[u8; 32]>, + R::Hash: AsRef<[u8]>, + ::Moment: TryInto, + pallet_scheduler::BlockNumberFor: TryFrom + TryInto, ::RuntimeOrigin: AsSystemOriginSigner + Clone, ::RuntimeCall: From> + From> @@ -205,6 +243,7 @@ where From>>, ::AddressMapping: AddressMapping, ::Balance: Into + TryFrom, + runtime_configuration::ProxyBalanceOf: Into, <::Lookup as StaticLookup>::Source: From, { fn execute(&self, handle: &mut impl PrecompileHandle) -> Option { @@ -280,6 +319,27 @@ where a if a == hash(BalancePrecompile::::INDEX) => { BalancePrecompile::::try_execute::(handle, PrecompileEnum::AccountBalance) } + a if a == hash(SchedulerPrecompile::::INDEX) => { + SchedulerPrecompile::::try_execute::(handle, PrecompileEnum::Scheduler) + } + a if a == hash(DrandPrecompile::::INDEX) => { + DrandPrecompile::::try_execute::(handle, PrecompileEnum::Drand) + } + a if a == hash(TimestampPrecompile::::INDEX) => { + TimestampPrecompile::::try_execute::(handle, PrecompileEnum::Timestamp) + } + a if a == hash(RuntimeConfigurationPrecompile::::INDEX) => { + RuntimeConfigurationPrecompile::::try_execute::( + handle, + PrecompileEnum::RuntimeConfiguration, + ) + } + a if a == hash(PrecompileRegistry::::INDEX) => { + PrecompileRegistry::::try_execute::( + handle, + PrecompileEnum::PrecompileRegistry, + ) + } _ => None, } } @@ -317,3 +377,431 @@ fn parse_slice(data: &[u8], from: usize, to: usize) -> Result<&[u8], PrecompileF }) } } + +#[cfg(test)] +mod address_and_selector_tests { + use super::*; + use crate::mock::{Runtime, selector_u32}; + use alloc::collections::BTreeSet; + use codec::Encode; + + #[test] + fn precompile_addresses_are_unique_and_new_addresses_are_locked() { + let addresses = Precompiles::::used_addresses(); + assert_eq!(addresses.len(), BTreeSet::from_iter(addresses).len()); + assert_eq!(SchedulerPrecompile::::INDEX, 2063); + assert_eq!(DrandPrecompile::::INDEX, 2064); + assert_eq!(TimestampPrecompile::::INDEX, 2065); + assert_eq!(RuntimeConfigurationPrecompile::::INDEX, 2066); + assert_eq!(PrecompileRegistry::::INDEX, 2067); + } + + #[test] + fn precompile_enable_keys_preserve_existing_scale_indices() { + let variants = [ + (PrecompileEnum::BalanceTransfer, 0), + (PrecompileEnum::Staking, 1), + (PrecompileEnum::Subnet, 2), + (PrecompileEnum::Metagraph, 3), + (PrecompileEnum::Neuron, 4), + (PrecompileEnum::UidLookup, 5), + (PrecompileEnum::Alpha, 6), + (PrecompileEnum::Crowdloan, 7), + (PrecompileEnum::Proxy, 8), + (PrecompileEnum::Leasing, 9), + (PrecompileEnum::AddressMapping, 10), + (PrecompileEnum::VotingPower, 11), + (PrecompileEnum::AccountBalance, 12), + (PrecompileEnum::Scheduler, 13), + (PrecompileEnum::Drand, 14), + (PrecompileEnum::Timestamp, 15), + (PrecompileEnum::RuntimeConfiguration, 16), + (PrecompileEnum::PrecompileRegistry, 17), + ]; + + for (variant, expected_index) in variants { + assert_eq!(variant.encode(), [expected_index]); + } + } + + #[test] + fn new_precompile_selectors_are_locked() { + for signature in [ + "getIncompleteSince()", + "getScheduledCallCount(uint64)", + "getScheduledCall(uint64,uint32)", + "getRetry(uint64,uint32)", + "getTaskAddress(bytes32)", + ] { + assert!( + scheduler::SchedulerPrecompileCall::::supports_selector(selector_u32( + signature + )), + "missing Scheduler selector {signature}" + ); + } + for signature in [ + "getBeaconConfig()", + "getPulse(uint64)", + "getStoredRoundRange()", + "getNextUnsignedAt()", + "hasMigrationRun(bytes)", + ] { + assert!( + drand::DrandPrecompileCall::::supports_selector(selector_u32(signature)), + "missing Drand selector {signature}" + ); + } + for signature in ["getTimestamp()", "wasUpdatedThisBlock()"] { + assert!( + timestamp::TimestampPrecompileCall::::supports_selector(selector_u32( + signature + )), + "missing Timestamp selector {signature}" + ); + } + let runtime_configuration_signatures = [ + "getEvmChainId()", + "getTransactionRateLimit()", + "getSubtensorEconomicConstants()", + "getSubtensorSubnetConstants()", + "getSubtensorConsensusConstants()", + "getSubtensorRegistrationConstants()", + "getSubtensorDelegationConstants()", + "getSubtensorRateLimitConstants()", + "getSubtensorProtocolConstants()", + "getSubtensorSystemAccounts()", + "getBalancesConstants()", + "getProxyConstants()", + "getSchedulerConstants()", + "getDrandConstants()", + "getCrowdloanConstants()", + "getSwapConstants()", + "getTimestampConstants()", + "getAdminConstants()", + ]; + assert_eq!( + runtime_configuration_signatures.len(), + runtime_configuration_signatures + .iter() + .map(|signature| selector_u32(signature)) + .collect::>() + .len(), + "runtime-configuration selectors collide" + ); + for signature in runtime_configuration_signatures { + assert!( + runtime_configuration::RuntimeConfigurationPrecompileCall::::supports_selector( + selector_u32(signature) + ), + "missing runtime-configuration selector {signature}" + ); + } + assert!( + registry::PrecompileRegistryCall::::supports_selector(selector_u32( + "getPrecompileStatus(address,bytes4)" + )) + ); + } + + #[test] + fn added_domain_selectors_are_locked() { + for signature in [ + "transferKeepAlive(bytes32,uint256)", + "transferAll(bytes32,bool)", + ] { + assert!( + balance_transfer::BalanceTransferPrecompileCall::::supports_selector( + selector_u32(signature) + ) + ); + } + for signature in ["burnBalance(uint256,bool)", "upgradeAccounts(bytes32[])"] { + assert!( + balance::BalancePrecompileCall::::supports_selector(selector_u32( + signature + )) + ); + } + for signature in [ + "enableVotingPowerTracking(uint16)", + "disableVotingPowerTracking(uint16)", + ] { + assert!( + voting_power::VotingPowerPrecompileCall::::supports_selector( + selector_u32(signature) + ) + ); + } + assert!( + leasing::LeasingPrecompileCall::::supports_selector(selector_u32( + "startCall(uint16)" + )) + ); + assert!( + crowdloan::CrowdloanPrecompileCall::::supports_selector(selector_u32( + "setMaxContribution(uint32,bool,uint64)" + )) + ); + for signature in [ + "setRecycleOrBurn(uint16,uint8)", + "setBurnHalfLife(uint16,uint16)", + "setBurnIncreaseMultiplier(uint16,uint128)", + ] { + assert!(alpha::AlphaPrecompileCall::::supports_selector( + selector_u32(signature) + )); + } + for signature in [ + "announce(bytes32,bytes32)", + "removeAnnouncement(bytes32,bytes32)", + "rejectAnnouncement(bytes32,bytes32)", + "setRealPaysFee(bytes32,bool)", + ] { + assert!(proxy::ProxyPrecompileCall::::supports_selector( + selector_u32(signature) + )); + } + for signature in [ + "setSubnetIdentity(uint16,string,string,string,string,string,string,string,string)", + "updateSubnetSymbol(uint16,string)", + "triggerEpoch(uint16)", + "setBondsPenalty(uint16,uint16)", + "setMaxAllowedUids(uint16,uint16)", + "setMaxBurnV2(uint16,uint64)", + "setMechanismCount(uint16,uint8)", + "setMechanismEmissionSplit(uint16,bool,uint16[])", + "setMinBurnV2(uint16,uint64)", + "setOwnerCutEnabled(uint16,bool)", + "setOwnerImmuneNeuronLimit(uint16,uint16)", + "setTempo(uint16,uint16)", + "trimToMaxAllowedUids(uint16,uint16)", + ] { + assert!( + subnet::SubnetPrecompileCall::::supports_selector(selector_u32(signature)), + "missing Subnet selector {signature}" + ); + } + for signature in [ + "decreaseTake(bytes32,uint16)", + "increaseTake(bytes32,uint16)", + "setChildkeyTake(bytes32,uint16,uint16)", + "unstakeAll(bytes32)", + "unstakeAllAlpha(bytes32)", + "swapStake(bytes32,uint16,uint16,uint64)", + "swapStakeLimit(bytes32,uint16,uint16,uint64,uint64,bool)", + "recycleAlpha(bytes32,uint64,uint16)", + "setColdkeyAutoStakeHotkey(uint16,bytes32)", + "claimRoot(uint16[])", + "setRootClaimType(uint8,uint16[])", + "setRootClaimThreshold(uint16,uint64)", + "addStakeBurn(bytes32,uint16,uint64,bool,uint64)", + "setAutoParentDelegationEnabled(bytes32,bool)", + "transferStakeAndHotkey(bytes32,bytes32,bytes32,uint16,uint16,uint64)", + "addCollateral(uint16,bytes32,uint64,uint64)", + "setMinCollateral(uint16,bytes32,uint64)", + "setMinChildkeyTakePerSubnet(uint16,uint16)", + "setCollateralLockShare(uint16,uint16)", + "setCollateralDrainRatio(uint16,uint128)", + ] { + assert!( + staking::StakingPrecompileV2Call::::supports_selector(selector_u32( + signature + )), + "missing Staking V2 selector {signature}" + ); + } + for signature in [ + "setMechanismWeights(uint16,uint8,uint16[],uint16[],uint64)", + "batchSetWeights(uint16[],uint16[][],uint16[][],uint64[])", + "commitMechanismWeights(uint16,uint8,bytes32)", + "batchCommitWeights(uint16[],bytes32[])", + "revealMechanismWeights(uint16,uint8,uint16[],uint16[],uint16[],uint64)", + "commitCrv3MechanismWeights(uint16,uint8,bytes,uint64)", + "batchRevealWeights(uint16,uint16[][],uint16[][],uint16[][],uint64[])", + "commitTimelockedWeights(uint16,bytes,uint64,uint16)", + "commitTimelockedMechanismWeights(uint16,uint8,bytes,uint64,uint16)", + "register(uint16,uint64,uint64,bytes,bytes32,bytes32)", + "rootRegister(bytes32)", + "swapHotkey(bytes32,bytes32,bool,uint16)", + "swapHotkeyV2(bytes32,bytes32,bool,uint16,bool)", + "setChildren(bytes32,uint16,uint64[],bytes32[])", + "setIdentity(string,string,string,string,string,string,string)", + "tryAssociateHotkey(bytes32)", + "associateEvmKey(uint16,address,uint64,bytes)", + "announceColdkeySwap(bytes32)", + "executeAnnouncedColdkeySwap(bytes32)", + "disputeColdkeySwap()", + "clearColdkeySwapAnnouncement()", + ] { + assert!( + neuron::NeuronPrecompileCall::::supports_selector(selector_u32(signature)), + "missing Neuron selector {signature}" + ); + } + } + + #[test] + fn state_reader_selectors_are_locked() { + for signature in [ + "getDelegate(bytes32)", + "getChildkeyTake(bytes32,uint16)", + "getPendingChildKeys(bytes32,uint16)", + "getChildKeys(bytes32,uint16)", + "getParentKeys(bytes32,uint16)", + "getPendingChildKeyCooldown()", + "getTakeLimits()", + "getMinChildkeyTakePerSubnet(uint16)", + "getHotkeyOwner(bytes32)", + "getOwnedHotkeys(bytes32)", + "getAutoStakeDestination(bytes32,uint16)", + "getAutoStakeDestinationColdkeys(bytes32,uint16)", + "getHotkeySuccessor(bytes32,uint16)", + "getHotkeyRoot(bytes32,uint16)", + "getColdkeySuccessor(bytes32)", + "getColdkeyRoot(bytes32)", + "getColdkeySwapStatus(bytes32)", + "getColdkeySwapDelays()", + "getLastHotkeySwapOnSubnet(bytes32,uint16)", + "getStakeAccounting()", + "getMinerCollateral(uint16,bytes32,bytes32)", + "getColdkeyCollateral(uint16,bytes32)", + "getCollateralConfig(uint16)", + ] { + assert!( + staking::StakingPrecompileV2Call::::supports_selector(selector_u32( + signature + )), + "missing Staking V2 reader selector {signature}" + ); + } + + for signature in [ + "getRegisteredSubnetCounter(uint16)", + "getSubnetDissolutionStatus(uint16)", + "getSubnetMetadata(uint16)", + "getSubnetCapacityConfig(uint16)", + "getMechanismEmissionSplit(uint16)", + "getBurnConfig(uint16)", + "getGlobalNetworkLimits()", + "getGlobalRateLimits()", + "getGlobalProtocolConfig()", + ] { + assert!( + subnet::SubnetPrecompileCall::::supports_selector(selector_u32(signature)), + "missing Subnet reader selector {signature}" + ); + } + + for signature in [ + "getEmissionAccounting(uint16,bytes32)", + "getSubnetEconomicState(uint16)", + "getSubnetFlowState(uint16)", + "getEmissionGateConfig()", + "getSwapState(uint16)", + "hasSwapMigrationRun(bytes)", + ] { + assert!( + alpha::AlphaPrecompileCall::::supports_selector(selector_u32(signature)), + "missing Alpha reader selector {signature}" + ); + } + + for signature in [ + "getUid(uint16,bytes32)", + "isNetworkMember(bytes32,uint16)", + "getWeights(uint16,uint16)", + "getBonds(uint16,uint16)", + "getBlockAtRegistration(uint16,uint16)", + "getNeuronCertificate(uint16,bytes32)", + "getPrometheus(uint16,bytes32)", + "getChainIdentity(bytes32)", + "getSubnetIdentity(uint16)", + "getLoadedEmission(uint16)", + "getTransactionKeyLastBlock(bytes32,uint16,uint16)", + "getLegacyTransactionRateBlocks(bytes32)", + "getWeightCommit(uint16,bytes32,uint32)", + "getWeightCommitCount(uint16,bytes32)", + "getTimelockedWeightCommit(uint16,uint64,uint32)", + "getTimelockedWeightCommitCount(uint16,uint64)", + "getLegacyTimelockedWeightCommit(uint8,uint16,uint64,uint32)", + "getLegacyTimelockedWeightCommitCount(uint8,uint16,uint64)", + ] { + assert!( + neuron::NeuronPrecompileCall::::supports_selector(selector_u32(signature)), + "missing Neuron reader selector {signature}" + ); + } + + for signature in [ + "getProxyDeposit(bytes32)", + "getAnnouncements(bytes32)", + "getLastCallResult(bytes32)", + "isRealPaysFee(bytes32,bytes32)", + ] { + assert!( + proxy::ProxyPrecompileCall::::supports_selector(selector_u32(signature)), + "missing Proxy reader selector {signature}" + ); + } + + for signature in ["getNextLeaseId()", "getAccumulatedLeaseDividends(uint32)"] { + assert!( + leasing::LeasingPrecompileCall::::supports_selector(selector_u32( + signature + )), + "missing Leasing reader selector {signature}" + ); + } + + assert!( + balance::BalancePrecompileCall::::supports_selector(selector_u32( + "getTotalIssuance()" + )) + ); + assert!( + uid_lookup::UidLookupPrecompileCall::::supports_selector(selector_u32( + "getAssociatedEvmAddress(uint16,uint16)" + )) + ); + + for (domain, selectors) in [ + ( + "Staking V2", + staking::StakingPrecompileV2Call::::selectors(), + ), + ( + "Subnet", + subnet::SubnetPrecompileCall::::selectors(), + ), + ("Alpha", alpha::AlphaPrecompileCall::::selectors()), + ( + "Neuron", + neuron::NeuronPrecompileCall::::selectors(), + ), + ("Proxy", proxy::ProxyPrecompileCall::::selectors()), + ( + "Leasing", + leasing::LeasingPrecompileCall::::selectors(), + ), + ( + "Balance", + balance::BalancePrecompileCall::::selectors(), + ), + ( + "UID lookup", + uid_lookup::UidLookupPrecompileCall::::selectors(), + ), + ] { + let unique = selectors + .iter() + .copied() + .collect::>(); + assert_eq!( + unique.len(), + selectors.len(), + "{domain} contains a selector collision" + ); + } + } +} diff --git a/precompiles/src/neuron.rs b/precompiles/src/neuron.rs index 8a7eac497f..21bf15edc0 100644 --- a/precompiles/src/neuron.rs +++ b/precompiles/src/neuron.rs @@ -1,13 +1,20 @@ use core::marker::PhantomData; use frame_support::dispatch::{DispatchInfo, GetDispatchInfo, PostDispatchInfo}; -use frame_support::traits::IsSubType; +use frame_support::traits::{ConstU32, IsSubType}; use frame_system::RawOrigin; use pallet_evm::{AddressMapping, PrecompileHandle}; -use precompile_utils::{EvmResult, prelude::UnboundedBytes}; -use sp_core::H256; +use precompile_utils::{ + EvmResult, + prelude::{ + Address, BoundedBytes, BoundedString, BoundedVec as SolidityBoundedVec, UnboundedBytes, + revert, + }, +}; +use sp_core::{H256, ecdsa::Signature}; use sp_runtime::traits::{AsSystemOriginSigner, Dispatchable}; use sp_std::vec::Vec; +use subtensor_runtime_common::{MechId, NetUid, NetUidStorageIndex}; use crate::{PrecompileExt, PrecompileHandleExt}; @@ -32,7 +39,7 @@ where + Send + Sync + scale_info::TypeInfo, - R::AccountId: From<[u8; 32]>, + R::AccountId: From<[u8; 32]> + Into<[u8; 32]>, ::RuntimeOrigin: AsSystemOriginSigner + Clone, ::RuntimeCall: From> + GetDispatchInfo @@ -58,7 +65,7 @@ where + Send + Sync + scale_info::TypeInfo, - R::AccountId: From<[u8; 32]>, + R::AccountId: From<[u8; 32]> + Into<[u8; 32]>, ::RuntimeOrigin: AsSystemOriginSigner + Clone, ::RuntimeCall: From> + GetDispatchInfo @@ -386,6 +393,872 @@ where RawOrigin::Signed(handle.caller_account_id::()), ) } + + #[precompile::public("setMechanismWeights(uint16,uint8,uint16[],uint16[],uint64)")] + fn set_mechanism_weights( + handle: &mut impl PrecompileHandle, + netuid: u16, + mecid: u8, + dests: SolidityBoundedVec>, + weights: SolidityBoundedVec>, + version_key: u64, + ) -> EvmResult<()> { + dispatch_neuron( + handle, + pallet_subtensor::Call::::set_mechanism_weights { + netuid: netuid.into(), + mecid: MechId::from(mecid), + dests: dests.into(), + weights: weights.into(), + version_key, + }, + ) + } + + #[precompile::public("commitMechanismWeights(uint16,uint8,bytes32)")] + fn commit_mechanism_weights( + handle: &mut impl PrecompileHandle, + netuid: u16, + mecid: u8, + commit_hash: H256, + ) -> EvmResult<()> { + dispatch_neuron( + handle, + pallet_subtensor::Call::::commit_mechanism_weights { + netuid: netuid.into(), + mecid: mecid.into(), + commit_hash, + }, + ) + } + + #[precompile::public("revealMechanismWeights(uint16,uint8,uint16[],uint16[],uint16[],uint64)")] + fn reveal_mechanism_weights( + handle: &mut impl PrecompileHandle, + netuid: u16, + mecid: u8, + uids: SolidityBoundedVec>, + values: SolidityBoundedVec>, + salt: SolidityBoundedVec>, + version_key: u64, + ) -> EvmResult<()> { + dispatch_neuron( + handle, + pallet_subtensor::Call::::reveal_mechanism_weights { + netuid: netuid.into(), + mecid: mecid.into(), + uids: uids.into(), + values: values.into(), + salt: salt.into(), + version_key, + }, + ) + } + + #[precompile::public("commitCrv3MechanismWeights(uint16,uint8,bytes,uint64)")] + fn commit_crv3_mechanism_weights( + handle: &mut impl PrecompileHandle, + netuid: u16, + mecid: u8, + commit: BoundedBytes>, + reveal_round: u64, + ) -> EvmResult<()> { + let commit = + frame_support::BoundedVec::>::try_from(Vec::::from(commit)) + .map_err(|_| revert("commit exceeds runtime bound"))?; + dispatch_neuron( + handle, + pallet_subtensor::Call::::commit_crv3_mechanism_weights { + netuid: netuid.into(), + mecid: mecid.into(), + commit, + reveal_round, + }, + ) + } + + #[precompile::public("commitTimelockedWeights(uint16,bytes,uint64,uint16)")] + fn commit_timelocked_weights( + handle: &mut impl PrecompileHandle, + netuid: u16, + commit: BoundedBytes>, + reveal_round: u64, + commit_reveal_version: u16, + ) -> EvmResult<()> { + let commit = + frame_support::BoundedVec::>::try_from(Vec::::from(commit)) + .map_err(|_| revert("commit exceeds runtime bound"))?; + dispatch_neuron( + handle, + pallet_subtensor::Call::::commit_timelocked_weights { + netuid: netuid.into(), + commit, + reveal_round, + commit_reveal_version, + }, + ) + } + + #[precompile::public("commitTimelockedMechanismWeights(uint16,uint8,bytes,uint64,uint16)")] + fn commit_timelocked_mechanism_weights( + handle: &mut impl PrecompileHandle, + netuid: u16, + mecid: u8, + commit: BoundedBytes>, + reveal_round: u64, + commit_reveal_version: u16, + ) -> EvmResult<()> { + let commit = + frame_support::BoundedVec::>::try_from(Vec::::from(commit)) + .map_err(|_| revert("commit exceeds runtime bound"))?; + dispatch_neuron( + handle, + pallet_subtensor::Call::::commit_timelocked_mechanism_weights { + netuid: netuid.into(), + mecid: mecid.into(), + commit, + reveal_round, + commit_reveal_version, + }, + ) + } + + #[precompile::public("batchSetWeights(uint16[],uint16[][],uint16[][],uint64[])")] + fn batch_set_weights( + handle: &mut impl PrecompileHandle, + netuids: SolidityBoundedVec>, + dests: SolidityBoundedVec>, ConstU32<16>>, + values: SolidityBoundedVec>, ConstU32<16>>, + version_keys: SolidityBoundedVec>, + ) -> EvmResult<()> { + let netuids = Vec::::from(netuids); + let dests = Vec::>>::from(dests); + let values = Vec::>>::from(values); + let version_keys = Vec::::from(version_keys); + if netuids.len() != dests.len() + || netuids.len() != values.len() + || netuids.len() != version_keys.len() + { + return Err(revert("batch weight arrays must have equal outer lengths")); + } + let mut weights = Vec::with_capacity(netuids.len()); + for (batch_dests, batch_values) in dests.into_iter().zip(values) { + let batch_dests = Vec::::from(batch_dests); + let batch_values = Vec::::from(batch_values); + if batch_dests.len() != batch_values.len() { + return Err(revert( + "batch destination and value arrays must have equal lengths", + )); + } + weights.push( + batch_dests + .into_iter() + .zip(batch_values) + .map(|(uid, value)| (codec::Compact(uid), codec::Compact(value))) + .collect(), + ); + } + dispatch_neuron( + handle, + pallet_subtensor::Call::::batch_set_weights { + netuids: netuids + .into_iter() + .map(|netuid| codec::Compact(NetUid::from(netuid))) + .collect(), + weights, + version_keys: version_keys.into_iter().map(codec::Compact).collect(), + }, + ) + } + + #[precompile::public("batchCommitWeights(uint16[],bytes32[])")] + fn batch_commit_weights( + handle: &mut impl PrecompileHandle, + netuids: SolidityBoundedVec>, + commit_hashes: SolidityBoundedVec>, + ) -> EvmResult<()> { + let netuids = Vec::::from(netuids); + let commit_hashes = Vec::::from(commit_hashes); + if netuids.len() != commit_hashes.len() { + return Err(revert( + "batch netuid and commitment arrays must have equal lengths", + )); + } + dispatch_neuron( + handle, + pallet_subtensor::Call::::batch_commit_weights { + netuids: netuids + .into_iter() + .map(|netuid| codec::Compact(NetUid::from(netuid))) + .collect(), + commit_hashes, + }, + ) + } + + #[precompile::public("batchRevealWeights(uint16,uint16[][],uint16[][],uint16[][],uint64[])")] + fn batch_reveal_weights( + handle: &mut impl PrecompileHandle, + netuid: u16, + uids_list: SolidityBoundedVec>, ConstU32<16>>, + values_list: SolidityBoundedVec>, ConstU32<16>>, + salts_list: SolidityBoundedVec>, ConstU32<16>>, + version_keys: SolidityBoundedVec>, + ) -> EvmResult<()> { + let uids_list = Vec::>>::from(uids_list); + let values_list = Vec::>>::from(values_list); + let salts_list = Vec::>>::from(salts_list); + let version_keys = Vec::::from(version_keys); + if uids_list.len() != values_list.len() + || uids_list.len() != salts_list.len() + || uids_list.len() != version_keys.len() + { + return Err(revert("batch reveal arrays must have equal outer lengths")); + } + dispatch_neuron( + handle, + pallet_subtensor::Call::::batch_reveal_weights { + netuid: netuid.into(), + uids_list: uids_list.into_iter().map(Into::into).collect(), + values_list: values_list.into_iter().map(Into::into).collect(), + salts_list: salts_list.into_iter().map(Into::into).collect(), + version_keys, + }, + ) + } + + #[precompile::public("register(uint16,uint64,uint64,bytes,bytes32,bytes32)")] + fn register( + handle: &mut impl PrecompileHandle, + netuid: u16, + block_number: u64, + nonce: u64, + work: BoundedBytes>, + hotkey: H256, + coldkey: H256, + ) -> EvmResult<()> { + dispatch_neuron( + handle, + pallet_subtensor::Call::::register { + netuid: netuid.into(), + block_number, + nonce, + work: work.into(), + hotkey: hotkey.0.into(), + coldkey: coldkey.0.into(), + }, + ) + } + + #[precompile::public("rootRegister(bytes32)")] + fn root_register(handle: &mut impl PrecompileHandle, hotkey: H256) -> EvmResult<()> { + dispatch_neuron( + handle, + pallet_subtensor::Call::::root_register { + hotkey: hotkey.0.into(), + }, + ) + } + + #[precompile::public("swapHotkey(bytes32,bytes32,bool,uint16)")] + fn swap_hotkey( + handle: &mut impl PrecompileHandle, + hotkey: H256, + new_hotkey: H256, + has_netuid: bool, + netuid: u16, + ) -> EvmResult<()> { + dispatch_neuron( + handle, + pallet_subtensor::Call::::swap_hotkey { + hotkey: hotkey.0.into(), + new_hotkey: new_hotkey.0.into(), + netuid: has_netuid.then_some(NetUid::from(netuid)), + }, + ) + } + + #[precompile::public("swapHotkeyV2(bytes32,bytes32,bool,uint16,bool)")] + fn swap_hotkey_v2( + handle: &mut impl PrecompileHandle, + hotkey: H256, + new_hotkey: H256, + has_netuid: bool, + netuid: u16, + keep_stake: bool, + ) -> EvmResult<()> { + dispatch_neuron( + handle, + pallet_subtensor::Call::::swap_hotkey_v2 { + hotkey: hotkey.0.into(), + new_hotkey: new_hotkey.0.into(), + netuid: has_netuid.then_some(NetUid::from(netuid)), + keep_stake, + }, + ) + } + + #[precompile::public("setChildren(bytes32,uint16,uint64[],bytes32[])")] + fn set_children( + handle: &mut impl PrecompileHandle, + hotkey: H256, + netuid: u16, + proportions: SolidityBoundedVec>, + children: SolidityBoundedVec>, + ) -> EvmResult<()> { + let proportions = Vec::::from(proportions); + let children = Vec::::from(children); + if proportions.len() != children.len() { + return Err(revert( + "child proportions and hotkeys must have equal length", + )); + } + let children = proportions + .into_iter() + .zip(children) + .map(|(proportion, child)| (proportion, child.0.into())) + .collect(); + dispatch_neuron( + handle, + pallet_subtensor::Call::::set_children { + hotkey: hotkey.0.into(), + netuid: netuid.into(), + children, + }, + ) + } + + #[precompile::public("setIdentity(string,string,string,string,string,string,string)")] + #[allow(clippy::too_many_arguments)] + fn set_identity( + handle: &mut impl PrecompileHandle, + name: BoundedString>, + url: BoundedString>, + github_repo: BoundedString>, + image: BoundedString>, + discord: BoundedString>, + description: BoundedString>, + additional: BoundedString>, + ) -> EvmResult<()> { + dispatch_neuron( + handle, + pallet_subtensor::Call::::set_identity { + name: name.into(), + url: url.into(), + github_repo: github_repo.into(), + image: image.into(), + discord: discord.into(), + description: description.into(), + additional: additional.into(), + }, + ) + } + + #[precompile::public("tryAssociateHotkey(bytes32)")] + fn try_associate_hotkey(handle: &mut impl PrecompileHandle, hotkey: H256) -> EvmResult<()> { + dispatch_neuron( + handle, + pallet_subtensor::Call::::try_associate_hotkey { + hotkey: hotkey.0.into(), + }, + ) + } + + #[precompile::public("associateEvmKey(uint16,address,uint64,bytes)")] + fn associate_evm_key( + handle: &mut impl PrecompileHandle, + netuid: u16, + evm_key: Address, + block_number: u64, + signature: BoundedBytes>, + ) -> EvmResult<()> { + let bytes = Vec::::from(signature); + let signature: [u8; 65] = bytes + .try_into() + .map_err(|_| revert("ECDSA signature must be exactly 65 bytes"))?; + dispatch_neuron( + handle, + pallet_subtensor::Call::::associate_evm_key { + netuid: netuid.into(), + evm_key: evm_key.0, + block_number, + signature: Signature::from_raw(signature), + }, + ) + } + + #[precompile::public("announceColdkeySwap(bytes32)")] + fn announce_coldkey_swap( + handle: &mut impl PrecompileHandle, + new_coldkey_hash: H256, + ) -> EvmResult<()> { + let new_coldkey_hash = codec::Decode::decode(&mut new_coldkey_hash.as_bytes()) + .map_err(|_| revert("runtime hash is not compatible with bytes32"))?; + dispatch_neuron( + handle, + pallet_subtensor::Call::::announce_coldkey_swap { new_coldkey_hash }, + ) + } + + #[precompile::public("executeAnnouncedColdkeySwap(bytes32)")] + fn execute_announced_coldkey_swap( + handle: &mut impl PrecompileHandle, + new_coldkey: H256, + ) -> EvmResult<()> { + dispatch_neuron( + handle, + pallet_subtensor::Call::::swap_coldkey_announced { + new_coldkey: new_coldkey.0.into(), + }, + ) + } + + #[precompile::public("disputeColdkeySwap()")] + fn dispute_coldkey_swap(handle: &mut impl PrecompileHandle) -> EvmResult<()> { + dispatch_neuron(handle, pallet_subtensor::Call::::dispute_coldkey_swap {}) + } + + #[precompile::public("clearColdkeySwapAnnouncement()")] + fn clear_coldkey_swap_announcement(handle: &mut impl PrecompileHandle) -> EvmResult<()> { + dispatch_neuron( + handle, + pallet_subtensor::Call::::clear_coldkey_swap_announcement {}, + ) + } + + #[precompile::public("getUid(uint16,bytes32)")] + #[precompile::view] + fn get_uid( + handle: &mut impl PrecompileHandle, + netuid: u16, + hotkey: H256, + ) -> EvmResult<(bool, u16)> { + handle.record_db_reads::(1)?; + Ok( + match pallet_subtensor::Uids::::get( + NetUid::from(netuid), + R::AccountId::from(hotkey.0), + ) { + Some(uid) => (true, uid), + None => (false, 0), + }, + ) + } + + #[precompile::public("isNetworkMember(bytes32,uint16)")] + #[precompile::view] + fn is_network_member( + handle: &mut impl PrecompileHandle, + hotkey: H256, + netuid: u16, + ) -> EvmResult { + handle.record_db_reads::(1)?; + Ok(pallet_subtensor::IsNetworkMember::::get( + R::AccountId::from(hotkey.0), + NetUid::from(netuid), + )) + } + + #[precompile::public("getWeights(uint16,uint16)")] + #[precompile::view] + fn get_weights( + handle: &mut impl PrecompileHandle, + netuid: u16, + uid: u16, + ) -> EvmResult> { + handle.record_db_reads::(1)?; + Ok(pallet_subtensor::Weights::::get( + NetUidStorageIndex::from(NetUid::from(netuid)), + uid, + )) + } + + #[precompile::public("getBonds(uint16,uint16)")] + #[precompile::view] + fn get_bonds( + handle: &mut impl PrecompileHandle, + netuid: u16, + uid: u16, + ) -> EvmResult> { + handle.record_db_reads::(1)?; + Ok(pallet_subtensor::Bonds::::get( + NetUidStorageIndex::from(NetUid::from(netuid)), + uid, + )) + } + + #[precompile::public("getBlockAtRegistration(uint16,uint16)")] + #[precompile::view] + fn get_block_at_registration( + handle: &mut impl PrecompileHandle, + netuid: u16, + uid: u16, + ) -> EvmResult { + handle.record_db_reads::(1)?; + Ok(pallet_subtensor::BlockAtRegistration::::get( + NetUid::from(netuid), + uid, + )) + } + + #[precompile::public("getNeuronCertificate(uint16,bytes32)")] + #[precompile::view] + fn get_neuron_certificate( + handle: &mut impl PrecompileHandle, + netuid: u16, + hotkey: H256, + ) -> EvmResult<(bool, u8, UnboundedBytes)> { + handle.record_db_reads::(1)?; + Ok( + match pallet_subtensor::NeuronCertificates::::get( + NetUid::from(netuid), + R::AccountId::from(hotkey.0), + ) { + Some(certificate) => ( + true, + certificate.algorithm, + UnboundedBytes::from(certificate.public_key.into_inner()), + ), + None => (false, 0, UnboundedBytes::default()), + }, + ) + } + + #[precompile::public("getPrometheus(uint16,bytes32)")] + #[precompile::view] + fn get_prometheus( + handle: &mut impl PrecompileHandle, + netuid: u16, + hotkey: H256, + ) -> EvmResult<(bool, u64, u32, u128, u16, u8)> { + handle.record_db_reads::(1)?; + Ok( + match pallet_subtensor::Prometheus::::get( + NetUid::from(netuid), + R::AccountId::from(hotkey.0), + ) { + Some(info) => ( + true, + info.block, + info.version, + info.ip, + info.port, + info.ip_type, + ), + None => (false, 0, 0, 0, 0, 0), + }, + ) + } + + #[precompile::public("getChainIdentity(bytes32)")] + #[precompile::view] + fn get_chain_identity( + handle: &mut impl PrecompileHandle, + coldkey: H256, + ) -> EvmResult<( + bool, + UnboundedBytes, + UnboundedBytes, + UnboundedBytes, + UnboundedBytes, + UnboundedBytes, + UnboundedBytes, + UnboundedBytes, + )> { + handle.record_db_reads::(1)?; + Ok( + match pallet_subtensor::IdentitiesV2::::get(R::AccountId::from(coldkey.0)) { + Some(identity) => ( + true, + identity.name.into(), + identity.url.into(), + identity.github_repo.into(), + identity.image.into(), + identity.discord.into(), + identity.description.into(), + identity.additional.into(), + ), + None => ( + false, + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + ), + }, + ) + } + + #[precompile::public("getSubnetIdentity(uint16)")] + #[precompile::view] + fn get_subnet_identity( + handle: &mut impl PrecompileHandle, + netuid: u16, + ) -> EvmResult<( + bool, + UnboundedBytes, + UnboundedBytes, + UnboundedBytes, + UnboundedBytes, + UnboundedBytes, + UnboundedBytes, + UnboundedBytes, + UnboundedBytes, + )> { + handle.record_db_reads::(1)?; + Ok( + match pallet_subtensor::SubnetIdentitiesV3::::get(NetUid::from(netuid)) { + Some(identity) => ( + true, + identity.subnet_name.into(), + identity.github_repo.into(), + identity.subnet_contact.into(), + identity.subnet_url.into(), + identity.discord.into(), + identity.description.into(), + identity.logo_url.into(), + identity.additional.into(), + ), + None => ( + false, + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + ), + }, + ) + } + + #[precompile::public("getLoadedEmission(uint16)")] + #[precompile::view] + fn get_loaded_emission( + handle: &mut impl PrecompileHandle, + netuid: u16, + ) -> EvmResult<(bool, Vec<(H256, u64, u64)>)> { + handle.record_db_reads::(1)?; + Ok( + match pallet_subtensor::LoadedEmission::::get(NetUid::from(netuid)) { + Some(emission) => ( + true, + emission + .into_iter() + .map(|(hotkey, server, validator)| { + (H256::from(hotkey.into()), server, validator) + }) + .collect(), + ), + None => (false, Vec::new()), + }, + ) + } + + #[precompile::public("getTransactionKeyLastBlock(bytes32,uint16,uint16)")] + #[precompile::view] + fn get_transaction_key_last_block( + handle: &mut impl PrecompileHandle, + hotkey: H256, + netuid: u16, + transaction_key: u16, + ) -> EvmResult { + handle.record_db_reads::(1)?; + Ok(pallet_subtensor::TransactionKeyLastBlock::::get(( + R::AccountId::from(hotkey.0), + NetUid::from(netuid), + transaction_key, + ))) + } + + #[allow(deprecated)] + #[precompile::public("getLegacyTransactionRateBlocks(bytes32)")] + #[precompile::view] + fn get_legacy_transaction_rate_blocks( + handle: &mut impl PrecompileHandle, + hotkey: H256, + ) -> EvmResult<(u64, u64, u64)> { + handle.record_db_reads::(3)?; + let hotkey = R::AccountId::from(hotkey.0); + Ok(( + pallet_subtensor::LastTxBlock::::get(&hotkey), + pallet_subtensor::LastTxBlockChildKeyTake::::get(&hotkey), + pallet_subtensor::LastTxBlockDelegateTake::::get(hotkey), + )) + } + + #[precompile::public("getWeightCommit(uint16,bytes32,uint32)")] + #[precompile::view] + fn get_weight_commit( + handle: &mut impl PrecompileHandle, + netuid: u16, + hotkey: H256, + index: u32, + ) -> EvmResult<(bool, H256, u64, u64)> { + handle.record_db_reads::(1)?; + let commits = pallet_subtensor::WeightCommits::::get( + NetUidStorageIndex::from(NetUid::from(netuid)), + R::AccountId::from(hotkey.0), + ); + Ok(commits + .and_then(|commits| commits.get(index as usize).copied()) + .map(|(hash, epoch, block, _)| (true, hash, epoch, block)) + .unwrap_or((false, H256::zero(), 0, 0))) + } + + #[precompile::public("getWeightCommitCount(uint16,bytes32)")] + #[precompile::view] + fn get_weight_commit_count( + handle: &mut impl PrecompileHandle, + netuid: u16, + hotkey: H256, + ) -> EvmResult { + handle.record_db_reads::(1)?; + Ok(pallet_subtensor::WeightCommits::::get( + NetUidStorageIndex::from(NetUid::from(netuid)), + R::AccountId::from(hotkey.0), + ) + .map(|commits| commits.len() as u32) + .unwrap_or(0)) + } + + #[precompile::public("getTimelockedWeightCommit(uint16,uint64,uint32)")] + #[precompile::view] + fn get_timelocked_weight_commit( + handle: &mut impl PrecompileHandle, + netuid: u16, + epoch: u64, + index: u32, + ) -> EvmResult<(bool, H256, u64, H256, u32, u64)> { + handle.record_db_reads::(1)?; + let commits = pallet_subtensor::TimelockedWeightCommits::::get( + NetUidStorageIndex::from(NetUid::from(netuid)), + epoch, + ); + Ok(commits + .get(index as usize) + .map(|(who, block, ciphertext, round)| { + ( + true, + H256::from(who.clone().into()), + *block, + H256::from(sp_io::hashing::keccak_256(ciphertext.as_slice())), + ciphertext.len() as u32, + *round, + ) + }) + .unwrap_or((false, H256::zero(), 0, H256::zero(), 0, 0))) + } + + #[precompile::public("getTimelockedWeightCommitCount(uint16,uint64)")] + #[precompile::view] + fn get_timelocked_weight_commit_count( + handle: &mut impl PrecompileHandle, + netuid: u16, + epoch: u64, + ) -> EvmResult { + handle.record_db_reads::(1)?; + Ok(pallet_subtensor::TimelockedWeightCommits::::get( + NetUidStorageIndex::from(NetUid::from(netuid)), + epoch, + ) + .len() as u32) + } + + #[precompile::public("getLegacyTimelockedWeightCommit(uint8,uint16,uint64,uint32)")] + #[precompile::view] + fn get_legacy_timelocked_weight_commit( + handle: &mut impl PrecompileHandle, + version: u8, + netuid: u16, + epoch: u64, + index: u32, + ) -> EvmResult<(bool, H256, u64, H256, u32, u64)> { + handle.record_db_reads::(1)?; + let netuid = NetUidStorageIndex::from(NetUid::from(netuid)); + match version { + 1 => Ok(pallet_subtensor::CRV3WeightCommits::::get(netuid, epoch) + .get(index as usize) + .map(|(who, ciphertext, round)| { + ( + true, + H256::from(who.clone().into()), + 0, + H256::from(sp_io::hashing::keccak_256(ciphertext.as_slice())), + ciphertext.len() as u32, + *round, + ) + }) + .unwrap_or((false, H256::zero(), 0, H256::zero(), 0, 0))), + 2 => Ok( + pallet_subtensor::CRV3WeightCommitsV2::::get(netuid, epoch) + .get(index as usize) + .map(|(who, block, ciphertext, round)| { + ( + true, + H256::from(who.clone().into()), + *block, + H256::from(sp_io::hashing::keccak_256(ciphertext.as_slice())), + ciphertext.len() as u32, + *round, + ) + }) + .unwrap_or((false, H256::zero(), 0, H256::zero(), 0, 0)), + ), + _ => Err(revert("unsupported legacy weight-commit version")), + } + } + + #[precompile::public("getLegacyTimelockedWeightCommitCount(uint8,uint16,uint64)")] + #[precompile::view] + fn get_legacy_timelocked_weight_commit_count( + handle: &mut impl PrecompileHandle, + version: u8, + netuid: u16, + epoch: u64, + ) -> EvmResult { + handle.record_db_reads::(1)?; + let netuid = NetUidStorageIndex::from(NetUid::from(netuid)); + match version { + 1 => Ok(pallet_subtensor::CRV3WeightCommits::::get(netuid, epoch).len() as u32), + 2 => Ok(pallet_subtensor::CRV3WeightCommitsV2::::get(netuid, epoch).len() as u32), + _ => Err(revert("unsupported legacy weight-commit version")), + } + } +} + +fn dispatch_neuron( + handle: &mut impl PrecompileHandle, + call: pallet_subtensor::Call, +) -> EvmResult<()> +where + R: frame_system::Config + + pallet_balances::Config + + pallet_evm::Config + + pallet_subtensor::Config + + pallet_shield::Config + + pallet_subtensor_proxy::Config + + Send + + Sync + + scale_info::TypeInfo, + R::AccountId: From<[u8; 32]> + Into<[u8; 32]>, + ::RuntimeOrigin: AsSystemOriginSigner + Clone, + ::RuntimeCall: From> + + GetDispatchInfo + + Dispatchable + + IsSubType> + + IsSubType> + + IsSubType> + + IsSubType>, + ::AddressMapping: AddressMapping, +{ + let caller = handle.caller_account_id::(); + handle.try_dispatch_runtime_call::(call, RawOrigin::Signed(caller)) } #[cfg(test)] @@ -875,4 +1748,143 @@ mod tests { assert_eq!(prometheus.ip_type, SERVE_IP_TYPE); }); } + + #[test] + fn neuron_state_views_return_typed_values_and_missing_state() { + new_test_ext().execute_with(|| { + let caller = addr_from_index(0x8234); + let address = addr_from_index(NeuronPrecompile::::INDEX); + let precompiles = precompiles::>(); + let netuid = NetUid::from(TEST_NETUID_U16); + let netuid_index = NetUidStorageIndex::from(netuid); + let hotkey = AccountId::from([0x81; 32]); + let hotkey_word = H256::from_slice(hotkey.as_ref()); + let uid = 7_u16; + let weights = vec![(1_u16, 2_u16), (3_u16, 4_u16)]; + let bonds = vec![(5_u16, 6_u16)]; + + pallet_subtensor::Uids::::insert(netuid, &hotkey, uid); + pallet_subtensor::IsNetworkMember::::insert(&hotkey, netuid, true); + pallet_subtensor::Weights::::insert(netuid_index, uid, weights.clone()); + pallet_subtensor::Bonds::::insert(netuid_index, uid, bonds.clone()); + pallet_subtensor::BlockAtRegistration::::insert(netuid, uid, 91_u64); + + macro_rules! assert_view { + ($signature:literal, $arguments:expr, $expected:expr) => { + precompiles + .prepare_test( + caller, + address, + encode_with_selector(selector_u32($signature), $arguments), + ) + .with_static_call(true) + .execute_returns($expected); + }; + } + + assert_view!( + "getUid(uint16,bytes32)", + (TEST_NETUID_U16, hotkey_word), + (true, uid) + ); + assert_view!( + "isNetworkMember(bytes32,uint16)", + (hotkey_word, TEST_NETUID_U16), + true + ); + assert_view!("getWeights(uint16,uint16)", (TEST_NETUID_U16, uid), weights); + assert_view!("getBonds(uint16,uint16)", (TEST_NETUID_U16, uid), bonds); + assert_view!( + "getBlockAtRegistration(uint16,uint16)", + (TEST_NETUID_U16, uid), + 91_u64 + ); + assert_view!( + "getNeuronCertificate(uint16,bytes32)", + (TEST_NETUID_U16, hotkey_word), + (false, 0_u8, UnboundedBytes::default()) + ); + assert_view!( + "getPrometheus(uint16,bytes32)", + (TEST_NETUID_U16, hotkey_word), + (false, 0_u64, 0_u32, 0_u128, 0_u16, 0_u8) + ); + assert_view!( + "getChainIdentity(bytes32)", + (hotkey_word,), + ( + false, + UnboundedBytes::default(), + UnboundedBytes::default(), + UnboundedBytes::default(), + UnboundedBytes::default(), + UnboundedBytes::default(), + UnboundedBytes::default(), + UnboundedBytes::default(), + ) + ); + assert_view!( + "getSubnetIdentity(uint16)", + (TEST_NETUID_U16,), + ( + false, + UnboundedBytes::default(), + UnboundedBytes::default(), + UnboundedBytes::default(), + UnboundedBytes::default(), + UnboundedBytes::default(), + UnboundedBytes::default(), + UnboundedBytes::default(), + UnboundedBytes::default(), + ) + ); + assert_view!( + "getLoadedEmission(uint16)", + (TEST_NETUID_U16,), + (false, Vec::<(H256, u64, u64)>::new()) + ); + assert_view!( + "getTransactionKeyLastBlock(bytes32,uint16,uint16)", + (hotkey_word, TEST_NETUID_U16, 4_u16), + 0_u64 + ); + assert_view!( + "getLegacyTransactionRateBlocks(bytes32)", + (hotkey_word,), + (0_u64, 0_u64, 0_u64) + ); + assert_view!( + "getWeightCommit(uint16,bytes32,uint32)", + (TEST_NETUID_U16, hotkey_word, 0_u32), + (false, H256::zero(), 0_u64, 0_u64) + ); + assert_view!( + "getWeightCommitCount(uint16,bytes32)", + (TEST_NETUID_U16, hotkey_word), + 0_u32 + ); + assert_view!( + "getTimelockedWeightCommit(uint16,uint64,uint32)", + (TEST_NETUID_U16, 2_u64, 0_u32), + (false, H256::zero(), 0_u64, H256::zero(), 0_u32, 0_u64) + ); + assert_view!( + "getTimelockedWeightCommitCount(uint16,uint64)", + (TEST_NETUID_U16, 2_u64), + 0_u32 + ); + for version in [1_u8, 2_u8] { + assert_view!( + "getLegacyTimelockedWeightCommit(uint8,uint16,uint64,uint32)", + (version, TEST_NETUID_U16, 2_u64, 0_u32), + (false, H256::zero(), 0_u64, H256::zero(), 0_u32, 0_u64) + ); + assert_view!( + "getLegacyTimelockedWeightCommitCount(uint8,uint16,uint64)", + (version, TEST_NETUID_U16, 2_u64), + 0_u32 + ); + } + }); + } } diff --git a/precompiles/src/proxy.rs b/precompiles/src/proxy.rs index 78d59f5ce2..5a2df9ab57 100644 --- a/precompiles/src/proxy.rs +++ b/precompiles/src/proxy.rs @@ -12,8 +12,11 @@ use pallet_subtensor_proxy as pallet_proxy; use precompile_utils::EvmResult; use sp_core::{H256, U256}; use sp_runtime::{ + DispatchError, codec::DecodeLimit, - traits::{AsSystemOriginSigner, Dispatchable, StaticLookup}, + traits::{ + AsSystemOriginSigner, Dispatchable, SaturatedConversion, StaticLookup, UniqueSaturatedInto, + }, }; use sp_std::boxed::Box; use sp_std::convert::{TryFrom, TryInto}; @@ -291,4 +294,259 @@ where Ok(result) } + + #[precompile::public("getProxyDeposit(bytes32)")] + #[precompile::view] + pub fn get_proxy_deposit( + handle: &mut impl PrecompileHandle, + account_id: H256, + ) -> EvmResult { + handle.record_db_reads::(1)?; + let (_, deposit) = pallet_proxy::Proxies::::get(R::AccountId::from(account_id.0)); + Ok(U256::from(deposit.saturated_into::())) + } + + #[precompile::public("getAnnouncements(bytes32)")] + #[precompile::view] + pub fn get_announcements( + handle: &mut impl PrecompileHandle, + account_id: H256, + ) -> EvmResult<(Vec<(H256, H256, u64)>, U256)> { + handle.record_db_reads::(1)?; + let (announcements, deposit) = + pallet_proxy::Announcements::::get(R::AccountId::from(account_id.0)); + let announcements = announcements + .into_iter() + .map(|announcement| { + ( + H256::from(>::into( + announcement.real().clone(), + )), + H256::from_slice(announcement.call_hash().as_ref()), + (*announcement.height()).unique_saturated_into(), + ) + }) + .collect(); + Ok((announcements, U256::from(deposit.saturated_into::()))) + } + + #[precompile::public("getLastCallResult(bytes32)")] + #[precompile::view] + pub fn get_last_call_result( + handle: &mut impl PrecompileHandle, + account_id: H256, + ) -> EvmResult<(bool, bool, u8, u8, H256)> { + handle.record_db_reads::(1)?; + let Some(result) = pallet_proxy::LastCallResult::::get(R::AccountId::from(account_id.0)) + else { + return Ok((false, false, 0, 0, H256::zero())); + }; + match result { + Ok(()) => Ok((true, true, 0, 0, H256::zero())), + Err(error) => { + let (kind, pallet_index, error_data) = dispatch_error_metadata(error); + Ok((true, false, kind, pallet_index, error_data)) + } + } + } + + #[precompile::public("isRealPaysFee(bytes32,bytes32)")] + #[precompile::view] + pub fn is_real_pays_fee( + handle: &mut impl PrecompileHandle, + real: H256, + delegate: H256, + ) -> EvmResult { + handle.record_db_reads::(1)?; + Ok(pallet_proxy::RealPaysFee::::contains_key( + R::AccountId::from(real.0), + R::AccountId::from(delegate.0), + )) + } + + #[precompile::public("announce(bytes32,bytes32)")] + pub fn announce( + handle: &mut impl PrecompileHandle, + real: H256, + call_hash: H256, + ) -> EvmResult<()> { + let account_id = handle.caller_account_id::(); + let call_hash = DecodeLimit::decode_all_with_depth_limit(1, &mut &call_hash.as_bytes()[..]) + .map_err(|_| PrecompileFailure::Error { + exit_status: ExitError::Other( + "runtime call hash is not compatible with bytes32".into(), + ), + })?; + let call = pallet_proxy::Call::::announce { + real: <::Lookup as StaticLookup>::Source::from( + real.0.into(), + ), + call_hash, + }; + handle.try_dispatch_runtime_call::(call, RawOrigin::Signed(account_id)) + } + + #[precompile::public("removeAnnouncement(bytes32,bytes32)")] + pub fn remove_announcement( + handle: &mut impl PrecompileHandle, + real: H256, + call_hash: H256, + ) -> EvmResult<()> { + let account_id = handle.caller_account_id::(); + let call_hash = DecodeLimit::decode_all_with_depth_limit(1, &mut &call_hash.as_bytes()[..]) + .map_err(|_| PrecompileFailure::Error { + exit_status: ExitError::Other( + "runtime call hash is not compatible with bytes32".into(), + ), + })?; + let call = pallet_proxy::Call::::remove_announcement { + real: <::Lookup as StaticLookup>::Source::from( + real.0.into(), + ), + call_hash, + }; + handle.try_dispatch_runtime_call::(call, RawOrigin::Signed(account_id)) + } + + #[precompile::public("rejectAnnouncement(bytes32,bytes32)")] + pub fn reject_announcement( + handle: &mut impl PrecompileHandle, + delegate: H256, + call_hash: H256, + ) -> EvmResult<()> { + let account_id = handle.caller_account_id::(); + let call_hash = DecodeLimit::decode_all_with_depth_limit(1, &mut &call_hash.as_bytes()[..]) + .map_err(|_| PrecompileFailure::Error { + exit_status: ExitError::Other( + "runtime call hash is not compatible with bytes32".into(), + ), + })?; + let call = pallet_proxy::Call::::reject_announcement { + delegate: <::Lookup as StaticLookup>::Source::from( + delegate.0.into(), + ), + call_hash, + }; + handle.try_dispatch_runtime_call::(call, RawOrigin::Signed(account_id)) + } + + #[precompile::public("setRealPaysFee(bytes32,bool)")] + pub fn set_real_pays_fee( + handle: &mut impl PrecompileHandle, + delegate: H256, + pays_fee: bool, + ) -> EvmResult<()> { + let account_id = handle.caller_account_id::(); + let call = pallet_proxy::Call::::set_real_pays_fee { + delegate: <::Lookup as StaticLookup>::Source::from( + delegate.0.into(), + ), + pays_fee, + }; + handle.try_dispatch_runtime_call::(call, RawOrigin::Signed(account_id)) + } +} + +fn dispatch_error_metadata(error: DispatchError) -> (u8, u8, H256) { + let mut data = [0u8; 32]; + match error { + DispatchError::Other(_) => (1, 0, H256::zero()), + DispatchError::CannotLookup => (2, 0, H256::zero()), + DispatchError::BadOrigin => (3, 0, H256::zero()), + DispatchError::Module(module) => { + data[..4].copy_from_slice(&module.error); + (4, module.index, H256::from(data)) + } + DispatchError::ConsumerRemaining => (5, 0, H256::zero()), + DispatchError::NoProviders => (6, 0, H256::zero()), + DispatchError::TooManyConsumers => (7, 0, H256::zero()), + DispatchError::Token(_) => (8, 0, H256::zero()), + DispatchError::Arithmetic(_) => (9, 0, H256::zero()), + DispatchError::Transactional(_) => (10, 0, H256::zero()), + DispatchError::Exhausted => (11, 0, H256::zero()), + DispatchError::Corruption => (12, 0, H256::zero()), + DispatchError::Unavailable => (13, 0, H256::zero()), + DispatchError::RootNotAllowed => (14, 0, H256::zero()), + DispatchError::Trie(_) => (15, 0, H256::zero()), + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::PrecompileExt; + use crate::mock::{ + AccountId, Runtime, addr_from_index, new_test_ext, precompiles, selector_u32, + }; + use precompile_utils::solidity::encode_with_selector; + use precompile_utils::testing::PrecompileTesterExt; + + #[test] + fn proxy_state_views_return_typed_values_and_missing_state() { + new_test_ext().execute_with(|| { + let caller = addr_from_index(0x80b1); + let address = addr_from_index(ProxyPrecompile::::INDEX); + let real = AccountId::from([0x31; 32]); + let delegate = AccountId::from([0x32; 32]); + let real_word = H256::from_slice(real.as_ref()); + let delegate_word = H256::from_slice(delegate.as_ref()); + let precompiles = precompiles::>(); + + precompiles + .prepare_test( + caller, + address, + encode_with_selector(selector_u32("getProxyDeposit(bytes32)"), (real_word,)), + ) + .with_static_call(true) + .execute_returns(U256::zero()); + + precompiles + .prepare_test( + caller, + address, + encode_with_selector( + selector_u32("getAnnouncements(bytes32)"), + (delegate_word,), + ), + ) + .with_static_call(true) + .execute_returns((Vec::<(H256, H256, u64)>::new(), U256::zero())); + + precompiles + .prepare_test( + caller, + address, + encode_with_selector(selector_u32("getLastCallResult(bytes32)"), (real_word,)), + ) + .with_static_call(true) + .execute_returns((false, false, 0_u8, 0_u8, H256::zero())); + + pallet_proxy::LastCallResult::::insert( + &real, + Err::<(), _>(DispatchError::BadOrigin), + ); + precompiles + .prepare_test( + caller, + address, + encode_with_selector(selector_u32("getLastCallResult(bytes32)"), (real_word,)), + ) + .with_static_call(true) + .execute_returns((true, false, 3_u8, 0_u8, H256::zero())); + + pallet_proxy::RealPaysFee::::insert(&real, &delegate, ()); + precompiles + .prepare_test( + caller, + address, + encode_with_selector( + selector_u32("isRealPaysFee(bytes32,bytes32)"), + (real_word, delegate_word), + ), + ) + .with_static_call(true) + .execute_returns(true); + }); + } } diff --git a/precompiles/src/registry.rs b/precompiles/src/registry.rs new file mode 100644 index 0000000000..8efd170e30 --- /dev/null +++ b/precompiles/src/registry.rs @@ -0,0 +1,191 @@ +use core::marker::PhantomData; + +use pallet_admin_utils::{PrecompileEnable, PrecompileEnum}; +use pallet_evm::PrecompileHandle; +use precompile_utils::{ + EvmResult, + prelude::{Address, UnboundedString}, + solidity::{ + Codec, + codec::{Reader, Writer}, + }, +}; +use sp_core::{H160, H256}; + +use crate::{PrecompileExt, PrecompileHandleExt}; + +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +pub struct Bytes4([u8; 4]); + +impl Codec for Bytes4 { + fn read(reader: &mut Reader) -> precompile_utils::solidity::revert::MayRevert { + let word = reader.read::()?; + let [a, b, c, d, ..] = word.to_fixed_bytes(); + Ok(Self([a, b, c, d])) + } + + fn write(writer: &mut Writer, value: Self) { + let mut word = [0u8; 32]; + word[..4].copy_from_slice(&value.0); + H256::write(writer, H256::from(word)); + } + + fn has_static_size() -> bool { + true + } + + fn signature() -> alloc::string::String { + "bytes4".into() + } +} + +#[derive(Codec)] +struct PrecompileStatus { + is_deprecated: bool, + is_disabled: bool, + new_precompile: Address, + new_selector: Bytes4, + message: UnboundedString, +} + +pub struct PrecompileRegistry(PhantomData); + +impl PrecompileExt for PrecompileRegistry +where + R: frame_system::Config + + pallet_admin_utils::Config + + pallet_evm::Config + + pallet_subtensor::Config, + R::AccountId: From<[u8; 32]>, +{ + const INDEX: u64 = 2067; +} + +#[precompile_utils::precompile] +impl PrecompileRegistry +where + R: frame_system::Config + + pallet_admin_utils::Config + + pallet_evm::Config + + pallet_subtensor::Config, + R::AccountId: From<[u8; 32]>, +{ + #[precompile::public("getPrecompileStatus(address,bytes4)")] + #[precompile::view] + fn get_precompile_status( + handle: &mut impl PrecompileHandle, + precompile: Address, + _selector: Bytes4, + ) -> EvmResult { + let is_disabled = match precompile_enum::(precompile.0) { + Some(precompile_id) => { + handle.record_db_reads::(1)?; + !PrecompileEnable::::get(precompile_id) + } + None => false, + }; + + Ok(PrecompileStatus { + is_deprecated: false, + is_disabled, + new_precompile: Address(H160::zero()), + new_selector: Bytes4::default(), + message: UnboundedString::default(), + }) + } +} + +fn precompile_enum(address: H160) -> Option +where + R: frame_system::Config + + pallet_admin_utils::Config + + pallet_evm::Config + + pallet_subtensor::Config, + R::AccountId: From<[u8; 32]>, +{ + let _runtime = PhantomData::; + let at = |index| address == H160::from_low_u64_be(index); + if at(2048) { + Some(PrecompileEnum::BalanceTransfer) + } else if at(2049) || at(2053) { + Some(PrecompileEnum::Staking) + } else if at(2051) { + Some(PrecompileEnum::Subnet) + } else if at(2050) { + Some(PrecompileEnum::Metagraph) + } else if at(2052) { + Some(PrecompileEnum::Neuron) + } else if at(2054) { + Some(PrecompileEnum::UidLookup) + } else if at(2056) { + Some(PrecompileEnum::Alpha) + } else if at(2057) { + Some(PrecompileEnum::Crowdloan) + } else if at(2059) { + Some(PrecompileEnum::Proxy) + } else if at(2058) { + Some(PrecompileEnum::Leasing) + } else if at(2060) { + Some(PrecompileEnum::AddressMapping) + } else if at(2061) { + Some(PrecompileEnum::VotingPower) + } else if at(2062) { + Some(PrecompileEnum::AccountBalance) + } else if at(2063) { + Some(PrecompileEnum::Scheduler) + } else if at(2064) { + Some(PrecompileEnum::Drand) + } else if at(2065) { + Some(PrecompileEnum::Timestamp) + } else if at(2066) { + Some(PrecompileEnum::RuntimeConfiguration) + } else if at(2067) { + Some(PrecompileEnum::PrecompileRegistry) + } else { + None + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::mock::{Runtime, addr_from_index, new_test_ext, precompiles, selector_u32}; + use precompile_utils::{ + prelude::RuntimeHelper, + solidity::{encode_return_value, encode_with_selector}, + testing::PrecompileTesterExt, + }; + + #[test] + fn reports_reversible_disablement_at_reserved_address() { + new_test_ext().execute_with(|| { + assert_eq!(PrecompileRegistry::::INDEX, 2067); + PrecompileEnable::::insert(PrecompileEnum::Scheduler, false); + + let precompiles = precompiles::>(); + let caller = addr_from_index(1); + let registry = addr_from_index(2067); + let scheduler = Address(addr_from_index(2063)); + let selector = Bytes4(selector_u32("getIncompleteSince()").to_be_bytes()); + + precompiles + .prepare_test( + caller, + registry, + encode_with_selector( + selector_u32("getPrecompileStatus(address,bytes4)"), + (scheduler, selector), + ), + ) + .with_static_call(true) + .expect_cost(RuntimeHelper::::db_read_gas_cost()) + .execute_returns_raw(encode_return_value(PrecompileStatus { + is_deprecated: false, + is_disabled: true, + new_precompile: Address(H160::zero()), + new_selector: Bytes4::default(), + message: UnboundedString::default(), + })); + }); + } +} diff --git a/precompiles/src/runtime_configuration.rs b/precompiles/src/runtime_configuration.rs new file mode 100644 index 0000000000..e654266f4e --- /dev/null +++ b/precompiles/src/runtime_configuration.rs @@ -0,0 +1,786 @@ +use core::marker::PhantomData; + +use fp_evm::ExitError; +use frame_support::traits::{Currency, Get}; +use pallet_evm::{BalanceConverter, PrecompileHandle, SubstrateBalance}; +use precompile_utils::{EvmResult, prelude::UnboundedString}; +use sp_core::{H256, U256}; +use sp_runtime::traits::AccountIdConversion; +use subtensor_runtime_common::{TaoBalance, Token}; + +use crate::{PrecompileExt, PrecompileHandleExt}; + +type SubtensorEconomicConstants = ( + U256, + U256, + U256, + U256, + U256, + U256, + U256, + U256, + U256, + U256, + U256, + U256, + U256, +); +type SubtensorSubnetConstants = ( + u16, + u16, + u16, + u16, + u16, + u16, + u16, + u16, + u32, + u32, + u8, + u16, + u8, +); +type SubtensorConsensusConstants = ( + u16, + u16, + u16, + U256, + u16, + u64, + u16, + bool, + u64, + u16, + u16, + u64, + u64, +); +type SubtensorRegistrationConstants = (u64, u64, u64, u16, u64, u16, u16, u64, u64, u64, u64); +type SubtensorDelegationConstants = (u16, u16, u16, u16, u16, u16, u16, bool, bool); +type SubtensorRateLimitConstants = (u64, u64, u64, u64, u64, u64, u64, u64, u64, u64, u64); +type SubtensorProtocolConstants = ( + u32, + u32, + u32, + u128, + u64, + u64, + u16, + u8, + u64, + u64, + u64, + u64, + U256, + u32, + U256, +); +type BalancesConstants = (U256, u32, u32, u32); +type ProxyConstants = (U256, U256, u32, u32, U256, U256); +type SchedulerConstants = (u64, u64, u32); +type DrandConstants = (UnboundedString, u64, u64, u64, u64, u64); +type CrowdloanConstants = (U256, U256, u64, u64, u32, u32, H256); +type SwapConstants = (u16, U256, U256, H256); +pub(crate) type ProxyBalanceOf = <::Currency as Currency< + ::AccountId, +>>::Balance; + +pub struct RuntimeConfigurationPrecompile(PhantomData); + +impl PrecompileExt for RuntimeConfigurationPrecompile +where + R: frame_system::Config + + pallet_admin_utils::Config + + pallet_balances::Config + + pallet_crowdloan::Config + + pallet_drand::Config + + pallet_evm::Config + + pallet_evm_chain_id::Config + + pallet_scheduler::Config + + pallet_subtensor::Config + + pallet_subtensor_proxy::Config + + pallet_subtensor_swap::Config + + pallet_timestamp::Config, + R::AccountId: From<[u8; 32]> + Into<[u8; 32]>, + frame_system::pallet_prelude::BlockNumberFor: TryInto, + ::Moment: TryInto, + ::Balance: Into, + ProxyBalanceOf: Into, +{ + const INDEX: u64 = 2066; +} + +#[precompile_utils::precompile] +impl RuntimeConfigurationPrecompile +where + R: frame_system::Config + + pallet_admin_utils::Config + + pallet_balances::Config + + pallet_crowdloan::Config + + pallet_drand::Config + + pallet_evm::Config + + pallet_evm_chain_id::Config + + pallet_scheduler::Config + + pallet_subtensor::Config + + pallet_subtensor_proxy::Config + + pallet_subtensor_swap::Config + + pallet_timestamp::Config, + R::AccountId: From<[u8; 32]> + Into<[u8; 32]>, + frame_system::pallet_prelude::BlockNumberFor: TryInto, + ::Moment: TryInto, + ::Balance: Into, + ProxyBalanceOf: Into, +{ + #[precompile::public("getEvmChainId()")] + #[precompile::view] + fn get_evm_chain_id(handle: &mut impl PrecompileHandle) -> EvmResult { + handle.record_db_reads::(1)?; + Ok(pallet_evm_chain_id::ChainId::::get()) + } + + #[precompile::public("getTransactionRateLimit()")] + #[precompile::view] + fn get_transaction_rate_limit(handle: &mut impl PrecompileHandle) -> EvmResult { + handle.record_db_reads::(1)?; + Ok(pallet_subtensor::Pallet::::get_tx_rate_limit()) + } + + #[precompile::public("getSubtensorEconomicConstants()")] + #[precompile::view] + fn get_subtensor_economic_constants( + _handle: &mut impl PrecompileHandle, + ) -> EvmResult { + Ok(( + tao_to_evm::(::InitialIssuance::get())?, + tao_to_evm::( + ::InitialRAORecycledForRegistration::get(), + )?, + tao_to_evm::(::InitialBurn::get())?, + tao_to_evm::(::InitialMinBurn::get())?, + tao_to_evm::(::InitialMaxBurn::get())?, + tao_to_evm::(::InitialMinStake::get())?, + tao_to_evm::(::InitialMinTransfer::get())?, + tao_to_evm::(::MinBurnUpperBound::get())?, + tao_to_evm::(::MaxBurnLowerBound::get())?, + tao_to_evm::(::InitialNetworkMinLockCost::get())?, + tao_to_evm::(::KeySwapCost::get())?, + tao_to_evm::(::KeySwapOnSubnetCost::get())?, + tao_to_evm::(pallet_subtensor::pallet::MIN_BALANCE_TO_PERFORM_COLDKEY_SWAP)?, + )) + } + + #[precompile::public("getSubtensorSubnetConstants()")] + #[precompile::view] + fn get_subtensor_subnet_constants( + _handle: &mut impl PrecompileHandle, + ) -> EvmResult { + Ok(( + ::InitialTempo::get(), + ::MinTempo::get(), + ::MaxTempo::get(), + ::InitialMinAllowedUids::get(), + ::InitialMaxAllowedUids::get(), + ::InitialMaxAllowedValidators::get(), + ::InitialImmunityPeriod::get(), + ::InitialActivityCutoff::get(), + ::MinActivityCutoffFactorMilli::get(), + ::MaxActivityCutoffFactorMilli::get(), + ::MaxImmuneUidsPercentage::get().deconstruct(), + ::InitialSubnetOwnerCut::get(), + ::InitialMaxEpochsPerBlock::get(), + )) + } + + #[precompile::public("getSubtensorConsensusConstants()")] + #[precompile::view] + fn get_subtensor_consensus_constants( + _handle: &mut impl PrecompileHandle, + ) -> EvmResult { + Ok(( + ::InitialMinAllowedWeights::get(), + ::InitialEmissionValue::get(), + ::InitialRho::get(), + signed_i16_word(::InitialAlphaSigmoidSteepness::get()), + ::InitialKappa::get(), + ::InitialBondsMovingAverage::get(), + ::InitialBondsPenalty::get(), + ::InitialBondsResetOn::get(), + ::InitialValidatorPruneLen::get(), + ::InitialScalingLawPower::get(), + ::InitialPruningScore::get(), + ::InitialWeightsVersionKey::get(), + ::InitialTaoWeight::get(), + )) + } + + #[precompile::public("getSubtensorRegistrationConstants()")] + #[precompile::view] + fn get_subtensor_registration_constants( + _handle: &mut impl PrecompileHandle, + ) -> EvmResult { + Ok(( + ::InitialDifficulty::get(), + ::InitialMinDifficulty::get(), + ::InitialMaxDifficulty::get(), + ::InitialAdjustmentInterval::get(), + ::InitialAdjustmentAlpha::get(), + ::InitialMaxRegistrationsPerBlock::get(), + ::InitialTargetRegistrationsPerInterval::get(), + ::InitialNetworkRateLimit::get(), + ::InitialNetworkImmunityPeriod::get(), + ::InitialNetworkLockReductionInterval::get(), + ::InitialEmaPriceHalvingPeriod::get(), + )) + } + + #[precompile::public("getSubtensorDelegationConstants()")] + #[precompile::view] + fn get_subtensor_delegation_constants( + _handle: &mut impl PrecompileHandle, + ) -> EvmResult { + Ok(( + ::InitialDefaultDelegateTake::get(), + ::InitialMinDelegateTake::get(), + ::InitialDefaultChildKeyTake::get(), + ::InitialMinChildKeyTake::get(), + ::InitialMaxChildKeyTake::get(), + ::AlphaHigh::get(), + ::AlphaLow::get(), + ::LiquidAlphaOn::get(), + ::Yuma3On::get(), + )) + } + + #[precompile::public("getSubtensorRateLimitConstants()")] + #[precompile::view] + fn get_subtensor_rate_limit_constants( + _handle: &mut impl PrecompileHandle, + ) -> EvmResult { + Ok(( + ::InitialServingRateLimit::get(), + ::InitialTxRateLimit::get(), + ::InitialTxDelegateTakeRateLimit::get(), + ::InitialTxChildKeyTakeRateLimit::get(), + ::EvmKeyAssociateRateLimit::get(), + block_to_u64( + ::InitialColdkeySwapAnnouncementDelay::get(), + )?, + block_to_u64( + ::InitialColdkeySwapReannouncementDelay::get(), + )?, + block_to_u64( + ::InitialDissolveNetworkScheduleDuration::get(), + )?, + ::InitialStartCallDelay::get(), + ::HotkeySwapOnSubnetInterval::get(), + block_to_u64( + ::LeaseDividendsDistributionInterval::get(), + )?, + )) + } + + #[precompile::public("getSubtensorProtocolConstants()")] + #[precompile::view] + fn get_subtensor_protocol_constants( + _handle: &mut impl PrecompileHandle, + ) -> EvmResult { + Ok(( + pallet_subtensor::MAX_CRV3_COMMIT_SIZE_BYTES, + pallet_subtensor::MAX_ASSOCIATED_UIDS_PER_EVM_ADDRESS, + pallet_subtensor::MAX_COLDKEY_COLLATERAL_HOTKEYS, + pallet_subtensor::ACCOUNT_FLAGS_ACCEPT_LOCKED_ALPHA, + pallet_subtensor::pallet::MIN_COMMIT_REVEAL_PEROIDS, + pallet_subtensor::pallet::MAX_COMMIT_REVEAL_PEROIDS, + pallet_subtensor::subnets::mechanism::GLOBAL_MAX_SUBNET_COUNT, + pallet_subtensor::subnets::mechanism::MAX_MECHANISM_COUNT_PER_SUBNET, + pallet_subtensor::utils::voting_power::VOTING_POWER_DISABLE_GRACE_PERIOD_BLOCKS, + pallet_subtensor::utils::voting_power::MAX_VOTING_POWER_EMA_ALPHA, + pallet_subtensor::Pallet::::EMISSION_BAR_UPDATE_INTERVAL, + pallet_subtensor::staking::lock::ONE_YEAR, + tao_u64_to_evm::(pallet_subtensor::staking::lock::LOCK_STATE_ZERO_THRESHOLD)?, + pallet_subtensor::pallet::INITIAL_ACTIVITY_CUTOFF_FACTOR_MILLI, + tao_u64_to_evm::(pallet_subtensor::coinbase::tao::MAX_TAO_ISSUANCE)?, + )) + } + + #[precompile::public("getSubtensorSystemAccounts()")] + #[precompile::view] + fn get_subtensor_system_accounts( + _handle: &mut impl PrecompileHandle, + ) -> EvmResult<(H256, H256)> { + Ok(( + pallet_account::(::SubtensorPalletId::get()), + pallet_account::(::BurnAccountId::get()), + )) + } + + #[precompile::public("getBalancesConstants()")] + #[precompile::view] + fn get_balances_constants(_handle: &mut impl PrecompileHandle) -> EvmResult { + Ok(( + balance_to_evm::(::ExistentialDeposit::get())?, + ::MaxLocks::get(), + ::MaxReserves::get(), + ::MaxFreezes::get(), + )) + } + + #[precompile::public("getProxyConstants()")] + #[precompile::view] + fn get_proxy_constants(_handle: &mut impl PrecompileHandle) -> EvmResult { + Ok(( + balance_to_evm::(::ProxyDepositBase::get())?, + balance_to_evm::( + ::ProxyDepositFactor::get(), + )?, + ::MaxProxies::get(), + ::MaxPending::get(), + balance_to_evm::( + ::AnnouncementDepositBase::get(), + )?, + balance_to_evm::( + ::AnnouncementDepositFactor::get(), + )?, + )) + } + + #[precompile::public("getSchedulerConstants()")] + #[precompile::view] + fn get_scheduler_constants( + _handle: &mut impl PrecompileHandle, + ) -> EvmResult { + let maximum_weight = ::MaximumWeight::get(); + Ok(( + maximum_weight.ref_time(), + maximum_weight.proof_size(), + ::MaxScheduledPerBlock::get(), + )) + } + + #[precompile::public("getDrandConstants()")] + #[precompile::view] + fn get_drand_constants(_handle: &mut impl PrecompileHandle) -> EvmResult { + Ok(( + UnboundedString::from(pallet_drand::QUICKNET_CHAIN_HASH), + ::UnsignedPriority::get(), + ::HttpFetchTimeout::get(), + pallet_drand::MAX_PULSES_TO_FETCH, + pallet_drand::MAX_KEPT_PULSES, + pallet_drand::MAX_REMOVED_PULSES, + )) + } + + #[precompile::public("getCrowdloanConstants()")] + #[precompile::view] + fn get_crowdloan_constants( + _handle: &mut impl PrecompileHandle, + ) -> EvmResult { + Ok(( + tao_to_evm::(::MinimumDeposit::get())?, + tao_to_evm::(::AbsoluteMinimumContribution::get())?, + block_to_u64(::MinimumBlockDuration::get())?, + block_to_u64(::MaximumBlockDuration::get())?, + ::RefundContributorsLimit::get(), + ::MaxContributors::get(), + pallet_account::(::PalletId::get()), + )) + } + + #[precompile::public("getSwapConstants()")] + #[precompile::view] + fn get_swap_constants(_handle: &mut impl PrecompileHandle) -> EvmResult { + Ok(( + ::MaxFeeRate::get(), + tao_u64_to_evm::(::MinimumLiquidity::get())?, + tao_u64_to_evm::(::MinimumReserve::get().get())?, + pallet_account::(::ProtocolId::get()), + )) + } + + #[precompile::public("getTimestampConstants()")] + #[precompile::view] + fn get_timestamp_constants(_handle: &mut impl PrecompileHandle) -> EvmResult { + ::MinimumPeriod::get() + .try_into() + .map_err(|_| ExitError::InvalidRange.into()) + } + + #[precompile::public("getAdminConstants()")] + #[precompile::view] + fn get_admin_constants(_handle: &mut impl PrecompileHandle) -> EvmResult { + Ok(::MaxAuthorities::get()) + } +} + +fn tao_to_evm(value: TaoBalance) -> EvmResult +where + R: pallet_evm::Config, +{ + tao_u64_to_evm::(value.to_u64()) +} + +fn tao_u64_to_evm(value: u64) -> EvmResult +where + R: pallet_evm::Config, +{ + let value: SubstrateBalance = value.into(); + R::BalanceConverter::into_evm_balance(value) + .map(|amount| amount.into_u256()) + .ok_or_else(|| ExitError::InvalidRange.into()) +} + +fn balance_to_evm(value: Balance) -> EvmResult +where + R: pallet_evm::Config, + Balance: Into, +{ + let value = SubstrateBalance::new(value.into()); + R::BalanceConverter::into_evm_balance(value) + .map(|amount| amount.into_u256()) + .ok_or_else(|| ExitError::InvalidRange.into()) +} + +fn block_to_u64>(block: Block) -> EvmResult { + block.try_into().map_err(|_| ExitError::InvalidRange.into()) +} + +fn pallet_account(pallet_id: frame_support::PalletId) -> H256 +where + R: frame_system::Config, + R::AccountId: Into<[u8; 32]>, +{ + let account: R::AccountId = pallet_id.into_account_truncating(); + H256::from(>::into(account)) +} + +fn signed_i16_word(value: i16) -> U256 { + let mut encoded = [if value.is_negative() { 0xff } else { 0 }; 32]; + encoded[30..].copy_from_slice(&value.to_be_bytes()); + U256::from_big_endian(&encoded) +} + +#[cfg(test)] +mod tests { + #![allow(clippy::unwrap_used)] + + use super::*; + use crate::mock::{Runtime, addr_from_index, new_test_ext, precompiles, selector_u32}; + use precompile_utils::{ + prelude::RuntimeHelper, + solidity::{Codec, encode_return_value, encode_with_selector}, + testing::PrecompileTesterExt, + }; + + fn assert_view(signature: &str, expected: Output) { + let precompiles = precompiles::>(); + precompiles + .prepare_test( + addr_from_index(1), + addr_from_index(RuntimeConfigurationPrecompile::::INDEX), + encode_with_selector(selector_u32(signature), ()), + ) + .with_static_call(true) + .execute_returns_raw(encode_return_value(expected)); + } + + #[test] + fn address_storage_selectors_and_values_are_stable() { + new_test_ext().execute_with(|| { + assert_eq!(RuntimeConfigurationPrecompile::::INDEX, 2066); + pallet_evm_chain_id::ChainId::::put(9_999u64); + pallet_subtensor::TxRateLimit::::put(77u64); + + let precompiles = precompiles::>(); + let caller = addr_from_index(1); + let address = addr_from_index(2066); + let read_cost = RuntimeHelper::::db_read_gas_cost(); + + precompiles + .prepare_test( + caller, + address, + encode_with_selector(selector_u32("getEvmChainId()"), ()), + ) + .with_static_call(true) + .expect_cost(read_cost) + .execute_returns_raw(encode_return_value(9_999u64)); + precompiles + .prepare_test( + caller, + address, + encode_with_selector(selector_u32("getTransactionRateLimit()"), ()), + ) + .with_static_call(true) + .expect_cost(read_cost) + .execute_returns_raw(encode_return_value(77u64)); + }); + } + + #[test] + fn subtensor_constant_views_return_authoritative_runtime_values() { + new_test_ext().execute_with(|| { + assert_view( + "getSubtensorEconomicConstants()", + ( + tao_to_evm::(::InitialIssuance::get()).unwrap(), + tao_to_evm::(::InitialRAORecycledForRegistration::get()) + .unwrap(), + tao_to_evm::(::InitialBurn::get()).unwrap(), + tao_to_evm::(::InitialMinBurn::get()).unwrap(), + tao_to_evm::(::InitialMaxBurn::get()).unwrap(), + tao_to_evm::(::InitialMinStake::get()).unwrap(), + tao_to_evm::(::InitialMinTransfer::get()).unwrap(), + tao_to_evm::(::MinBurnUpperBound::get()).unwrap(), + tao_to_evm::(::MaxBurnLowerBound::get()).unwrap(), + tao_to_evm::(::InitialNetworkMinLockCost::get()).unwrap(), + tao_to_evm::(::KeySwapCost::get()).unwrap(), + tao_to_evm::(::KeySwapOnSubnetCost::get()).unwrap(), + tao_to_evm::( + pallet_subtensor::pallet::MIN_BALANCE_TO_PERFORM_COLDKEY_SWAP, + ) + .unwrap(), + ), + ); + assert_view( + "getSubtensorSubnetConstants()", + ( + ::InitialTempo::get(), + ::MinTempo::get(), + ::MaxTempo::get(), + ::InitialMinAllowedUids::get(), + ::InitialMaxAllowedUids::get(), + ::InitialMaxAllowedValidators::get(), + ::InitialImmunityPeriod::get(), + ::InitialActivityCutoff::get(), + ::MinActivityCutoffFactorMilli::get(), + ::MaxActivityCutoffFactorMilli::get(), + ::MaxImmuneUidsPercentage::get().deconstruct(), + ::InitialSubnetOwnerCut::get(), + ::InitialMaxEpochsPerBlock::get(), + ), + ); + assert_view( + "getSubtensorConsensusConstants()", + ( + ::InitialMinAllowedWeights::get(), + ::InitialEmissionValue::get(), + ::InitialRho::get(), + signed_i16_word(::InitialAlphaSigmoidSteepness::get()), + ::InitialKappa::get(), + ::InitialBondsMovingAverage::get(), + ::InitialBondsPenalty::get(), + ::InitialBondsResetOn::get(), + ::InitialValidatorPruneLen::get(), + ::InitialScalingLawPower::get(), + ::InitialPruningScore::get(), + ::InitialWeightsVersionKey::get(), + ::InitialTaoWeight::get(), + ), + ); + assert_view( + "getSubtensorRegistrationConstants()", + ( + ::InitialDifficulty::get(), + ::InitialMinDifficulty::get(), + ::InitialMaxDifficulty::get(), + ::InitialAdjustmentInterval::get(), + ::InitialAdjustmentAlpha::get(), + ::InitialMaxRegistrationsPerBlock::get(), + ::InitialTargetRegistrationsPerInterval::get(), + ::InitialNetworkRateLimit::get(), + ::InitialNetworkImmunityPeriod::get(), + ::InitialNetworkLockReductionInterval::get(), + ::InitialEmaPriceHalvingPeriod::get(), + ), + ); + assert_view( + "getSubtensorDelegationConstants()", + ( + ::InitialDefaultDelegateTake::get(), + ::InitialMinDelegateTake::get(), + ::InitialDefaultChildKeyTake::get(), + ::InitialMinChildKeyTake::get(), + ::InitialMaxChildKeyTake::get(), + ::AlphaHigh::get(), + ::AlphaLow::get(), + ::LiquidAlphaOn::get(), + ::Yuma3On::get(), + ), + ); + assert_view( + "getSubtensorRateLimitConstants()", + ( + ::InitialServingRateLimit::get(), + ::InitialTxRateLimit::get(), + ::InitialTxDelegateTakeRateLimit::get(), + ::InitialTxChildKeyTakeRateLimit::get(), + ::EvmKeyAssociateRateLimit::get(), + block_to_u64(::InitialColdkeySwapAnnouncementDelay::get()).unwrap(), + block_to_u64(::InitialColdkeySwapReannouncementDelay::get()).unwrap(), + block_to_u64(::InitialDissolveNetworkScheduleDuration::get()).unwrap(), + ::InitialStartCallDelay::get(), + ::HotkeySwapOnSubnetInterval::get(), + block_to_u64(::LeaseDividendsDistributionInterval::get()).unwrap(), + ), + ); + assert_view( + "getSubtensorProtocolConstants()", + ( + pallet_subtensor::MAX_CRV3_COMMIT_SIZE_BYTES, + pallet_subtensor::MAX_ASSOCIATED_UIDS_PER_EVM_ADDRESS, + pallet_subtensor::MAX_COLDKEY_COLLATERAL_HOTKEYS, + pallet_subtensor::ACCOUNT_FLAGS_ACCEPT_LOCKED_ALPHA, + pallet_subtensor::pallet::MIN_COMMIT_REVEAL_PEROIDS, + pallet_subtensor::pallet::MAX_COMMIT_REVEAL_PEROIDS, + pallet_subtensor::subnets::mechanism::GLOBAL_MAX_SUBNET_COUNT, + pallet_subtensor::subnets::mechanism::MAX_MECHANISM_COUNT_PER_SUBNET, + pallet_subtensor::utils::voting_power::VOTING_POWER_DISABLE_GRACE_PERIOD_BLOCKS, + pallet_subtensor::utils::voting_power::MAX_VOTING_POWER_EMA_ALPHA, + pallet_subtensor::Pallet::::EMISSION_BAR_UPDATE_INTERVAL, + pallet_subtensor::staking::lock::ONE_YEAR, + tao_u64_to_evm::( + pallet_subtensor::staking::lock::LOCK_STATE_ZERO_THRESHOLD, + ) + .unwrap(), + pallet_subtensor::pallet::INITIAL_ACTIVITY_CUTOFF_FACTOR_MILLI, + tao_u64_to_evm::(pallet_subtensor::coinbase::tao::MAX_TAO_ISSUANCE) + .unwrap(), + ), + ); + assert_view( + "getSubtensorSystemAccounts()", + ( + pallet_account::(::SubtensorPalletId::get()), + pallet_account::(::BurnAccountId::get()), + ), + ); + }); + } + + #[test] + fn other_pallet_constant_views_return_authoritative_runtime_values() { + new_test_ext().execute_with(|| { + assert_view( + "getBalancesConstants()", + ( + balance_to_evm::( + ::ExistentialDeposit::get(), + ) + .unwrap(), + <::MaxLocks as Get>::get(), + <::MaxReserves as Get>::get(), + ::MaxFreezes::get(), + ), + ); + assert_view( + "getProxyConstants()", + ( + balance_to_evm::( + ::ProxyDepositBase::get(), + ) + .unwrap(), + balance_to_evm::( + ::ProxyDepositFactor::get(), + ) + .unwrap(), + ::MaxProxies::get(), + ::MaxPending::get(), + balance_to_evm::( + ::AnnouncementDepositBase::get(), + ) + .unwrap(), + balance_to_evm::( + ::AnnouncementDepositFactor::get( + ), + ) + .unwrap(), + ), + ); + let maximum_weight = ::MaximumWeight::get(); + assert_view( + "getSchedulerConstants()", + ( + maximum_weight.ref_time(), + maximum_weight.proof_size(), + ::MaxScheduledPerBlock::get(), + ), + ); + assert_view( + "getDrandConstants()", + ( + UnboundedString::from(pallet_drand::QUICKNET_CHAIN_HASH), + <::UnsignedPriority as Get>::get(), + <::HttpFetchTimeout as Get>::get(), + pallet_drand::MAX_PULSES_TO_FETCH, + pallet_drand::MAX_KEPT_PULSES, + pallet_drand::MAX_REMOVED_PULSES, + ), + ); + assert_view( + "getCrowdloanConstants()", + ( + tao_to_evm::( + ::MinimumDeposit::get(), + ) + .unwrap(), + tao_to_evm::( + ::AbsoluteMinimumContribution::get(), + ) + .unwrap(), + block_to_u64( + ::MinimumBlockDuration::get(), + ) + .unwrap(), + block_to_u64( + ::MaximumBlockDuration::get(), + ) + .unwrap(), + ::RefundContributorsLimit::get(), + ::MaxContributors::get(), + pallet_account::( + ::PalletId::get(), + ), + ), + ); + assert_view( + "getSwapConstants()", + ( + ::MaxFeeRate::get(), + tao_u64_to_evm::( + ::MinimumLiquidity::get(), + ) + .unwrap(), + tao_u64_to_evm::( + ::MinimumReserve::get().get(), + ) + .unwrap(), + pallet_account::( + ::ProtocolId::get(), + ), + ), + ); + assert_view( + "getTimestampConstants()", + ::MinimumPeriod::get(), + ); + assert_view( + "getAdminConstants()", + ::MaxAuthorities::get(), + ); + }); + } + + #[test] + fn signed_i16_constants_use_solidity_sign_extension() { + assert_eq!(signed_i16_word(1), U256::one()); + assert_eq!(signed_i16_word(-1), U256::from_big_endian(&[0xff; 32])); + assert_eq!( + signed_i16_word(i16::MIN), + U256::from_big_endian(&{ + let mut word = [0xff; 32]; + word[30..].copy_from_slice(&i16::MIN.to_be_bytes()); + word + }) + ); + } +} diff --git a/precompiles/src/scheduler.rs b/precompiles/src/scheduler.rs new file mode 100644 index 0000000000..0182240478 --- /dev/null +++ b/precompiles/src/scheduler.rs @@ -0,0 +1,256 @@ +use core::marker::PhantomData; + +use codec::{Decode, Encode}; +use fp_evm::{ExitError, PrecompileFailure}; +use pallet_evm::PrecompileHandle; +use precompile_utils::EvmResult; +use sp_core::H256; + +use crate::{PrecompileExt, PrecompileHandleExt}; + +type ScheduledCallMetadata = (bool, bool, H256, u8, H256, bool, u32, bool, u64, u32); + +pub struct SchedulerPrecompile(PhantomData); + +impl PrecompileExt for SchedulerPrecompile +where + R: frame_system::Config + pallet_evm::Config + pallet_scheduler::Config, + R::AccountId: From<[u8; 32]>, + R::Hash: AsRef<[u8]>, + pallet_scheduler::BlockNumberFor: TryFrom + TryInto, +{ + const INDEX: u64 = 2063; +} + +#[precompile_utils::precompile] +impl SchedulerPrecompile +where + R: frame_system::Config + pallet_evm::Config + pallet_scheduler::Config, + R::AccountId: From<[u8; 32]>, + R::Hash: AsRef<[u8]>, + pallet_scheduler::BlockNumberFor: TryFrom + TryInto, +{ + #[precompile::public("getIncompleteSince()")] + #[precompile::view] + fn get_incomplete_since(handle: &mut impl PrecompileHandle) -> EvmResult<(bool, u64)> { + handle.record_db_reads::(1)?; + match pallet_scheduler::IncompleteSince::::get() { + Some(block) => Ok((true, block_number_to_u64::(block)?)), + None => Ok((false, 0)), + } + } + + #[precompile::public("getScheduledCallCount(uint64)")] + #[precompile::view] + fn get_scheduled_call_count(handle: &mut impl PrecompileHandle, when: u64) -> EvmResult { + handle.record_db_reads::(1)?; + let agenda = pallet_scheduler::Agenda::::get(block_number_from_u64::(when)?); + u32::try_from(agenda.len()).map_err(|_| conversion_error("scheduler agenda length")) + } + + #[precompile::public("getScheduledCall(uint64,uint32)")] + #[precompile::view] + fn get_scheduled_call( + handle: &mut impl PrecompileHandle, + when: u64, + index: u32, + ) -> EvmResult { + handle.record_db_reads::(1)?; + let agenda = pallet_scheduler::Agenda::::get(block_number_from_u64::(when)?); + let Some(Some(scheduled)) = agenda + .get(usize::try_from(index).map_err(|_| conversion_error("scheduler agenda index"))?) + else { + return Ok(( + false, + false, + H256::zero(), + 0, + H256::zero(), + false, + 0, + false, + 0, + 0, + )); + }; + + let (has_task_id, task_id) = scheduled + .maybe_id + .map(|id| (true, H256::from(id))) + .unwrap_or((false, H256::zero())); + let call_hash = <[u8; 32]>::try_from(scheduled.call.hash().as_ref()) + .map(H256::from) + .map_err(|_| conversion_error("scheduler call hash"))?; + let (has_call_length, call_length) = scheduled + .call + .len() + .map(|length| (true, length)) + .unwrap_or((false, 0)); + let (is_periodic, period, remaining) = match scheduled.maybe_periodic { + Some((period, remaining)) => (true, block_number_to_u64::(period)?, remaining), + None => (false, 0, 0), + }; + + Ok(( + true, + has_task_id, + task_id, + scheduled.priority, + call_hash, + has_call_length, + call_length, + is_periodic, + period, + remaining, + )) + } + + #[precompile::public("getRetry(uint64,uint32)")] + #[precompile::view] + fn get_retry( + handle: &mut impl PrecompileHandle, + when: u64, + index: u32, + ) -> EvmResult<(bool, u8, u8, u64)> { + handle.record_db_reads::(1)?; + let address = (block_number_from_u64::(when)?, index); + match pallet_scheduler::Retries::::get(address) { + Some(retry) => { + let encoded = retry.encode(); + let (total_retries, remaining, period) = + <(u8, u8, pallet_scheduler::BlockNumberFor)>::decode( + &mut encoded.as_slice(), + ) + .map_err(|_| conversion_error("scheduler retry metadata"))?; + Ok(( + true, + total_retries, + remaining, + block_number_to_u64::(period)?, + )) + } + None => Ok((false, 0, 0, 0)), + } + } + + #[precompile::public("getTaskAddress(bytes32)")] + #[precompile::view] + fn get_task_address( + handle: &mut impl PrecompileHandle, + task_id: H256, + ) -> EvmResult<(bool, u64, u32)> { + handle.record_db_reads::(1)?; + match pallet_scheduler::Lookup::::get(task_id.0) { + Some((when, index)) => Ok((true, block_number_to_u64::(when)?, index)), + None => Ok((false, 0, 0)), + } + } +} + +fn block_number_from_u64(block: u64) -> EvmResult> +where + R: pallet_scheduler::Config, + pallet_scheduler::BlockNumberFor: TryFrom, +{ + block + .try_into() + .map_err(|_| conversion_error("scheduler block number")) +} + +fn block_number_to_u64(block: pallet_scheduler::BlockNumberFor) -> EvmResult +where + R: pallet_scheduler::Config, + pallet_scheduler::BlockNumberFor: TryInto, +{ + block + .try_into() + .map_err(|_| conversion_error("scheduler block number")) +} + +fn conversion_error(field: &'static str) -> PrecompileFailure { + PrecompileFailure::Error { + exit_status: ExitError::Other(field.into()), + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::mock::{Runtime, addr_from_index, new_test_ext, precompiles, selector_u32}; + use precompile_utils::{ + prelude::RuntimeHelper, + solidity::{encode_return_value, encode_with_selector}, + testing::PrecompileTesterExt, + }; + + #[test] + fn address_selectors_and_empty_metadata_are_stable() { + new_test_ext().execute_with(|| { + assert_eq!(SchedulerPrecompile::::INDEX, 2063); + let precompiles = precompiles::>(); + let caller = addr_from_index(1); + let address = addr_from_index(2063); + let read_cost = RuntimeHelper::::db_read_gas_cost(); + + precompiles + .prepare_test( + caller, + address, + encode_with_selector(selector_u32("getIncompleteSince()"), ()), + ) + .with_static_call(true) + .expect_cost(read_cost) + .execute_returns_raw(encode_return_value((false, 0u64))); + precompiles + .prepare_test( + caller, + address, + encode_with_selector(selector_u32("getScheduledCallCount(uint64)"), (10u64,)), + ) + .with_static_call(true) + .expect_cost(read_cost) + .execute_returns_raw(encode_return_value(0u32)); + precompiles + .prepare_test( + caller, + address, + encode_with_selector( + selector_u32("getScheduledCall(uint64,uint32)"), + (10u64, 0u32), + ), + ) + .with_static_call(true) + .expect_cost(read_cost) + .execute_returns_raw(encode_return_value(( + false, + false, + H256::zero(), + 0u8, + H256::zero(), + false, + 0u32, + false, + 0u64, + 0u32, + ))); + precompiles + .prepare_test( + caller, + address, + encode_with_selector(selector_u32("getRetry(uint64,uint32)"), (10u64, 0u32)), + ) + .with_static_call(true) + .expect_cost(read_cost) + .execute_returns_raw(encode_return_value((false, 0u8, 0u8, 0u64))); + precompiles + .prepare_test( + caller, + address, + encode_with_selector(selector_u32("getTaskAddress(bytes32)"), (H256::zero(),)), + ) + .with_static_call(true) + .expect_cost(read_cost) + .execute_returns_raw(encode_return_value((false, 0u64, 0u32))); + }); + } +} diff --git a/precompiles/src/solidity/alpha.abi b/precompiles/src/solidity/alpha.abi index 14d6eb66dc..aa37571e54 100644 --- a/precompiles/src/solidity/alpha.abi +++ b/precompiles/src/solidity/alpha.abi @@ -326,5 +326,327 @@ ], "stateMutability": "view", "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "burnHalfLife", + "type": "uint16" + } + ], + "name": "setBurnHalfLife", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint128", + "name": "rawMultiplier", + "type": "uint128" + } + ], + "name": "setBurnIncreaseMultiplier", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint8", + "name": "mode", + "type": "uint8" + } + ], + "name": "setRecycleOrBurn", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, +{ + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + } + ], + "name": "getEmissionAccounting", + "outputs": [ + { + "internalType": "uint64", + "name": "alphaDividends", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "rootAlphaDividends", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "lastHotkeyEmission", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "pendingServerEmission", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "pendingValidatorEmission", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "pendingRootAlphaDividends", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "pendingOwnerCut", + "type": "uint64" + }, + { + "internalType": "uint128", + "name": "minerBurned", + "type": "uint128" + }, + { + "internalType": "uint64", + "name": "raoRecycledForRegistration", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getEmissionGateConfig", + "outputs": [ + { + "internalType": "uint64", + "name": "blockEmission", + "type": "uint64" + }, + { + "internalType": "int128", + "name": "movingAlpha", + "type": "int128" + }, + { + "internalType": "bool", + "name": "netTaoFlowEnabled", + "type": "bool" + }, + { + "internalType": "int128", + "name": "taoFlowCutoff", + "type": "int128" + }, + { + "internalType": "uint128", + "name": "flowNormExponent", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "emissionBarQuantile", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "emissionGateExponent", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "emissionGateBar", + "type": "uint128" + }, + { + "internalType": "uint64", + "name": "flowEmaSmoothingFactor", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "getSubnetEconomicState", + "outputs": [ + { + "internalType": "bool", + "name": "emissionEnabled", + "type": "bool" + }, + { + "internalType": "uint128", + "name": "rootProportion", + "type": "uint128" + }, + { + "internalType": "uint64", + "name": "excessTao", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "rootSellTao", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "protocolAlpha", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "getSubnetFlowState", + "outputs": [ + { + "internalType": "int64", + "name": "taoFlow", + "type": "int64" + }, + { + "internalType": "bool", + "name": "hasTaoFlowEma", + "type": "bool" + }, + { + "internalType": "uint64", + "name": "taoFlowEmaBlock", + "type": "uint64" + }, + { + "internalType": "int128", + "name": "taoFlowEma", + "type": "int128" + }, + { + "internalType": "int64", + "name": "protocolFlow", + "type": "int64" + }, + { + "internalType": "bool", + "name": "hasProtocolFlowEma", + "type": "bool" + }, + { + "internalType": "uint64", + "name": "protocolFlowEmaBlock", + "type": "uint64" + }, + { + "internalType": "int128", + "name": "protocolFlowEma", + "type": "int128" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "getSwapState", + "outputs": [ + { + "internalType": "uint16", + "name": "feeRate", + "type": "uint16" + }, + { + "internalType": "bool", + "name": "initialized", + "type": "bool" + }, + { + "internalType": "uint64", + "name": "quoteWeight", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "taoReservoir", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "alphaReservoir", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "migrationName", + "type": "bytes" + } + ], + "name": "hasSwapMigrationRun", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" } ] \ No newline at end of file diff --git a/precompiles/src/solidity/alpha.sol b/precompiles/src/solidity/alpha.sol index c99252ff48..3bbcf9ad55 100644 --- a/precompiles/src/solidity/alpha.sol +++ b/precompiles/src/solidity/alpha.sol @@ -98,4 +98,86 @@ interface IAlpha { /// @dev Returns the CK burn rate. /// @return The CK burn rate. function getCKBurn() external view returns (uint256); + + /// mode: 0 = burn, 1 = recycle. + function setRecycleOrBurn(uint16 netuid, uint8 mode) external; + function setBurnHalfLife(uint16 netuid, uint16 burnHalfLife) external; + /// Raw U64F64 bits. + function setBurnIncreaseMultiplier( + uint16 netuid, + uint128 rawMultiplier + ) external; + function getEmissionAccounting( + uint16 netuid, + bytes32 hotkey + ) + external + view + returns ( + uint64 alphaDividends, + uint64 rootAlphaDividends, + uint64 lastHotkeyEmission, + uint64 pendingServerEmission, + uint64 pendingValidatorEmission, + uint64 pendingRootAlphaDividends, + uint64 pendingOwnerCut, + uint128 minerBurned, + uint64 raoRecycledForRegistration + ); + function getSubnetEconomicState( + uint16 netuid + ) + external + view + returns ( + bool emissionEnabled, + uint128 rootProportion, + uint64 excessTao, + uint64 rootSellTao, + uint64 protocolAlpha + ); + function getSubnetFlowState( + uint16 netuid + ) + external + view + returns ( + int64 taoFlow, + bool hasTaoFlowEma, + uint64 taoFlowEmaBlock, + int128 taoFlowEma, + int64 protocolFlow, + bool hasProtocolFlowEma, + uint64 protocolFlowEmaBlock, + int128 protocolFlowEma + ); + function getEmissionGateConfig() + external + view + returns ( + uint64 blockEmission, + int128 movingAlpha, + bool netTaoFlowEnabled, + int128 taoFlowCutoff, + uint128 flowNormExponent, + uint128 emissionBarQuantile, + uint128 emissionGateExponent, + uint128 emissionGateBar, + uint64 flowEmaSmoothingFactor + ); + function getSwapState( + uint16 netuid + ) + external + view + returns ( + uint16 feeRate, + bool initialized, + uint64 quoteWeight, + uint64 taoReservoir, + uint64 alphaReservoir + ); + function hasSwapMigrationRun( + bytes calldata migrationName + ) external view returns (bool); } diff --git a/precompiles/src/solidity/balance.abi b/precompiles/src/solidity/balance.abi index 6f6e51c1af..52c19b6eb6 100644 --- a/precompiles/src/solidity/balance.abi +++ b/precompiles/src/solidity/balance.abi @@ -17,5 +17,49 @@ ], "stateMutability": "view", "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "keepAlive", + "type": "bool" + } + ], + "name": "burnBalance", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32[]", + "name": "accounts", + "type": "bytes32[]" + } + ], + "name": "upgradeAccounts", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, +{ + "inputs": [], + "name": "getTotalIssuance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" } -] +] \ No newline at end of file diff --git a/precompiles/src/solidity/balance.sol b/precompiles/src/solidity/balance.sol index 004cf8762b..a20075c5ea 100644 --- a/precompiles/src/solidity/balance.sol +++ b/precompiles/src/solidity/balance.sol @@ -1,11 +1,14 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -address constant IBALANCE_ADDRESS = 0x000000000000000000000000000000000000080E; +address constant IBALANCE_ADDRESS = 0x000000000000000000000000000000000000080e; interface IBalance { /// @dev Returns the native free TAO balance for an ss58 account public key. /// @param coldkey The coldkey public key (32 bytes). /// @return The free balance in rao (1 TAO = 1e9 rao). function getFreeBalance(bytes32 coldkey) external view returns (uint256); + function getTotalIssuance() external view returns (uint256); + function burnBalance(uint256 amount, bool keepAlive) external; + function upgradeAccounts(bytes32[] calldata accounts) external; } diff --git a/precompiles/src/solidity/balanceTransfer.abi b/precompiles/src/solidity/balanceTransfer.abi index 99913b9005..b7b5041ab8 100644 --- a/precompiles/src/solidity/balanceTransfer.abi +++ b/precompiles/src/solidity/balanceTransfer.abi @@ -11,5 +11,41 @@ "outputs": [], "stateMutability": "payable", "type": "function" - } + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "destination", + "type": "bytes32" + }, + { + "internalType": "bool", + "name": "keepAlive", + "type": "bool" + } + ], + "name": "transferAll", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "destination", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transferKeepAlive", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } ] \ No newline at end of file diff --git a/precompiles/src/solidity/balanceTransfer.sol b/precompiles/src/solidity/balanceTransfer.sol index 42790b9005..773252f962 100644 --- a/precompiles/src/solidity/balanceTransfer.sol +++ b/precompiles/src/solidity/balanceTransfer.sol @@ -4,4 +4,6 @@ address constant ISUBTENSOR_BALANCE_TRANSFER_ADDRESS = 0x00000000000000000000000 interface ISubtensorBalanceTransfer { function transfer(bytes32 data) external payable; -} \ No newline at end of file + function transferKeepAlive(bytes32 destination, uint256 amount) external; + function transferAll(bytes32 destination, bool keepAlive) external; +} diff --git a/precompiles/src/solidity/crowdloan.abi b/precompiles/src/solidity/crowdloan.abi index c507afcca2..3601e1fb1e 100644 --- a/precompiles/src/solidity/crowdloan.abi +++ b/precompiles/src/solidity/crowdloan.abi @@ -255,5 +255,28 @@ "outputs": [], "stateMutability": "payable", "type": "function" - } + }, + { + "inputs": [ + { + "internalType": "uint32", + "name": "crowdloanId", + "type": "uint32" + }, + { + "internalType": "bool", + "name": "hasMaxContribution", + "type": "bool" + }, + { + "internalType": "uint64", + "name": "maxContribution", + "type": "uint64" + } + ], + "name": "setMaxContribution", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } ] \ No newline at end of file diff --git a/precompiles/src/solidity/crowdloan.sol b/precompiles/src/solidity/crowdloan.sol index e8bf30c5d0..283517b9fc 100644 --- a/precompiles/src/solidity/crowdloan.sol +++ b/precompiles/src/solidity/crowdloan.sol @@ -94,6 +94,11 @@ interface ICrowdloan { * @param newCap The new cap. */ function updateCap(uint32 crowdloanId, uint64 newCap) external payable; + function setMaxContribution( + uint32 crowdloanId, + bool hasMaxContribution, + uint64 maxContribution + ) external; } struct CrowdloanInfo { @@ -108,4 +113,4 @@ struct CrowdloanInfo { bytes32 target_address; bool finalized; uint32 contributors_count; -} \ No newline at end of file +} diff --git a/precompiles/src/solidity/drand.abi b/precompiles/src/solidity/drand.abi new file mode 100644 index 0000000000..691f53f05b --- /dev/null +++ b/precompiles/src/solidity/drand.abi @@ -0,0 +1,129 @@ +[ + { + "inputs": [], + "name": "getBeaconConfig", + "outputs": [ + { + "internalType": "bytes", + "name": "publicKey", + "type": "bytes" + }, + { + "internalType": "uint32", + "name": "period", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "genesisTime", + "type": "uint32" + }, + { + "internalType": "bytes", + "name": "chainHash", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "groupHash", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "schemeId", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "beaconId", + "type": "bytes" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getNextUnsignedAt", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "round", + "type": "uint64" + } + ], + "name": "getPulse", + "outputs": [ + { + "internalType": "bool", + "name": "exists", + "type": "bool" + }, + { + "internalType": "uint64", + "name": "storedRound", + "type": "uint64" + }, + { + "internalType": "bytes", + "name": "randomness", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getStoredRoundRange", + "outputs": [ + { + "internalType": "uint64", + "name": "oldest", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "latest", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "key", + "type": "bytes" + } + ], + "name": "hasMigrationRun", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/precompiles/src/solidity/drand.sol b/precompiles/src/solidity/drand.sol new file mode 100644 index 0000000000..7827c325c6 --- /dev/null +++ b/precompiles/src/solidity/drand.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +address constant IDRAND_ADDRESS = 0x0000000000000000000000000000000000000810; + +interface IDrand { + function getBeaconConfig() + external + view + returns ( + bytes memory publicKey, + uint32 period, + uint32 genesisTime, + bytes memory chainHash, + bytes memory groupHash, + bytes memory schemeId, + bytes memory beaconId + ); + function getPulse( + uint64 round + ) external view returns (bool exists, uint64 storedRound, bytes memory randomness, bytes memory signature); + function getStoredRoundRange() external view returns (uint64 oldest, uint64 latest); + function getNextUnsignedAt() external view returns (uint64); + function hasMigrationRun(bytes calldata key) external view returns (bool); +} diff --git a/precompiles/src/solidity/leasing.abi b/precompiles/src/solidity/leasing.abi index 88115ee29c..541ad0cedf 100644 --- a/precompiles/src/solidity/leasing.abi +++ b/precompiles/src/solidity/leasing.abi @@ -168,5 +168,50 @@ "outputs": [], "stateMutability": "payable", "type": "function" - } + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "startCall", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, +{ + "inputs": [ + { + "internalType": "uint32", + "name": "leaseId", + "type": "uint32" + } + ], + "name": "getAccumulatedLeaseDividends", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getNextLeaseId", + "outputs": [ + { + "internalType": "uint32", + "name": "", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + } ] \ No newline at end of file diff --git a/precompiles/src/solidity/leasing.sol b/precompiles/src/solidity/leasing.sol index 1b9a406fac..95ff1927b4 100644 --- a/precompiles/src/solidity/leasing.sol +++ b/precompiles/src/solidity/leasing.sol @@ -29,6 +29,10 @@ interface ILeasing { * @return The lease id. */ function getLeaseIdForSubnet(uint16 netuid) external view returns (uint32); + function getNextLeaseId() external view returns (uint32); + function getAccumulatedLeaseDividends( + uint32 leaseId + ) external view returns (uint64); /** * @dev Create a lease crowdloan. @@ -56,6 +60,7 @@ interface ILeasing { * @param hotkey The hotkey of beneficiary, it must be owned by the beneficiary coldkey. */ function terminateLease(uint32 leaseId, bytes32 hotkey) external payable; + function startCall(uint16 netuid) external; } struct LeaseInfo { diff --git a/precompiles/src/solidity/neuron.abi b/precompiles/src/solidity/neuron.abi index 44d47449eb..a8639ab2ce 100644 --- a/precompiles/src/solidity/neuron.abi +++ b/precompiles/src/solidity/neuron.abi @@ -252,5 +252,1209 @@ "outputs": [], "stateMutability": "payable", "type": "function" - } -] + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "newColdkeyHash", + "type": "bytes32" + } + ], + "name": "announceColdkeySwap", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "address", + "name": "evmKey", + "type": "address" + }, + { + "internalType": "uint64", + "name": "blockNumber", + "type": "uint64" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "name": "associateEvmKey", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16[]", + "name": "netuids", + "type": "uint16[]" + }, + { + "internalType": "bytes32[]", + "name": "commitHashes", + "type": "bytes32[]" + } + ], + "name": "batchCommitWeights", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint16[][]", + "name": "uids", + "type": "uint16[][]" + }, + { + "internalType": "uint16[][]", + "name": "values", + "type": "uint16[][]" + }, + { + "internalType": "uint16[][]", + "name": "salts", + "type": "uint16[][]" + }, + { + "internalType": "uint64[]", + "name": "versionKeys", + "type": "uint64[]" + } + ], + "name": "batchRevealWeights", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16[]", + "name": "netuids", + "type": "uint16[]" + }, + { + "internalType": "uint16[][]", + "name": "dests", + "type": "uint16[][]" + }, + { + "internalType": "uint16[][]", + "name": "values", + "type": "uint16[][]" + }, + { + "internalType": "uint64[]", + "name": "versionKeys", + "type": "uint64[]" + } + ], + "name": "batchSetWeights", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "clearColdkeySwapAnnouncement", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint8", + "name": "mecid", + "type": "uint8" + }, + { + "internalType": "bytes", + "name": "commit", + "type": "bytes" + }, + { + "internalType": "uint64", + "name": "revealRound", + "type": "uint64" + } + ], + "name": "commitCrv3MechanismWeights", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint8", + "name": "mecid", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "commitHash", + "type": "bytes32" + } + ], + "name": "commitMechanismWeights", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint8", + "name": "mecid", + "type": "uint8" + }, + { + "internalType": "bytes", + "name": "commit", + "type": "bytes" + }, + { + "internalType": "uint64", + "name": "revealRound", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "commitRevealVersion", + "type": "uint16" + } + ], + "name": "commitTimelockedMechanismWeights", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "bytes", + "name": "commit", + "type": "bytes" + }, + { + "internalType": "uint64", + "name": "revealRound", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "commitRevealVersion", + "type": "uint16" + } + ], + "name": "commitTimelockedWeights", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "disputeColdkeySwap", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "newColdkey", + "type": "bytes32" + } + ], + "name": "executeAnnouncedColdkeySwap", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint64", + "name": "blockNumber", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "nonce", + "type": "uint64" + }, + { + "internalType": "bytes", + "name": "work", + "type": "bytes" + }, + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "coldkey", + "type": "bytes32" + } + ], + "name": "register", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint8", + "name": "mecid", + "type": "uint8" + }, + { + "internalType": "uint16[]", + "name": "uids", + "type": "uint16[]" + }, + { + "internalType": "uint16[]", + "name": "values", + "type": "uint16[]" + }, + { + "internalType": "uint16[]", + "name": "salt", + "type": "uint16[]" + }, + { + "internalType": "uint64", + "name": "versionKey", + "type": "uint64" + } + ], + "name": "revealMechanismWeights", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + } + ], + "name": "rootRegister", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint64[]", + "name": "proportions", + "type": "uint64[]" + }, + { + "internalType": "bytes32[]", + "name": "children", + "type": "bytes32[]" + } + ], + "name": "setChildren", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "string", + "name": "url", + "type": "string" + }, + { + "internalType": "string", + "name": "githubRepo", + "type": "string" + }, + { + "internalType": "string", + "name": "image", + "type": "string" + }, + { + "internalType": "string", + "name": "discord", + "type": "string" + }, + { + "internalType": "string", + "name": "description", + "type": "string" + }, + { + "internalType": "string", + "name": "additional", + "type": "string" + } + ], + "name": "setIdentity", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint8", + "name": "mecid", + "type": "uint8" + }, + { + "internalType": "uint16[]", + "name": "dests", + "type": "uint16[]" + }, + { + "internalType": "uint16[]", + "name": "weights", + "type": "uint16[]" + }, + { + "internalType": "uint64", + "name": "versionKey", + "type": "uint64" + } + ], + "name": "setMechanismWeights", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "newHotkey", + "type": "bytes32" + }, + { + "internalType": "bool", + "name": "hasNetuid", + "type": "bool" + }, + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "swapHotkey", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "newHotkey", + "type": "bytes32" + }, + { + "internalType": "bool", + "name": "hasNetuid", + "type": "bool" + }, + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "bool", + "name": "keepStake", + "type": "bool" + } + ], + "name": "swapHotkeyV2", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + } + ], + "name": "tryAssociateHotkey", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, +{ + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "uid", + "type": "uint16" + } + ], + "name": "getBlockAtRegistration", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "uid", + "type": "uint16" + } + ], + "name": "getBonds", + "outputs": [ + { + "components": [ + { + "internalType": "uint16", + "name": "uid", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "value", + "type": "uint16" + } + ], + "internalType": "struct INeuron.WeightPair[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "coldkey", + "type": "bytes32" + } + ], + "name": "getChainIdentity", + "outputs": [ + { + "internalType": "bool", + "name": "exists", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "name", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "url", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "githubRepo", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "image", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "discord", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "description", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "additional", + "type": "bytes" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint8", + "name": "version", + "type": "uint8" + }, + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint64", + "name": "epoch", + "type": "uint64" + }, + { + "internalType": "uint32", + "name": "index", + "type": "uint32" + } + ], + "name": "getLegacyTimelockedWeightCommit", + "outputs": [ + { + "internalType": "bool", + "name": "exists", + "type": "bool" + }, + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + }, + { + "internalType": "uint64", + "name": "blockNumber", + "type": "uint64" + }, + { + "internalType": "bytes32", + "name": "ciphertextHash", + "type": "bytes32" + }, + { + "internalType": "uint32", + "name": "ciphertextLength", + "type": "uint32" + }, + { + "internalType": "uint64", + "name": "revealRound", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint8", + "name": "version", + "type": "uint8" + }, + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint64", + "name": "epoch", + "type": "uint64" + } + ], + "name": "getLegacyTimelockedWeightCommitCount", + "outputs": [ + { + "internalType": "uint32", + "name": "", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + } + ], + "name": "getLegacyTransactionRateBlocks", + "outputs": [ + { + "internalType": "uint64", + "name": "lastTransactionBlock", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "lastChildkeyTakeBlock", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "lastDelegateTakeBlock", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "getLoadedEmission", + "outputs": [ + { + "internalType": "bool", + "name": "exists", + "type": "bool" + }, + { + "components": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + }, + { + "internalType": "uint64", + "name": "serverEmission", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "validatorEmission", + "type": "uint64" + } + ], + "internalType": "struct INeuron.LoadedEmission[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + } + ], + "name": "getNeuronCertificate", + "outputs": [ + { + "internalType": "bool", + "name": "exists", + "type": "bool" + }, + { + "internalType": "uint8", + "name": "algorithm", + "type": "uint8" + }, + { + "internalType": "bytes", + "name": "publicKey", + "type": "bytes" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + } + ], + "name": "getPrometheus", + "outputs": [ + { + "internalType": "bool", + "name": "exists", + "type": "bool" + }, + { + "internalType": "uint64", + "name": "blockNumber", + "type": "uint64" + }, + { + "internalType": "uint32", + "name": "version", + "type": "uint32" + }, + { + "internalType": "uint128", + "name": "ip", + "type": "uint128" + }, + { + "internalType": "uint16", + "name": "port", + "type": "uint16" + }, + { + "internalType": "uint8", + "name": "ipType", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "getSubnetIdentity", + "outputs": [ + { + "internalType": "bool", + "name": "exists", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "subnetName", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "githubRepo", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "subnetContact", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "subnetUrl", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "discord", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "description", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "logoUrl", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "additional", + "type": "bytes" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint64", + "name": "epoch", + "type": "uint64" + }, + { + "internalType": "uint32", + "name": "index", + "type": "uint32" + } + ], + "name": "getTimelockedWeightCommit", + "outputs": [ + { + "internalType": "bool", + "name": "exists", + "type": "bool" + }, + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + }, + { + "internalType": "uint64", + "name": "blockNumber", + "type": "uint64" + }, + { + "internalType": "bytes32", + "name": "ciphertextHash", + "type": "bytes32" + }, + { + "internalType": "uint32", + "name": "ciphertextLength", + "type": "uint32" + }, + { + "internalType": "uint64", + "name": "revealRound", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint64", + "name": "epoch", + "type": "uint64" + } + ], + "name": "getTimelockedWeightCommitCount", + "outputs": [ + { + "internalType": "uint32", + "name": "", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "transactionKey", + "type": "uint16" + } + ], + "name": "getTransactionKeyLastBlock", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + } + ], + "name": "getUid", + "outputs": [ + { + "internalType": "bool", + "name": "exists", + "type": "bool" + }, + { + "internalType": "uint16", + "name": "uid", + "type": "uint16" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + }, + { + "internalType": "uint32", + "name": "index", + "type": "uint32" + } + ], + "name": "getWeightCommit", + "outputs": [ + { + "internalType": "bool", + "name": "exists", + "type": "bool" + }, + { + "internalType": "bytes32", + "name": "hash", + "type": "bytes32" + }, + { + "internalType": "uint64", + "name": "epoch", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "blockNumber", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + } + ], + "name": "getWeightCommitCount", + "outputs": [ + { + "internalType": "uint32", + "name": "", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "uid", + "type": "uint16" + } + ], + "name": "getWeights", + "outputs": [ + { + "components": [ + { + "internalType": "uint16", + "name": "uid", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "value", + "type": "uint16" + } + ], + "internalType": "struct INeuron.WeightPair[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "isNetworkMember", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + } +] \ No newline at end of file diff --git a/precompiles/src/solidity/neuron.sol b/precompiles/src/solidity/neuron.sol index e06da79f2f..3d8649a7e5 100644 --- a/precompiles/src/solidity/neuron.sol +++ b/precompiles/src/solidity/neuron.sol @@ -3,6 +3,11 @@ pragma solidity ^0.8.0; address constant INeuron_ADDRESS = 0x0000000000000000000000000000000000000804; interface INeuron { + struct WeightPair { + uint16 uid; + uint16 value; + } + /** * @dev Registers a neuron by calling `do_burned_registration` internally with the origin set to the ss58 mirror of the H160 address. * This allows the H160 to further call neuron-related methods and receive emissions. @@ -133,4 +138,253 @@ interface INeuron { uint16[] memory salt, uint64 versionKey ) external payable; + + function setMechanismWeights( + uint16 netuid, + uint8 mecid, + uint16[] calldata dests, + uint16[] calldata weights, + uint64 versionKey + ) external; + function batchSetWeights( + uint16[] calldata netuids, + uint16[][] calldata dests, + uint16[][] calldata values, + uint64[] calldata versionKeys + ) external; + function commitMechanismWeights( + uint16 netuid, + uint8 mecid, + bytes32 commitHash + ) external; + function batchCommitWeights( + uint16[] calldata netuids, + bytes32[] calldata commitHashes + ) external; + function revealMechanismWeights( + uint16 netuid, + uint8 mecid, + uint16[] calldata uids, + uint16[] calldata values, + uint16[] calldata salt, + uint64 versionKey + ) external; + function commitCrv3MechanismWeights( + uint16 netuid, + uint8 mecid, + bytes calldata commit, + uint64 revealRound + ) external; + function batchRevealWeights( + uint16 netuid, + uint16[][] calldata uids, + uint16[][] calldata values, + uint16[][] calldata salts, + uint64[] calldata versionKeys + ) external; + function commitTimelockedWeights( + uint16 netuid, + bytes calldata commit, + uint64 revealRound, + uint16 commitRevealVersion + ) external; + function commitTimelockedMechanismWeights( + uint16 netuid, + uint8 mecid, + bytes calldata commit, + uint64 revealRound, + uint16 commitRevealVersion + ) external; + function register( + uint16 netuid, + uint64 blockNumber, + uint64 nonce, + bytes calldata work, + bytes32 hotkey, + bytes32 coldkey + ) external; + function rootRegister(bytes32 hotkey) external; + function swapHotkey( + bytes32 hotkey, + bytes32 newHotkey, + bool hasNetuid, + uint16 netuid + ) external; + function swapHotkeyV2( + bytes32 hotkey, + bytes32 newHotkey, + bool hasNetuid, + uint16 netuid, + bool keepStake + ) external; + function setChildren( + bytes32 hotkey, + uint16 netuid, + uint64[] calldata proportions, + bytes32[] calldata children + ) external; + function setIdentity( + string calldata name, + string calldata url, + string calldata githubRepo, + string calldata image, + string calldata discord, + string calldata description, + string calldata additional + ) external; + function tryAssociateHotkey(bytes32 hotkey) external; + function associateEvmKey( + uint16 netuid, + address evmKey, + uint64 blockNumber, + bytes calldata signature + ) external; + function announceColdkeySwap(bytes32 newColdkeyHash) external; + function executeAnnouncedColdkeySwap(bytes32 newColdkey) external; + function disputeColdkeySwap() external; + function clearColdkeySwapAnnouncement() external; + function getUid( + uint16 netuid, + bytes32 hotkey + ) external view returns (bool exists, uint16 uid); + function isNetworkMember( + bytes32 hotkey, + uint16 netuid + ) external view returns (bool); + function getWeights( + uint16 netuid, + uint16 uid + ) external view returns (WeightPair[] memory); + function getBonds( + uint16 netuid, + uint16 uid + ) external view returns (WeightPair[] memory); + function getBlockAtRegistration( + uint16 netuid, + uint16 uid + ) external view returns (uint64); + function getNeuronCertificate( + uint16 netuid, + bytes32 hotkey + ) external view returns (bool exists, uint8 algorithm, bytes memory publicKey); + function getPrometheus( + uint16 netuid, + bytes32 hotkey + ) + external + view + returns ( + bool exists, + uint64 blockNumber, + uint32 version, + uint128 ip, + uint16 port, + uint8 ipType + ); + function getChainIdentity( + bytes32 coldkey + ) + external + view + returns ( + bool exists, + bytes memory name, + bytes memory url, + bytes memory githubRepo, + bytes memory image, + bytes memory discord, + bytes memory description, + bytes memory additional + ); + function getSubnetIdentity( + uint16 netuid + ) + external + view + returns ( + bool exists, + bytes memory subnetName, + bytes memory githubRepo, + bytes memory subnetContact, + bytes memory subnetUrl, + bytes memory discord, + bytes memory description, + bytes memory logoUrl, + bytes memory additional + ); + struct LoadedEmission { + bytes32 hotkey; + uint64 serverEmission; + uint64 validatorEmission; + } + function getLoadedEmission( + uint16 netuid + ) external view returns (bool exists, LoadedEmission[] memory); + function getTransactionKeyLastBlock( + bytes32 hotkey, + uint16 netuid, + uint16 transactionKey + ) external view returns (uint64); + function getLegacyTransactionRateBlocks( + bytes32 hotkey + ) + external + view + returns ( + uint64 lastTransactionBlock, + uint64 lastChildkeyTakeBlock, + uint64 lastDelegateTakeBlock + ); + function getWeightCommit( + uint16 netuid, + bytes32 hotkey, + uint32 index + ) + external + view + returns (bool exists, bytes32 hash, uint64 epoch, uint64 blockNumber); + function getWeightCommitCount( + uint16 netuid, + bytes32 hotkey + ) external view returns (uint32); + function getTimelockedWeightCommit( + uint16 netuid, + uint64 epoch, + uint32 index + ) + external + view + returns ( + bool exists, + bytes32 hotkey, + uint64 blockNumber, + bytes32 ciphertextHash, + uint32 ciphertextLength, + uint64 revealRound + ); + function getTimelockedWeightCommitCount( + uint16 netuid, + uint64 epoch + ) external view returns (uint32); + function getLegacyTimelockedWeightCommit( + uint8 version, + uint16 netuid, + uint64 epoch, + uint32 index + ) + external + view + returns ( + bool exists, + bytes32 hotkey, + uint64 blockNumber, + bytes32 ciphertextHash, + uint32 ciphertextLength, + uint64 revealRound + ); + function getLegacyTimelockedWeightCommitCount( + uint8 version, + uint16 netuid, + uint64 epoch + ) external view returns (uint32); } diff --git a/precompiles/src/solidity/proxy.abi b/precompiles/src/solidity/proxy.abi index 2f751002b7..cb7644a637 100644 --- a/precompiles/src/solidity/proxy.abi +++ b/precompiles/src/solidity/proxy.abi @@ -173,5 +173,200 @@ } ], "stateMutability": "view" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "real", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "callHash", + "type": "bytes32" + } + ], + "name": "announce", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "delegate", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "callHash", + "type": "bytes32" + } + ], + "name": "rejectAnnouncement", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "real", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "callHash", + "type": "bytes32" + } + ], + "name": "removeAnnouncement", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "delegate", + "type": "bytes32" + }, + { + "internalType": "bool", + "name": "paysFee", + "type": "bool" + } + ], + "name": "setRealPaysFee", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, +{ + "inputs": [ + { + "internalType": "bytes32", + "name": "account", + "type": "bytes32" + } + ], + "name": "getAnnouncements", + "outputs": [ + { + "components": [ + { + "internalType": "bytes32", + "name": "real", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "callHash", + "type": "bytes32" + }, + { + "internalType": "uint64", + "name": "height", + "type": "uint64" + } + ], + "internalType": "struct IProxy.AnnouncementInfo[]", + "name": "", + "type": "tuple[]" + }, + { + "internalType": "uint256", + "name": "deposit", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "account", + "type": "bytes32" + } + ], + "name": "getLastCallResult", + "outputs": [ + { + "internalType": "bool", + "name": "exists", + "type": "bool" + }, + { + "internalType": "bool", + "name": "succeeded", + "type": "bool" + }, + { + "internalType": "uint8", + "name": "errorKind", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "palletIndex", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "errorData", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "account", + "type": "bytes32" + } + ], + "name": "getProxyDeposit", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "real", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "delegate", + "type": "bytes32" + } + ], + "name": "isRealPaysFee", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" } -] +] \ No newline at end of file diff --git a/precompiles/src/solidity/proxy.sol b/precompiles/src/solidity/proxy.sol index 1e79eebc94..d60910285f 100644 --- a/precompiles/src/solidity/proxy.sol +++ b/precompiles/src/solidity/proxy.sol @@ -49,4 +49,34 @@ interface IProxy { function getProxies( bytes32 account ) external view returns (ProxyInfo[] memory); + + function announce(bytes32 real, bytes32 callHash) external; + function removeAnnouncement(bytes32 real, bytes32 callHash) external; + function rejectAnnouncement(bytes32 delegate, bytes32 callHash) external; + function setRealPaysFee(bytes32 delegate, bool paysFee) external; + function getProxyDeposit(bytes32 account) external view returns (uint256); + struct AnnouncementInfo { + bytes32 real; + bytes32 callHash; + uint64 height; + } + function getAnnouncements( + bytes32 account + ) external view returns (AnnouncementInfo[] memory, uint256 deposit); + function getLastCallResult( + bytes32 account + ) + external + view + returns ( + bool exists, + bool succeeded, + uint8 errorKind, + uint8 palletIndex, + bytes32 errorData + ); + function isRealPaysFee( + bytes32 real, + bytes32 delegate + ) external view returns (bool); } diff --git a/precompiles/src/solidity/registry.abi b/precompiles/src/solidity/registry.abi new file mode 100644 index 0000000000..b15d2cabd9 --- /dev/null +++ b/precompiles/src/solidity/registry.abi @@ -0,0 +1,53 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "precompile", + "type": "address" + }, + { + "internalType": "bytes4", + "name": "selector", + "type": "bytes4" + } + ], + "name": "getPrecompileStatus", + "outputs": [ + { + "components": [ + { + "internalType": "bool", + "name": "isDeprecated", + "type": "bool" + }, + { + "internalType": "bool", + "name": "isDisabled", + "type": "bool" + }, + { + "internalType": "address", + "name": "newPrecompile", + "type": "address" + }, + { + "internalType": "bytes4", + "name": "newSelector", + "type": "bytes4" + }, + { + "internalType": "string", + "name": "message", + "type": "string" + } + ], + "internalType": "struct IPrecompileRegistry.PrecompileStatus", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/precompiles/src/solidity/registry.sol b/precompiles/src/solidity/registry.sol new file mode 100644 index 0000000000..ba67235154 --- /dev/null +++ b/precompiles/src/solidity/registry.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +address constant IPRECOMPILE_REGISTRY_ADDRESS = 0x0000000000000000000000000000000000000813; + +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); +} diff --git a/precompiles/src/solidity/runtimeConfiguration.abi b/precompiles/src/solidity/runtimeConfiguration.abi new file mode 100644 index 0000000000..257bd47289 --- /dev/null +++ b/precompiles/src/solidity/runtimeConfiguration.abi @@ -0,0 +1,787 @@ +[ + { + "inputs": [ + + ], + "name": "getEvmChainId", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + + ], + "name": "getTransactionRateLimit", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + + ], + "name": "getSubtensorEconomicConstants", + "outputs": [ + { + "internalType": "uint256", + "name": "initialIssuance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "initialRaoRecycledForRegistration", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "initialBurn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "initialMinBurn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "initialMaxBurn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "initialMinStake", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "initialMinTransfer", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minBurnUpperBound", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxBurnLowerBound", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "initialNetworkMinLockCost", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "keySwapCost", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "keySwapOnSubnetCost", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minBalanceToPerformColdkeySwap", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + + ], + "name": "getSubtensorSubnetConstants", + "outputs": [ + { + "internalType": "uint16", + "name": "initialTempo", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "minTempo", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "maxTempo", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "initialMinAllowedUids", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "initialMaxAllowedUids", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "initialMaxAllowedValidators", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "initialImmunityPeriod", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "initialActivityCutoff", + "type": "uint16" + }, + { + "internalType": "uint32", + "name": "minActivityCutoffFactorMilli", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "maxActivityCutoffFactorMilli", + "type": "uint32" + }, + { + "internalType": "uint8", + "name": "maxImmuneUidsPercentage", + "type": "uint8" + }, + { + "internalType": "uint16", + "name": "initialSubnetOwnerCut", + "type": "uint16" + }, + { + "internalType": "uint8", + "name": "initialMaxEpochsPerBlock", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + + ], + "name": "getSubtensorConsensusConstants", + "outputs": [ + { + "internalType": "uint16", + "name": "initialMinAllowedWeights", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "initialEmissionValue", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "initialRho", + "type": "uint16" + }, + { + "internalType": "int16", + "name": "initialAlphaSigmoidSteepness", + "type": "int16" + }, + { + "internalType": "uint16", + "name": "initialKappa", + "type": "uint16" + }, + { + "internalType": "uint64", + "name": "initialBondsMovingAverage", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "initialBondsPenalty", + "type": "uint16" + }, + { + "internalType": "bool", + "name": "initialBondsResetOn", + "type": "bool" + }, + { + "internalType": "uint64", + "name": "initialValidatorPruneLen", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "initialScalingLawPower", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "initialPruningScore", + "type": "uint16" + }, + { + "internalType": "uint64", + "name": "initialWeightsVersionKey", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "initialTaoWeight", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + + ], + "name": "getSubtensorRegistrationConstants", + "outputs": [ + { + "internalType": "uint64", + "name": "initialDifficulty", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "initialMinDifficulty", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "initialMaxDifficulty", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "initialAdjustmentInterval", + "type": "uint16" + }, + { + "internalType": "uint64", + "name": "initialAdjustmentAlpha", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "initialMaxRegistrationsPerBlock", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "initialTargetRegistrationsPerInterval", + "type": "uint16" + }, + { + "internalType": "uint64", + "name": "initialNetworkRateLimit", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "initialNetworkImmunityPeriod", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "initialNetworkLockReductionInterval", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "initialEmaPriceHalvingPeriod", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + + ], + "name": "getSubtensorDelegationConstants", + "outputs": [ + { + "internalType": "uint16", + "name": "initialDefaultDelegateTake", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "initialMinDelegateTake", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "initialDefaultChildKeyTake", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "initialMinChildKeyTake", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "initialMaxChildKeyTake", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "alphaHigh", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "alphaLow", + "type": "uint16" + }, + { + "internalType": "bool", + "name": "liquidAlphaOn", + "type": "bool" + }, + { + "internalType": "bool", + "name": "yuma3On", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + + ], + "name": "getSubtensorRateLimitConstants", + "outputs": [ + { + "internalType": "uint64", + "name": "initialServingRateLimit", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "initialTxRateLimit", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "initialTxDelegateTakeRateLimit", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "initialTxChildKeyTakeRateLimit", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "evmKeyAssociateRateLimit", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "initialColdkeySwapAnnouncementDelay", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "initialColdkeySwapReannouncementDelay", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "initialDissolveNetworkScheduleDuration", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "initialStartCallDelay", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "hotkeySwapOnSubnetInterval", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "leaseDividendsDistributionInterval", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + + ], + "name": "getSubtensorProtocolConstants", + "outputs": [ + { + "internalType": "uint32", + "name": "maxCrv3CommitSizeBytes", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "maxAssociatedUidsPerEvmAddress", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "maxColdkeyCollateralHotkeys", + "type": "uint32" + }, + { + "internalType": "uint128", + "name": "accountFlagsAcceptLockedAlpha", + "type": "uint128" + }, + { + "internalType": "uint64", + "name": "minCommitRevealPeriods", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "maxCommitRevealPeriods", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "globalMaxSubnetCount", + "type": "uint16" + }, + { + "internalType": "uint8", + "name": "maxMechanismCountPerSubnet", + "type": "uint8" + }, + { + "internalType": "uint64", + "name": "votingPowerDisableGracePeriodBlocks", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "maxVotingPowerEmaAlpha", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "emissionBarUpdateInterval", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "stakingLockDuration", + "type": "uint64" + }, + { + "internalType": "uint256", + "name": "lockStateZeroThreshold", + "type": "uint256" + }, + { + "internalType": "uint32", + "name": "initialActivityCutoffFactorMilli", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "maxTaoIssuance", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + + ], + "name": "getSubtensorSystemAccounts", + "outputs": [ + { + "internalType": "bytes32", + "name": "subtensorPalletAccount", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "burnAccount", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + + ], + "name": "getBalancesConstants", + "outputs": [ + { + "internalType": "uint256", + "name": "existentialDeposit", + "type": "uint256" + }, + { + "internalType": "uint32", + "name": "maxLocks", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "maxReserves", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "maxFreezes", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + + ], + "name": "getProxyConstants", + "outputs": [ + { + "internalType": "uint256", + "name": "proxyDepositBase", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "proxyDepositFactor", + "type": "uint256" + }, + { + "internalType": "uint32", + "name": "maxProxies", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "maxPending", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "announcementDepositBase", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "announcementDepositFactor", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + + ], + "name": "getSchedulerConstants", + "outputs": [ + { + "internalType": "uint64", + "name": "maximumWeightRefTime", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "maximumWeightProofSize", + "type": "uint64" + }, + { + "internalType": "uint32", + "name": "maxScheduledPerBlock", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + + ], + "name": "getDrandConstants", + "outputs": [ + { + "internalType": "string", + "name": "quicknetChainHash", + "type": "string" + }, + { + "internalType": "uint64", + "name": "unsignedPriority", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "httpFetchTimeout", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "maxPulsesToFetch", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "maxKeptPulses", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "maxRemovedPulses", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + + ], + "name": "getCrowdloanConstants", + "outputs": [ + { + "internalType": "uint256", + "name": "minimumDeposit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "absoluteMinimumContribution", + "type": "uint256" + }, + { + "internalType": "uint64", + "name": "minimumBlockDuration", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "maximumBlockDuration", + "type": "uint64" + }, + { + "internalType": "uint32", + "name": "refundContributorsLimit", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "maxContributors", + "type": "uint32" + }, + { + "internalType": "bytes32", + "name": "palletAccount", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + + ], + "name": "getSwapConstants", + "outputs": [ + { + "internalType": "uint16", + "name": "maxFeeRate", + "type": "uint16" + }, + { + "internalType": "uint256", + "name": "minimumLiquidity", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minimumReserve", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "protocolAccount", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + + ], + "name": "getTimestampConstants", + "outputs": [ + { + "internalType": "uint64", + "name": "minimumPeriod", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + + ], + "name": "getAdminConstants", + "outputs": [ + { + "internalType": "uint32", + "name": "maxAuthorities", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/precompiles/src/solidity/runtimeConfiguration.sol b/precompiles/src/solidity/runtimeConfiguration.sol new file mode 100644 index 0000000000..1c32ae66c2 --- /dev/null +++ b/precompiles/src/solidity/runtimeConfiguration.sol @@ -0,0 +1,171 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +address constant IRUNTIME_CONFIGURATION_ADDRESS = 0x0000000000000000000000000000000000000812; + +interface IRuntimeConfiguration { + function getEvmChainId() external view returns (uint64); + function getTransactionRateLimit() external view returns (uint64); + + function getSubtensorEconomicConstants() external view returns ( + uint256 initialIssuance, + uint256 initialRaoRecycledForRegistration, + uint256 initialBurn, + uint256 initialMinBurn, + uint256 initialMaxBurn, + uint256 initialMinStake, + uint256 initialMinTransfer, + uint256 minBurnUpperBound, + uint256 maxBurnLowerBound, + uint256 initialNetworkMinLockCost, + uint256 keySwapCost, + uint256 keySwapOnSubnetCost, + uint256 minBalanceToPerformColdkeySwap + ); + + function getSubtensorSubnetConstants() external view returns ( + uint16 initialTempo, + uint16 minTempo, + uint16 maxTempo, + uint16 initialMinAllowedUids, + uint16 initialMaxAllowedUids, + uint16 initialMaxAllowedValidators, + uint16 initialImmunityPeriod, + uint16 initialActivityCutoff, + uint32 minActivityCutoffFactorMilli, + uint32 maxActivityCutoffFactorMilli, + uint8 maxImmuneUidsPercentage, + uint16 initialSubnetOwnerCut, + uint8 initialMaxEpochsPerBlock + ); + + function getSubtensorConsensusConstants() external view returns ( + uint16 initialMinAllowedWeights, + uint16 initialEmissionValue, + uint16 initialRho, + int16 initialAlphaSigmoidSteepness, + uint16 initialKappa, + uint64 initialBondsMovingAverage, + uint16 initialBondsPenalty, + bool initialBondsResetOn, + uint64 initialValidatorPruneLen, + uint16 initialScalingLawPower, + uint16 initialPruningScore, + uint64 initialWeightsVersionKey, + uint64 initialTaoWeight + ); + + function getSubtensorRegistrationConstants() external view returns ( + uint64 initialDifficulty, + uint64 initialMinDifficulty, + uint64 initialMaxDifficulty, + uint16 initialAdjustmentInterval, + uint64 initialAdjustmentAlpha, + uint16 initialMaxRegistrationsPerBlock, + uint16 initialTargetRegistrationsPerInterval, + uint64 initialNetworkRateLimit, + uint64 initialNetworkImmunityPeriod, + uint64 initialNetworkLockReductionInterval, + uint64 initialEmaPriceHalvingPeriod + ); + + function getSubtensorDelegationConstants() external view returns ( + uint16 initialDefaultDelegateTake, + uint16 initialMinDelegateTake, + uint16 initialDefaultChildKeyTake, + uint16 initialMinChildKeyTake, + uint16 initialMaxChildKeyTake, + uint16 alphaHigh, + uint16 alphaLow, + bool liquidAlphaOn, + bool yuma3On + ); + + function getSubtensorRateLimitConstants() external view returns ( + uint64 initialServingRateLimit, + uint64 initialTxRateLimit, + uint64 initialTxDelegateTakeRateLimit, + uint64 initialTxChildKeyTakeRateLimit, + uint64 evmKeyAssociateRateLimit, + uint64 initialColdkeySwapAnnouncementDelay, + uint64 initialColdkeySwapReannouncementDelay, + uint64 initialDissolveNetworkScheduleDuration, + uint64 initialStartCallDelay, + uint64 hotkeySwapOnSubnetInterval, + uint64 leaseDividendsDistributionInterval + ); + + function getSubtensorProtocolConstants() external view returns ( + uint32 maxCrv3CommitSizeBytes, + uint32 maxAssociatedUidsPerEvmAddress, + uint32 maxColdkeyCollateralHotkeys, + uint128 accountFlagsAcceptLockedAlpha, + uint64 minCommitRevealPeriods, + uint64 maxCommitRevealPeriods, + uint16 globalMaxSubnetCount, + uint8 maxMechanismCountPerSubnet, + uint64 votingPowerDisableGracePeriodBlocks, + uint64 maxVotingPowerEmaAlpha, + uint64 emissionBarUpdateInterval, + uint64 stakingLockDuration, + uint256 lockStateZeroThreshold, + uint32 initialActivityCutoffFactorMilli, + uint256 maxTaoIssuance + ); + + function getSubtensorSystemAccounts() external view returns ( + bytes32 subtensorPalletAccount, + bytes32 burnAccount + ); + + function getBalancesConstants() external view returns ( + uint256 existentialDeposit, + uint32 maxLocks, + uint32 maxReserves, + uint32 maxFreezes + ); + + function getProxyConstants() external view returns ( + uint256 proxyDepositBase, + uint256 proxyDepositFactor, + uint32 maxProxies, + uint32 maxPending, + uint256 announcementDepositBase, + uint256 announcementDepositFactor + ); + + function getSchedulerConstants() external view returns ( + uint64 maximumWeightRefTime, + uint64 maximumWeightProofSize, + uint32 maxScheduledPerBlock + ); + + function getDrandConstants() external view returns ( + string memory quicknetChainHash, + uint64 unsignedPriority, + uint64 httpFetchTimeout, + uint64 maxPulsesToFetch, + uint64 maxKeptPulses, + uint64 maxRemovedPulses + ); + + function getCrowdloanConstants() external view returns ( + uint256 minimumDeposit, + uint256 absoluteMinimumContribution, + uint64 minimumBlockDuration, + uint64 maximumBlockDuration, + uint32 refundContributorsLimit, + uint32 maxContributors, + bytes32 palletAccount + ); + + function getSwapConstants() external view returns ( + uint16 maxFeeRate, + uint256 minimumLiquidity, + uint256 minimumReserve, + bytes32 protocolAccount + ); + + function getTimestampConstants() external view returns (uint64 minimumPeriod); + function getAdminConstants() external view returns (uint32 maxAuthorities); +} diff --git a/precompiles/src/solidity/scheduler.abi b/precompiles/src/solidity/scheduler.abi new file mode 100644 index 0000000000..8bf7ecbe5c --- /dev/null +++ b/precompiles/src/solidity/scheduler.abi @@ -0,0 +1,183 @@ +[ + { + "inputs": [], + "name": "getIncompleteSince", + "outputs": [ + { + "internalType": "bool", + "name": "exists", + "type": "bool" + }, + { + "internalType": "uint64", + "name": "blockNumber", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "when", + "type": "uint64" + }, + { + "internalType": "uint32", + "name": "index", + "type": "uint32" + } + ], + "name": "getRetry", + "outputs": [ + { + "internalType": "bool", + "name": "exists", + "type": "bool" + }, + { + "internalType": "uint8", + "name": "totalRetries", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "remaining", + "type": "uint8" + }, + { + "internalType": "uint64", + "name": "period", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "when", + "type": "uint64" + }, + { + "internalType": "uint32", + "name": "index", + "type": "uint32" + } + ], + "name": "getScheduledCall", + "outputs": [ + { + "components": [ + { + "internalType": "bool", + "name": "exists", + "type": "bool" + }, + { + "internalType": "bool", + "name": "hasTaskId", + "type": "bool" + }, + { + "internalType": "bytes32", + "name": "taskId", + "type": "bytes32" + }, + { + "internalType": "uint8", + "name": "priority", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "callHash", + "type": "bytes32" + }, + { + "internalType": "bool", + "name": "hasCallLength", + "type": "bool" + }, + { + "internalType": "uint32", + "name": "callLength", + "type": "uint32" + }, + { + "internalType": "bool", + "name": "isPeriodic", + "type": "bool" + }, + { + "internalType": "uint64", + "name": "period", + "type": "uint64" + }, + { + "internalType": "uint32", + "name": "remaining", + "type": "uint32" + } + ], + "internalType": "struct IScheduler.ScheduledCall", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "when", + "type": "uint64" + } + ], + "name": "getScheduledCallCount", + "outputs": [ + { + "internalType": "uint32", + "name": "", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "taskId", + "type": "bytes32" + } + ], + "name": "getTaskAddress", + "outputs": [ + { + "internalType": "bool", + "name": "exists", + "type": "bool" + }, + { + "internalType": "uint64", + "name": "when", + "type": "uint64" + }, + { + "internalType": "uint32", + "name": "index", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/precompiles/src/solidity/scheduler.sol b/precompiles/src/solidity/scheduler.sol new file mode 100644 index 0000000000..5e2bcb3b93 --- /dev/null +++ b/precompiles/src/solidity/scheduler.sol @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +address constant ISCHEDULER_ADDRESS = 0x000000000000000000000000000000000000080F; + +interface IScheduler { + struct ScheduledCall { + bool exists; + bool hasTaskId; + bytes32 taskId; + uint8 priority; + bytes32 callHash; + bool hasCallLength; + uint32 callLength; + bool isPeriodic; + uint64 period; + uint32 remaining; + } + + function getIncompleteSince() external view returns (bool exists, uint64 blockNumber); + function getScheduledCallCount(uint64 when) external view returns (uint32); + function getScheduledCall( + uint64 when, + uint32 index + ) external view returns (ScheduledCall memory); + function getRetry( + uint64 when, + uint32 index + ) external view returns (bool exists, uint8 totalRetries, uint8 remaining, uint64 period); + function getTaskAddress( + bytes32 taskId + ) external view returns (bool exists, uint64 when, uint32 index); +} diff --git a/precompiles/src/solidity/stakingV2.abi b/precompiles/src/solidity/stakingV2.abi index 64edc7ae3d..61a7310a82 100644 --- a/precompiles/src/solidity/stakingV2.abi +++ b/precompiles/src/solidity/stakingV2.abi @@ -809,5 +809,1064 @@ "outputs": [], "stateMutability": "payable", "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + }, + { + "internalType": "uint64", + "name": "alpha", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "limitPrice", + "type": "uint64" + } + ], + "name": "addCollateral", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint64", + "name": "amount", + "type": "uint64" + }, + { + "internalType": "bool", + "name": "hasLimit", + "type": "bool" + }, + { + "internalType": "uint64", + "name": "limit", + "type": "uint64" + } + ], + "name": "addStakeBurn", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16[]", + "name": "subnets", + "type": "uint16[]" + } + ], + "name": "claimRoot", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "take", + "type": "uint16" + } + ], + "name": "decreaseTake", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "take", + "type": "uint16" + } + ], + "name": "increaseTake", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + }, + { + "internalType": "uint64", + "name": "amount", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "recycleAlpha", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + }, + { + "internalType": "bool", + "name": "enabled", + "type": "bool" + } + ], + "name": "setAutoParentDelegationEnabled", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "take", + "type": "uint16" + } + ], + "name": "setChildkeyTake", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + } + ], + "name": "setColdkeyAutoStakeHotkey", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint128", + "name": "rawRatio", + "type": "uint128" + } + ], + "name": "setCollateralDrainRatio", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "lockShare", + "type": "uint16" + } + ], + "name": "setCollateralLockShare", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "take", + "type": "uint16" + } + ], + "name": "setMinChildkeyTakePerSubnet", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + }, + { + "internalType": "uint64", + "name": "minLocked", + "type": "uint64" + } + ], + "name": "setMinCollateral", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint64", + "name": "threshold", + "type": "uint64" + } + ], + "name": "setRootClaimThreshold", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint8", + "name": "claimType", + "type": "uint8" + }, + { + "internalType": "uint16[]", + "name": "subnets", + "type": "uint16[]" + } + ], + "name": "setRootClaimType", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "originNetuid", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "destinationNetuid", + "type": "uint16" + }, + { + "internalType": "uint64", + "name": "alphaAmount", + "type": "uint64" + } + ], + "name": "swapStake", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "originNetuid", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "destinationNetuid", + "type": "uint16" + }, + { + "internalType": "uint64", + "name": "alphaAmount", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "limitPrice", + "type": "uint64" + }, + { + "internalType": "bool", + "name": "allowPartial", + "type": "bool" + } + ], + "name": "swapStakeLimit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "destinationColdkey", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "originHotkey", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "destinationHotkey", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "originNetuid", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "destinationNetuid", + "type": "uint16" + }, + { + "internalType": "uint64", + "name": "alphaAmount", + "type": "uint64" + } + ], + "name": "transferStakeAndHotkey", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + } + ], + "name": "unstakeAll", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + } + ], + "name": "unstakeAllAlpha", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, +{ + "inputs": [ + { + "internalType": "bytes32", + "name": "coldkey", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "getAutoStakeDestination", + "outputs": [ + { + "internalType": "bool", + "name": "exists", + "type": "bool" + }, + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "getAutoStakeDestinationColdkeys", + "outputs": [ + { + "internalType": "bytes32[]", + "name": "", + "type": "bytes32[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "parent", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "getChildKeys", + "outputs": [ + { + "components": [ + { + "internalType": "uint64", + "name": "proportion", + "type": "uint64" + }, + { + "internalType": "bytes32", + "name": "account", + "type": "bytes32" + } + ], + "internalType": "struct IStaking.KeyLink[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "getChildkeyTake", + "outputs": [ + { + "internalType": "uint16", + "name": "", + "type": "uint16" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "bytes32", + "name": "coldkey", + "type": "bytes32" + } + ], + "name": "getColdkeyCollateral", + "outputs": [ + { + "internalType": "uint64", + "name": "locked", + "type": "uint64" + }, + { + "internalType": "bytes32[]", + "name": "hotkeys", + "type": "bytes32[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "coldkey", + "type": "bytes32" + } + ], + "name": "getColdkeyRoot", + "outputs": [ + { + "internalType": "bool", + "name": "exists", + "type": "bool" + }, + { + "internalType": "bytes32", + "name": "root", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "coldkey", + "type": "bytes32" + } + ], + "name": "getColdkeySuccessor", + "outputs": [ + { + "internalType": "bool", + "name": "exists", + "type": "bool" + }, + { + "internalType": "bytes32", + "name": "successor", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getColdkeySwapDelays", + "outputs": [ + { + "internalType": "uint64", + "name": "announcementDelay", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "reannouncementDelay", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "coldkey", + "type": "bytes32" + } + ], + "name": "getColdkeySwapStatus", + "outputs": [ + { + "internalType": "bool", + "name": "hasAnnouncement", + "type": "bool" + }, + { + "internalType": "uint64", + "name": "announcementBlock", + "type": "uint64" + }, + { + "internalType": "bytes32", + "name": "callHash", + "type": "bytes32" + }, + { + "internalType": "bool", + "name": "hasDispute", + "type": "bool" + }, + { + "internalType": "uint64", + "name": "disputeBlock", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "getCollateralConfig", + "outputs": [ + { + "internalType": "uint16", + "name": "lockShare", + "type": "uint16" + }, + { + "internalType": "uint128", + "name": "drainRatio", + "type": "uint128" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + } + ], + "name": "getDelegate", + "outputs": [ + { + "internalType": "bool", + "name": "exists", + "type": "bool" + }, + { + "internalType": "uint16", + "name": "take", + "type": "uint16" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + } + ], + "name": "getHotkeyOwner", + "outputs": [ + { + "internalType": "bool", + "name": "exists", + "type": "bool" + }, + { + "internalType": "bytes32", + "name": "owner", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "getHotkeyRoot", + "outputs": [ + { + "internalType": "bool", + "name": "exists", + "type": "bool" + }, + { + "internalType": "bytes32", + "name": "root", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "getHotkeySuccessor", + "outputs": [ + { + "internalType": "bool", + "name": "exists", + "type": "bool" + }, + { + "internalType": "bytes32", + "name": "successor", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "coldkey", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "getLastHotkeySwapOnSubnet", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "getMinChildkeyTakePerSubnet", + "outputs": [ + { + "internalType": "uint16", + "name": "", + "type": "uint16" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "coldkey", + "type": "bytes32" + } + ], + "name": "getMinerCollateral", + "outputs": [ + { + "internalType": "bool", + "name": "exists", + "type": "bool" + }, + { + "internalType": "uint64", + "name": "locked", + "type": "uint64" + }, + { + "internalType": "uint128", + "name": "drainRatio", + "type": "uint128" + }, + { + "internalType": "uint64", + "name": "minLocked", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "earned", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "coldkey", + "type": "bytes32" + } + ], + "name": "getOwnedHotkeys", + "outputs": [ + { + "internalType": "bytes32[]", + "name": "", + "type": "bytes32[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "child", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "getParentKeys", + "outputs": [ + { + "components": [ + { + "internalType": "uint64", + "name": "proportion", + "type": "uint64" + }, + { + "internalType": "bytes32", + "name": "account", + "type": "bytes32" + } + ], + "internalType": "struct IStaking.KeyLink[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getPendingChildKeyCooldown", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "parent", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "getPendingChildKeys", + "outputs": [ + { + "components": [ + { + "internalType": "uint64", + "name": "proportion", + "type": "uint64" + }, + { + "internalType": "bytes32", + "name": "account", + "type": "bytes32" + } + ], + "internalType": "struct IStaking.KeyLink[]", + "name": "children", + "type": "tuple[]" + }, + { + "internalType": "uint64", + "name": "cooldownBlock", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getStakeAccounting", + "outputs": [ + { + "internalType": "uint64", + "name": "totalIssuance", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "totalStake", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getTakeLimits", + "outputs": [ + { + "internalType": "uint16", + "name": "minDelegateTake", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "maxDelegateTake", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "minChildkeyTake", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "maxChildkeyTake", + "type": "uint16" + } + ], + "stateMutability": "view", + "type": "function" } -] +] \ No newline at end of file diff --git a/precompiles/src/solidity/stakingV2.sol b/precompiles/src/solidity/stakingV2.sol index 9170abc829..b85296132e 100644 --- a/precompiles/src/solidity/stakingV2.sol +++ b/precompiles/src/solidity/stakingV2.sol @@ -559,4 +559,170 @@ interface IStaking { uint256 destinationNetuid, uint256 amount ) external; + + function decreaseTake(bytes32 hotkey, uint16 take) external; + function increaseTake(bytes32 hotkey, uint16 take) external; + function setChildkeyTake(bytes32 hotkey, uint16 netuid, uint16 take) external; + function unstakeAll(bytes32 hotkey) external; + function unstakeAllAlpha(bytes32 hotkey) external; + function swapStake( + bytes32 hotkey, + uint16 originNetuid, + uint16 destinationNetuid, + uint64 alphaAmount + ) external; + function swapStakeLimit( + bytes32 hotkey, + uint16 originNetuid, + uint16 destinationNetuid, + uint64 alphaAmount, + uint64 limitPrice, + bool allowPartial + ) external; + function recycleAlpha(bytes32 hotkey, uint64 amount, uint16 netuid) external; + function setColdkeyAutoStakeHotkey(uint16 netuid, bytes32 hotkey) external; + function claimRoot(uint16[] calldata subnets) external; + /// claimType: 0 = swap, 1 = keep, 2 = keep only listed subnets. + function setRootClaimType( + uint8 claimType, + uint16[] calldata subnets + ) external; + function setRootClaimThreshold(uint16 netuid, uint64 threshold) external; + function addStakeBurn( + bytes32 hotkey, + uint16 netuid, + uint64 amount, + bool hasLimit, + uint64 limit + ) external; + function setAutoParentDelegationEnabled( + bytes32 hotkey, + bool enabled + ) external; + function transferStakeAndHotkey( + bytes32 destinationColdkey, + bytes32 originHotkey, + bytes32 destinationHotkey, + uint16 originNetuid, + uint16 destinationNetuid, + uint64 alphaAmount + ) external; + function addCollateral( + uint16 netuid, + bytes32 hotkey, + uint64 alpha, + uint64 limitPrice + ) external; + function setMinCollateral( + uint16 netuid, + bytes32 hotkey, + uint64 minLocked + ) external; + function setMinChildkeyTakePerSubnet(uint16 netuid, uint16 take) external; + function setCollateralLockShare(uint16 netuid, uint16 lockShare) external; + /// Raw U64F64 bits. + function setCollateralDrainRatio( + uint16 netuid, + uint128 rawRatio + ) external; + + struct KeyLink { + uint64 proportion; + bytes32 account; + } + + function getDelegate(bytes32 hotkey) external view returns (bool exists, uint16 take); + function getChildkeyTake(bytes32 hotkey, uint16 netuid) external view returns (uint16); + function getPendingChildKeys( + bytes32 parent, + uint16 netuid + ) external view returns (KeyLink[] memory children, uint64 cooldownBlock); + function getChildKeys( + bytes32 parent, + uint16 netuid + ) external view returns (KeyLink[] memory); + function getParentKeys( + bytes32 child, + uint16 netuid + ) external view returns (KeyLink[] memory); + function getPendingChildKeyCooldown() external view returns (uint64); + function getTakeLimits() + external + view + returns ( + uint16 minDelegateTake, + uint16 maxDelegateTake, + uint16 minChildkeyTake, + uint16 maxChildkeyTake + ); + function getMinChildkeyTakePerSubnet(uint16 netuid) external view returns (uint16); + function getHotkeyOwner(bytes32 hotkey) external view returns (bool exists, bytes32 owner); + function getOwnedHotkeys(bytes32 coldkey) external view returns (bytes32[] memory); + function getAutoStakeDestination( + bytes32 coldkey, + uint16 netuid + ) external view returns (bool exists, bytes32 hotkey); + function getAutoStakeDestinationColdkeys( + bytes32 hotkey, + uint16 netuid + ) external view returns (bytes32[] memory); + function getHotkeySuccessor( + bytes32 hotkey, + uint16 netuid + ) external view returns (bool exists, bytes32 successor); + function getHotkeyRoot( + bytes32 hotkey, + uint16 netuid + ) external view returns (bool exists, bytes32 root); + function getColdkeySuccessor( + bytes32 coldkey + ) external view returns (bool exists, bytes32 successor); + function getColdkeyRoot( + bytes32 coldkey + ) external view returns (bool exists, bytes32 root); + function getColdkeySwapStatus( + bytes32 coldkey + ) + external + view + returns ( + bool hasAnnouncement, + uint64 announcementBlock, + bytes32 callHash, + bool hasDispute, + uint64 disputeBlock + ); + function getColdkeySwapDelays() + external + view + returns (uint64 announcementDelay, uint64 reannouncementDelay); + function getLastHotkeySwapOnSubnet( + bytes32 coldkey, + uint16 netuid + ) external view returns (uint64); + function getStakeAccounting() + external + view + returns (uint64 totalIssuance, uint64 totalStake); + function getMinerCollateral( + uint16 netuid, + bytes32 hotkey, + bytes32 coldkey + ) + external + view + returns ( + bool exists, + uint64 locked, + uint128 drainRatio, + uint64 minLocked, + uint64 earned + ); + function getColdkeyCollateral( + uint16 netuid, + bytes32 coldkey + ) external view returns (uint64 locked, bytes32[] memory hotkeys); + function getCollateralConfig( + uint16 netuid + ) external view returns (uint16 lockShare, uint128 drainRatio); } diff --git a/precompiles/src/solidity/subnet.abi b/precompiles/src/solidity/subnet.abi index e765c37c4a..a21dbd1c2e 100644 --- a/precompiles/src/solidity/subnet.abi +++ b/precompiles/src/solidity/subnet.abi @@ -1159,5 +1159,617 @@ "outputs": [], "stateMutability": "payable", "type": "function" - } + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "string", + "name": "subnetName", + "type": "string" + }, + { + "internalType": "string", + "name": "githubRepo", + "type": "string" + }, + { + "internalType": "string", + "name": "subnetContact", + "type": "string" + }, + { + "internalType": "string", + "name": "subnetUrl", + "type": "string" + }, + { + "internalType": "string", + "name": "discord", + "type": "string" + }, + { + "internalType": "string", + "name": "description", + "type": "string" + }, + { + "internalType": "string", + "name": "logoUrl", + "type": "string" + }, + { + "internalType": "string", + "name": "additional", + "type": "string" + } + ], + "name": "setSubnetIdentity", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "string", + "name": "symbol", + "type": "string" + } + ], + "name": "updateSubnetSymbol", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "triggerEpoch", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "bondsPenalty", + "type": "uint16" + } + ], + "name": "setBondsPenalty", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "maxAllowedUids", + "type": "uint16" + } + ], + "name": "setMaxAllowedUids", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint64", + "name": "maxBurn", + "type": "uint64" + } + ], + "name": "setMaxBurnV2", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint8", + "name": "mechanismCount", + "type": "uint8" + } + ], + "name": "setMechanismCount", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "bool", + "name": "hasSplit", + "type": "bool" + }, + { + "internalType": "uint16[]", + "name": "split", + "type": "uint16[]" + } + ], + "name": "setMechanismEmissionSplit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint64", + "name": "minBurn", + "type": "uint64" + } + ], + "name": "setMinBurnV2", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "bool", + "name": "enabled", + "type": "bool" + } + ], + "name": "setOwnerCutEnabled", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "immuneNeurons", + "type": "uint16" + } + ], + "name": "setOwnerImmuneNeuronLimit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "tempo", + "type": "uint16" + } + ], + "name": "setTempo", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "maxUids", + "type": "uint16" + } + ], + "name": "trimToMaxAllowedUids", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, +{ + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "getBurnConfig", + "outputs": [ + { + "internalType": "uint16", + "name": "halfLife", + "type": "uint16" + }, + { + "internalType": "uint128", + "name": "increaseMultiplier", + "type": "uint128" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getGlobalNetworkLimits", + "outputs": [ + { + "internalType": "uint16", + "name": "minActivityCutoff", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "adminFreezeWindow", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "ownerHyperparamRateLimit", + "type": "uint16" + }, + { + "internalType": "uint64", + "name": "dissolveScheduleDuration", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "subnetLimit", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "totalNetworks", + "type": "uint16" + }, + { + "internalType": "uint64", + "name": "networkImmunityPeriod", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "startCallDelay", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "minNetworkLockCost", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "lastNetworkLockCost", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "networkLockReductionInterval", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "subnetOwnerCut", + "type": "uint16" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getGlobalProtocolConfig", + "outputs": [ + { + "internalType": "uint8", + "name": "maxMechanismCount", + "type": "uint8" + }, + { + "internalType": "uint16", + "name": "commitRevealWeightsVersion", + "type": "uint16" + }, + { + "internalType": "uint64", + "name": "networkRegistrationStartBlock", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "taoInRefundDeploymentBlock", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getGlobalRateLimits", + "outputs": [ + { + "internalType": "uint64", + "name": "networkRateLimit", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "weightsVersionKeyRateLimit", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "transactionRateLimit", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "delegateTakeRateLimit", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "childkeyTakeRateLimit", + "type": "uint64" + }, + { + "internalType": "uint8", + "name": "maxEpochsPerBlock", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "getMechanismEmissionSplit", + "outputs": [ + { + "internalType": "bool", + "name": "exists", + "type": "bool" + }, + { + "internalType": "uint16[]", + "name": "split", + "type": "uint16[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "getSubnetCapacityConfig", + "outputs": [ + { + "internalType": "uint16", + "name": "minAllowedUids", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "maxAllowedUids", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "maxAllowedValidators", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "adjustmentInterval", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "targetRegistrationsPerInterval", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "minNonImmuneUids", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "immuneOwnerUidsLimit", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "bondsPenalty", + "type": "uint16" + }, + { + "internalType": "bool", + "name": "ownerCutEnabled", + "type": "bool" + }, + { + "internalType": "bool", + "name": "transfersEnabled", + "type": "bool" + }, + { + "internalType": "uint16", + "name": "maxRegistrationsPerBlock", + "type": "uint16" + }, + { + "internalType": "uint8", + "name": "mechanismCount", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "getSubnetMetadata", + "outputs": [ + { + "internalType": "bytes", + "name": "tokenSymbol", + "type": "bytes" + }, + { + "internalType": "bytes32", + "name": "owner", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "ownerHotkey", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "tempo", + "type": "uint16" + }, + { + "internalType": "uint8", + "name": "recycleOrBurn", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "getRegisteredSubnetCounter", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "getSubnetDissolutionStatus", + "outputs": [ + { + "internalType": "bool", + "name": "isDissolving", + "type": "bool" + }, + { + "internalType": "bool", + "name": "cleanupInProgress", + "type": "bool" + }, + { + "internalType": "uint8", + "name": "cleanupPhase", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + } ] diff --git a/precompiles/src/solidity/subnet.sol b/precompiles/src/solidity/subnet.sol index 174814cc28..23582d2f0f 100644 --- a/precompiles/src/solidity/subnet.sol +++ b/precompiles/src/solidity/subnet.sol @@ -35,6 +35,14 @@ interface ISubnet { uint16 netuid ) external view returns (uint64); + /** + * @dev Returns the monotonic registration generation for a netuid. + * The value increments whenever the netuid is successfully registered. + */ + function getRegisteredSubnetCounter( + uint16 netuid + ) external view returns (uint64); + function setServingRateLimit( uint16 netuid, uint64 servingRateLimit @@ -83,7 +91,7 @@ interface ISubnet { function setImmunityPeriod( uint16 netuid, - uint64 immunityPeriod + uint16 immunityPeriod ) external payable; function getMinAllowedWeights(uint16 netuid) external view returns (uint16); @@ -107,7 +115,7 @@ interface ISubnet { function setAlphaSigmoidSteepness( uint16 netuid, - int16 steepness + uint16 steepness ) external payable; function getActivityCutoff(uint16 netuid) external view returns (uint16); @@ -194,6 +202,34 @@ interface ISubnet { function isSubnetDissolving(uint16 netuid) external view returns (bool); + /** + * @dev Returns stable dissolution and cleanup state for a subnet. + * + * cleanupPhase is zero while cleanup has not started. Once cleanup is in + * progress, the append-only phase codes are: + * 1 root claimable dividends; 2 root claimed dividends; + * 3 calculate stake value; 4 settle stakes; 5 clear alpha; + * 6 clear hotkey totals; 7 clear stake locks; 8 clear decaying stake locks; + * 9 finish stake cleanup; 10 clear protocol liquidity; + * 11 purge subnet commitments; 12 clear network membership; + * 13 clear network parameters; 14 clear network maps; + * 15 update root weights; 16 clear childkey takes; + * 17 clear childkeys; 18 clear parentkeys; + * 19 clear last hotkey emissions; 20 clear last-epoch hotkey alpha; + * 21 clear transaction rate-limit records; 22 clear network locks; + * 23 clear decaying network locks. + */ + function getSubnetDissolutionStatus( + uint16 netuid + ) + external + view + returns ( + bool isDissolving, + bool cleanupInProgress, + uint8 cleanupPhase + ); + function setLiquidAlphaEnabled( uint16 netuid, bool liquidAlphaEnabled @@ -232,4 +268,111 @@ interface ISubnet { uint16 netuid, uint64 commitRevealWeightsInterval ) external payable; + + function toggleTransfers(uint16 netuid, bool toggle) external payable; + + function setSubnetIdentity( + uint16 netuid, + string calldata subnetName, + string calldata githubRepo, + string calldata subnetContact, + string calldata subnetUrl, + string calldata discord, + string calldata description, + string calldata logoUrl, + string calldata additional + ) external; + function updateSubnetSymbol(uint16 netuid, string calldata symbol) external; + function triggerEpoch(uint16 netuid) external; + function setBondsPenalty(uint16 netuid, uint16 bondsPenalty) external; + function setMaxAllowedUids(uint16 netuid, uint16 maxAllowedUids) external; + function setMaxBurnV2(uint16 netuid, uint64 maxBurn) external; + function setMechanismCount(uint16 netuid, uint8 mechanismCount) external; + function setMechanismEmissionSplit( + uint16 netuid, + bool hasSplit, + uint16[] calldata split + ) external; + function setMinBurnV2(uint16 netuid, uint64 minBurn) external; + function setOwnerCutEnabled(uint16 netuid, bool enabled) external; + function setOwnerImmuneNeuronLimit( + uint16 netuid, + uint16 immuneNeurons + ) external; + function setTempo(uint16 netuid, uint16 tempo) external; + function trimToMaxAllowedUids(uint16 netuid, uint16 maxUids) external; + function getSubnetMetadata( + uint16 netuid + ) + external + view + returns ( + bytes memory tokenSymbol, + bytes32 owner, + bytes32 ownerHotkey, + uint16 tempo, + uint8 recycleOrBurn + ); + function getSubnetCapacityConfig( + uint16 netuid + ) + external + view + returns ( + uint16 minAllowedUids, + uint16 maxAllowedUids, + uint16 maxAllowedValidators, + uint16 adjustmentInterval, + uint16 targetRegistrationsPerInterval, + uint16 minNonImmuneUids, + uint16 immuneOwnerUidsLimit, + uint16 bondsPenalty, + bool ownerCutEnabled, + bool transfersEnabled, + uint16 maxRegistrationsPerBlock, + uint8 mechanismCount + ); + function getMechanismEmissionSplit( + uint16 netuid + ) external view returns (bool exists, uint16[] memory split); + function getBurnConfig( + uint16 netuid + ) external view returns (uint16 halfLife, uint128 increaseMultiplier); + function getGlobalNetworkLimits() + external + view + returns ( + uint16 minActivityCutoff, + uint16 adminFreezeWindow, + uint16 ownerHyperparamRateLimit, + uint64 dissolveScheduleDuration, + uint16 subnetLimit, + uint16 totalNetworks, + uint64 networkImmunityPeriod, + uint64 startCallDelay, + uint64 minNetworkLockCost, + uint64 lastNetworkLockCost, + uint64 networkLockReductionInterval, + uint16 subnetOwnerCut + ); + function getGlobalRateLimits() + external + view + returns ( + uint64 networkRateLimit, + uint64 weightsVersionKeyRateLimit, + uint64 transactionRateLimit, + uint64 delegateTakeRateLimit, + uint64 childkeyTakeRateLimit, + uint8 maxEpochsPerBlock + ); + function getGlobalProtocolConfig() + external + view + returns ( + uint8 maxMechanismCount, + uint16 commitRevealWeightsVersion, + uint64 networkRegistrationStartBlock, + uint64 taoInRefundDeploymentBlock + ); } diff --git a/precompiles/src/solidity/timestamp.abi b/precompiles/src/solidity/timestamp.abi new file mode 100644 index 0000000000..78d2ee9784 --- /dev/null +++ b/precompiles/src/solidity/timestamp.abi @@ -0,0 +1,28 @@ +[ + { + "inputs": [], + "name": "getTimestamp", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "wasUpdatedThisBlock", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/precompiles/src/solidity/timestamp.sol b/precompiles/src/solidity/timestamp.sol new file mode 100644 index 0000000000..5923e23fc9 --- /dev/null +++ b/precompiles/src/solidity/timestamp.sol @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +address constant ITIMESTAMP_ADDRESS = 0x0000000000000000000000000000000000000811; + +interface ITimestamp { + function getTimestamp() external view returns (uint64); + function wasUpdatedThisBlock() external view returns (bool); +} diff --git a/precompiles/src/solidity/uidLookup.abi b/precompiles/src/solidity/uidLookup.abi index 558358dcaa..dfe4f8ebfc 100644 --- a/precompiles/src/solidity/uidLookup.abi +++ b/precompiles/src/solidity/uidLookup.abi @@ -39,5 +39,39 @@ ], "stateMutability": "view", "type": "function" - } + }, +{ + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "uid", + "type": "uint16" + } + ], + "name": "getAssociatedEvmAddress", + "outputs": [ + { + "internalType": "bool", + "name": "exists", + "type": "bool" + }, + { + "internalType": "address", + "name": "evmAddress", + "type": "address" + }, + { + "internalType": "uint64", + "name": "blockAssociated", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + } ] \ No newline at end of file diff --git a/precompiles/src/solidity/uidLookup.sol b/precompiles/src/solidity/uidLookup.sol index 4eae98899c..42a604ed15 100644 --- a/precompiles/src/solidity/uidLookup.sol +++ b/precompiles/src/solidity/uidLookup.sol @@ -13,4 +13,8 @@ interface IUidLookup { address evm_address, uint16 limit ) external view returns (LookupItem[] memory); + function getAssociatedEvmAddress( + uint16 netuid, + uint16 uid + ) external view returns (bool exists, address evmAddress, uint64 blockAssociated); } diff --git a/precompiles/src/solidity/votingPower.abi b/precompiles/src/solidity/votingPower.abi index a2694e9a99..d825bcdfde 100644 --- a/precompiles/src/solidity/votingPower.abi +++ b/precompiles/src/solidity/votingPower.abi @@ -98,5 +98,31 @@ ], "stateMutability": "view", "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "disableVotingPowerTracking", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "enableVotingPowerTracking", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" } -] +] \ No newline at end of file diff --git a/precompiles/src/solidity/votingPower.sol b/precompiles/src/solidity/votingPower.sol index 043772a66d..7f8725544c 100644 --- a/precompiles/src/solidity/votingPower.sol +++ b/precompiles/src/solidity/votingPower.sol @@ -40,4 +40,7 @@ interface IVotingPower { /// useful for computing voting thresholds (e.g. a 51% quorum). /// @param netuid The subnet identifier. function getTotalVotingPower(uint16 netuid) external view returns (uint256); + + function enableVotingPowerTracking(uint16 netuid) external; + function disableVotingPowerTracking(uint16 netuid) external; } diff --git a/precompiles/src/staking.rs b/precompiles/src/staking.rs index 7f47bf8ab3..5b8be6801a 100644 --- a/precompiles/src/staking.rs +++ b/precompiles/src/staking.rs @@ -46,10 +46,13 @@ use pallet_subtensor_proxy as pallet_proxy; use precompile_utils::EvmResult; use precompile_utils::prelude::{Address, BoundedVec, revert}; use sp_core::{H160, H256, U256}; -use sp_runtime::traits::{AsSystemOriginSigner, Dispatchable, StaticLookup, UniqueSaturatedInto}; +use sp_runtime::{ + PerU16, + traits::{AsSystemOriginSigner, Dispatchable, StaticLookup, UniqueSaturatedInto}, +}; use sp_std::vec; use substrate_fixed::types::U64F64; -use subtensor_runtime_common::{AlphaBalance, NetUid, ProxyType, Token}; +use subtensor_runtime_common::{AlphaBalance, NetUid, ProxyType, TaoBalance, Token}; use crate::{PrecompileExt, PrecompileHandleExt}; @@ -65,6 +68,16 @@ const MAX_CONVICTION_HOTKEYS: usize = 64; const COLDKEY_LOCK_READS: u64 = 6; // Aggregate state reads the owner hotkey, global rates, current block, and up to four buckets. const HOTKEY_LOCK_READS: u64 = 8; +// Each hotkey-wide total visits every possible subnet. Besides the +// `NetworksAdded` entry, one active subnet can read TotalHotkeyAlpha plus the +// four values used by `current_alpha_price`. +const TOTAL_HOTKEY_STAKE_READS_PER_SUBNET: u64 = 5; +// For each raw Alpha/AlphaV2 position, the coldkey totals read the position +// once while accounting and once in the released helper. A matching position +// can then perform the conservative V2 stake lookup, swap simulation, and +// current-price reads. +const TOTAL_COLDKEY_POSITION_BASE_READS: u64 = 2; +const TOTAL_COLDKEY_MATCHED_POSITION_READS: u64 = STAKE_INFO_READS_PER_HOTKEY + 9 + 4; /// Prefix for the Allowances map in Substrate storage. pub struct AllowancesPrefix; @@ -103,6 +116,7 @@ where + pallet_balances::Config + pallet_evm::Config + pallet_subtensor::Config + + pallet_admin_utils::Config + pallet_proxy::Config + pallet_shield::Config + pallet_subtensor_proxy::Config @@ -112,6 +126,7 @@ where R::AccountId: From<[u8; 32]> + Into<[u8; 32]>, ::RuntimeOrigin: AsSystemOriginSigner + Clone, ::RuntimeCall: From> + + From> + From> + GetDispatchInfo + Dispatchable @@ -132,6 +147,7 @@ where + pallet_balances::Config + pallet_evm::Config + pallet_subtensor::Config + + pallet_admin_utils::Config + pallet_proxy::Config + pallet_shield::Config + pallet_subtensor_proxy::Config @@ -141,6 +157,7 @@ where R::AccountId: From<[u8; 32]> + Into<[u8; 32]>, ::RuntimeOrigin: AsSystemOriginSigner + Clone, ::RuntimeCall: From> + + From> + From> + GetDispatchInfo + Dispatchable @@ -314,9 +331,8 @@ where handle: &mut impl PrecompileHandle, coldkey: H256, ) -> EvmResult { - // StakingHotkeys + per-hotkey stake reads - handle.record_db_reads::(2)?; let coldkey = R::AccountId::from(coldkey.0); + record_total_coldkey_stake_reads::(handle, &coldkey, None)?; let stake = pallet_subtensor::Pallet::::get_total_stake_for_coldkey(&coldkey); Ok(stake.to_u64().into()) @@ -325,8 +341,7 @@ where #[precompile::public("getTotalHotkeyStake(bytes32)")] #[precompile::view] fn get_total_hotkey_stake(handle: &mut impl PrecompileHandle, hotkey: H256) -> EvmResult { - // Per-subnet stake + alpha price reads - handle.record_db_reads::(2)?; + record_total_hotkey_stake_reads::(handle)?; let hotkey = R::AccountId::from(hotkey.0); let stake = pallet_subtensor::Pallet::::get_total_stake_for_hotkey(&hotkey); @@ -784,10 +799,9 @@ where coldkey: H256, netuid: U256, ) -> EvmResult { - // StakingHotkeys + per-hotkey stake reads - handle.record_db_reads::(2)?; let coldkey = R::AccountId::from(coldkey.0); let netuid = try_u16_from_u256(netuid)?; + record_total_coldkey_stake_reads::(handle, &coldkey, Some(netuid.into()))?; let stake = pallet_subtensor::Pallet::::get_total_stake_for_coldkey_on_subnet( &coldkey, netuid.into(), @@ -977,6 +991,804 @@ where handle.try_dispatch_runtime_call::(call, RawOrigin::Signed(source_id)) } + + #[precompile::public("decreaseTake(bytes32,uint16)")] + fn decrease_take(handle: &mut impl PrecompileHandle, hotkey: H256, take: u16) -> EvmResult<()> { + dispatch_subtensor( + handle, + pallet_subtensor::Call::::decrease_take { + hotkey: hotkey.0.into(), + take: PerU16::from_parts(take), + }, + ) + } + + #[precompile::public("increaseTake(bytes32,uint16)")] + fn increase_take(handle: &mut impl PrecompileHandle, hotkey: H256, take: u16) -> EvmResult<()> { + dispatch_subtensor( + handle, + pallet_subtensor::Call::::increase_take { + hotkey: hotkey.0.into(), + take: PerU16::from_parts(take), + }, + ) + } + + #[precompile::public("setChildkeyTake(bytes32,uint16,uint16)")] + fn set_childkey_take( + handle: &mut impl PrecompileHandle, + hotkey: H256, + netuid: u16, + take: u16, + ) -> EvmResult<()> { + dispatch_subtensor( + handle, + pallet_subtensor::Call::::set_childkey_take { + hotkey: hotkey.0.into(), + netuid: netuid.into(), + take: PerU16::from_parts(take), + }, + ) + } + + #[precompile::public("unstakeAll(bytes32)")] + fn unstake_all(handle: &mut impl PrecompileHandle, hotkey: H256) -> EvmResult<()> { + dispatch_subtensor( + handle, + pallet_subtensor::Call::::unstake_all { + hotkey: hotkey.0.into(), + }, + ) + } + + #[precompile::public("unstakeAllAlpha(bytes32)")] + fn unstake_all_alpha(handle: &mut impl PrecompileHandle, hotkey: H256) -> EvmResult<()> { + dispatch_subtensor( + handle, + pallet_subtensor::Call::::unstake_all_alpha { + hotkey: hotkey.0.into(), + }, + ) + } + + #[precompile::public("swapStake(bytes32,uint16,uint16,uint64)")] + fn swap_stake( + handle: &mut impl PrecompileHandle, + hotkey: H256, + origin_netuid: u16, + destination_netuid: u16, + alpha_amount: u64, + ) -> EvmResult<()> { + dispatch_subtensor( + handle, + pallet_subtensor::Call::::swap_stake { + hotkey: hotkey.0.into(), + origin_netuid: origin_netuid.into(), + destination_netuid: destination_netuid.into(), + alpha_amount: AlphaBalance::from(alpha_amount), + }, + ) + } + + #[precompile::public("swapStakeLimit(bytes32,uint16,uint16,uint64,uint64,bool)")] + fn swap_stake_limit( + handle: &mut impl PrecompileHandle, + hotkey: H256, + origin_netuid: u16, + destination_netuid: u16, + alpha_amount: u64, + limit_price: u64, + allow_partial: bool, + ) -> EvmResult<()> { + dispatch_subtensor( + handle, + pallet_subtensor::Call::::swap_stake_limit { + hotkey: hotkey.0.into(), + origin_netuid: origin_netuid.into(), + destination_netuid: destination_netuid.into(), + alpha_amount: AlphaBalance::from(alpha_amount), + limit_price: TaoBalance::from(limit_price), + allow_partial, + }, + ) + } + + #[precompile::public("recycleAlpha(bytes32,uint64,uint16)")] + fn recycle_alpha( + handle: &mut impl PrecompileHandle, + hotkey: H256, + amount: u64, + netuid: u16, + ) -> EvmResult<()> { + dispatch_subtensor( + handle, + pallet_subtensor::Call::::recycle_alpha { + hotkey: hotkey.0.into(), + amount: AlphaBalance::from(amount), + netuid: netuid.into(), + }, + ) + } + + #[precompile::public("setColdkeyAutoStakeHotkey(uint16,bytes32)")] + fn set_coldkey_auto_stake_hotkey( + handle: &mut impl PrecompileHandle, + netuid: u16, + hotkey: H256, + ) -> EvmResult<()> { + dispatch_subtensor( + handle, + pallet_subtensor::Call::::set_coldkey_auto_stake_hotkey { + netuid: netuid.into(), + hotkey: hotkey.0.into(), + }, + ) + } + + #[precompile::public("claimRoot(uint16[])")] + fn claim_root( + handle: &mut impl PrecompileHandle, + subnets: BoundedVec>, + ) -> EvmResult<()> { + let subnets = Vec::::from(subnets) + .into_iter() + .map(NetUid::from) + .collect::>(); + dispatch_subtensor(handle, pallet_subtensor::Call::::claim_root { subnets }) + } + + #[precompile::public("setRootClaimType(uint8,uint16[])")] + fn set_root_claim_type( + handle: &mut impl PrecompileHandle, + claim_type: u8, + subnets: BoundedVec>, + ) -> EvmResult<()> { + let subnets = Vec::::from(subnets) + .into_iter() + .map(NetUid::from) + .collect::>(); + let new_root_claim_type = match claim_type { + 0 => pallet_subtensor::RootClaimTypeEnum::Swap, + 1 => pallet_subtensor::RootClaimTypeEnum::Keep, + 2 => pallet_subtensor::RootClaimTypeEnum::KeepSubnets { subnets }, + _ => return Err(revert("invalid root claim type")), + }; + dispatch_subtensor( + handle, + pallet_subtensor::Call::::set_root_claim_type { + new_root_claim_type, + }, + ) + } + + #[precompile::public("setRootClaimThreshold(uint16,uint64)")] + fn set_root_claim_threshold( + handle: &mut impl PrecompileHandle, + netuid: u16, + new_value: u64, + ) -> EvmResult<()> { + dispatch_subtensor( + handle, + pallet_subtensor::Call::::sudo_set_root_claim_threshold { + netuid: netuid.into(), + new_value, + }, + ) + } + + #[precompile::public("addStakeBurn(bytes32,uint16,uint64,bool,uint64)")] + fn add_stake_burn( + handle: &mut impl PrecompileHandle, + hotkey: H256, + netuid: u16, + amount: u64, + has_limit: bool, + limit: u64, + ) -> EvmResult<()> { + dispatch_subtensor( + handle, + pallet_subtensor::Call::::add_stake_burn { + hotkey: hotkey.0.into(), + netuid: netuid.into(), + amount: TaoBalance::from(amount), + limit: has_limit.then_some(TaoBalance::from(limit)), + }, + ) + } + + #[precompile::public("setAutoParentDelegationEnabled(bytes32,bool)")] + fn set_auto_parent_delegation_enabled( + handle: &mut impl PrecompileHandle, + hotkey: H256, + enabled: bool, + ) -> EvmResult<()> { + dispatch_subtensor( + handle, + pallet_subtensor::Call::::set_auto_parent_delegation_enabled { + hotkey: hotkey.0.into(), + enabled, + }, + ) + } + + #[precompile::public("transferStakeAndHotkey(bytes32,bytes32,bytes32,uint16,uint16,uint64)")] + fn transfer_stake_and_hotkey( + handle: &mut impl PrecompileHandle, + destination_coldkey: H256, + origin_hotkey: H256, + destination_hotkey: H256, + origin_netuid: u16, + destination_netuid: u16, + alpha_amount: u64, + ) -> EvmResult<()> { + dispatch_subtensor( + handle, + pallet_subtensor::Call::::transfer_stake_and_hotkey { + destination_coldkey: destination_coldkey.0.into(), + origin_hotkey: origin_hotkey.0.into(), + destination_hotkey: destination_hotkey.0.into(), + origin_netuid: origin_netuid.into(), + destination_netuid: destination_netuid.into(), + alpha_amount: AlphaBalance::from(alpha_amount), + }, + ) + } + + #[precompile::public("addCollateral(uint16,bytes32,uint64,uint64)")] + fn add_collateral( + handle: &mut impl PrecompileHandle, + netuid: u16, + hotkey: H256, + alpha: u64, + limit_price: u64, + ) -> EvmResult<()> { + dispatch_subtensor( + handle, + pallet_subtensor::Call::::add_collateral { + netuid: netuid.into(), + hotkey: hotkey.0.into(), + alpha: AlphaBalance::from(alpha), + limit_price: TaoBalance::from(limit_price), + }, + ) + } + + #[precompile::public("setMinCollateral(uint16,bytes32,uint64)")] + fn set_min_collateral( + handle: &mut impl PrecompileHandle, + netuid: u16, + hotkey: H256, + min_locked: u64, + ) -> EvmResult<()> { + dispatch_subtensor( + handle, + pallet_subtensor::Call::::set_min_collateral { + netuid: netuid.into(), + hotkey: hotkey.0.into(), + min_locked: AlphaBalance::from(min_locked), + }, + ) + } + + #[precompile::public("setMinChildkeyTakePerSubnet(uint16,uint16)")] + fn set_min_childkey_take_per_subnet( + handle: &mut impl PrecompileHandle, + netuid: u16, + take: u16, + ) -> EvmResult<()> { + dispatch_staking_admin( + handle, + pallet_admin_utils::Call::::sudo_set_min_childkey_take_per_subnet { + netuid: netuid.into(), + take: PerU16::from_parts(take), + }, + ) + } + + #[precompile::public("setCollateralLockShare(uint16,uint16)")] + fn set_collateral_lock_share( + handle: &mut impl PrecompileHandle, + netuid: u16, + lock_share: u16, + ) -> EvmResult<()> { + dispatch_staking_admin( + handle, + pallet_admin_utils::Call::::sudo_set_collateral_lock_share { + netuid: netuid.into(), + lock_share, + }, + ) + } + + #[precompile::public("setCollateralDrainRatio(uint16,uint128)")] + fn set_collateral_drain_ratio( + handle: &mut impl PrecompileHandle, + netuid: u16, + raw_ratio: u128, + ) -> EvmResult<()> { + dispatch_staking_admin( + handle, + pallet_admin_utils::Call::::sudo_set_collateral_drain_ratio { + netuid: netuid.into(), + drain_ratio: U64F64::from_bits(raw_ratio), + }, + ) + } + + #[precompile::public("getDelegate(bytes32)")] + #[precompile::view] + fn get_delegate(handle: &mut impl PrecompileHandle, hotkey: H256) -> EvmResult<(bool, u16)> { + handle.record_db_reads::(1)?; + let hotkey = R::AccountId::from(hotkey.0); + Ok(match pallet_subtensor::Delegates::::try_get(hotkey) { + Ok(take) => (true, take.deconstruct()), + Err(()) => (false, 0), + }) + } + + #[precompile::public("getChildkeyTake(bytes32,uint16)")] + #[precompile::view] + fn get_childkey_take( + handle: &mut impl PrecompileHandle, + hotkey: H256, + netuid: u16, + ) -> EvmResult { + handle.record_db_reads::(1)?; + Ok(pallet_subtensor::ChildkeyTake::::get( + R::AccountId::from(hotkey.0), + NetUid::from(netuid), + ) + .deconstruct()) + } + + #[precompile::public("getPendingChildKeys(bytes32,uint16)")] + #[precompile::view] + fn get_pending_child_keys( + handle: &mut impl PrecompileHandle, + parent: H256, + netuid: u16, + ) -> EvmResult<(Vec<(u64, H256)>, u64)> { + handle.record_db_reads::(1)?; + let (children, cooldown_block) = pallet_subtensor::PendingChildKeys::::get( + NetUid::from(netuid), + R::AccountId::from(parent.0), + ); + Ok(( + children + .into_iter() + .map(|(proportion, child)| (proportion, account_to_h256(child))) + .collect(), + cooldown_block, + )) + } + + #[precompile::public("getChildKeys(bytes32,uint16)")] + #[precompile::view] + fn get_child_keys( + handle: &mut impl PrecompileHandle, + parent: H256, + netuid: u16, + ) -> EvmResult> { + handle.record_db_reads::(1)?; + Ok(pallet_subtensor::ChildKeys::::get( + R::AccountId::from(parent.0), + NetUid::from(netuid), + ) + .into_iter() + .map(|(proportion, child)| (proportion, account_to_h256(child))) + .collect()) + } + + #[precompile::public("getParentKeys(bytes32,uint16)")] + #[precompile::view] + fn get_parent_keys( + handle: &mut impl PrecompileHandle, + child: H256, + netuid: u16, + ) -> EvmResult> { + handle.record_db_reads::(1)?; + Ok(pallet_subtensor::ParentKeys::::get( + R::AccountId::from(child.0), + NetUid::from(netuid), + ) + .into_iter() + .map(|(proportion, parent)| (proportion, account_to_h256(parent))) + .collect()) + } + + #[precompile::public("getPendingChildKeyCooldown()")] + #[precompile::view] + fn get_pending_childkey_cooldown(handle: &mut impl PrecompileHandle) -> EvmResult { + handle.record_db_reads::(1)?; + Ok(pallet_subtensor::PendingChildKeyCooldown::::get()) + } + + #[precompile::public("getTakeLimits()")] + #[precompile::view] + fn get_take_limits(handle: &mut impl PrecompileHandle) -> EvmResult<(u16, u16, u16, u16)> { + handle.record_db_reads::(4)?; + Ok(( + pallet_subtensor::MinDelegateTake::::get().deconstruct(), + pallet_subtensor::MaxDelegateTake::::get().deconstruct(), + pallet_subtensor::MinChildkeyTake::::get().deconstruct(), + pallet_subtensor::MaxChildkeyTake::::get().deconstruct(), + )) + } + + #[precompile::public("getMinChildkeyTakePerSubnet(uint16)")] + #[precompile::view] + fn get_min_childkey_take_per_subnet( + handle: &mut impl PrecompileHandle, + netuid: u16, + ) -> EvmResult { + handle.record_db_reads::(1)?; + Ok( + pallet_subtensor::MinChildkeyTakePerSubnet::::get(NetUid::from(netuid)) + .deconstruct(), + ) + } + + #[precompile::public("getHotkeyOwner(bytes32)")] + #[precompile::view] + fn get_hotkey_owner( + handle: &mut impl PrecompileHandle, + hotkey: H256, + ) -> EvmResult<(bool, H256)> { + handle.record_db_reads::(1)?; + let hotkey = R::AccountId::from(hotkey.0); + Ok(match pallet_subtensor::Owner::::try_get(hotkey) { + Ok(owner) => (true, account_to_h256(owner)), + Err(()) => (false, H256::zero()), + }) + } + + #[precompile::public("getOwnedHotkeys(bytes32)")] + #[precompile::view] + fn get_owned_hotkeys( + handle: &mut impl PrecompileHandle, + coldkey: H256, + ) -> EvmResult> { + handle.record_db_reads::(1)?; + Ok( + pallet_subtensor::OwnedHotkeys::::get(R::AccountId::from(coldkey.0)) + .into_iter() + .map(account_to_h256) + .collect(), + ) + } + + #[precompile::public("getAutoStakeDestination(bytes32,uint16)")] + #[precompile::view] + fn get_auto_stake_destination( + handle: &mut impl PrecompileHandle, + coldkey: H256, + netuid: u16, + ) -> EvmResult<(bool, H256)> { + handle.record_db_reads::(1)?; + Ok( + match pallet_subtensor::AutoStakeDestination::::get( + R::AccountId::from(coldkey.0), + NetUid::from(netuid), + ) { + Some(hotkey) => (true, account_to_h256(hotkey)), + None => (false, H256::zero()), + }, + ) + } + + #[precompile::public("getAutoStakeDestinationColdkeys(bytes32,uint16)")] + #[precompile::view] + fn get_auto_stake_destination_coldkeys( + handle: &mut impl PrecompileHandle, + hotkey: H256, + netuid: u16, + ) -> EvmResult> { + handle.record_db_reads::(1)?; + Ok(pallet_subtensor::AutoStakeDestinationColdkeys::::get( + R::AccountId::from(hotkey.0), + NetUid::from(netuid), + ) + .into_iter() + .map(account_to_h256) + .collect()) + } + + #[precompile::public("getHotkeySuccessor(bytes32,uint16)")] + #[precompile::view] + fn get_hotkey_successor( + handle: &mut impl PrecompileHandle, + hotkey: H256, + netuid: u16, + ) -> EvmResult<(bool, H256)> { + handle.record_db_reads::(1)?; + Ok(optional_account( + pallet_subtensor::HotkeySuccessor::::get( + NetUid::from(netuid), + R::AccountId::from(hotkey.0), + ), + )) + } + + #[precompile::public("getHotkeyRoot(bytes32,uint16)")] + #[precompile::view] + fn get_hotkey_root( + handle: &mut impl PrecompileHandle, + hotkey: H256, + netuid: u16, + ) -> EvmResult<(bool, H256)> { + handle.record_db_reads::(1)?; + Ok(optional_account(pallet_subtensor::HotkeyRoot::::get( + NetUid::from(netuid), + R::AccountId::from(hotkey.0), + ))) + } + + #[precompile::public("getColdkeySuccessor(bytes32)")] + #[precompile::view] + fn get_coldkey_successor( + handle: &mut impl PrecompileHandle, + coldkey: H256, + ) -> EvmResult<(bool, H256)> { + handle.record_db_reads::(1)?; + Ok(optional_account( + pallet_subtensor::ColdkeySuccessor::::get(R::AccountId::from(coldkey.0)), + )) + } + + #[precompile::public("getColdkeyRoot(bytes32)")] + #[precompile::view] + fn get_coldkey_root( + handle: &mut impl PrecompileHandle, + coldkey: H256, + ) -> EvmResult<(bool, H256)> { + handle.record_db_reads::(1)?; + Ok(optional_account(pallet_subtensor::ColdkeyRoot::::get( + R::AccountId::from(coldkey.0), + ))) + } + + #[precompile::public("getColdkeySwapStatus(bytes32)")] + #[precompile::view] + fn get_coldkey_swap_status( + handle: &mut impl PrecompileHandle, + coldkey: H256, + ) -> EvmResult<(bool, u64, H256, bool, u64)> { + handle.record_db_reads::(2)?; + let coldkey = R::AccountId::from(coldkey.0); + let announcement = pallet_subtensor::ColdkeySwapAnnouncements::::get(&coldkey); + let dispute = pallet_subtensor::ColdkeySwapDisputes::::get(&coldkey); + let (has_announcement, announcement_block, call_hash) = match announcement { + Some((block, hash)) => ( + true, + block.unique_saturated_into(), + H256::from_slice(hash.as_ref()), + ), + None => (false, 0, H256::zero()), + }; + Ok(( + has_announcement, + announcement_block, + call_hash, + dispute.is_some(), + dispute + .map(UniqueSaturatedInto::unique_saturated_into) + .unwrap_or(0), + )) + } + + #[precompile::public("getColdkeySwapDelays()")] + #[precompile::view] + fn get_coldkey_swap_delays(handle: &mut impl PrecompileHandle) -> EvmResult<(u64, u64)> { + handle.record_db_reads::(2)?; + Ok(( + pallet_subtensor::ColdkeySwapAnnouncementDelay::::get().unique_saturated_into(), + pallet_subtensor::ColdkeySwapReannouncementDelay::::get().unique_saturated_into(), + )) + } + + #[precompile::public("getLastHotkeySwapOnSubnet(bytes32,uint16)")] + #[precompile::view] + fn get_last_hotkey_swap_on_subnet( + handle: &mut impl PrecompileHandle, + coldkey: H256, + netuid: u16, + ) -> EvmResult { + handle.record_db_reads::(1)?; + Ok(pallet_subtensor::LastHotkeySwapOnNetuid::::get( + NetUid::from(netuid), + R::AccountId::from(coldkey.0), + )) + } + + #[precompile::public("getStakeAccounting()")] + #[precompile::view] + fn get_stake_accounting(handle: &mut impl PrecompileHandle) -> EvmResult<(u64, u64)> { + handle.record_db_reads::(2)?; + Ok(( + pallet_subtensor::TotalIssuance::::get().to_u64(), + pallet_subtensor::TotalStake::::get().to_u64(), + )) + } + + #[precompile::public("getMinerCollateral(uint16,bytes32,bytes32)")] + #[precompile::view] + fn get_miner_collateral( + handle: &mut impl PrecompileHandle, + netuid: u16, + hotkey: H256, + coldkey: H256, + ) -> EvmResult<(bool, u64, u128, u64, u64)> { + handle.record_db_reads::(1)?; + Ok( + match pallet_subtensor::MinerCollateral::::get(( + NetUid::from(netuid), + R::AccountId::from(hotkey.0), + R::AccountId::from(coldkey.0), + )) { + Some(state) => ( + true, + state.locked.to_u64(), + state.drain_ratio.to_bits(), + state.min_locked.to_u64(), + state.earned.to_u64(), + ), + None => (false, 0, 0, 0, 0), + }, + ) + } + + #[precompile::public("getColdkeyCollateral(uint16,bytes32)")] + #[precompile::view] + fn get_coldkey_collateral( + handle: &mut impl PrecompileHandle, + netuid: u16, + coldkey: H256, + ) -> EvmResult<(u64, Vec)> { + handle.record_db_reads::(2)?; + let coldkey = R::AccountId::from(coldkey.0); + Ok(( + pallet_subtensor::ColdkeyMinerCollateral::::get(NetUid::from(netuid), &coldkey) + .to_u64(), + pallet_subtensor::ColdkeyCollateralHotkeys::::get(NetUid::from(netuid), coldkey) + .into_iter() + .map(account_to_h256) + .collect(), + )) + } + + #[precompile::public("getCollateralConfig(uint16)")] + #[precompile::view] + fn get_collateral_config( + handle: &mut impl PrecompileHandle, + netuid: u16, + ) -> EvmResult<(u16, u128)> { + handle.record_db_reads::(2)?; + Ok(( + pallet_subtensor::CollateralLockShare::::get(NetUid::from(netuid)), + pallet_subtensor::CollateralDrainRatio::::get(NetUid::from(netuid)).to_bits(), + )) + } +} + +fn account_to_h256>(account: AccountId) -> H256 { + H256::from(account.into()) +} + +fn optional_account>(account: Option) -> (bool, H256) { + account + .map(|account| (true, account_to_h256(account))) + .unwrap_or((false, H256::zero())) +} + +fn dispatch_subtensor( + handle: &mut impl PrecompileHandle, + call: pallet_subtensor::Call, +) -> EvmResult<()> +where + R: frame_system::Config + + pallet_balances::Config + + pallet_evm::Config + + pallet_subtensor::Config + + pallet_admin_utils::Config + + pallet_proxy::Config + + pallet_shield::Config + + pallet_subtensor_proxy::Config + + Send + + Sync + + scale_info::TypeInfo, + R::AccountId: From<[u8; 32]> + Into<[u8; 32]>, + ::RuntimeOrigin: AsSystemOriginSigner + Clone, + ::RuntimeCall: From> + + GetDispatchInfo + + Dispatchable + + IsSubType> + + IsSubType> + + IsSubType> + + IsSubType>, + ::AddressMapping: AddressMapping, +{ + let caller = handle.caller_account_id::(); + handle.try_dispatch_runtime_call::(call, RawOrigin::Signed(caller)) +} + +fn dispatch_staking_admin( + handle: &mut impl PrecompileHandle, + call: pallet_admin_utils::Call, +) -> EvmResult<()> +where + R: frame_system::Config + + pallet_balances::Config + + pallet_evm::Config + + pallet_subtensor::Config + + pallet_admin_utils::Config + + pallet_proxy::Config + + pallet_shield::Config + + pallet_subtensor_proxy::Config + + Send + + Sync + + scale_info::TypeInfo, + R::AccountId: From<[u8; 32]> + Into<[u8; 32]>, + ::RuntimeOrigin: AsSystemOriginSigner + Clone, + ::RuntimeCall: From> + + GetDispatchInfo + + Dispatchable + + IsSubType> + + IsSubType> + + IsSubType> + + IsSubType>, + ::AddressMapping: AddressMapping, +{ + let caller = handle.caller_account_id::(); + handle.try_dispatch_runtime_call::(call, RawOrigin::Signed(caller)) +} + +fn record_total_hotkey_stake_reads(handle: &mut impl PrecompileHandle) -> EvmResult<()> +where + R: frame_system::Config + pallet_subtensor::Config + pallet_evm::Config, +{ + // Charge the SubnetLimit read plus the maximum permitted work before the + // released helper scans NetworksAdded. + handle.record_db_reads::(1)?; + let subnet_limit: u64 = pallet_subtensor::SubnetLimit::::get().unique_saturated_into(); + handle.record_db_reads::(subnet_limit.saturating_mul(TOTAL_HOTKEY_STAKE_READS_PER_SUBNET)) +} + +fn record_total_coldkey_stake_reads( + handle: &mut impl PrecompileHandle, + coldkey: &R::AccountId, + selected_netuid: Option, +) -> EvmResult<()> +where + R: frame_system::Config + pallet_subtensor::Config + pallet_evm::Config, + R::AccountId: Clone, +{ + // Read the bounded-by-state list once here and once in the released + // aggregate helper. + handle.record_db_reads::(2)?; + let hotkeys = pallet_subtensor::StakingHotkeys::::get(coldkey); + + let mut raw_positions = 0u64; + let mut matched_positions = 0u64; + for hotkey in hotkeys { + for (netuid, _) in pallet_subtensor::Alpha::::iter_prefix((&hotkey, coldkey)) { + raw_positions = raw_positions.saturating_add(1); + if selected_netuid.is_none_or(|selected| selected == netuid) { + matched_positions = matched_positions.saturating_add(1); + } + } + for (netuid, _) in pallet_subtensor::AlphaV2::::iter_prefix((&hotkey, coldkey)) { + raw_positions = raw_positions.saturating_add(1); + if selected_netuid.is_none_or(|selected| selected == netuid) { + matched_positions = matched_positions.saturating_add(1); + } + } + } + + handle.record_db_reads::( + raw_positions + .saturating_mul(TOTAL_COLDKEY_POSITION_BASE_READS) + .saturating_add(matched_positions.saturating_mul(TOTAL_COLDKEY_MATCHED_POSITION_READS)), + ) } // Deprecated, exists for backward compatibility. @@ -1094,9 +1906,8 @@ where handle: &mut impl PrecompileHandle, coldkey: H256, ) -> EvmResult { - // StakingHotkeys + per-hotkey stake reads - handle.record_db_reads::(2)?; let coldkey = R::AccountId::from(coldkey.0); + record_total_coldkey_stake_reads::(handle, &coldkey, None)?; // get total stake of coldkey let total_stake = @@ -1113,8 +1924,7 @@ where #[precompile::public("getTotalHotkeyStake(bytes32)")] #[precompile::view] fn get_total_hotkey_stake(handle: &mut impl PrecompileHandle, hotkey: H256) -> EvmResult { - // Per-subnet stake + alpha price reads - handle.record_db_reads::(2)?; + record_total_hotkey_stake_reads::(handle)?; let hotkey = R::AccountId::from(hotkey.0); // get total stake of hotkey @@ -1242,12 +2052,12 @@ mod tests { )] use super::*; - use crate::PrecompileExt; use crate::mock::{ AccountId, Proxy, Runtime, RuntimeCall, RuntimeOrigin, addr_from_index, assert_static_call, execute_precompile, fund_account, mapped_account, new_test_ext, precompiles, selector_u32, substrate_to_evm, }; + use crate::{PrecompileExt, Precompiles}; use precompile_utils::prelude::RuntimeHelper; use precompile_utils::solidity::{encode_return_value, encode_with_selector}; use precompile_utils::testing::PrecompileTesterExt; @@ -3099,4 +3909,280 @@ mod tests { assert_eq!(stake_after, stake_before); }); } + + #[test] + fn aggregate_stake_views_charge_their_scans() { + new_test_ext().execute_with(|| { + setup_staking_subnet(); + let caller = addr_from_index(0x3005); + let empty_account = AccountId::from([0x91; 32]); + let account_arg = H256::from_slice(empty_account.as_ref()); + let db_read = RuntimeHelper::::db_read_gas_cost(); + let hotkey_reads = 1u64.saturating_add( + u64::from(pallet_subtensor::SubnetLimit::::get()) + .saturating_mul(TOTAL_HOTKEY_STAKE_READS_PER_SUBNET), + ); + + for (address, is_v2) in [ + (addr_from_index(StakingPrecompileV2::::INDEX), true), + (addr_from_index(StakingPrecompile::::INDEX), false), + ] { + let precompiles = Precompiles::::new(); + precompiles + .prepare_test( + caller, + address, + encode_with_selector( + selector_u32("getTotalHotkeyStake(bytes32)"), + (account_arg,), + ), + ) + .with_static_call(true) + .expect_cost(db_read.saturating_mul(hotkey_reads)) + .execute_returns(U256::zero()); + + precompiles + .prepare_test( + caller, + address, + encode_with_selector( + selector_u32("getTotalColdkeyStake(bytes32)"), + (account_arg,), + ), + ) + .with_static_call(true) + .expect_cost(db_read.saturating_mul(2)) + .execute_returns(U256::zero()); + + if is_v2 { + precompiles + .prepare_test( + caller, + address, + encode_with_selector( + selector_u32("getTotalColdkeyStakeOnSubnet(bytes32,uint256)"), + (account_arg, U256::from(TEST_NETUID_U16)), + ), + ) + .with_static_call(true) + .expect_cost(db_read.saturating_mul(2)) + .execute_returns(U256::zero()); + } + } + }); + } + + #[test] + fn coldkey_aggregate_views_charge_each_stake_position() { + new_test_ext().execute_with(|| { + let netuid = setup_staking_subnet(); + let caller = addr_from_index(0x3007); + let coldkey = AccountId::from([0x92; 32]); + let hotkey = AccountId::from([0x93; 32]); + let coldkey_word = H256::from_slice(coldkey.as_ref()); + pallet_subtensor::Pallet::::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &coldkey, + netuid, + AlphaBalance::from(1_000_u64), + ); + + let total = + pallet_subtensor::Pallet::::get_total_stake_for_coldkey(&coldkey).to_u64(); + let subnet_total = + pallet_subtensor::Pallet::::get_total_stake_for_coldkey_on_subnet( + &coldkey, netuid, + ) + .to_u64(); + let reads = 2_u64 + .saturating_add(TOTAL_COLDKEY_POSITION_BASE_READS) + .saturating_add(TOTAL_COLDKEY_MATCHED_POSITION_READS); + let cost = RuntimeHelper::::db_read_gas_cost().saturating_mul(reads); + let address = addr_from_index(StakingPrecompileV2::::INDEX); + let precompiles = Precompiles::::new(); + + precompiles + .prepare_test( + caller, + address, + encode_with_selector( + selector_u32("getTotalColdkeyStake(bytes32)"), + (coldkey_word,), + ), + ) + .with_static_call(true) + .expect_cost(cost) + .execute_returns(U256::from(total)); + + precompiles + .prepare_test( + caller, + address, + encode_with_selector( + selector_u32("getTotalColdkeyStakeOnSubnet(bytes32,uint256)"), + (coldkey_word, U256::from(TEST_NETUID_U16)), + ), + ) + .with_static_call(true) + .expect_cost(cost) + .execute_returns(U256::from(subnet_total)); + }); + } + + #[test] + fn staking_state_views_return_typed_values_and_missing_state() { + new_test_ext().execute_with(|| { + let netuid = NetUid::from(TEST_NETUID_U16); + let caller = addr_from_index(0x3006); + let address = addr_from_index(StakingPrecompileV2::::INDEX); + let hotkey = AccountId::from([0x71; 32]); + let coldkey = AccountId::from([0x72; 32]); + let hotkey_word = H256::from_slice(hotkey.as_ref()); + let coldkey_word = H256::from_slice(coldkey.as_ref()); + let precompiles = precompiles::>(); + + macro_rules! assert_view { + ($signature:literal, $arguments:expr, $expected:expr) => { + precompiles + .prepare_test( + caller, + address, + encode_with_selector(selector_u32($signature), $arguments), + ) + .with_static_call(true) + .execute_returns($expected); + }; + } + + pallet_subtensor::Delegates::::insert(&hotkey, PerU16::from_parts(123)); + pallet_subtensor::Owner::::insert(&hotkey, &coldkey); + pallet_subtensor::OwnedHotkeys::::insert(&coldkey, vec![hotkey.clone()]); + + assert_view!("getDelegate(bytes32)", (hotkey_word,), (true, 123_u16)); + assert_view!( + "getChildkeyTake(bytes32,uint16)", + (hotkey_word, TEST_NETUID_U16), + pallet_subtensor::ChildkeyTake::::get(&hotkey, netuid).deconstruct() + ); + assert_view!( + "getPendingChildKeys(bytes32,uint16)", + (hotkey_word, TEST_NETUID_U16), + (Vec::<(u64, H256)>::new(), 0_u64) + ); + assert_view!( + "getChildKeys(bytes32,uint16)", + (hotkey_word, TEST_NETUID_U16), + Vec::<(u64, H256)>::new() + ); + assert_view!( + "getParentKeys(bytes32,uint16)", + (hotkey_word, TEST_NETUID_U16), + Vec::<(u64, H256)>::new() + ); + assert_view!( + "getPendingChildKeyCooldown()", + (), + pallet_subtensor::PendingChildKeyCooldown::::get() + ); + assert_view!( + "getTakeLimits()", + (), + ( + pallet_subtensor::MinDelegateTake::::get().deconstruct(), + pallet_subtensor::MaxDelegateTake::::get().deconstruct(), + pallet_subtensor::MinChildkeyTake::::get().deconstruct(), + pallet_subtensor::MaxChildkeyTake::::get().deconstruct(), + ) + ); + assert_view!( + "getMinChildkeyTakePerSubnet(uint16)", + (TEST_NETUID_U16,), + pallet_subtensor::MinChildkeyTakePerSubnet::::get(netuid).deconstruct() + ); + assert_view!( + "getHotkeyOwner(bytes32)", + (hotkey_word,), + (true, coldkey_word) + ); + assert_view!( + "getOwnedHotkeys(bytes32)", + (coldkey_word,), + vec![hotkey_word] + ); + assert_view!( + "getAutoStakeDestination(bytes32,uint16)", + (coldkey_word, TEST_NETUID_U16), + (false, H256::zero()) + ); + assert_view!( + "getAutoStakeDestinationColdkeys(bytes32,uint16)", + (hotkey_word, TEST_NETUID_U16), + Vec::::new() + ); + assert_view!( + "getHotkeySuccessor(bytes32,uint16)", + (hotkey_word, TEST_NETUID_U16), + (false, H256::zero()) + ); + assert_view!( + "getHotkeyRoot(bytes32,uint16)", + (hotkey_word, TEST_NETUID_U16), + (false, H256::zero()) + ); + assert_view!( + "getColdkeySuccessor(bytes32)", + (coldkey_word,), + (false, H256::zero()) + ); + assert_view!( + "getColdkeyRoot(bytes32)", + (coldkey_word,), + (false, H256::zero()) + ); + assert_view!( + "getColdkeySwapStatus(bytes32)", + (coldkey_word,), + (false, 0_u64, H256::zero(), false, 0_u64) + ); + assert_view!( + "getColdkeySwapDelays()", + (), + ( + pallet_subtensor::ColdkeySwapAnnouncementDelay::::get(), + pallet_subtensor::ColdkeySwapReannouncementDelay::::get(), + ) + ); + assert_view!( + "getLastHotkeySwapOnSubnet(bytes32,uint16)", + (coldkey_word, TEST_NETUID_U16), + 0_u64 + ); + assert_view!( + "getStakeAccounting()", + (), + ( + pallet_subtensor::TotalIssuance::::get().to_u64(), + pallet_subtensor::TotalStake::::get().to_u64(), + ) + ); + assert_view!( + "getMinerCollateral(uint16,bytes32,bytes32)", + (TEST_NETUID_U16, hotkey_word, coldkey_word), + (false, 0_u64, 0_u128, 0_u64, 0_u64) + ); + assert_view!( + "getColdkeyCollateral(uint16,bytes32)", + (TEST_NETUID_U16, coldkey_word), + (0_u64, Vec::::new()) + ); + assert_view!( + "getCollateralConfig(uint16)", + (TEST_NETUID_U16,), + ( + pallet_subtensor::CollateralLockShare::::get(netuid), + pallet_subtensor::CollateralDrainRatio::::get(netuid).to_bits(), + ) + ); + }); + } } diff --git a/precompiles/src/subnet.rs b/precompiles/src/subnet.rs index a591a7f0d8..6551c7ee0f 100644 --- a/precompiles/src/subnet.rs +++ b/precompiles/src/subnet.rs @@ -5,13 +5,17 @@ use frame_support::traits::ConstU32; use frame_support::traits::IsSubType; use frame_system::RawOrigin; use pallet_evm::{AddressMapping, PrecompileHandle}; -use precompile_utils::{EvmResult, prelude::BoundedString}; +use precompile_utils::{ + EvmResult, + prelude::{BoundedString, BoundedVec, UnboundedBytes}, +}; use sp_core::H256; -use sp_runtime::traits::{AsSystemOriginSigner, Dispatchable}; -use sp_std::vec; -use subtensor_runtime_common::{NetUid, Token}; +use sp_runtime::traits::{AsSystemOriginSigner, Dispatchable, UniqueSaturatedInto}; +use sp_std::{vec, vec::Vec}; +use subtensor_runtime_common::{NetUid, TaoBalance, Token}; use crate::{PrecompileExt, PrecompileHandleExt}; +use pallet_subtensor::subnets::dissolution::DissolveCleanupPhase; pub struct SubnetPrecompile(PhantomData); @@ -27,7 +31,7 @@ where + Send + Sync + scale_info::TypeInfo, - R::AccountId: From<[u8; 32]>, + R::AccountId: From<[u8; 32]> + Into<[u8; 32]>, ::RuntimeOrigin: AsSystemOriginSigner + Clone, ::RuntimeCall: From> + From> @@ -55,7 +59,7 @@ where + Send + Sync + scale_info::TypeInfo, - R::AccountId: From<[u8; 32]>, + R::AccountId: From<[u8; 32]> + Into<[u8; 32]>, ::RuntimeOrigin: AsSystemOriginSigner + Clone, ::RuntimeCall: From> + From> @@ -173,6 +177,18 @@ where )) } + #[precompile::public("getRegisteredSubnetCounter(uint16)")] + #[precompile::view] + fn get_registered_subnet_counter( + handle: &mut impl PrecompileHandle, + netuid: u16, + ) -> EvmResult { + handle.record_db_reads::(1)?; + Ok(pallet_subtensor::RegisteredSubnetCounter::::get( + NetUid::from(netuid), + )) + } + #[precompile::public("getServingRateLimit(uint16)")] #[precompile::view] fn get_serving_rate_limit(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { @@ -891,6 +907,420 @@ where handle.record_db_reads::(1)?; Ok(pallet_subtensor::DissolveCleanupQueue::::get().contains(&NetUid::from(netuid))) } + + #[precompile::public("getSubnetDissolutionStatus(uint16)")] + #[precompile::view] + fn get_subnet_dissolution_status( + handle: &mut impl PrecompileHandle, + netuid: u16, + ) -> EvmResult<(bool, bool, u8)> { + handle.record_db_reads::(2)?; + let netuid = NetUid::from(netuid); + let is_queued = pallet_subtensor::DissolveCleanupQueue::::get().contains(&netuid); + + match pallet_subtensor::CurrentDissolveCleanupStatus::::get() { + Some(status) if status.netuid == netuid => { + Ok((true, true, dissolution_cleanup_phase_code(&status.phase))) + } + _ => Ok((is_queued, false, 0)), + } + } + + #[precompile::public( + "setSubnetIdentity(uint16,string,string,string,string,string,string,string,string)" + )] + #[allow(clippy::too_many_arguments)] + fn set_subnet_identity( + handle: &mut impl PrecompileHandle, + netuid: u16, + subnet_name: BoundedString>, + github_repo: BoundedString>, + subnet_contact: BoundedString>, + subnet_url: BoundedString>, + discord: BoundedString>, + description: BoundedString>, + logo_url: BoundedString>, + additional: BoundedString>, + ) -> EvmResult<()> { + let call = pallet_subtensor::Call::::set_subnet_identity { + netuid: NetUid::from(netuid), + subnet_name: subnet_name.into(), + github_repo: github_repo.into(), + subnet_contact: subnet_contact.into(), + subnet_url: subnet_url.into(), + discord: discord.into(), + description: description.into(), + logo_url: logo_url.into(), + additional: additional.into(), + }; + handle.try_dispatch_runtime_call::( + call, + RawOrigin::Signed(handle.caller_account_id::()), + ) + } + + #[precompile::public("updateSubnetSymbol(uint16,string)")] + fn update_subnet_symbol( + handle: &mut impl PrecompileHandle, + netuid: u16, + symbol: BoundedString>, + ) -> EvmResult<()> { + let call = pallet_subtensor::Call::::update_symbol { + netuid: NetUid::from(netuid), + symbol: symbol.into(), + }; + handle.try_dispatch_runtime_call::( + call, + RawOrigin::Signed(handle.caller_account_id::()), + ) + } + + #[precompile::public("triggerEpoch(uint16)")] + fn trigger_epoch(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult<()> { + let call = pallet_subtensor::Call::::trigger_epoch { + netuid: NetUid::from(netuid), + }; + handle.try_dispatch_runtime_call::( + call, + RawOrigin::Signed(handle.caller_account_id::()), + ) + } + + #[precompile::public("setBondsPenalty(uint16,uint16)")] + fn set_bonds_penalty( + handle: &mut impl PrecompileHandle, + netuid: u16, + bonds_penalty: u16, + ) -> EvmResult<()> { + dispatch_admin( + handle, + pallet_admin_utils::Call::::sudo_set_bonds_penalty { + netuid: netuid.into(), + bonds_penalty, + }, + ) + } + + #[precompile::public("setMaxAllowedUids(uint16,uint16)")] + fn set_max_allowed_uids( + handle: &mut impl PrecompileHandle, + netuid: u16, + max_allowed_uids: u16, + ) -> EvmResult<()> { + dispatch_admin( + handle, + pallet_admin_utils::Call::::sudo_set_max_allowed_uids { + netuid: netuid.into(), + max_allowed_uids, + }, + ) + } + + #[precompile::public("setMaxBurnV2(uint16,uint64)")] + fn set_max_burn_v2( + handle: &mut impl PrecompileHandle, + netuid: u16, + max_burn: u64, + ) -> EvmResult<()> { + dispatch_admin( + handle, + pallet_admin_utils::Call::::sudo_set_max_burn { + netuid: netuid.into(), + max_burn: TaoBalance::from(max_burn), + }, + ) + } + + #[precompile::public("setMechanismCount(uint16,uint8)")] + fn set_mechanism_count( + handle: &mut impl PrecompileHandle, + netuid: u16, + mechanism_count: u8, + ) -> EvmResult<()> { + dispatch_admin( + handle, + pallet_admin_utils::Call::::sudo_set_mechanism_count { + netuid: netuid.into(), + mechanism_count: mechanism_count.into(), + }, + ) + } + + #[precompile::public("setMechanismEmissionSplit(uint16,bool,uint16[])")] + fn set_mechanism_emission_split( + handle: &mut impl PrecompileHandle, + netuid: u16, + has_split: bool, + split: BoundedVec>, + ) -> EvmResult<()> { + dispatch_admin( + handle, + pallet_admin_utils::Call::::sudo_set_mechanism_emission_split { + netuid: netuid.into(), + maybe_split: has_split.then(|| Vec::::from(split)), + }, + ) + } + + #[precompile::public("setMinBurnV2(uint16,uint64)")] + fn set_min_burn_v2( + handle: &mut impl PrecompileHandle, + netuid: u16, + min_burn: u64, + ) -> EvmResult<()> { + dispatch_admin( + handle, + pallet_admin_utils::Call::::sudo_set_min_burn { + netuid: netuid.into(), + min_burn: TaoBalance::from(min_burn), + }, + ) + } + + #[precompile::public("setOwnerCutEnabled(uint16,bool)")] + fn set_owner_cut_enabled( + handle: &mut impl PrecompileHandle, + netuid: u16, + enabled: bool, + ) -> EvmResult<()> { + dispatch_admin( + handle, + pallet_admin_utils::Call::::sudo_set_owner_cut_enabled { + netuid: netuid.into(), + enabled, + }, + ) + } + + #[precompile::public("setOwnerImmuneNeuronLimit(uint16,uint16)")] + fn set_owner_immune_neuron_limit( + handle: &mut impl PrecompileHandle, + netuid: u16, + immune_neurons: u16, + ) -> EvmResult<()> { + dispatch_admin( + handle, + pallet_admin_utils::Call::::sudo_set_owner_immune_neuron_limit { + netuid: netuid.into(), + immune_neurons, + }, + ) + } + + #[precompile::public("setTempo(uint16,uint16)")] + fn set_tempo(handle: &mut impl PrecompileHandle, netuid: u16, tempo: u16) -> EvmResult<()> { + dispatch_admin( + handle, + pallet_admin_utils::Call::::sudo_set_tempo { + netuid: netuid.into(), + tempo, + }, + ) + } + + #[precompile::public("trimToMaxAllowedUids(uint16,uint16)")] + fn trim_to_max_allowed_uids( + handle: &mut impl PrecompileHandle, + netuid: u16, + max_n: u16, + ) -> EvmResult<()> { + dispatch_admin( + handle, + pallet_admin_utils::Call::::sudo_trim_to_max_allowed_uids { + netuid: netuid.into(), + max_n, + }, + ) + } + + #[precompile::public("getSubnetMetadata(uint16)")] + #[precompile::view] + fn get_subnet_metadata( + handle: &mut impl PrecompileHandle, + netuid: u16, + ) -> EvmResult<(UnboundedBytes, H256, H256, u16, u8)> { + handle.record_db_reads::(5)?; + let netuid = NetUid::from(netuid); + let recycle_or_burn = match pallet_subtensor::RecycleOrBurn::::get(netuid) { + pallet_subtensor::RecycleOrBurnEnum::Burn => 0, + pallet_subtensor::RecycleOrBurnEnum::Recycle => 1, + }; + Ok(( + UnboundedBytes::from(pallet_subtensor::TokenSymbol::::get(netuid)), + account_to_h256(pallet_subtensor::SubnetOwner::::get(netuid)), + account_to_h256(pallet_subtensor::SubnetOwnerHotkey::::get(netuid)), + pallet_subtensor::Tempo::::get(netuid), + recycle_or_burn, + )) + } + + #[precompile::public("getSubnetCapacityConfig(uint16)")] + #[precompile::view] + fn get_subnet_capacity_config( + handle: &mut impl PrecompileHandle, + netuid: u16, + ) -> EvmResult<(u16, u16, u16, u16, u16, u16, u16, u16, bool, bool, u16, u8)> { + handle.record_db_reads::(12)?; + let netuid = NetUid::from(netuid); + Ok(( + pallet_subtensor::MinAllowedUids::::get(netuid), + pallet_subtensor::MaxAllowedUids::::get(netuid), + pallet_subtensor::MaxAllowedValidators::::get(netuid), + pallet_subtensor::AdjustmentInterval::::get(netuid), + pallet_subtensor::TargetRegistrationsPerInterval::::get(netuid), + pallet_subtensor::MinNonImmuneUids::::get(netuid), + pallet_subtensor::ImmuneOwnerUidsLimit::::get(netuid), + pallet_subtensor::BondsPenalty::::get(netuid), + pallet_subtensor::OwnerCutEnabled::::get(netuid), + pallet_subtensor::TransferToggle::::get(netuid), + pallet_subtensor::MaxRegistrationsPerBlock::::get(netuid), + pallet_subtensor::MechanismCountCurrent::::get(netuid).into(), + )) + } + + #[precompile::public("getMechanismEmissionSplit(uint16)")] + #[precompile::view] + fn get_mechanism_emission_split( + handle: &mut impl PrecompileHandle, + netuid: u16, + ) -> EvmResult<(bool, Vec)> { + handle.record_db_reads::(1)?; + Ok( + match pallet_subtensor::MechanismEmissionSplit::::get(NetUid::from(netuid)) { + Some(split) => (true, split), + None => (false, Vec::new()), + }, + ) + } + + #[precompile::public("getBurnConfig(uint16)")] + #[precompile::view] + fn get_burn_config(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult<(u16, u128)> { + handle.record_db_reads::(2)?; + let netuid = NetUid::from(netuid); + Ok(( + pallet_subtensor::BurnHalfLife::::get(netuid), + pallet_subtensor::BurnIncreaseMult::::get(netuid).to_bits(), + )) + } + + #[precompile::public("getGlobalNetworkLimits()")] + #[precompile::view] + fn get_global_network_limits( + handle: &mut impl PrecompileHandle, + ) -> EvmResult<(u16, u16, u16, u64, u16, u16, u64, u64, u64, u64, u64, u16)> { + handle.record_db_reads::(12)?; + Ok(( + pallet_subtensor::MinActivityCutoff::::get(), + pallet_subtensor::AdminFreezeWindow::::get(), + pallet_subtensor::OwnerHyperparamRateLimit::::get(), + pallet_subtensor::DissolveNetworkScheduleDuration::::get().unique_saturated_into(), + pallet_subtensor::SubnetLimit::::get(), + pallet_subtensor::TotalNetworks::::get(), + pallet_subtensor::NetworkImmunityPeriod::::get(), + pallet_subtensor::StartCallDelay::::get(), + pallet_subtensor::NetworkMinLockCost::::get().to_u64(), + pallet_subtensor::NetworkLastLockCost::::get().to_u64(), + pallet_subtensor::NetworkLockReductionInterval::::get(), + pallet_subtensor::SubnetOwnerCut::::get(), + )) + } + + #[precompile::public("getGlobalRateLimits()")] + #[precompile::view] + fn get_global_rate_limits( + handle: &mut impl PrecompileHandle, + ) -> EvmResult<(u64, u64, u64, u64, u64, u8)> { + handle.record_db_reads::(6)?; + Ok(( + pallet_subtensor::NetworkRateLimit::::get(), + pallet_subtensor::WeightsVersionKeyRateLimit::::get(), + pallet_subtensor::TxRateLimit::::get(), + pallet_subtensor::TxDelegateTakeRateLimit::::get(), + pallet_subtensor::TxChildkeyTakeRateLimit::::get(), + pallet_subtensor::MaxEpochsPerBlock::::get(), + )) + } + + #[precompile::public("getGlobalProtocolConfig()")] + #[precompile::view] + fn get_global_protocol_config( + handle: &mut impl PrecompileHandle, + ) -> EvmResult<(u8, u16, u64, u64)> { + handle.record_db_reads::(4)?; + Ok(( + pallet_subtensor::MaxMechanismCount::::get().into(), + pallet_subtensor::CommitRevealWeightsVersion::::get(), + pallet_subtensor::NetworkRegistrationStartBlock::::get(), + pallet_subtensor::TaoInRefundDeploymentBlock::::get(), + )) + } +} + +fn account_to_h256>(account: AccountId) -> H256 { + H256::from(account.into()) +} + +fn dispatch_admin( + handle: &mut impl PrecompileHandle, + call: pallet_admin_utils::Call, +) -> EvmResult<()> +where + R: frame_system::Config + + pallet_balances::Config + + pallet_evm::Config + + pallet_subtensor::Config + + pallet_admin_utils::Config + + pallet_shield::Config + + pallet_subtensor_proxy::Config + + Send + + Sync + + scale_info::TypeInfo, + R::AccountId: From<[u8; 32]> + Into<[u8; 32]>, + ::RuntimeOrigin: AsSystemOriginSigner + Clone, + ::RuntimeCall: From> + + GetDispatchInfo + + Dispatchable + + IsSubType> + + IsSubType> + + IsSubType> + + IsSubType>, + ::AddressMapping: AddressMapping, +{ + let caller = handle.caller_account_id::(); + handle.try_dispatch_runtime_call::(call, RawOrigin::Signed(caller)) +} + +/// Stable, append-only EVM codes for the runtime's detailed cleanup phases. +/// +/// These values intentionally do not use the Rust enum discriminant. Runtime +/// phases may be reordered internally without changing the Solidity contract. +fn dissolution_cleanup_phase_code(phase: &DissolveCleanupPhase) -> u8 { + match phase { + DissolveCleanupPhase::SubnetRootDividendsRootClaimable => 1, + DissolveCleanupPhase::SubnetRootDividendsRootClaimed => 2, + DissolveCleanupPhase::AlphaInOutStakesGetTotalAlphaValue => 3, + DissolveCleanupPhase::AlphaInOutStakesSettleStakes => 4, + DissolveCleanupPhase::AlphaInOutStakesAlpha => 5, + DissolveCleanupPhase::AlphaInOutStakesHotkeyTotals => 6, + DissolveCleanupPhase::AlphaInOutStakesLocks => 7, + DissolveCleanupPhase::AlphaInOutStakesDecayingLocks => 8, + DissolveCleanupPhase::AlphaInOutStakes => 9, + DissolveCleanupPhase::ProtocolLiquidity => 10, + DissolveCleanupPhase::PurgeNetuid => 11, + DissolveCleanupPhase::NetworkIsNetworkMember => 12, + DissolveCleanupPhase::NetworkParameters => 13, + DissolveCleanupPhase::NetworkMapParameters => 14, + DissolveCleanupPhase::NetworkUpdateWeightsOnRoot => 15, + DissolveCleanupPhase::NetworkChildkeyTake => 16, + DissolveCleanupPhase::NetworkChildkeys => 17, + DissolveCleanupPhase::NetworkParentkeys => 18, + DissolveCleanupPhase::NetworkLastHotkeyEmissionOnNetuid => 19, + DissolveCleanupPhase::NetworkTotalHotkeyAlphaLastEpoch => 20, + DissolveCleanupPhase::NetworkTransactionKeyLastBlock => 21, + DissolveCleanupPhase::NetworkLock => 22, + DissolveCleanupPhase::NetworkDecayingLock => 23, + } } #[cfg(test)] @@ -904,8 +1334,8 @@ mod tests { use super::*; use crate::PrecompileExt; use crate::mock::{ - AccountId, Runtime, addr_from_index, assert_static_call, mapped_account, new_test_ext, - precompiles, selector_u32, + AccountId, Runtime, addr_from_index, assert_static_call, execute_precompile, + mapped_account, new_test_ext, precompiles, selector_u32, }; use precompile_utils::solidity::encode_with_selector; use precompile_utils::testing::PrecompileTesterExt; @@ -1451,6 +1881,42 @@ mod tests { }); } + #[test] + fn subnet_precompile_gets_registered_subnet_counter() { + new_test_ext().execute_with(|| { + let caller = addr_from_index(0x5003); + let netuid = setup_owner_subnet(caller); + let precompiles = precompiles::>(); + let precompile_addr = addr_from_index(SubnetPrecompile::::INDEX); + + pallet_subtensor::RegisteredSubnetCounter::::insert(netuid, 7); + + assert_static_call( + &precompiles, + caller, + precompile_addr, + encode_with_selector( + selector_u32("getRegisteredSubnetCounter(uint16)"), + (TEST_NETUID_U16,), + ), + U256::from(7_u64), + ); + + pallet_subtensor::RegisteredSubnetCounter::::remove(netuid); + + assert_static_call( + &precompiles, + caller, + precompile_addr, + encode_with_selector( + selector_u32("getRegisteredSubnetCounter(uint16)"), + (TEST_NETUID_U16,), + ), + U256::zero(), + ); + }); + } + #[test] fn subnet_precompile_is_subnet_dissolving() { new_test_ext().execute_with(|| { @@ -1484,4 +1950,242 @@ mod tests { ); }); } + + #[test] + fn subnet_precompile_reports_stable_dissolution_cleanup_status() { + new_test_ext().execute_with(|| { + let caller = addr_from_index(0x5003); + let netuid = setup_owner_subnet(caller); + let precompiles = precompiles::>(); + let precompile_addr = addr_from_index(SubnetPrecompile::::INDEX); + let input = || { + encode_with_selector( + selector_u32("getSubnetDissolutionStatus(uint16)"), + (TEST_NETUID_U16,), + ) + }; + + precompiles + .prepare_test(caller, precompile_addr, input()) + .with_static_call(true) + .execute_returns((false, false, 0_u8)); + + pallet_subtensor::DissolveCleanupQueue::::set(vec![netuid]); + + precompiles + .prepare_test(caller, precompile_addr, input()) + .with_static_call(true) + .execute_returns((true, false, 0_u8)); + + let mut status = + pallet_subtensor::subnets::dissolution::DissolveCleanupStatus::new(netuid); + status.set_phase(DissolveCleanupPhase::AlphaInOutStakesSettleStakes); + pallet_subtensor::CurrentDissolveCleanupStatus::::set(Some(status)); + + precompiles + .prepare_test(caller, precompile_addr, input()) + .with_static_call(true) + .execute_returns((true, true, 4_u8)); + }); + } + + #[test] + fn dissolution_cleanup_phase_codes_are_stable() { + let phases = [ + (DissolveCleanupPhase::SubnetRootDividendsRootClaimable, 1), + (DissolveCleanupPhase::SubnetRootDividendsRootClaimed, 2), + (DissolveCleanupPhase::AlphaInOutStakesGetTotalAlphaValue, 3), + (DissolveCleanupPhase::AlphaInOutStakesSettleStakes, 4), + (DissolveCleanupPhase::AlphaInOutStakesAlpha, 5), + (DissolveCleanupPhase::AlphaInOutStakesHotkeyTotals, 6), + (DissolveCleanupPhase::AlphaInOutStakesLocks, 7), + (DissolveCleanupPhase::AlphaInOutStakesDecayingLocks, 8), + (DissolveCleanupPhase::AlphaInOutStakes, 9), + (DissolveCleanupPhase::ProtocolLiquidity, 10), + (DissolveCleanupPhase::PurgeNetuid, 11), + (DissolveCleanupPhase::NetworkIsNetworkMember, 12), + (DissolveCleanupPhase::NetworkParameters, 13), + (DissolveCleanupPhase::NetworkMapParameters, 14), + (DissolveCleanupPhase::NetworkUpdateWeightsOnRoot, 15), + (DissolveCleanupPhase::NetworkChildkeyTake, 16), + (DissolveCleanupPhase::NetworkChildkeys, 17), + (DissolveCleanupPhase::NetworkParentkeys, 18), + (DissolveCleanupPhase::NetworkLastHotkeyEmissionOnNetuid, 19), + (DissolveCleanupPhase::NetworkTotalHotkeyAlphaLastEpoch, 20), + (DissolveCleanupPhase::NetworkTransactionKeyLastBlock, 21), + (DissolveCleanupPhase::NetworkLock, 22), + (DissolveCleanupPhase::NetworkDecayingLock, 23), + ]; + + for (phase, expected) in phases { + assert_eq!(dissolution_cleanup_phase_code(&phase), expected); + } + } + + #[test] + fn subnet_state_views_return_grouped_runtime_configuration() { + new_test_ext().execute_with(|| { + let caller = addr_from_index(0x5020); + let netuid = setup_owner_subnet(caller); + let address = addr_from_index(SubnetPrecompile::::INDEX); + let precompiles = precompiles::>(); + + precompiles + .prepare_test( + caller, + address, + encode_with_selector( + selector_u32("getSubnetMetadata(uint16)"), + (TEST_NETUID_U16,), + ), + ) + .with_static_call(true) + .execute_returns(( + UnboundedBytes::from(pallet_subtensor::TokenSymbol::::get(netuid)), + account_to_h256(pallet_subtensor::SubnetOwner::::get(netuid)), + account_to_h256(pallet_subtensor::SubnetOwnerHotkey::::get(netuid)), + pallet_subtensor::Tempo::::get(netuid), + 0_u8, + )); + + precompiles + .prepare_test( + caller, + address, + encode_with_selector( + selector_u32("getSubnetCapacityConfig(uint16)"), + (TEST_NETUID_U16,), + ), + ) + .with_static_call(true) + .execute_returns(( + pallet_subtensor::MinAllowedUids::::get(netuid), + pallet_subtensor::MaxAllowedUids::::get(netuid), + pallet_subtensor::MaxAllowedValidators::::get(netuid), + pallet_subtensor::AdjustmentInterval::::get(netuid), + pallet_subtensor::TargetRegistrationsPerInterval::::get(netuid), + pallet_subtensor::MinNonImmuneUids::::get(netuid), + pallet_subtensor::ImmuneOwnerUidsLimit::::get(netuid), + pallet_subtensor::BondsPenalty::::get(netuid), + pallet_subtensor::OwnerCutEnabled::::get(netuid), + pallet_subtensor::TransferToggle::::get(netuid), + pallet_subtensor::MaxRegistrationsPerBlock::::get(netuid), + u8::from(pallet_subtensor::MechanismCountCurrent::::get( + netuid, + )), + )); + + precompiles + .prepare_test( + caller, + address, + encode_with_selector( + selector_u32("getMechanismEmissionSplit(uint16)"), + (TEST_NETUID_U16,), + ), + ) + .with_static_call(true) + .execute_returns((false, Vec::::new())); + + precompiles + .prepare_test( + caller, + address, + encode_with_selector(selector_u32("getBurnConfig(uint16)"), (TEST_NETUID_U16,)), + ) + .with_static_call(true) + .execute_returns(( + pallet_subtensor::BurnHalfLife::::get(netuid), + pallet_subtensor::BurnIncreaseMult::::get(netuid).to_bits(), + )); + + let dissolve_schedule_duration: u64 = + pallet_subtensor::DissolveNetworkScheduleDuration::::get() + .unique_saturated_into(); + precompiles + .prepare_test( + caller, + address, + selector_u32("getGlobalNetworkLimits()") + .to_be_bytes() + .to_vec(), + ) + .with_static_call(true) + .execute_returns(( + pallet_subtensor::MinActivityCutoff::::get(), + pallet_subtensor::AdminFreezeWindow::::get(), + pallet_subtensor::OwnerHyperparamRateLimit::::get(), + dissolve_schedule_duration, + pallet_subtensor::SubnetLimit::::get(), + pallet_subtensor::TotalNetworks::::get(), + pallet_subtensor::NetworkImmunityPeriod::::get(), + pallet_subtensor::StartCallDelay::::get(), + pallet_subtensor::NetworkMinLockCost::::get().to_u64(), + pallet_subtensor::NetworkLastLockCost::::get().to_u64(), + pallet_subtensor::NetworkLockReductionInterval::::get(), + pallet_subtensor::SubnetOwnerCut::::get(), + )); + + precompiles + .prepare_test( + caller, + address, + selector_u32("getGlobalRateLimits()").to_be_bytes().to_vec(), + ) + .with_static_call(true) + .execute_returns(( + pallet_subtensor::NetworkRateLimit::::get(), + pallet_subtensor::WeightsVersionKeyRateLimit::::get(), + pallet_subtensor::TxRateLimit::::get(), + pallet_subtensor::TxDelegateTakeRateLimit::::get(), + pallet_subtensor::TxChildkeyTakeRateLimit::::get(), + pallet_subtensor::MaxEpochsPerBlock::::get(), + )); + + precompiles + .prepare_test( + caller, + address, + selector_u32("getGlobalProtocolConfig()") + .to_be_bytes() + .to_vec(), + ) + .with_static_call(true) + .execute_returns(( + u8::from(pallet_subtensor::MaxMechanismCount::::get()), + pallet_subtensor::CommitRevealWeightsVersion::::get(), + pallet_subtensor::NetworkRegistrationStartBlock::::get(), + pallet_subtensor::TaoInRefundDeploymentBlock::::get(), + )); + }); + } + + #[test] + fn added_admin_call_preserves_subnet_owner_authorization() { + new_test_ext().execute_with(|| { + let owner = addr_from_index(0x5010); + let non_owner = addr_from_index(0x5011); + let netuid = setup_owner_subnet(owner); + let address = addr_from_index(SubnetPrecompile::::INDEX); + let input = encode_with_selector( + selector_u32("setBondsPenalty(uint16,uint16)"), + (TEST_NETUID_U16, 123u16), + ); + + precompiles::>() + .prepare_test(owner, address, input.clone()) + .execute_returns(()); + assert_eq!(pallet_subtensor::BondsPenalty::::get(netuid), 123); + + let rejected = execute_precompile( + &precompiles::>(), + address, + non_owner, + input, + U256::zero(), + ); + assert!(matches!(rejected, Some(Err(_)))); + assert_eq!(pallet_subtensor::BondsPenalty::::get(netuid), 123); + }); + } } diff --git a/precompiles/src/timestamp.rs b/precompiles/src/timestamp.rs new file mode 100644 index 0000000000..7878200ddc --- /dev/null +++ b/precompiles/src/timestamp.rs @@ -0,0 +1,107 @@ +use core::marker::PhantomData; + +use fp_evm::{ExitError, PrecompileFailure}; +use frame_support::pallet_prelude::{StorageValue, ValueQuery}; +use frame_support::traits::StorageInstance; +use pallet_evm::PrecompileHandle; +use precompile_utils::EvmResult; + +use crate::{PrecompileExt, PrecompileHandleExt}; + +struct DidUpdateStorage; + +impl StorageInstance for DidUpdateStorage { + const STORAGE_PREFIX: &'static str = "DidUpdate"; + + fn pallet_prefix() -> &'static str { + "Timestamp" + } +} + +type DidUpdate = StorageValue; + +pub struct TimestampPrecompile(PhantomData); + +impl PrecompileExt for TimestampPrecompile +where + R: frame_system::Config + pallet_evm::Config + pallet_timestamp::Config, + R::AccountId: From<[u8; 32]>, + R::Moment: TryInto, +{ + const INDEX: u64 = 2065; +} + +#[precompile_utils::precompile] +impl TimestampPrecompile +where + R: frame_system::Config + pallet_evm::Config + pallet_timestamp::Config, + R::AccountId: From<[u8; 32]>, + R::Moment: TryInto, +{ + #[precompile::public("getTimestamp()")] + #[precompile::view] + fn get_timestamp(handle: &mut impl PrecompileHandle) -> EvmResult { + handle.record_db_reads::(1)?; + pallet_timestamp::Pallet::::get() + .try_into() + .map_err(|_| conversion_error("timestamp moment")) + } + + #[precompile::public("wasUpdatedThisBlock()")] + #[precompile::view] + fn was_updated_this_block(handle: &mut impl PrecompileHandle) -> EvmResult { + handle.record_db_reads::(1)?; + Ok(DidUpdate::get()) + } +} + +fn conversion_error(field: &'static str) -> PrecompileFailure { + PrecompileFailure::Error { + exit_status: ExitError::Other(field.into()), + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::mock::{ + Runtime, Timestamp, addr_from_index, new_test_ext, precompiles, selector_u32, + }; + use precompile_utils::{ + prelude::RuntimeHelper, + solidity::{encode_return_value, encode_with_selector}, + testing::PrecompileTesterExt, + }; + + #[test] + fn address_selectors_and_values_are_stable() { + new_test_ext().execute_with(|| { + assert_eq!(TimestampPrecompile::::INDEX, 2065); + Timestamp::set_timestamp(1_234); + + let precompiles = precompiles::>(); + let caller = addr_from_index(1); + let address = addr_from_index(2065); + let read_cost = RuntimeHelper::::db_read_gas_cost(); + + precompiles + .prepare_test( + caller, + address, + encode_with_selector(selector_u32("getTimestamp()"), ()), + ) + .with_static_call(true) + .expect_cost(read_cost) + .execute_returns_raw(encode_return_value(1_234u64)); + precompiles + .prepare_test( + caller, + address, + encode_with_selector(selector_u32("wasUpdatedThisBlock()"), ()), + ) + .with_static_call(true) + .expect_cost(read_cost) + .execute_returns_raw(encode_return_value(true)); + }); + } +} diff --git a/precompiles/src/uid_lookup.rs b/precompiles/src/uid_lookup.rs index 9846eb0463..4291a8189c 100644 --- a/precompiles/src/uid_lookup.rs +++ b/precompiles/src/uid_lookup.rs @@ -5,6 +5,7 @@ use pallet_evm::PrecompileHandle; use precompile_utils::{EvmResult, prelude::Address}; use sp_runtime::traits::{Dispatchable, StaticLookup}; use sp_std::vec::Vec; +use subtensor_runtime_common::NetUid; use crate::{PrecompileExt, PrecompileHandleExt}; @@ -51,6 +52,22 @@ where limit, )) } + + #[precompile::public("getAssociatedEvmAddress(uint16,uint16)")] + #[precompile::view] + fn get_associated_evm_address( + handle: &mut impl PrecompileHandle, + netuid: u16, + uid: u16, + ) -> EvmResult<(bool, Address, u64)> { + handle.record_db_reads::(1)?; + Ok( + match pallet_subtensor::AssociatedEvmAddress::::get(NetUid::from(netuid), uid) { + Some((address, block)) => (true, Address(address), block), + None => (false, Address::default(), 0), + }, + ) + } } #[cfg(test)] @@ -102,6 +119,32 @@ mod tests { .with_static_call(true) .expect_cost(RuntimeHelper::::db_read_gas_cost()) .execute_returns_raw(encode_return_value(expected)); + + precompiles + .prepare_test( + caller, + precompile_addr, + encode_with_selector( + selector_u32("getAssociatedEvmAddress(uint16,uint16)"), + (TEST_NETUID_U16, uid), + ), + ) + .with_static_call(true) + .expect_cost(RuntimeHelper::::db_read_gas_cost()) + .execute_returns((true, Address(evm_address), block_associated)); + + precompiles + .prepare_test( + caller, + precompile_addr, + encode_with_selector( + selector_u32("getAssociatedEvmAddress(uint16,uint16)"), + (TEST_NETUID_U16, uid + 1), + ), + ) + .with_static_call(true) + .expect_cost(RuntimeHelper::::db_read_gas_cost()) + .execute_returns((false, Address::default(), 0_u64)); }); } } diff --git a/precompiles/src/voting_power.rs b/precompiles/src/voting_power.rs index 4cad7fcb89..4c08c3f7a3 100644 --- a/precompiles/src/voting_power.rs +++ b/precompiles/src/voting_power.rs @@ -1,8 +1,15 @@ use core::marker::PhantomData; use fp_evm::PrecompileHandle; +use frame_support::{ + dispatch::{DispatchInfo, GetDispatchInfo, PostDispatchInfo}, + traits::IsSubType, +}; +use frame_system::RawOrigin; +use pallet_evm::AddressMapping; use precompile_utils::EvmResult; use sp_core::{ByteArray, H256, U256}; +use sp_runtime::traits::{AsSystemOriginSigner, Dispatchable}; use subtensor_runtime_common::NetUid; use crate::PrecompileExt; @@ -16,8 +23,25 @@ pub struct VotingPowerPrecompile(PhantomData); impl PrecompileExt for VotingPowerPrecompile where - R: frame_system::Config + pallet_subtensor::Config + pallet_evm::Config, + R: frame_system::Config + + pallet_balances::Config + + pallet_subtensor::Config + + pallet_evm::Config + + pallet_shield::Config + + pallet_subtensor_proxy::Config + + Send + + Sync + + scale_info::TypeInfo, R::AccountId: From<[u8; 32]> + ByteArray, + ::RuntimeOrigin: AsSystemOriginSigner + Clone, + ::RuntimeCall: From> + + GetDispatchInfo + + Dispatchable + + IsSubType> + + IsSubType> + + IsSubType> + + IsSubType>, + ::AddressMapping: AddressMapping, { const INDEX: u64 = 2061; } @@ -25,8 +49,25 @@ where #[precompile_utils::precompile] impl VotingPowerPrecompile where - R: frame_system::Config + pallet_subtensor::Config + pallet_evm::Config, - R::AccountId: From<[u8; 32]>, + R: frame_system::Config + + pallet_balances::Config + + pallet_subtensor::Config + + pallet_evm::Config + + pallet_shield::Config + + pallet_subtensor_proxy::Config + + Send + + Sync + + scale_info::TypeInfo, + R::AccountId: From<[u8; 32]> + ByteArray, + ::RuntimeOrigin: AsSystemOriginSigner + Clone, + ::RuntimeCall: From> + + GetDispatchInfo + + Dispatchable + + IsSubType> + + IsSubType> + + IsSubType> + + IsSubType>, + ::AddressMapping: AddressMapping, { /// Get voting power for a hotkey on a subnet. /// @@ -131,14 +172,34 @@ where #[precompile::public("getTotalVotingPower(uint16)")] #[precompile::view] fn get_total_voting_power(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { - let mut total: u64 = 0; - for (_, voting_power) in - pallet_subtensor::VotingPower::::iter_prefix(NetUid::from(netuid)) - { - handle.record_db_reads::(1)?; - total = total.saturating_add(voting_power); - } - Ok(U256::from(total)) + handle.record_db_reads::(1)?; + Ok(U256::from(pallet_subtensor::TotalVotingPower::::get( + NetUid::from(netuid), + ))) + } + + #[precompile::public("enableVotingPowerTracking(uint16)")] + fn enable_voting_power_tracking( + handle: &mut impl PrecompileHandle, + netuid: u16, + ) -> EvmResult<()> { + let caller = handle.caller_account_id::(); + let call = pallet_subtensor::Call::::enable_voting_power_tracking { + netuid: NetUid::from(netuid), + }; + handle.try_dispatch_runtime_call::(call, RawOrigin::Signed(caller)) + } + + #[precompile::public("disableVotingPowerTracking(uint16)")] + fn disable_voting_power_tracking( + handle: &mut impl PrecompileHandle, + netuid: u16, + ) -> EvmResult<()> { + let caller = handle.caller_account_id::(); + let call = pallet_subtensor::Call::::disable_voting_power_tracking { + netuid: NetUid::from(netuid), + }; + handle.try_dispatch_runtime_call::(call, RawOrigin::Signed(caller)) } } @@ -246,6 +307,7 @@ mod tests { pallet_subtensor::VotingPowerTrackingEnabled::::insert(netuid, true); pallet_subtensor::VotingPower::::insert(netuid, &first_hotkey, 123_u64); pallet_subtensor::VotingPower::::insert(netuid, &second_hotkey, 456_u64); + pallet_subtensor::TotalVotingPower::::insert(netuid, 579_u64); assert_voting_power_call( caller, diff --git a/sdk/python/bittensor/evm/abi/alpha.json b/sdk/python/bittensor/evm/abi/alpha.json index 14d6eb66dc..aa37571e54 100644 --- a/sdk/python/bittensor/evm/abi/alpha.json +++ b/sdk/python/bittensor/evm/abi/alpha.json @@ -326,5 +326,327 @@ ], "stateMutability": "view", "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "burnHalfLife", + "type": "uint16" + } + ], + "name": "setBurnHalfLife", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint128", + "name": "rawMultiplier", + "type": "uint128" + } + ], + "name": "setBurnIncreaseMultiplier", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint8", + "name": "mode", + "type": "uint8" + } + ], + "name": "setRecycleOrBurn", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, +{ + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + } + ], + "name": "getEmissionAccounting", + "outputs": [ + { + "internalType": "uint64", + "name": "alphaDividends", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "rootAlphaDividends", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "lastHotkeyEmission", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "pendingServerEmission", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "pendingValidatorEmission", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "pendingRootAlphaDividends", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "pendingOwnerCut", + "type": "uint64" + }, + { + "internalType": "uint128", + "name": "minerBurned", + "type": "uint128" + }, + { + "internalType": "uint64", + "name": "raoRecycledForRegistration", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getEmissionGateConfig", + "outputs": [ + { + "internalType": "uint64", + "name": "blockEmission", + "type": "uint64" + }, + { + "internalType": "int128", + "name": "movingAlpha", + "type": "int128" + }, + { + "internalType": "bool", + "name": "netTaoFlowEnabled", + "type": "bool" + }, + { + "internalType": "int128", + "name": "taoFlowCutoff", + "type": "int128" + }, + { + "internalType": "uint128", + "name": "flowNormExponent", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "emissionBarQuantile", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "emissionGateExponent", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "emissionGateBar", + "type": "uint128" + }, + { + "internalType": "uint64", + "name": "flowEmaSmoothingFactor", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "getSubnetEconomicState", + "outputs": [ + { + "internalType": "bool", + "name": "emissionEnabled", + "type": "bool" + }, + { + "internalType": "uint128", + "name": "rootProportion", + "type": "uint128" + }, + { + "internalType": "uint64", + "name": "excessTao", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "rootSellTao", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "protocolAlpha", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "getSubnetFlowState", + "outputs": [ + { + "internalType": "int64", + "name": "taoFlow", + "type": "int64" + }, + { + "internalType": "bool", + "name": "hasTaoFlowEma", + "type": "bool" + }, + { + "internalType": "uint64", + "name": "taoFlowEmaBlock", + "type": "uint64" + }, + { + "internalType": "int128", + "name": "taoFlowEma", + "type": "int128" + }, + { + "internalType": "int64", + "name": "protocolFlow", + "type": "int64" + }, + { + "internalType": "bool", + "name": "hasProtocolFlowEma", + "type": "bool" + }, + { + "internalType": "uint64", + "name": "protocolFlowEmaBlock", + "type": "uint64" + }, + { + "internalType": "int128", + "name": "protocolFlowEma", + "type": "int128" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "getSwapState", + "outputs": [ + { + "internalType": "uint16", + "name": "feeRate", + "type": "uint16" + }, + { + "internalType": "bool", + "name": "initialized", + "type": "bool" + }, + { + "internalType": "uint64", + "name": "quoteWeight", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "taoReservoir", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "alphaReservoir", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "migrationName", + "type": "bytes" + } + ], + "name": "hasSwapMigrationRun", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" } ] \ No newline at end of file diff --git a/sdk/python/bittensor/evm/abi/balance.json b/sdk/python/bittensor/evm/abi/balance.json index 6f6e51c1af..52c19b6eb6 100644 --- a/sdk/python/bittensor/evm/abi/balance.json +++ b/sdk/python/bittensor/evm/abi/balance.json @@ -17,5 +17,49 @@ ], "stateMutability": "view", "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "keepAlive", + "type": "bool" + } + ], + "name": "burnBalance", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32[]", + "name": "accounts", + "type": "bytes32[]" + } + ], + "name": "upgradeAccounts", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, +{ + "inputs": [], + "name": "getTotalIssuance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" } -] +] \ No newline at end of file diff --git a/sdk/python/bittensor/evm/abi/balanceTransfer.json b/sdk/python/bittensor/evm/abi/balanceTransfer.json index 99913b9005..b7b5041ab8 100644 --- a/sdk/python/bittensor/evm/abi/balanceTransfer.json +++ b/sdk/python/bittensor/evm/abi/balanceTransfer.json @@ -11,5 +11,41 @@ "outputs": [], "stateMutability": "payable", "type": "function" - } + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "destination", + "type": "bytes32" + }, + { + "internalType": "bool", + "name": "keepAlive", + "type": "bool" + } + ], + "name": "transferAll", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "destination", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transferKeepAlive", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } ] \ No newline at end of file diff --git a/sdk/python/bittensor/evm/abi/crowdloan.json b/sdk/python/bittensor/evm/abi/crowdloan.json index c507afcca2..3601e1fb1e 100644 --- a/sdk/python/bittensor/evm/abi/crowdloan.json +++ b/sdk/python/bittensor/evm/abi/crowdloan.json @@ -255,5 +255,28 @@ "outputs": [], "stateMutability": "payable", "type": "function" - } + }, + { + "inputs": [ + { + "internalType": "uint32", + "name": "crowdloanId", + "type": "uint32" + }, + { + "internalType": "bool", + "name": "hasMaxContribution", + "type": "bool" + }, + { + "internalType": "uint64", + "name": "maxContribution", + "type": "uint64" + } + ], + "name": "setMaxContribution", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } ] \ No newline at end of file diff --git a/sdk/python/bittensor/evm/abi/drand.json b/sdk/python/bittensor/evm/abi/drand.json new file mode 100644 index 0000000000..691f53f05b --- /dev/null +++ b/sdk/python/bittensor/evm/abi/drand.json @@ -0,0 +1,129 @@ +[ + { + "inputs": [], + "name": "getBeaconConfig", + "outputs": [ + { + "internalType": "bytes", + "name": "publicKey", + "type": "bytes" + }, + { + "internalType": "uint32", + "name": "period", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "genesisTime", + "type": "uint32" + }, + { + "internalType": "bytes", + "name": "chainHash", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "groupHash", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "schemeId", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "beaconId", + "type": "bytes" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getNextUnsignedAt", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "round", + "type": "uint64" + } + ], + "name": "getPulse", + "outputs": [ + { + "internalType": "bool", + "name": "exists", + "type": "bool" + }, + { + "internalType": "uint64", + "name": "storedRound", + "type": "uint64" + }, + { + "internalType": "bytes", + "name": "randomness", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getStoredRoundRange", + "outputs": [ + { + "internalType": "uint64", + "name": "oldest", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "latest", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "key", + "type": "bytes" + } + ], + "name": "hasMigrationRun", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/sdk/python/bittensor/evm/abi/leasing.json b/sdk/python/bittensor/evm/abi/leasing.json index 88115ee29c..541ad0cedf 100644 --- a/sdk/python/bittensor/evm/abi/leasing.json +++ b/sdk/python/bittensor/evm/abi/leasing.json @@ -168,5 +168,50 @@ "outputs": [], "stateMutability": "payable", "type": "function" - } + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "startCall", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, +{ + "inputs": [ + { + "internalType": "uint32", + "name": "leaseId", + "type": "uint32" + } + ], + "name": "getAccumulatedLeaseDividends", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getNextLeaseId", + "outputs": [ + { + "internalType": "uint32", + "name": "", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + } ] \ No newline at end of file diff --git a/sdk/python/bittensor/evm/abi/neuron.json b/sdk/python/bittensor/evm/abi/neuron.json index 44d47449eb..a8639ab2ce 100644 --- a/sdk/python/bittensor/evm/abi/neuron.json +++ b/sdk/python/bittensor/evm/abi/neuron.json @@ -252,5 +252,1209 @@ "outputs": [], "stateMutability": "payable", "type": "function" - } -] + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "newColdkeyHash", + "type": "bytes32" + } + ], + "name": "announceColdkeySwap", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "address", + "name": "evmKey", + "type": "address" + }, + { + "internalType": "uint64", + "name": "blockNumber", + "type": "uint64" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "name": "associateEvmKey", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16[]", + "name": "netuids", + "type": "uint16[]" + }, + { + "internalType": "bytes32[]", + "name": "commitHashes", + "type": "bytes32[]" + } + ], + "name": "batchCommitWeights", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint16[][]", + "name": "uids", + "type": "uint16[][]" + }, + { + "internalType": "uint16[][]", + "name": "values", + "type": "uint16[][]" + }, + { + "internalType": "uint16[][]", + "name": "salts", + "type": "uint16[][]" + }, + { + "internalType": "uint64[]", + "name": "versionKeys", + "type": "uint64[]" + } + ], + "name": "batchRevealWeights", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16[]", + "name": "netuids", + "type": "uint16[]" + }, + { + "internalType": "uint16[][]", + "name": "dests", + "type": "uint16[][]" + }, + { + "internalType": "uint16[][]", + "name": "values", + "type": "uint16[][]" + }, + { + "internalType": "uint64[]", + "name": "versionKeys", + "type": "uint64[]" + } + ], + "name": "batchSetWeights", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "clearColdkeySwapAnnouncement", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint8", + "name": "mecid", + "type": "uint8" + }, + { + "internalType": "bytes", + "name": "commit", + "type": "bytes" + }, + { + "internalType": "uint64", + "name": "revealRound", + "type": "uint64" + } + ], + "name": "commitCrv3MechanismWeights", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint8", + "name": "mecid", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "commitHash", + "type": "bytes32" + } + ], + "name": "commitMechanismWeights", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint8", + "name": "mecid", + "type": "uint8" + }, + { + "internalType": "bytes", + "name": "commit", + "type": "bytes" + }, + { + "internalType": "uint64", + "name": "revealRound", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "commitRevealVersion", + "type": "uint16" + } + ], + "name": "commitTimelockedMechanismWeights", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "bytes", + "name": "commit", + "type": "bytes" + }, + { + "internalType": "uint64", + "name": "revealRound", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "commitRevealVersion", + "type": "uint16" + } + ], + "name": "commitTimelockedWeights", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "disputeColdkeySwap", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "newColdkey", + "type": "bytes32" + } + ], + "name": "executeAnnouncedColdkeySwap", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint64", + "name": "blockNumber", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "nonce", + "type": "uint64" + }, + { + "internalType": "bytes", + "name": "work", + "type": "bytes" + }, + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "coldkey", + "type": "bytes32" + } + ], + "name": "register", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint8", + "name": "mecid", + "type": "uint8" + }, + { + "internalType": "uint16[]", + "name": "uids", + "type": "uint16[]" + }, + { + "internalType": "uint16[]", + "name": "values", + "type": "uint16[]" + }, + { + "internalType": "uint16[]", + "name": "salt", + "type": "uint16[]" + }, + { + "internalType": "uint64", + "name": "versionKey", + "type": "uint64" + } + ], + "name": "revealMechanismWeights", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + } + ], + "name": "rootRegister", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint64[]", + "name": "proportions", + "type": "uint64[]" + }, + { + "internalType": "bytes32[]", + "name": "children", + "type": "bytes32[]" + } + ], + "name": "setChildren", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "string", + "name": "url", + "type": "string" + }, + { + "internalType": "string", + "name": "githubRepo", + "type": "string" + }, + { + "internalType": "string", + "name": "image", + "type": "string" + }, + { + "internalType": "string", + "name": "discord", + "type": "string" + }, + { + "internalType": "string", + "name": "description", + "type": "string" + }, + { + "internalType": "string", + "name": "additional", + "type": "string" + } + ], + "name": "setIdentity", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint8", + "name": "mecid", + "type": "uint8" + }, + { + "internalType": "uint16[]", + "name": "dests", + "type": "uint16[]" + }, + { + "internalType": "uint16[]", + "name": "weights", + "type": "uint16[]" + }, + { + "internalType": "uint64", + "name": "versionKey", + "type": "uint64" + } + ], + "name": "setMechanismWeights", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "newHotkey", + "type": "bytes32" + }, + { + "internalType": "bool", + "name": "hasNetuid", + "type": "bool" + }, + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "swapHotkey", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "newHotkey", + "type": "bytes32" + }, + { + "internalType": "bool", + "name": "hasNetuid", + "type": "bool" + }, + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "bool", + "name": "keepStake", + "type": "bool" + } + ], + "name": "swapHotkeyV2", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + } + ], + "name": "tryAssociateHotkey", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, +{ + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "uid", + "type": "uint16" + } + ], + "name": "getBlockAtRegistration", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "uid", + "type": "uint16" + } + ], + "name": "getBonds", + "outputs": [ + { + "components": [ + { + "internalType": "uint16", + "name": "uid", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "value", + "type": "uint16" + } + ], + "internalType": "struct INeuron.WeightPair[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "coldkey", + "type": "bytes32" + } + ], + "name": "getChainIdentity", + "outputs": [ + { + "internalType": "bool", + "name": "exists", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "name", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "url", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "githubRepo", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "image", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "discord", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "description", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "additional", + "type": "bytes" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint8", + "name": "version", + "type": "uint8" + }, + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint64", + "name": "epoch", + "type": "uint64" + }, + { + "internalType": "uint32", + "name": "index", + "type": "uint32" + } + ], + "name": "getLegacyTimelockedWeightCommit", + "outputs": [ + { + "internalType": "bool", + "name": "exists", + "type": "bool" + }, + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + }, + { + "internalType": "uint64", + "name": "blockNumber", + "type": "uint64" + }, + { + "internalType": "bytes32", + "name": "ciphertextHash", + "type": "bytes32" + }, + { + "internalType": "uint32", + "name": "ciphertextLength", + "type": "uint32" + }, + { + "internalType": "uint64", + "name": "revealRound", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint8", + "name": "version", + "type": "uint8" + }, + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint64", + "name": "epoch", + "type": "uint64" + } + ], + "name": "getLegacyTimelockedWeightCommitCount", + "outputs": [ + { + "internalType": "uint32", + "name": "", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + } + ], + "name": "getLegacyTransactionRateBlocks", + "outputs": [ + { + "internalType": "uint64", + "name": "lastTransactionBlock", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "lastChildkeyTakeBlock", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "lastDelegateTakeBlock", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "getLoadedEmission", + "outputs": [ + { + "internalType": "bool", + "name": "exists", + "type": "bool" + }, + { + "components": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + }, + { + "internalType": "uint64", + "name": "serverEmission", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "validatorEmission", + "type": "uint64" + } + ], + "internalType": "struct INeuron.LoadedEmission[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + } + ], + "name": "getNeuronCertificate", + "outputs": [ + { + "internalType": "bool", + "name": "exists", + "type": "bool" + }, + { + "internalType": "uint8", + "name": "algorithm", + "type": "uint8" + }, + { + "internalType": "bytes", + "name": "publicKey", + "type": "bytes" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + } + ], + "name": "getPrometheus", + "outputs": [ + { + "internalType": "bool", + "name": "exists", + "type": "bool" + }, + { + "internalType": "uint64", + "name": "blockNumber", + "type": "uint64" + }, + { + "internalType": "uint32", + "name": "version", + "type": "uint32" + }, + { + "internalType": "uint128", + "name": "ip", + "type": "uint128" + }, + { + "internalType": "uint16", + "name": "port", + "type": "uint16" + }, + { + "internalType": "uint8", + "name": "ipType", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "getSubnetIdentity", + "outputs": [ + { + "internalType": "bool", + "name": "exists", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "subnetName", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "githubRepo", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "subnetContact", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "subnetUrl", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "discord", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "description", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "logoUrl", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "additional", + "type": "bytes" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint64", + "name": "epoch", + "type": "uint64" + }, + { + "internalType": "uint32", + "name": "index", + "type": "uint32" + } + ], + "name": "getTimelockedWeightCommit", + "outputs": [ + { + "internalType": "bool", + "name": "exists", + "type": "bool" + }, + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + }, + { + "internalType": "uint64", + "name": "blockNumber", + "type": "uint64" + }, + { + "internalType": "bytes32", + "name": "ciphertextHash", + "type": "bytes32" + }, + { + "internalType": "uint32", + "name": "ciphertextLength", + "type": "uint32" + }, + { + "internalType": "uint64", + "name": "revealRound", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint64", + "name": "epoch", + "type": "uint64" + } + ], + "name": "getTimelockedWeightCommitCount", + "outputs": [ + { + "internalType": "uint32", + "name": "", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "transactionKey", + "type": "uint16" + } + ], + "name": "getTransactionKeyLastBlock", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + } + ], + "name": "getUid", + "outputs": [ + { + "internalType": "bool", + "name": "exists", + "type": "bool" + }, + { + "internalType": "uint16", + "name": "uid", + "type": "uint16" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + }, + { + "internalType": "uint32", + "name": "index", + "type": "uint32" + } + ], + "name": "getWeightCommit", + "outputs": [ + { + "internalType": "bool", + "name": "exists", + "type": "bool" + }, + { + "internalType": "bytes32", + "name": "hash", + "type": "bytes32" + }, + { + "internalType": "uint64", + "name": "epoch", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "blockNumber", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + } + ], + "name": "getWeightCommitCount", + "outputs": [ + { + "internalType": "uint32", + "name": "", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "uid", + "type": "uint16" + } + ], + "name": "getWeights", + "outputs": [ + { + "components": [ + { + "internalType": "uint16", + "name": "uid", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "value", + "type": "uint16" + } + ], + "internalType": "struct INeuron.WeightPair[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "isNetworkMember", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + } +] \ No newline at end of file diff --git a/sdk/python/bittensor/evm/abi/proxy.json b/sdk/python/bittensor/evm/abi/proxy.json index 2f751002b7..cb7644a637 100644 --- a/sdk/python/bittensor/evm/abi/proxy.json +++ b/sdk/python/bittensor/evm/abi/proxy.json @@ -173,5 +173,200 @@ } ], "stateMutability": "view" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "real", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "callHash", + "type": "bytes32" + } + ], + "name": "announce", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "delegate", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "callHash", + "type": "bytes32" + } + ], + "name": "rejectAnnouncement", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "real", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "callHash", + "type": "bytes32" + } + ], + "name": "removeAnnouncement", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "delegate", + "type": "bytes32" + }, + { + "internalType": "bool", + "name": "paysFee", + "type": "bool" + } + ], + "name": "setRealPaysFee", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, +{ + "inputs": [ + { + "internalType": "bytes32", + "name": "account", + "type": "bytes32" + } + ], + "name": "getAnnouncements", + "outputs": [ + { + "components": [ + { + "internalType": "bytes32", + "name": "real", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "callHash", + "type": "bytes32" + }, + { + "internalType": "uint64", + "name": "height", + "type": "uint64" + } + ], + "internalType": "struct IProxy.AnnouncementInfo[]", + "name": "", + "type": "tuple[]" + }, + { + "internalType": "uint256", + "name": "deposit", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "account", + "type": "bytes32" + } + ], + "name": "getLastCallResult", + "outputs": [ + { + "internalType": "bool", + "name": "exists", + "type": "bool" + }, + { + "internalType": "bool", + "name": "succeeded", + "type": "bool" + }, + { + "internalType": "uint8", + "name": "errorKind", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "palletIndex", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "errorData", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "account", + "type": "bytes32" + } + ], + "name": "getProxyDeposit", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "real", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "delegate", + "type": "bytes32" + } + ], + "name": "isRealPaysFee", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" } -] +] \ No newline at end of file diff --git a/sdk/python/bittensor/evm/abi/registry.json b/sdk/python/bittensor/evm/abi/registry.json new file mode 100644 index 0000000000..b15d2cabd9 --- /dev/null +++ b/sdk/python/bittensor/evm/abi/registry.json @@ -0,0 +1,53 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "precompile", + "type": "address" + }, + { + "internalType": "bytes4", + "name": "selector", + "type": "bytes4" + } + ], + "name": "getPrecompileStatus", + "outputs": [ + { + "components": [ + { + "internalType": "bool", + "name": "isDeprecated", + "type": "bool" + }, + { + "internalType": "bool", + "name": "isDisabled", + "type": "bool" + }, + { + "internalType": "address", + "name": "newPrecompile", + "type": "address" + }, + { + "internalType": "bytes4", + "name": "newSelector", + "type": "bytes4" + }, + { + "internalType": "string", + "name": "message", + "type": "string" + } + ], + "internalType": "struct IPrecompileRegistry.PrecompileStatus", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/sdk/python/bittensor/evm/abi/runtimeConfiguration.json b/sdk/python/bittensor/evm/abi/runtimeConfiguration.json new file mode 100644 index 0000000000..257bd47289 --- /dev/null +++ b/sdk/python/bittensor/evm/abi/runtimeConfiguration.json @@ -0,0 +1,787 @@ +[ + { + "inputs": [ + + ], + "name": "getEvmChainId", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + + ], + "name": "getTransactionRateLimit", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + + ], + "name": "getSubtensorEconomicConstants", + "outputs": [ + { + "internalType": "uint256", + "name": "initialIssuance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "initialRaoRecycledForRegistration", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "initialBurn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "initialMinBurn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "initialMaxBurn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "initialMinStake", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "initialMinTransfer", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minBurnUpperBound", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxBurnLowerBound", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "initialNetworkMinLockCost", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "keySwapCost", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "keySwapOnSubnetCost", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minBalanceToPerformColdkeySwap", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + + ], + "name": "getSubtensorSubnetConstants", + "outputs": [ + { + "internalType": "uint16", + "name": "initialTempo", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "minTempo", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "maxTempo", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "initialMinAllowedUids", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "initialMaxAllowedUids", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "initialMaxAllowedValidators", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "initialImmunityPeriod", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "initialActivityCutoff", + "type": "uint16" + }, + { + "internalType": "uint32", + "name": "minActivityCutoffFactorMilli", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "maxActivityCutoffFactorMilli", + "type": "uint32" + }, + { + "internalType": "uint8", + "name": "maxImmuneUidsPercentage", + "type": "uint8" + }, + { + "internalType": "uint16", + "name": "initialSubnetOwnerCut", + "type": "uint16" + }, + { + "internalType": "uint8", + "name": "initialMaxEpochsPerBlock", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + + ], + "name": "getSubtensorConsensusConstants", + "outputs": [ + { + "internalType": "uint16", + "name": "initialMinAllowedWeights", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "initialEmissionValue", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "initialRho", + "type": "uint16" + }, + { + "internalType": "int16", + "name": "initialAlphaSigmoidSteepness", + "type": "int16" + }, + { + "internalType": "uint16", + "name": "initialKappa", + "type": "uint16" + }, + { + "internalType": "uint64", + "name": "initialBondsMovingAverage", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "initialBondsPenalty", + "type": "uint16" + }, + { + "internalType": "bool", + "name": "initialBondsResetOn", + "type": "bool" + }, + { + "internalType": "uint64", + "name": "initialValidatorPruneLen", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "initialScalingLawPower", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "initialPruningScore", + "type": "uint16" + }, + { + "internalType": "uint64", + "name": "initialWeightsVersionKey", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "initialTaoWeight", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + + ], + "name": "getSubtensorRegistrationConstants", + "outputs": [ + { + "internalType": "uint64", + "name": "initialDifficulty", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "initialMinDifficulty", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "initialMaxDifficulty", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "initialAdjustmentInterval", + "type": "uint16" + }, + { + "internalType": "uint64", + "name": "initialAdjustmentAlpha", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "initialMaxRegistrationsPerBlock", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "initialTargetRegistrationsPerInterval", + "type": "uint16" + }, + { + "internalType": "uint64", + "name": "initialNetworkRateLimit", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "initialNetworkImmunityPeriod", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "initialNetworkLockReductionInterval", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "initialEmaPriceHalvingPeriod", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + + ], + "name": "getSubtensorDelegationConstants", + "outputs": [ + { + "internalType": "uint16", + "name": "initialDefaultDelegateTake", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "initialMinDelegateTake", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "initialDefaultChildKeyTake", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "initialMinChildKeyTake", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "initialMaxChildKeyTake", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "alphaHigh", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "alphaLow", + "type": "uint16" + }, + { + "internalType": "bool", + "name": "liquidAlphaOn", + "type": "bool" + }, + { + "internalType": "bool", + "name": "yuma3On", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + + ], + "name": "getSubtensorRateLimitConstants", + "outputs": [ + { + "internalType": "uint64", + "name": "initialServingRateLimit", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "initialTxRateLimit", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "initialTxDelegateTakeRateLimit", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "initialTxChildKeyTakeRateLimit", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "evmKeyAssociateRateLimit", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "initialColdkeySwapAnnouncementDelay", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "initialColdkeySwapReannouncementDelay", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "initialDissolveNetworkScheduleDuration", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "initialStartCallDelay", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "hotkeySwapOnSubnetInterval", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "leaseDividendsDistributionInterval", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + + ], + "name": "getSubtensorProtocolConstants", + "outputs": [ + { + "internalType": "uint32", + "name": "maxCrv3CommitSizeBytes", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "maxAssociatedUidsPerEvmAddress", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "maxColdkeyCollateralHotkeys", + "type": "uint32" + }, + { + "internalType": "uint128", + "name": "accountFlagsAcceptLockedAlpha", + "type": "uint128" + }, + { + "internalType": "uint64", + "name": "minCommitRevealPeriods", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "maxCommitRevealPeriods", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "globalMaxSubnetCount", + "type": "uint16" + }, + { + "internalType": "uint8", + "name": "maxMechanismCountPerSubnet", + "type": "uint8" + }, + { + "internalType": "uint64", + "name": "votingPowerDisableGracePeriodBlocks", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "maxVotingPowerEmaAlpha", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "emissionBarUpdateInterval", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "stakingLockDuration", + "type": "uint64" + }, + { + "internalType": "uint256", + "name": "lockStateZeroThreshold", + "type": "uint256" + }, + { + "internalType": "uint32", + "name": "initialActivityCutoffFactorMilli", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "maxTaoIssuance", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + + ], + "name": "getSubtensorSystemAccounts", + "outputs": [ + { + "internalType": "bytes32", + "name": "subtensorPalletAccount", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "burnAccount", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + + ], + "name": "getBalancesConstants", + "outputs": [ + { + "internalType": "uint256", + "name": "existentialDeposit", + "type": "uint256" + }, + { + "internalType": "uint32", + "name": "maxLocks", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "maxReserves", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "maxFreezes", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + + ], + "name": "getProxyConstants", + "outputs": [ + { + "internalType": "uint256", + "name": "proxyDepositBase", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "proxyDepositFactor", + "type": "uint256" + }, + { + "internalType": "uint32", + "name": "maxProxies", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "maxPending", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "announcementDepositBase", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "announcementDepositFactor", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + + ], + "name": "getSchedulerConstants", + "outputs": [ + { + "internalType": "uint64", + "name": "maximumWeightRefTime", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "maximumWeightProofSize", + "type": "uint64" + }, + { + "internalType": "uint32", + "name": "maxScheduledPerBlock", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + + ], + "name": "getDrandConstants", + "outputs": [ + { + "internalType": "string", + "name": "quicknetChainHash", + "type": "string" + }, + { + "internalType": "uint64", + "name": "unsignedPriority", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "httpFetchTimeout", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "maxPulsesToFetch", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "maxKeptPulses", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "maxRemovedPulses", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + + ], + "name": "getCrowdloanConstants", + "outputs": [ + { + "internalType": "uint256", + "name": "minimumDeposit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "absoluteMinimumContribution", + "type": "uint256" + }, + { + "internalType": "uint64", + "name": "minimumBlockDuration", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "maximumBlockDuration", + "type": "uint64" + }, + { + "internalType": "uint32", + "name": "refundContributorsLimit", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "maxContributors", + "type": "uint32" + }, + { + "internalType": "bytes32", + "name": "palletAccount", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + + ], + "name": "getSwapConstants", + "outputs": [ + { + "internalType": "uint16", + "name": "maxFeeRate", + "type": "uint16" + }, + { + "internalType": "uint256", + "name": "minimumLiquidity", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minimumReserve", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "protocolAccount", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + + ], + "name": "getTimestampConstants", + "outputs": [ + { + "internalType": "uint64", + "name": "minimumPeriod", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + + ], + "name": "getAdminConstants", + "outputs": [ + { + "internalType": "uint32", + "name": "maxAuthorities", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/sdk/python/bittensor/evm/abi/scheduler.json b/sdk/python/bittensor/evm/abi/scheduler.json new file mode 100644 index 0000000000..8bf7ecbe5c --- /dev/null +++ b/sdk/python/bittensor/evm/abi/scheduler.json @@ -0,0 +1,183 @@ +[ + { + "inputs": [], + "name": "getIncompleteSince", + "outputs": [ + { + "internalType": "bool", + "name": "exists", + "type": "bool" + }, + { + "internalType": "uint64", + "name": "blockNumber", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "when", + "type": "uint64" + }, + { + "internalType": "uint32", + "name": "index", + "type": "uint32" + } + ], + "name": "getRetry", + "outputs": [ + { + "internalType": "bool", + "name": "exists", + "type": "bool" + }, + { + "internalType": "uint8", + "name": "totalRetries", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "remaining", + "type": "uint8" + }, + { + "internalType": "uint64", + "name": "period", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "when", + "type": "uint64" + }, + { + "internalType": "uint32", + "name": "index", + "type": "uint32" + } + ], + "name": "getScheduledCall", + "outputs": [ + { + "components": [ + { + "internalType": "bool", + "name": "exists", + "type": "bool" + }, + { + "internalType": "bool", + "name": "hasTaskId", + "type": "bool" + }, + { + "internalType": "bytes32", + "name": "taskId", + "type": "bytes32" + }, + { + "internalType": "uint8", + "name": "priority", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "callHash", + "type": "bytes32" + }, + { + "internalType": "bool", + "name": "hasCallLength", + "type": "bool" + }, + { + "internalType": "uint32", + "name": "callLength", + "type": "uint32" + }, + { + "internalType": "bool", + "name": "isPeriodic", + "type": "bool" + }, + { + "internalType": "uint64", + "name": "period", + "type": "uint64" + }, + { + "internalType": "uint32", + "name": "remaining", + "type": "uint32" + } + ], + "internalType": "struct IScheduler.ScheduledCall", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "when", + "type": "uint64" + } + ], + "name": "getScheduledCallCount", + "outputs": [ + { + "internalType": "uint32", + "name": "", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "taskId", + "type": "bytes32" + } + ], + "name": "getTaskAddress", + "outputs": [ + { + "internalType": "bool", + "name": "exists", + "type": "bool" + }, + { + "internalType": "uint64", + "name": "when", + "type": "uint64" + }, + { + "internalType": "uint32", + "name": "index", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/sdk/python/bittensor/evm/abi/stakingV2.json b/sdk/python/bittensor/evm/abi/stakingV2.json index 64edc7ae3d..61a7310a82 100644 --- a/sdk/python/bittensor/evm/abi/stakingV2.json +++ b/sdk/python/bittensor/evm/abi/stakingV2.json @@ -809,5 +809,1064 @@ "outputs": [], "stateMutability": "payable", "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + }, + { + "internalType": "uint64", + "name": "alpha", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "limitPrice", + "type": "uint64" + } + ], + "name": "addCollateral", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint64", + "name": "amount", + "type": "uint64" + }, + { + "internalType": "bool", + "name": "hasLimit", + "type": "bool" + }, + { + "internalType": "uint64", + "name": "limit", + "type": "uint64" + } + ], + "name": "addStakeBurn", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16[]", + "name": "subnets", + "type": "uint16[]" + } + ], + "name": "claimRoot", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "take", + "type": "uint16" + } + ], + "name": "decreaseTake", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "take", + "type": "uint16" + } + ], + "name": "increaseTake", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + }, + { + "internalType": "uint64", + "name": "amount", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "recycleAlpha", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + }, + { + "internalType": "bool", + "name": "enabled", + "type": "bool" + } + ], + "name": "setAutoParentDelegationEnabled", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "take", + "type": "uint16" + } + ], + "name": "setChildkeyTake", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + } + ], + "name": "setColdkeyAutoStakeHotkey", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint128", + "name": "rawRatio", + "type": "uint128" + } + ], + "name": "setCollateralDrainRatio", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "lockShare", + "type": "uint16" + } + ], + "name": "setCollateralLockShare", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "take", + "type": "uint16" + } + ], + "name": "setMinChildkeyTakePerSubnet", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + }, + { + "internalType": "uint64", + "name": "minLocked", + "type": "uint64" + } + ], + "name": "setMinCollateral", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint64", + "name": "threshold", + "type": "uint64" + } + ], + "name": "setRootClaimThreshold", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint8", + "name": "claimType", + "type": "uint8" + }, + { + "internalType": "uint16[]", + "name": "subnets", + "type": "uint16[]" + } + ], + "name": "setRootClaimType", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "originNetuid", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "destinationNetuid", + "type": "uint16" + }, + { + "internalType": "uint64", + "name": "alphaAmount", + "type": "uint64" + } + ], + "name": "swapStake", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "originNetuid", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "destinationNetuid", + "type": "uint16" + }, + { + "internalType": "uint64", + "name": "alphaAmount", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "limitPrice", + "type": "uint64" + }, + { + "internalType": "bool", + "name": "allowPartial", + "type": "bool" + } + ], + "name": "swapStakeLimit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "destinationColdkey", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "originHotkey", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "destinationHotkey", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "originNetuid", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "destinationNetuid", + "type": "uint16" + }, + { + "internalType": "uint64", + "name": "alphaAmount", + "type": "uint64" + } + ], + "name": "transferStakeAndHotkey", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + } + ], + "name": "unstakeAll", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + } + ], + "name": "unstakeAllAlpha", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, +{ + "inputs": [ + { + "internalType": "bytes32", + "name": "coldkey", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "getAutoStakeDestination", + "outputs": [ + { + "internalType": "bool", + "name": "exists", + "type": "bool" + }, + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "getAutoStakeDestinationColdkeys", + "outputs": [ + { + "internalType": "bytes32[]", + "name": "", + "type": "bytes32[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "parent", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "getChildKeys", + "outputs": [ + { + "components": [ + { + "internalType": "uint64", + "name": "proportion", + "type": "uint64" + }, + { + "internalType": "bytes32", + "name": "account", + "type": "bytes32" + } + ], + "internalType": "struct IStaking.KeyLink[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "getChildkeyTake", + "outputs": [ + { + "internalType": "uint16", + "name": "", + "type": "uint16" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "bytes32", + "name": "coldkey", + "type": "bytes32" + } + ], + "name": "getColdkeyCollateral", + "outputs": [ + { + "internalType": "uint64", + "name": "locked", + "type": "uint64" + }, + { + "internalType": "bytes32[]", + "name": "hotkeys", + "type": "bytes32[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "coldkey", + "type": "bytes32" + } + ], + "name": "getColdkeyRoot", + "outputs": [ + { + "internalType": "bool", + "name": "exists", + "type": "bool" + }, + { + "internalType": "bytes32", + "name": "root", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "coldkey", + "type": "bytes32" + } + ], + "name": "getColdkeySuccessor", + "outputs": [ + { + "internalType": "bool", + "name": "exists", + "type": "bool" + }, + { + "internalType": "bytes32", + "name": "successor", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getColdkeySwapDelays", + "outputs": [ + { + "internalType": "uint64", + "name": "announcementDelay", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "reannouncementDelay", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "coldkey", + "type": "bytes32" + } + ], + "name": "getColdkeySwapStatus", + "outputs": [ + { + "internalType": "bool", + "name": "hasAnnouncement", + "type": "bool" + }, + { + "internalType": "uint64", + "name": "announcementBlock", + "type": "uint64" + }, + { + "internalType": "bytes32", + "name": "callHash", + "type": "bytes32" + }, + { + "internalType": "bool", + "name": "hasDispute", + "type": "bool" + }, + { + "internalType": "uint64", + "name": "disputeBlock", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "getCollateralConfig", + "outputs": [ + { + "internalType": "uint16", + "name": "lockShare", + "type": "uint16" + }, + { + "internalType": "uint128", + "name": "drainRatio", + "type": "uint128" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + } + ], + "name": "getDelegate", + "outputs": [ + { + "internalType": "bool", + "name": "exists", + "type": "bool" + }, + { + "internalType": "uint16", + "name": "take", + "type": "uint16" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + } + ], + "name": "getHotkeyOwner", + "outputs": [ + { + "internalType": "bool", + "name": "exists", + "type": "bool" + }, + { + "internalType": "bytes32", + "name": "owner", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "getHotkeyRoot", + "outputs": [ + { + "internalType": "bool", + "name": "exists", + "type": "bool" + }, + { + "internalType": "bytes32", + "name": "root", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "getHotkeySuccessor", + "outputs": [ + { + "internalType": "bool", + "name": "exists", + "type": "bool" + }, + { + "internalType": "bytes32", + "name": "successor", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "coldkey", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "getLastHotkeySwapOnSubnet", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "getMinChildkeyTakePerSubnet", + "outputs": [ + { + "internalType": "uint16", + "name": "", + "type": "uint16" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "coldkey", + "type": "bytes32" + } + ], + "name": "getMinerCollateral", + "outputs": [ + { + "internalType": "bool", + "name": "exists", + "type": "bool" + }, + { + "internalType": "uint64", + "name": "locked", + "type": "uint64" + }, + { + "internalType": "uint128", + "name": "drainRatio", + "type": "uint128" + }, + { + "internalType": "uint64", + "name": "minLocked", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "earned", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "coldkey", + "type": "bytes32" + } + ], + "name": "getOwnedHotkeys", + "outputs": [ + { + "internalType": "bytes32[]", + "name": "", + "type": "bytes32[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "child", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "getParentKeys", + "outputs": [ + { + "components": [ + { + "internalType": "uint64", + "name": "proportion", + "type": "uint64" + }, + { + "internalType": "bytes32", + "name": "account", + "type": "bytes32" + } + ], + "internalType": "struct IStaking.KeyLink[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getPendingChildKeyCooldown", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "parent", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "getPendingChildKeys", + "outputs": [ + { + "components": [ + { + "internalType": "uint64", + "name": "proportion", + "type": "uint64" + }, + { + "internalType": "bytes32", + "name": "account", + "type": "bytes32" + } + ], + "internalType": "struct IStaking.KeyLink[]", + "name": "children", + "type": "tuple[]" + }, + { + "internalType": "uint64", + "name": "cooldownBlock", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getStakeAccounting", + "outputs": [ + { + "internalType": "uint64", + "name": "totalIssuance", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "totalStake", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getTakeLimits", + "outputs": [ + { + "internalType": "uint16", + "name": "minDelegateTake", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "maxDelegateTake", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "minChildkeyTake", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "maxChildkeyTake", + "type": "uint16" + } + ], + "stateMutability": "view", + "type": "function" } -] +] \ No newline at end of file diff --git a/sdk/python/bittensor/evm/abi/subnet.json b/sdk/python/bittensor/evm/abi/subnet.json index e765c37c4a..a21dbd1c2e 100644 --- a/sdk/python/bittensor/evm/abi/subnet.json +++ b/sdk/python/bittensor/evm/abi/subnet.json @@ -1159,5 +1159,617 @@ "outputs": [], "stateMutability": "payable", "type": "function" - } + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "string", + "name": "subnetName", + "type": "string" + }, + { + "internalType": "string", + "name": "githubRepo", + "type": "string" + }, + { + "internalType": "string", + "name": "subnetContact", + "type": "string" + }, + { + "internalType": "string", + "name": "subnetUrl", + "type": "string" + }, + { + "internalType": "string", + "name": "discord", + "type": "string" + }, + { + "internalType": "string", + "name": "description", + "type": "string" + }, + { + "internalType": "string", + "name": "logoUrl", + "type": "string" + }, + { + "internalType": "string", + "name": "additional", + "type": "string" + } + ], + "name": "setSubnetIdentity", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "string", + "name": "symbol", + "type": "string" + } + ], + "name": "updateSubnetSymbol", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "triggerEpoch", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "bondsPenalty", + "type": "uint16" + } + ], + "name": "setBondsPenalty", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "maxAllowedUids", + "type": "uint16" + } + ], + "name": "setMaxAllowedUids", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint64", + "name": "maxBurn", + "type": "uint64" + } + ], + "name": "setMaxBurnV2", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint8", + "name": "mechanismCount", + "type": "uint8" + } + ], + "name": "setMechanismCount", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "bool", + "name": "hasSplit", + "type": "bool" + }, + { + "internalType": "uint16[]", + "name": "split", + "type": "uint16[]" + } + ], + "name": "setMechanismEmissionSplit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint64", + "name": "minBurn", + "type": "uint64" + } + ], + "name": "setMinBurnV2", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "bool", + "name": "enabled", + "type": "bool" + } + ], + "name": "setOwnerCutEnabled", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "immuneNeurons", + "type": "uint16" + } + ], + "name": "setOwnerImmuneNeuronLimit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "tempo", + "type": "uint16" + } + ], + "name": "setTempo", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "maxUids", + "type": "uint16" + } + ], + "name": "trimToMaxAllowedUids", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, +{ + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "getBurnConfig", + "outputs": [ + { + "internalType": "uint16", + "name": "halfLife", + "type": "uint16" + }, + { + "internalType": "uint128", + "name": "increaseMultiplier", + "type": "uint128" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getGlobalNetworkLimits", + "outputs": [ + { + "internalType": "uint16", + "name": "minActivityCutoff", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "adminFreezeWindow", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "ownerHyperparamRateLimit", + "type": "uint16" + }, + { + "internalType": "uint64", + "name": "dissolveScheduleDuration", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "subnetLimit", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "totalNetworks", + "type": "uint16" + }, + { + "internalType": "uint64", + "name": "networkImmunityPeriod", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "startCallDelay", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "minNetworkLockCost", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "lastNetworkLockCost", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "networkLockReductionInterval", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "subnetOwnerCut", + "type": "uint16" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getGlobalProtocolConfig", + "outputs": [ + { + "internalType": "uint8", + "name": "maxMechanismCount", + "type": "uint8" + }, + { + "internalType": "uint16", + "name": "commitRevealWeightsVersion", + "type": "uint16" + }, + { + "internalType": "uint64", + "name": "networkRegistrationStartBlock", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "taoInRefundDeploymentBlock", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getGlobalRateLimits", + "outputs": [ + { + "internalType": "uint64", + "name": "networkRateLimit", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "weightsVersionKeyRateLimit", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "transactionRateLimit", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "delegateTakeRateLimit", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "childkeyTakeRateLimit", + "type": "uint64" + }, + { + "internalType": "uint8", + "name": "maxEpochsPerBlock", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "getMechanismEmissionSplit", + "outputs": [ + { + "internalType": "bool", + "name": "exists", + "type": "bool" + }, + { + "internalType": "uint16[]", + "name": "split", + "type": "uint16[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "getSubnetCapacityConfig", + "outputs": [ + { + "internalType": "uint16", + "name": "minAllowedUids", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "maxAllowedUids", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "maxAllowedValidators", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "adjustmentInterval", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "targetRegistrationsPerInterval", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "minNonImmuneUids", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "immuneOwnerUidsLimit", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "bondsPenalty", + "type": "uint16" + }, + { + "internalType": "bool", + "name": "ownerCutEnabled", + "type": "bool" + }, + { + "internalType": "bool", + "name": "transfersEnabled", + "type": "bool" + }, + { + "internalType": "uint16", + "name": "maxRegistrationsPerBlock", + "type": "uint16" + }, + { + "internalType": "uint8", + "name": "mechanismCount", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "getSubnetMetadata", + "outputs": [ + { + "internalType": "bytes", + "name": "tokenSymbol", + "type": "bytes" + }, + { + "internalType": "bytes32", + "name": "owner", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "ownerHotkey", + "type": "bytes32" + }, + { + "internalType": "uint16", + "name": "tempo", + "type": "uint16" + }, + { + "internalType": "uint8", + "name": "recycleOrBurn", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "getRegisteredSubnetCounter", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "getSubnetDissolutionStatus", + "outputs": [ + { + "internalType": "bool", + "name": "isDissolving", + "type": "bool" + }, + { + "internalType": "bool", + "name": "cleanupInProgress", + "type": "bool" + }, + { + "internalType": "uint8", + "name": "cleanupPhase", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + } ] diff --git a/sdk/python/bittensor/evm/abi/timestamp.json b/sdk/python/bittensor/evm/abi/timestamp.json new file mode 100644 index 0000000000..78d2ee9784 --- /dev/null +++ b/sdk/python/bittensor/evm/abi/timestamp.json @@ -0,0 +1,28 @@ +[ + { + "inputs": [], + "name": "getTimestamp", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "wasUpdatedThisBlock", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/sdk/python/bittensor/evm/abi/uidLookup.json b/sdk/python/bittensor/evm/abi/uidLookup.json index 558358dcaa..dfe4f8ebfc 100644 --- a/sdk/python/bittensor/evm/abi/uidLookup.json +++ b/sdk/python/bittensor/evm/abi/uidLookup.json @@ -39,5 +39,39 @@ ], "stateMutability": "view", "type": "function" - } + }, +{ + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "uid", + "type": "uint16" + } + ], + "name": "getAssociatedEvmAddress", + "outputs": [ + { + "internalType": "bool", + "name": "exists", + "type": "bool" + }, + { + "internalType": "address", + "name": "evmAddress", + "type": "address" + }, + { + "internalType": "uint64", + "name": "blockAssociated", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + } ] \ No newline at end of file diff --git a/sdk/python/bittensor/evm/abi/votingPower.json b/sdk/python/bittensor/evm/abi/votingPower.json index a2694e9a99..d825bcdfde 100644 --- a/sdk/python/bittensor/evm/abi/votingPower.json +++ b/sdk/python/bittensor/evm/abi/votingPower.json @@ -98,5 +98,31 @@ ], "stateMutability": "view", "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "disableVotingPowerTracking", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "enableVotingPowerTracking", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" } -] +] \ No newline at end of file diff --git a/sdk/python/bittensor/evm/precompiles.py b/sdk/python/bittensor/evm/precompiles.py index 5d627cfd79..d1061f63ad 100644 --- a/sdk/python/bittensor/evm/precompiles.py +++ b/sdk/python/bittensor/evm/precompiles.py @@ -66,7 +66,7 @@ def _load_abi(filename: str) -> list[dict]: "balance-transfer", 2048, "balanceTransfer.json", - "Send TAO from an EVM account to any ss58 address (payable transfer(bytes32 pubkey)).", + "Transfer TAO from an EVM account to an ss58 account.", ), Precompile( "staking", @@ -109,7 +109,7 @@ def _load_abi(filename: str) -> list[dict]: "alpha", 2056, "alpha.json", - "Subnet alpha token info: prices, pool reserves, and alpha amounts.", + "Subnet Alpha pools, prices, issuance, emissions, and owner configuration.", ), Precompile( "crowdloan", @@ -127,7 +127,7 @@ def _load_abi(filename: str) -> list[dict]: "proxy", 2059, "proxy.json", - "Add/remove proxy delegations from EVM.", + "Manage proxy delegations and delayed proxy announcements from EVM.", ), Precompile( "address-mapping", @@ -145,7 +145,37 @@ def _load_abi(filename: str) -> list[dict]: "balance", 2062, "balance.json", - "Read native free TAO balance for any ss58 coldkey (getFreeBalance(bytes32) -> rao).", + "Read free TAO balances and dispatch signed Balances operations.", + ), + Precompile( + "scheduler", + 2063, + "scheduler.json", + "Read stable metadata for scheduled runtime calls.", + ), + Precompile( + "drand", + 2064, + "drand.json", + "Read stored Drand beacon configuration and pulse data.", + ), + Precompile( + "timestamp", + 2065, + "timestamp.json", + "Read the runtime timestamp and its per-block update state.", + ), + Precompile( + "runtime-configuration", + 2066, + "runtimeConfiguration.json", + "Read stable global runtime configuration.", + ), + Precompile( + "precompile-registry", + 2067, + "registry.json", + "Inspect precompile lifecycle and operational availability.", ), Precompile( "ed25519-verify", @@ -297,6 +327,16 @@ def coerce_argument(abi_type: str, raw: Any) -> Any: nobody should have to run the conversion by hand. """ text = str(raw).strip() + if abi_type.endswith("[]"): + inner = abi_type[:-2] + parts = ( + raw + if isinstance(raw, list) + else json.loads(text) + if text.startswith("[") + else text.split(",") + ) + return [coerce_argument(inner, part) for part in parts] if abi_type.startswith(("uint", "int")): return int(text, 16 if text.startswith("0x") else 10) if abi_type == "bool": @@ -308,10 +348,6 @@ def coerce_argument(abi_type: str, raw: Any) -> Any: # ss58 -> 32-byte public key, the shape hotkey/coldkey params take return bytes.fromhex(ss58_to_pubkey(text)[2:]) return bytes.fromhex(text.removeprefix("0x")) - if abi_type.endswith("[]"): - inner = abi_type[:-2] - parts = json.loads(text) if text.startswith("[") else text.split(",") - return [coerce_argument(inner, part) for part in parts] return text diff --git a/sdk/python/tests/unit/test_evm.py b/sdk/python/tests/unit/test_evm.py index 931404dd5d..b3a85d0145 100644 --- a/sdk/python/tests/unit/test_evm.py +++ b/sdk/python/tests/unit/test_evm.py @@ -86,6 +86,16 @@ def test_balance_transfer_encode(self): data = precompiles.encode_call(fn_abi, [addresses.ss58_to_pubkey(ALICE)]) assert data.startswith("0x") + def test_new_bounded_array_calls_encode(self): + claim_root = precompiles.get_precompile("staking-v2").function("claimRoot") + assert precompiles.encode_call(claim_root, ["[1, 2]"]).startswith("0x") + + batch_commit = precompiles.get_precompile("neuron").function("batchCommitWeights") + assert precompiles.encode_call( + batch_commit, + ["[1, 2]", ["0x" + "11" * 32, "0x" + "22" * 32]], + ).startswith("0x") + # The vendored ABIs in bittensor/evm/abi must stay in sync with the canonical # .abi artifacts in precompiles/src/solidity (see the bittensor.evm.precompiles