code/pallets/subtensor/src/macros/dispatches.rs
#![allow(clippy::crate_in_macro_def)]
use frame_support::pallet_macros::pallet_section;
/// A [`pallet_section`] that defines the errors for a pallet.
/// This can later be imported into the pallet using [`import_section`].
#[pallet_section]
mod dispatches {
use frame_support::pallet_prelude::DispatchResultWithPostInfo;
use frame_support::traits::schedule::v3::Anon as ScheduleAnon;
use frame_system::pallet_prelude::BlockNumberFor;
use sp_core::ecdsa::Signature;
use sp_runtime::{Percent, Saturating, traits::Hash};
use crate::MAX_CRV3_COMMIT_SIZE_BYTES;
use crate::MAX_NUM_ROOT_CLAIMS;
use crate::MAX_ROOT_CLAIM_THRESHOLD;
use crate::MAX_SUBNET_CLAIMS;
/// Dispatchable functions allow users to interact with the pallet and invoke state changes.
/// These functions materialize as "extrinsics", which are often compared to transactions.
/// Dispatchable functions must be annotated with a weight and must return a DispatchResult.
#[pallet::call]
impl<T: Config> Pallet<T> {
#![deny(clippy::expect_used)]
/// Sets the caller weights for the incentive mechanism. The call can be
/// made from the hotkey account so is potentially insecure, however, the damage
/// of changing weights is minimal if caught early. This function includes all the
/// checks that the passed weights meet the requirements. Stored weights are u16s
/// max-upscaled by the pallet, so the largest non-zero supplied weight is stored
/// as `u16::MAX`. The weights determine how inflation propagates outward
/// from this peer.
///
/// # Note
/// Input weights are relative. They do not need to sum to a particular
/// value before submission.
///
/// # Arguments
/// * `origin`: The caller, a hotkey who wishes to set their weights.
///
/// * `netuid`: The network uid we are setting these weights on.
///
/// * `dests`: The edge endpoint for the weight, i.e. j for w_ij.
///
/// * `weights`: Relative u16-encoded weights, max-upscaled by the pallet before storage.
///
/// * `version_key`: The network version key to check if the validator is up to date.
///
/// # Events
/// * `WeightsSet`: On successfully setting the weights on chain.
///
/// # Errors
/// * `MechanismDoesNotExist`: Attempting to set weights on a non-existent network.
///
/// * `NotRegistered`: Attempting to set weights from a non registered account.
///
/// * `WeightVecNotEqualSize`: Attempting to set weights with uids not of same length.
///
/// * `DuplicateUids`: Attempting to set weights with duplicate uids.
///
/// * `UidsLengthExceedUidsInSubNet`: Attempting to set weights above the max allowed uids.
///
/// * `UidVecContainInvalidOne`: Attempting to set weights with invalid uids.
///
/// * `WeightVecLengthIsLow`: Attempting to set weights with fewer weights than min.
///
/// * `MaxWeightExceeded`: Attempting to set weights with max value exceeding limit.
#[pallet::call_index(0)]
#[pallet::weight((<T as crate::pallet::Config>::WeightInfo::set_weights(), DispatchClass::Normal, Pays::No))]
pub fn set_weights(
origin: OriginFor<T>,
netuid: NetUid,
dests: Vec<u16>,
weights: Vec<u16>,
version_key: u64,
) -> DispatchResult {
if Self::get_commit_reveal_weights_enabled(netuid) {
Err(Error::<T>::CommitRevealEnabled.into())
} else {
Self::do_set_weights(origin, netuid, dests, weights, version_key)
}
}
/// Sets the caller weights for the incentive mechanism for mechanisms. The call
/// can be made from the hotkey account so is potentially insecure, however, the damage
/// of changing weights is minimal if caught early. This function includes all the
/// checks that the passed weights meet the requirements. Stored weights are u16s
/// max-upscaled by the pallet, so the largest non-zero supplied weight is stored
/// as `u16::MAX`. The weights determine how inflation propagates outward
/// from this peer.
///
/// # Note
/// Input weights are relative. They do not need to sum to a particular
/// value before submission.
///
/// # Arguments
/// * `origin`: The caller, a hotkey who wishes to set their weights.
///
/// * `netuid`: The network uid we are setting these weights on.
///
/// * `mecid`: The u8 mechnism identifier.
///
/// * `dests`: The edge endpoint for the weight, i.e. j for w_ij.
///
/// * `weights`: Relative u16-encoded weights, max-upscaled by the pallet before storage.
///
/// * `version_key`: The network version key to check if the validator is up to date.
///
/// # Events
/// * `WeightsSet`: On successfully setting the weights on chain.
///
/// # Errors
/// * `MechanismDoesNotExist`: Attempting to set weights on a non-existent network.
///
/// * `NotRegistered`: Attempting to set weights from a non registered account.
///
/// * `WeightVecNotEqualSize`: Attempting to set weights with uids not of same length.
///
/// * `DuplicateUids`: Attempting to set weights with duplicate uids.
///
/// * `UidsLengthExceedUidsInSubNet`: Attempting to set weights above the max allowed uids.
///
/// * `UidVecContainInvalidOne`: Attempting to set weights with invalid uids.
///
/// * `WeightVecLengthIsLow`: Attempting to set weights with fewer weights than min.
///
/// * `MaxWeightExceeded`: Attempting to set weights with max value exceeding limit.
#[pallet::call_index(119)]
#[pallet::weight((Weight::from_parts(15_540_000_000, 0)
.saturating_add(T::DbWeight::get().reads(4111))
.saturating_add(T::DbWeight::get().writes(2)), DispatchClass::Normal, Pays::No))]
pub fn set_mechanism_weights(
origin: OriginFor<T>,
netuid: NetUid,
mecid: MechId,
dests: Vec<u16>,
weights: Vec<u16>,
version_key: u64,
) -> DispatchResult {
if Self::get_commit_reveal_weights_enabled(netuid) {
Err(Error::<T>::CommitRevealEnabled.into())
} else {
Self::do_set_mechanism_weights(origin, netuid, mecid, dests, weights, version_key)
}
}
/// Allows a hotkey to set weights for multiple netuids as a batch.
///
/// # Arguments
/// * `origin`: The caller, a hotkey who wishes to set their weights.
///
/// * `netuids`: The network uids we are setting these weights on.
///
/// * `weights`: The weights to set for each network. [(uid, weight), ...].
///
/// * `version_keys`: The network version keys to check if the validator is up to date.
///
/// # Events
/// * `WeightsSet`: On successfully setting the weights on chain.
/// * `BatchWeightsCompleted`: On success of the batch.
/// * `BatchCompletedWithErrors`: On failure of any of the weights in the batch.
/// * `BatchWeightItemFailed`: On failure for each failed item in the batch.
///
#[pallet::call_index(80)]
#[pallet::weight((<T as crate::pallet::Config>::WeightInfo::batch_set_weights(), DispatchClass::Normal, Pays::No))]
pub fn batch_set_weights(
origin: OriginFor<T>,
netuids: Vec<Compact<NetUid>>,
weights: Vec<Vec<(Compact<u16>, Compact<u16>)>>,
version_keys: Vec<Compact<u64>>,
) -> DispatchResult {
Self::do_batch_set_weights(origin, netuids, weights, version_keys)
}
/// Used to commit a hash of your weight values to later be revealed.
///
/// # Arguments
/// * `origin`: The signature of the committing hotkey.
///
/// * `netuid`: The u16 network identifier.
///
/// * `commit_hash`: The hash representing the committed weights.
///
/// # Errors
/// * `CommitRevealDisabled`: Attempting to commit when the commit-reveal mechanism is disabled.
///
/// * `TooManyUnrevealedCommits`: Attempting to commit when the user has more than the allowed limit of unrevealed commits.
///
#[pallet::call_index(96)]
#[pallet::weight((<T as crate::pallet::Config>::WeightInfo::commit_weights(), DispatchClass::Normal, Pays::No))]
pub fn commit_weights(
origin: OriginFor<T>,
netuid: NetUid,
commit_hash: H256,
) -> DispatchResult {
Self::do_commit_weights(origin, netuid, commit_hash)
}
/// Used to commit a hash of your weight values to later be revealed for mechanisms.
///
/// # Arguments
/// * `origin`: The signature of the committing hotkey.
///
/// * `netuid`: The u16 network identifier.
///
/// * `mecid`: The u8 mechanism identifier.
///
/// * `commit_hash`: The hash representing the committed weights.
///
/// # Errors
/// * `CommitRevealDisabled`: Attempting to commit when the commit-reveal mechanism is disabled.
///
/// * `TooManyUnrevealedCommits`: Attempting to commit when the user has more than the allowed limit of unrevealed commits.
///
#[pallet::call_index(115)]
#[pallet::weight((Weight::from_parts(55_130_000, 0)
.saturating_add(T::DbWeight::get().reads(7))
.saturating_add(T::DbWeight::get().writes(2)), DispatchClass::Normal, Pays::No))]
pub fn commit_mechanism_weights(
origin: OriginFor<T>,
netuid: NetUid,
mecid: MechId,
commit_hash: H256,
) -> DispatchResult {
Self::do_commit_mechanism_weights(origin, netuid, mecid, commit_hash)
}
/// Allows a hotkey to commit weight hashes for multiple netuids as a batch.
///
/// # Arguments
/// * `origin`: The caller, a hotkey who wishes to set their weights.
///
/// * `netuids`: The network uids we are setting these weights on.
///
/// * `commit_hashes`: The commit hashes to commit.
///
/// # Events
/// * `WeightsSet`: On successfully setting the weights on chain.
/// * `BatchWeightsCompleted`: On success of the batch.
/// * `BatchCompletedWithErrors`: On failure of any of the weights in the batch.
/// * `BatchWeightItemFailed`: On failure for each failed item in the batch.
///
#[pallet::call_index(100)]
#[pallet::weight((<T as crate::pallet::Config>::WeightInfo::batch_commit_weights(), DispatchClass::Normal, Pays::No))]
pub fn batch_commit_weights(
origin: OriginFor<T>,
netuids: Vec<Compact<NetUid>>,
commit_hashes: Vec<H256>,
) -> DispatchResult {
Self::do_batch_commit_weights(origin, netuids, commit_hashes)
}
/// Used to reveal the weights for a previously committed hash.
///
/// # Arguments
/// * `origin`: The signature of the revealing hotkey.
///
/// * `netuid`: The u16 network identifier.
///
/// * `uids`: The uids for the weights being revealed.
///
/// * `values`: The values of the weights being revealed.
///
/// * `salt`: The salt used to generate the commit hash.
///
/// * `version_key`: The network version key.
///
/// # Errors
/// * `CommitRevealDisabled`: Attempting to reveal weights when the commit-reveal mechanism is disabled.
///
/// * `NoWeightsCommitFound`: Attempting to reveal weights without an existing commit.
///
/// * `ExpiredWeightCommit`: Attempting to reveal a weight commit that has expired.
///
/// * `RevealTooEarly`: Attempting to reveal weights outside the valid reveal period.
///
/// * `InvalidRevealCommitHashNotMatch`: The revealed hash does not match any committed hash.
///
#[pallet::call_index(97)]
#[pallet::weight((<T as crate::pallet::Config>::WeightInfo::reveal_weights(), DispatchClass::Normal, Pays::No))]
pub fn reveal_weights(
origin: OriginFor<T>,
netuid: NetUid,
uids: Vec<u16>,
values: Vec<u16>,
salt: Vec<u16>,
version_key: u64,
) -> DispatchResult {
Self::do_reveal_weights(origin, netuid, uids, values, salt, version_key)
}
/// Used to reveal the weights for a previously committed hash for mechanisms.
///
/// # Arguments
/// * `origin`: The signature of the revealing hotkey.
///
/// * `netuid`: The u16 network identifier.
///
/// * `mecid`: The u8 mechanism identifier.
///
/// * `uids`: The uids for the weights being revealed.
///
/// * `values`: The values of the weights being revealed.
///
/// * `salt`: The salt used to generate the commit hash.
///
/// * `version_key`: The network version key.
///
/// # Errors
/// * `CommitRevealDisabled`: Attempting to reveal weights when the commit-reveal mechanism is disabled.
///
/// * `NoWeightsCommitFound`: Attempting to reveal weights without an existing commit.
///
/// * `ExpiredWeightCommit`: Attempting to reveal a weight commit that has expired.
///
/// * `RevealTooEarly`: Attempting to reveal weights outside the valid reveal period.
///
/// * `InvalidRevealCommitHashNotMatch`: The revealed hash does not match any committed hash.
///
#[pallet::call_index(116)]
#[pallet::weight((Weight::from_parts(122_000_000, 0)
.saturating_add(T::DbWeight::get().reads(16))
.saturating_add(T::DbWeight::get().writes(2)), DispatchClass::Normal, Pays::No))]
pub fn reveal_mechanism_weights(
origin: OriginFor<T>,
netuid: NetUid,
mecid: MechId,
uids: Vec<u16>,
values: Vec<u16>,
salt: Vec<u16>,
version_key: u64,
) -> DispatchResult {
Self::do_reveal_mechanism_weights(
origin,
netuid,
mecid,
uids,
values,
salt,
version_key,
)
}
// Used to commit encrypted commit-reveal v3 weight values to later be revealed.
//
// # Arguments
// * `origin`: The committing hotkey.
//
// * `netuid`: The u16 network identifier.
//
// * `commit`: The encrypted compressed commit.
// The steps for this are:
// 1. Instantiate [`WeightsTlockPayload`]
// 2. Serialize it using the `parity_scale_codec::Encode` trait
// 3. Encrypt it following the steps (here)[https://github.com/ideal-lab5/tle/blob/f8e6019f0fb02c380ebfa6b30efb61786dede07b/timelock/src/tlock.rs#L283-L336]
// to produce a [`TLECiphertext<TinyBLS381>`] type.
// 4. Serialize and compress using the `ark-serialize` `CanonicalSerialize` trait.
//
// * `reveal_round`: The drand reveal round which will be avaliable during epoch `n+1` from the current
// epoch.
//
// # Errors
// * `CommitRevealV3Disabled`: Attempting to commit when the commit-reveal mechanism is disabled.
//
// * `TooManyUnrevealedCommits`: Attempting to commit when the user has more than the allowed limit of unrevealed commits.
//
// #[pallet::call_index(99)]
// #[pallet::weight((Weight::from_parts(77_750_000, 0)
// .saturating_add(T::DbWeight::get().reads(9_u64))
// .saturating_add(T::DbWeight::get().writes(2)), DispatchClass::Normal, Pays::No))]
// pub fn commit_crv3_weights(
// origin: T::RuntimeOrigin,
// netuid: NetUid,
// commit: BoundedVec<u8, ConstU32<MAX_CRV3_COMMIT_SIZE_BYTES>>,
// reveal_round: u64,
// ) -> DispatchResult {
// Self::do_commit_timelocked_weights(origin, netuid, commit, reveal_round, 4)
// }
/// Used to commit encrypted commit-reveal v3 weight values to later be revealed for mechanisms.
///
/// # Arguments
/// * `origin`: The committing hotkey.
///
/// * `netuid`: The u16 network identifier.
///
/// * `mecid`: The u8 mechanism identifier.
///
/// * `commit`: The encrypted compressed commit.
/// The steps for this are:
/// 1. Instantiate [`WeightsTlockPayload`]
/// 2. Serialize it using the `parity_scale_codec::Encode` trait
/// 3. Encrypt it following the steps (here)[https://github.com/ideal-lab5/tle/blob/f8e6019f0fb02c380ebfa6b30efb61786dede07b/timelock/src/tlock.rs#L283-L336]
/// to produce a [`TLECiphertext<TinyBLS381>`] type.
/// 4. Serialize and compress using the `ark-serialize` `CanonicalSerialize` trait.
///
/// * `reveal_round`: The drand reveal round which will be avaliable during epoch `n+1` from the current
/// epoch.
///
/// # Errors
/// * `CommitRevealV3Disabled`: Attempting to commit when the commit-reveal mechanism is disabled.
///
/// * `TooManyUnrevealedCommits`: Attempting to commit when the user has more than the allowed limit of unrevealed commits.
///
#[pallet::call_index(117)]
#[pallet::weight((Weight::from_parts(77_750_000, 0)
.saturating_add(T::DbWeight::get().reads(7_u64))
.saturating_add(T::DbWeight::get().writes(2)), DispatchClass::Normal, Pays::No))]
pub fn commit_crv3_mechanism_weights(
origin: OriginFor<T>,
netuid: NetUid,
mecid: MechId,
commit: BoundedVec<u8, ConstU32<MAX_CRV3_COMMIT_SIZE_BYTES>>,
reveal_round: u64,
) -> DispatchResult {
Self::do_commit_timelocked_mechanism_weights(
origin,
netuid,
mecid,
commit,
reveal_round,
4,
)
}
/// The implementation for batch revealing committed weights.
///
/// # Arguments
/// * `origin`: The signature of the revealing hotkey.
///
/// * `netuid`: The u16 network identifier.
///
/// * `uids_list`: A list of uids for each set of weights being revealed.
///
/// * `values_list`: A list of values for each set of weights being revealed.
///
/// * `salts_list`: A list of salts used to generate the commit hashes.
///
/// * `version_keys`: A list of network version keys.
///
/// # Errors
/// * `CommitRevealDisabled`: Attempting to reveal weights when the commit-reveal mechanism is disabled.
///
/// * `NoWeightsCommitFound`: Attempting to reveal weights without an existing commit.
///
/// * `ExpiredWeightCommit`: Attempting to reveal a weight commit that has expired.
///
/// * `RevealTooEarly`: Attempting to reveal weights outside the valid reveal period.
///
/// * `InvalidRevealCommitHashNotMatch`: The revealed hash does not match any committed hash.
///
/// * `InvalidInputLengths`: The input vectors are of mismatched lengths.
#[pallet::call_index(98)]
#[pallet::weight((<T as crate::pallet::Config>::WeightInfo::batch_reveal_weights(), DispatchClass::Normal, Pays::No))]
pub fn batch_reveal_weights(
origin: OriginFor<T>,
netuid: NetUid,
uids_list: Vec<Vec<u16>>,
values_list: Vec<Vec<u16>>,
salts_list: Vec<Vec<u16>>,
version_keys: Vec<u64>,
) -> DispatchResult {
Self::do_batch_reveal_weights(
origin,
netuid,
uids_list,
values_list,
salts_list,
version_keys,
)
}
/// Allows delegates to decrease its take value.
///
/// # Arguments
/// * `origin`: The signature of the caller's coldkey.
///
/// * `hotkey`: The hotkey we are delegating (must be owned by the coldkey).
///
/// * `netuid`: Subnet ID to decrease take for.
///
/// * `take`: The new stake proportion that this hotkey takes from delegations.
/// The new value can be between 0 and 11_796 parts and should be strictly
/// lower than the previous value. If T is the new value (rational number),
/// the the parameter is calculated as [65535 * T]. For example, 1% would be
/// [0.01 * 65535] = [655.35] = 655
///
/// # Events
/// * `TakeDecreased`: On successfully setting a decreased take for this hotkey.
///
/// # Errors
/// * `NotRegistered`: The hotkey we are delegating is not registered on the network.
///
/// * `NonAssociatedColdKey`: The hotkey we are delegating is not owned by the calling coldkey.
///
/// * `DelegateTakeTooLow`: The delegate is setting a take which is not lower than the previous.
///
#[pallet::call_index(65)]
#[pallet::weight((<T as crate::pallet::Config>::WeightInfo::decrease_take(), DispatchClass::Normal, Pays::Yes))]
pub fn decrease_take(
origin: OriginFor<T>,
hotkey: T::AccountId,
take: PerU16,
) -> DispatchResult {
Self::do_decrease_take(origin, hotkey, take)
}
/// Allows delegates to increase its take value. This call is rate-limited.
///
/// # Arguments
/// * `origin`: The signature of the caller's coldkey.
///
/// * `hotkey`: The hotkey we are delegating (must be owned by the coldkey).
///
/// * `take`: The new stake proportion that this hotkey takes from delegations.
/// The new value can be between 0 and 11_796 parts and should be strictly
/// greater than the previous value. T is the new value (rational number),
/// the the parameter is calculated as [65535 * T]. For example, 1% would be
/// [0.01 * 65535] = [655.35] = 655
///
/// # Events
/// * `TakeIncreased`: On successfully setting a increased take for this hotkey.
///
/// # Errors
/// * `NotRegistered`: The hotkey we are delegating is not registered on the network.
///
/// * `NonAssociatedColdKey`: The hotkey we are delegating is not owned by the calling coldkey.
///
/// * `DelegateTakeTooHigh`: The delegate is setting a take which is not greater than the previous.
///
#[pallet::call_index(66)]
#[pallet::weight((<T as crate::pallet::Config>::WeightInfo::increase_take(), DispatchClass::Normal, Pays::Yes))]
pub fn increase_take(
origin: OriginFor<T>,
hotkey: T::AccountId,
take: PerU16,
) -> DispatchResult {
Self::do_increase_take(origin, hotkey, take)
}
/// Adds stake to a hotkey. The call is made from a coldkey account.
/// This delegates stake to the hotkey.
///
/// # Note
/// The coldkey account may own the hotkey, in which case they are
/// delegating to themselves.
///
/// # Arguments
/// * `origin`: The signature of the caller's coldkey.
///
/// * `hotkey`: The associated hotkey account.
///
/// * `netuid`: Subnetwork UID.
///
/// * `amount_staked`: The amount of stake to be added to the hotkey staking account.
///
/// # Events
/// * `StakeAdded`: On the successfully adding stake to a global account.
///
/// # Errors
/// * `NotEnoughBalanceToStake`: Not enough balance on the coldkey to add onto the global account.
///
/// * `NonAssociatedColdKey`: The calling coldkey is not associated with this hotkey.
///
/// * `BalanceWithdrawalError`: Errors stemming from transaction pallet.
///
#[pallet::call_index(2)]
#[pallet::weight(<T as crate::pallet::Config>::WeightInfo::add_stake())]
pub fn add_stake(
origin: OriginFor<T>,
hotkey: T::AccountId,
netuid: NetUid,
amount_staked: TaoBalance,
) -> DispatchResult {
Self::do_add_stake(origin, hotkey, netuid, amount_staked).map(|_| ())
}
/// Remove stake from the staking account. The call must be made
/// from the coldkey account attached to the neuron metadata. Only this key
/// has permission to make staking and unstaking requests.
///
/// # Arguments
/// * `origin`: The signature of the caller's coldkey.
///
/// * `hotkey`: The associated hotkey account.
///
/// * `netuid`: Subnetwork UID.
///
/// * `amount_unstaked`: The amount of stake to be added to the hotkey staking account.
///
/// # Events
/// * `StakeRemoved`: On the successfully removing stake from the hotkey account.
///
/// # Errors
/// * `NotRegistered`: Thrown if the account we are attempting to unstake from is non existent.
///
/// * `NonAssociatedColdKey`: Thrown if the coldkey does not own the hotkey we are unstaking from.
///
/// * `NotEnoughStakeToWithdraw`: Thrown if there is not enough stake on the hotkey to withdwraw this amount.
///
#[pallet::call_index(3)]
#[pallet::weight(<T as crate::pallet::Config>::WeightInfo::remove_stake())]
pub fn remove_stake(
origin: OriginFor<T>,
hotkey: T::AccountId,
netuid: NetUid,
amount_unstaked: AlphaBalance,
) -> DispatchResult {
Self::do_remove_stake(origin, hotkey, netuid, amount_unstaked)
}
/// Serves or updates axon /prometheus information for the neuron associated with the caller. If the caller is
/// already registered the metadata is updated. If the caller is not registered this call throws NotRegistered.
///
/// # Arguments
/// * `origin`: The signature of the caller.
///
/// * `netuid`: The u16 network identifier.
///
/// * `version`: The bittensor version identifier.
///
/// * `ip`: The endpoint ip information as a u128 encoded integer.
///
/// * `port`: The endpoint port information as a u16 encoded integer.
///
/// * `ip_type`: The endpoint ip version as a u8, 4 or 6.
///
/// * `protocol`: UDP:1 or TCP:0.
///
/// * `placeholder1`: Placeholder for further extra params.
///
/// * `placeholder2`: Placeholder for further extra params.
///
/// # Events
/// * `AxonServed`: On successfully serving the axon info.
///
/// # Errors
/// * `MechanismDoesNotExist`: Attempting to set weights on a non-existent network.
///
/// * `NotRegistered`: Attempting to set weights from a non registered account.
///
/// * `InvalidIpType`: The ip type is not 4 or 6.
///
/// * `InvalidIpAddress`: The numerically encoded ip address does not resolve to a proper ip.
///
/// * `ServingRateLimitExceeded`: Attempting to set prometheus information withing the rate limit min.
///
#[pallet::call_index(4)]
#[pallet::weight((<T as crate::pallet::Config>::WeightInfo::serve_axon(), DispatchClass::Normal, Pays::No))]
pub fn serve_axon(
origin: OriginFor<T>,
netuid: NetUid,
version: u32,
ip: u128,
port: u16,
ip_type: u8,
protocol: u8,
placeholder1: u8,
placeholder2: u8,
) -> DispatchResult {
Self::do_serve_axon(
origin,
netuid,
version,
ip,
port,
ip_type,
protocol,
placeholder1,
placeholder2,
None,
)
}
/// Same as `serve_axon` but takes a certificate as an extra optional argument.
/// Serves or updates axon /prometheus information for the neuron associated with the caller. If the caller is
/// already registered the metadata is updated. If the caller is not registered this call throws NotRegistered.
///
/// # Arguments
/// * `origin`: The signature of the caller.
///
/// * `netuid`: The u16 network identifier.
///
/// * `version`: The bittensor version identifier.
///
/// * `ip`: The endpoint ip information as a u128 encoded integer.
///
/// * `port`: The endpoint port information as a u16 encoded integer.
///
/// * `ip_type`: The endpoint ip version as a u8, 4 or 6.
///
/// * `protocol`: UDP:1 or TCP:0.
///
/// * `placeholder1`: Placeholder for further extra params.
///
/// * `placeholder2`: Placeholder for further extra params.
///
/// * `certificate`: TLS certificate for inter neuron communitation.
///
/// # Events
/// * `AxonServed`: On successfully serving the axon info.
///
/// # Errors
/// * `MechanismDoesNotExist`: Attempting to set weights on a non-existent network.
///
/// * `NotRegistered`: Attempting to set weights from a non registered account.
///
/// * `InvalidIpType`: The ip type is not 4 or 6.
///
/// * `InvalidIpAddress`: The numerically encoded ip address does not resolve to a proper ip.
///
/// * `ServingRateLimitExceeded`: Attempting to set prometheus information withing the rate limit min.
///
#[pallet::call_index(40)]
#[pallet::weight((<T as crate::pallet::Config>::WeightInfo::serve_axon_tls(), DispatchClass::Normal, Pays::No))]
pub fn serve_axon_tls(
origin: OriginFor<T>,
netuid: NetUid,
version: u32,
ip: u128,
port: u16,
ip_type: u8,
protocol: u8,
placeholder1: u8,
placeholder2: u8,
certificate: Vec<u8>,
) -> DispatchResult {
Self::do_serve_axon(
origin,
netuid,
version,
ip,
port,
ip_type,
protocol,
placeholder1,
placeholder2,
Some(certificate),
)
}
/// Set prometheus information for the neuron.
/// # Arguments
/// * `origin`: The signature of the calling hotkey.
///
/// * `netuid`: The u16 network identifier.
///
/// * `version`: The bittensor version identifier.
///
/// * `ip`: The prometheus ip information as a u128 encoded integer.
///
/// * `port`: The prometheus port information as a u16 encoded integer.
///
/// * `ip_type`: The ip type v4 or v6.
///
#[pallet::call_index(5)]
#[pallet::weight((<T as crate::pallet::Config>::WeightInfo::serve_prometheus(), DispatchClass::Normal, Pays::No))]
pub fn serve_prometheus(
origin: OriginFor<T>,
netuid: NetUid,
version: u32,
ip: u128,
port: u16,
ip_type: u8,
) -> DispatchResult {
Self::do_serve_prometheus(origin, netuid, version, ip, port, ip_type)
}
/// Registers a new neuron to the subnetwork.
///
/// # Arguments
/// * `origin`: The signature of the calling hotkey.
///
/// * `netuid`: The u16 network identifier.
///
/// * `block_number`: Block hash used to prove work done.
///
/// * `nonce`: Positive integer nonce used in POW.
///
/// * `work`: Vector encoded bytes representing work done.
///
/// * `hotkey`: Hotkey to be registered to the network.
///
/// * `coldkey`: Associated coldkey account.
///
/// # Events
/// * `NeuronRegistered`: On successfully registering a uid to a neuron slot on a subnetwork.
///
/// # Errors
/// * `MechanismDoesNotExist`: Attempting to register to a non existent network.
///
/// * `TooManyRegistrationsThisBlock`: This registration exceeds the total allowed on this network this block.
///
/// * `HotKeyAlreadyRegisteredInSubNet`: The hotkey is already registered on this network.
///
/// * `InvalidWorkBlock`: The work has been performed on a stale, future, or non existent block.
///
/// * `InvalidDifficulty`: The work does not match the difficulty.
///
/// * `InvalidSeal`: The seal is incorrect.
///
#[pallet::call_index(6)]
#[pallet::weight(<T as crate::pallet::Config>::WeightInfo::register())]
pub fn register(
origin: OriginFor<T>,
netuid: NetUid,
_block_number: u64,
_nonce: u64,
_work: Vec<u8>,
hotkey: T::AccountId,
_coldkey: T::AccountId,
) -> DispatchResult {
Self::do_register(origin, netuid, hotkey)
}
/// Register the hotkey to root network
#[pallet::call_index(62)]
#[pallet::weight(<T as crate::pallet::Config>::WeightInfo::root_register())]
pub fn root_register(origin: OriginFor<T>, hotkey: T::AccountId) -> DispatchResult {
Self::do_root_register(origin, hotkey)
}
/// User register a new subnetwork via burning token
#[pallet::call_index(7)]
#[pallet::weight(<T as crate::pallet::Config>::WeightInfo::burned_register())]
pub fn burned_register(
origin: OriginFor<T>,
netuid: NetUid,
hotkey: T::AccountId,
) -> DispatchResult {
Self::do_register(origin, netuid, hotkey)
}
/// The extrinsic for user to change its hotkey in subnet or all subnets.
///
/// # Arguments
/// * `origin`: The origin of the transaction (must be signed by the coldkey).
/// * `hotkey`: The old hotkey to be swapped.
/// * `new_hotkey`: The new hotkey to replace the old one.
/// * `netuid`: Optional subnet ID. If `Some`, swap only on that subnet; if `None`, swap on all subnets.
#[deprecated(
note = "Please use swap_hotkey_v2 instead. This extrinsic will be removed some time after June 2026."
)]
#[pallet::call_index(70)]
#[pallet::weight((
<T as crate::pallet::Config>::WeightInfo::swap_hotkey(),
DispatchClass::Normal,
Pays::Yes
))]
pub fn swap_hotkey(
origin: OriginFor<T>,
hotkey: T::AccountId,
new_hotkey: T::AccountId,
netuid: Option<NetUid>,
) -> DispatchResultWithPostInfo {
Self::do_swap_hotkey(origin, &hotkey, &new_hotkey, netuid, false)
}
/// The extrinsic for user to change its hotkey in subnet or all subnets. This extrinsic is
/// similar to swap_hotkey, but with keep_stake parameter bo be able to keep the stake when swapping
/// a root key to a child key
///
/// # Arguments
/// * `origin`: The origin of the transaction (must be signed by the coldkey).
/// * `hotkey`: The old hotkey to be swapped.
/// * `new_hotkey`: The new hotkey to replace the old one.
/// * `netuid`: Optional subnet ID. If `Some`, swap only on that subnet; if `None`, swap on all subnets.
/// * `keep_stake`: If `true`, stake remains on the old hotkey and the rest metadata
/// is transferred to the new hotkey.
#[pallet::call_index(72)]
#[pallet::weight((
crate::Pallet::<T>::swap_hotkey_v2_dispatch_weight(netuid, *keep_stake),
DispatchClass::Normal,
Pays::Yes
))]
pub fn swap_hotkey_v2(
origin: OriginFor<T>,
hotkey: T::AccountId,
new_hotkey: T::AccountId,
netuid: Option<NetUid>,
keep_stake: bool,
) -> DispatchResultWithPostInfo {
Self::do_swap_hotkey(origin, &hotkey, &new_hotkey, netuid, keep_stake)
}
/// Performs an arbitrary coldkey swap for any coldkey.
///
/// Only callable by root as it doesn't require an announcement and can be used to swap any coldkey.
#[pallet::call_index(71)]
#[pallet::weight(<T as crate::pallet::Config>::WeightInfo::swap_coldkey())]
pub fn swap_coldkey(
origin: OriginFor<T>,
old_coldkey: T::AccountId,
new_coldkey: T::AccountId,
swap_cost: TaoBalance,
) -> DispatchResult {
ensure_root(origin)?;
if !swap_cost.is_zero() {
Self::charge_swap_cost(&old_coldkey, swap_cost)?;
}
Self::do_swap_coldkey(&old_coldkey, &new_coldkey)?;
// We also clear any announcement or dispute for security reasons
ColdkeySwapAnnouncements::<T>::remove(&old_coldkey);
ColdkeySwapDisputes::<T>::remove(old_coldkey);
Ok(())
}
/// Sets the childkey take for a given hotkey.
///
/// This function allows a coldkey to set the childkey take for a given hotkey.
/// The childkey take determines the proportion of stake that the hotkey keeps for itself
/// when distributing stake to its children.
///
/// # Arguments
/// * `origin`: The signature of the calling coldkey. Setting childkey take can only be done by the coldkey.
///
/// * `hotkey`: The hotkey for which the childkey take will be set.
///
/// * `take`: The new childkey take value. This is a ratio represented in parts per 65535,
/// where 65535 represents 100%.
///
/// # Events
/// * `ChildkeyTakeSet`: On successfully setting the childkey take for a hotkey.
///
/// # Errors
/// * `NonAssociatedColdKey`: The coldkey does not own the hotkey.
/// * `InvalidChildkeyTake`: The provided take value is invalid (greater than the maximum allowed take).
/// * `TxChildkeyTakeRateLimitExceeded`: The rate limit for changing childkey take has been exceeded.
///
#[pallet::call_index(75)]
#[pallet::weight(<T as crate::pallet::Config>::WeightInfo::set_childkey_take())]
pub fn set_childkey_take(
origin: OriginFor<T>,
hotkey: T::AccountId,
netuid: NetUid,
take: PerU16,
) -> DispatchResult {
let coldkey = ensure_signed(origin)?;
// Call the utility function to set the childkey take
Self::do_set_childkey_take(coldkey, hotkey, netuid, take)
}
// ---- SUDO ONLY FUNCTIONS ------------------------------------------------------------
/// Sets the transaction rate limit for changing childkey take.
///
/// This function can only be called by the root origin.
///
/// # Arguments
/// * `origin`: The origin of the call, must be root.
/// * `tx_rate_limit`: The new rate limit in blocks.
///
/// # Errors
/// * `BadOrigin`: If the origin is not root.
///
#[pallet::call_index(69)]
#[pallet::weight(<T as crate::pallet::Config>::WeightInfo::sudo_set_tx_childkey_take_rate_limit())]
pub fn sudo_set_tx_childkey_take_rate_limit(
origin: OriginFor<T>,
tx_rate_limit: u64,
) -> DispatchResult {
ensure_root(origin)?;
Self::set_tx_childkey_take_rate_limit(tx_rate_limit);
Ok(())
}
/// Sets the minimum allowed childkey take.
///
/// This function can only be called by the root origin.
///
/// # Arguments
/// * `origin`: The origin of the call, must be root.
/// * `take`: The new minimum childkey take value.
///
/// # Errors
/// * `BadOrigin`: If the origin is not root.
///
#[pallet::call_index(76)]
#[pallet::weight(Weight::from_parts(6_000, 0)
.saturating_add(T::DbWeight::get().reads(1))
.saturating_add(T::DbWeight::get().writes(1)))]
pub fn sudo_set_min_childkey_take(origin: OriginFor<T>, take: PerU16) -> DispatchResult {
ensure_root(origin)?;
Self::set_min_childkey_take(take);
Ok(())
}
/// Sets the maximum allowed childkey take.
///
/// This function can only be called by the root origin.
///
/// # Arguments
/// * `origin`: The origin of the call, must be root.
/// * `take`: The new maximum childkey take value.
///
/// # Errors
/// * `BadOrigin`: If the origin is not root.
///
#[pallet::call_index(77)]
#[pallet::weight(Weight::from_parts(6_000, 0)
.saturating_add(T::DbWeight::get().reads(1))
.saturating_add(T::DbWeight::get().writes(1)))]
pub fn sudo_set_max_childkey_take(origin: OriginFor<T>, take: PerU16) -> DispatchResult {
ensure_root(origin)?;
Self::set_max_childkey_take(take);
Ok(())
}
/// User register a new subnetwork
#[pallet::call_index(59)]
#[pallet::weight(<T as crate::pallet::Config>::WeightInfo::register_network())]
pub fn register_network(origin: OriginFor<T>, hotkey: T::AccountId) -> DispatchResult {
Self::do_register_network(origin, &hotkey, 1, None)
}
/// Facility extrinsic for user to get taken from faucet
/// It is only available when pow-faucet feature enabled
/// Just deployed in testnet and devnet for testing purpose
#[pallet::call_index(60)]
#[pallet::weight((Weight::from_parts(91_000_000, 0)
.saturating_add(T::DbWeight::get().reads(27))
.saturating_add(T::DbWeight::get().writes(22)), DispatchClass::Normal, Pays::No))]
#[cfg(feature = "pow-faucet")]
pub fn faucet(
origin: OriginFor<T>,
block_number: u64,
nonce: u64,
work: Vec<u8>,
) -> DispatchResult {
if cfg!(feature = "pow-faucet") {
return Self::do_faucet(origin, block_number, nonce, work);
}
Err(Error::<T>::FaucetDisabled.into())
}
/// Remove a user's subnetwork
/// The caller must be the owner of the network
#[pallet::call_index(61)]
#[pallet::weight(<T as crate::pallet::Config>::WeightInfo::dissolve_network())]
pub fn dissolve_network(
origin: OriginFor<T>,
_coldkey: T::AccountId,
netuid: NetUid,
) -> DispatchResult {
ensure_root(origin)?;
Self::do_dissolve_network(netuid)
}
/// Set a single child for a given hotkey on a specified network.
///
/// This function allows a coldkey to set a single child for a given hotkey on a specified network.
/// The proportion of the hotkey's stake to be allocated to the child is also specified.
///
/// # Arguments
/// * `origin`: The signature of the calling coldkey. Setting a hotkey child can only be done by the coldkey.
///
/// * `hotkey`: The hotkey which will be assigned the child.
///
/// * `child`: The child which will be assigned to the hotkey.
///
/// * `netuid`: The u16 network identifier where the childkey will exist.
///
/// * `proportion`: Proportion of the hotkey's stake to be given to the child, the value must be u64 normalized.
///
/// # Events
/// * `ChildAddedSingular`: On successfully registering a child to a hotkey.
///
/// # Errors
/// * `MechanismDoesNotExist`: Attempting to register to a non-existent network.
/// * `RegistrationNotPermittedOnRootSubnet`: Attempting to register a child on the root network.
/// * `NonAssociatedColdKey`: The coldkey does not own the hotkey or the child is the same as the hotkey.
/// * `HotKeyAccountNotExists`: The hotkey account does not exist.
///
/// # Note
/// 1. **Signature Verification**: Ensures that the caller has signed the transaction, verifying the coldkey.
/// 2. **Root Network Check**: Ensures that the delegation is not on the root network, as child hotkeys are not valid on the root.
/// 3. **Network Existence Check**: Ensures that the specified network exists.
/// 4. **Ownership Verification**: Ensures that the coldkey owns the hotkey.
/// 5. **Hotkey Account Existence Check**: Ensures that the hotkey account already exists.
/// 6. **Child-Hotkey Distinction**: Ensures that the child is not the same as the hotkey.
/// 7. **Old Children Cleanup**: Removes the hotkey from the parent list of its old children.
/// 8. **New Children Assignment**: Assigns the new child to the hotkey and updates the parent list for the new child.
// TODO: Benchmark this call
#[pallet::call_index(67)]
#[pallet::weight((Weight::from_parts(119_000_000, 0)
.saturating_add(T::DbWeight::get().reads(6))
.saturating_add(T::DbWeight::get().writes(31)), DispatchClass::Normal, Pays::Yes))]
pub fn set_children(
origin: OriginFor<T>,
hotkey: T::AccountId,
netuid: NetUid,
children: Vec<(u64, T::AccountId)>,
) -> DispatchResultWithPostInfo {
Self::do_schedule_children(origin, hotkey, netuid, children)?;
Ok(().into())
}
/// Schedules a coldkey swap operation to be executed at a future block.
///
/// # Note
/// This function is deprecated; please migrate to
/// `announce_coldkey_swap` / `coldkey_swap`.
#[pallet::call_index(73)]
#[pallet::weight(T::DbWeight::get().reads(5))]
#[deprecated(note = "Deprecated, please migrate to `announce_coldkey_swap`/`coldkey_swap`")]
pub fn schedule_swap_coldkey(
_origin: OriginFor<T>,
_new_coldkey: T::AccountId,
) -> DispatchResult {
Err(Error::<T>::Deprecated.into())
}
/// Set prometheus information for the neuron.
/// # Arguments
/// * `origin`: The signature of the calling hotkey.
///
/// * `netuid`: The u16 network identifier.
///
/// * `version`: The bittensor version identifier.
///
/// * `ip`: The prometheus ip information as a u128 encoded integer.
///
/// * `port`: The prometheus port information as a u16 encoded integer.
///
/// * `ip_type`: The ip type v4 or v6.
///
#[pallet::call_index(68)]
#[pallet::weight(<T as crate::pallet::Config>::WeightInfo::set_identity())]
pub fn set_identity(
origin: OriginFor<T>,
name: Vec<u8>,
url: Vec<u8>,
github_repo: Vec<u8>,
image: Vec<u8>,
discord: Vec<u8>,
description: Vec<u8>,
additional: Vec<u8>,
) -> DispatchResult {
Self::do_set_identity(
origin,
name,
url,
github_repo,
image,
discord,
description,
additional,
)
}
/// Set the identity information for a subnet.
/// # Arguments
/// * `origin`: The signature of the calling coldkey, which must be the owner of the subnet.
///
/// * `netuid`: The unique network identifier of the subnet.
///
/// * `subnet_name`: The name of the subnet.
///
/// * `github_repo`: The GitHub repository associated with the subnet identity.
///
/// * `subnet_contact`: The contact information for the subnet.
#[pallet::call_index(78)]
#[pallet::weight(<T as crate::pallet::Config>::WeightInfo::set_subnet_identity())]
pub fn set_subnet_identity(
origin: OriginFor<T>,
netuid: NetUid,
subnet_name: Vec<u8>,
github_repo: Vec<u8>,
subnet_contact: Vec<u8>,
subnet_url: Vec<u8>,
discord: Vec<u8>,
description: Vec<u8>,
logo_url: Vec<u8>,
additional: Vec<u8>,
) -> DispatchResult {
Self::do_set_subnet_identity(
origin,
netuid,
subnet_name,
github_repo,
subnet_contact,
subnet_url,
discord,
description,
logo_url,
additional,
)
}
/// User register a new subnetwork
#[pallet::call_index(79)]
#[pallet::weight(<T as crate::pallet::Config>::WeightInfo::register_network_with_identity())]
pub fn register_network_with_identity(
origin: OriginFor<T>,
hotkey: T::AccountId,
identity: Option<SubnetIdentityOfV3>,
) -> DispatchResult {
Self::do_register_network(origin, &hotkey, 1, identity)
}
/// The implementation for the extrinsic unstake_all: Removes all stake from a hotkey account across all subnets and adds it onto a coldkey.
///
/// # Arguments
/// * `origin`: The signature of the caller's coldkey.
///
/// * `hotkey`: The associated hotkey account.
///
/// # Events
/// * `StakeRemoved`: On the successfully removing stake from the hotkey account.
///
/// # Errors
/// * `NotRegistered`: Thrown if the account we are attempting to unstake from is non existent.
///
/// * `NonAssociatedColdKey`: Thrown if the coldkey does not own the hotkey we are unstaking from.
///
/// * `NotEnoughStakeToWithdraw`: Thrown if there is not enough stake on the hotkey to withdraw this amount.
///
/// * `TxRateLimitExceeded`: Thrown if key has hit transaction rate limit.
#[pallet::call_index(83)]
#[pallet::weight(<T as crate::pallet::Config>::WeightInfo::unstake_all())]
pub fn unstake_all(origin: OriginFor<T>, hotkey: T::AccountId) -> DispatchResult {
Self::do_unstake_all(origin, hotkey)
}
/// The implementation for the extrinsic unstake_all: Removes all stake from a hotkey account across all subnets and adds it onto a coldkey.
///
/// # Arguments
/// * `origin`: The signature of the caller's coldkey.
///
/// * `hotkey`: The associated hotkey account.
///
/// # Events
/// * `StakeRemoved`: On the successfully removing stake from the hotkey account.
///
/// # Errors
/// * `NotRegistered`: Thrown if the account we are attempting to unstake from is non existent.
///
/// * `NonAssociatedColdKey`: Thrown if the coldkey does not own the hotkey we are unstaking from.
///
/// * `NotEnoughStakeToWithdraw`: Thrown if there is not enough stake on the hotkey to withdraw this amount.
///
/// * `TxRateLimitExceeded`: Thrown if key has hit transaction rate limit.
#[pallet::call_index(84)]
#[pallet::weight(<T as crate::pallet::Config>::WeightInfo::unstake_all_alpha())]
pub fn unstake_all_alpha(origin: OriginFor<T>, hotkey: T::AccountId) -> DispatchResult {
Self::do_unstake_all_alpha(origin, hotkey)
}
/// The implementation for the extrinsic move_stake: Moves specified amount of stake from a hotkey to another across subnets.
///
/// # Arguments
/// * `origin`: The signature of the caller's coldkey.
///
/// * `origin_hotkey`: The hotkey account to move stake from.
///
/// * `destination_hotkey`: The hotkey account to move stake to.
///
/// * `origin_netuid`: The subnet ID to move stake from.
///
/// * `destination_netuid`: The subnet ID to move stake to.
///
/// * `alpha_amount`: The alpha stake amount to move.
///
#[pallet::call_index(85)]
#[pallet::weight(<T as crate::pallet::Config>::WeightInfo::move_stake())]
pub fn move_stake(
origin: OriginFor<T>,
origin_hotkey: T::AccountId,
destination_hotkey: T::AccountId,
origin_netuid: NetUid,
destination_netuid: NetUid,
alpha_amount: AlphaBalance,
) -> DispatchResult {
Self::do_move_stake(
origin,
origin_hotkey,
destination_hotkey,
origin_netuid,
destination_netuid,
alpha_amount,
)
}
/// Transfers a specified amount of stake from one coldkey to another, optionally across subnets,
/// while keeping the same hotkey.
///
/// # Arguments
/// * `origin`: The origin of the transaction, which must be signed by the `origin_coldkey`.
/// * `destination_coldkey`: The coldkey to which the stake is transferred.
/// * `hotkey`: The hotkey associated with the stake.
/// * `origin_netuid`: The network/subnet ID to move stake from.
/// * `destination_netuid`: The network/subnet ID to move stake to (for cross-subnet transfer).
/// * `alpha_amount`: The amount of stake to transfer.
///
/// # Errors
/// * `BadOrigin`: The transaction is not signed.
/// * `SubnetNotExists`: Either `origin_netuid` or `destination_netuid` does not exist.
/// * `SubtokenDisabled`: The subtoken is disabled on the origin or destination subnet.
/// * `HotKeyAccountNotExists`: The `hotkey` account does not exist.
/// * `NotEnoughStakeToWithdraw`: The `(origin_coldkey, hotkey, origin_netuid)` position has less stake than `alpha_amount`.
/// * `InsufficientLiquidity`: The swap simulation on the origin subnet fails.
/// * `AmountTooLow`: The TAO-equivalent of the transfer is below the minimum stake requirement.
/// * `TransferDisallowed`: Transfers are disabled on the origin or destination subnet.
/// * `StakeUnavailable`: The remaining stake would not cover the locked amount on the origin subnet.
///
/// # Events
/// May emit a `StakeTransferred` event on success.
#[pallet::call_index(86)]
#[pallet::weight(<T as crate::pallet::Config>::WeightInfo::transfer_stake())]
pub fn transfer_stake(
origin: OriginFor<T>,
destination_coldkey: T::AccountId,
hotkey: T::AccountId,
origin_netuid: NetUid,
destination_netuid: NetUid,
alpha_amount: AlphaBalance,
) -> DispatchResult {
Self::do_transfer_stake(
origin,
destination_coldkey,
hotkey,
origin_netuid,
destination_netuid,
alpha_amount,
)
}
/// Swaps a specified amount of stake from one subnet to another, while keeping the same coldkey and hotkey.
///
/// # Arguments
/// * `origin`: The origin of the transaction, which must be signed by the coldkey that owns the `hotkey`.
/// * `hotkey`: The hotkey whose stake is being swapped.
/// * `origin_netuid`: The network/subnet ID from which stake is removed.
/// * `destination_netuid`: The network/subnet ID to which stake is added.
/// * `alpha_amount`: The amount of stake to swap.
///
/// # Errors
/// * `BadOrigin`: The transaction is not signed.
/// * `SameNetuid`: `origin_netuid` and `destination_netuid` are the same.
/// * `SubnetNotExists`: Either `origin_netuid` or `destination_netuid` does not exist.
/// * `SubtokenDisabled`: The subtoken is disabled on the origin or destination subnet.
/// * `HotKeyAccountNotExists`: The `hotkey` account does not exist.
/// * `NotEnoughStakeToWithdraw`: The `(coldkey, hotkey, origin_netuid)` position has less stake than `alpha_amount`.
/// * `InsufficientLiquidity`: The swap simulation on the origin subnet fails.
/// * `AmountTooLow`: The TAO-equivalent of the swap is below the minimum stake requirement.
/// * `StakeUnavailable`: The remaining stake would not cover the locked amount on the origin subnet.
///
/// # Events
/// May emit a `StakeSwapped` event on success.
#[pallet::call_index(87)]
#[pallet::weight(<T as crate::pallet::Config>::WeightInfo::swap_stake())]
pub fn swap_stake(
origin: OriginFor<T>,
hotkey: T::AccountId,
origin_netuid: NetUid,
destination_netuid: NetUid,
alpha_amount: AlphaBalance,
) -> DispatchResult {
Self::do_swap_stake(
origin,
hotkey,
origin_netuid,
destination_netuid,
alpha_amount,
)
}
/// Adds stake to a hotkey on a subnet with a price limit.
/// This extrinsic allows to specify the limit price for alpha token
/// at which or better (lower) the staking should execute.
///
/// In case if slippage occurs and the price shall move beyond the limit
/// price, the staking order may execute only partially or not execute
/// at all.
///
/// # Arguments
/// * `origin`: The signature of the caller's coldkey.
///
/// * `hotkey`: The associated hotkey account.
///
/// * `netuid`: Subnetwork UID.
///
/// * `amount_staked`: The amount of stake to be added to the hotkey staking account.
///
/// * `limit_price`: The limit price expressed in units of RAO per one Alpha.
///
/// * `allow_partial`: Allows partial execution of the amount. If set to false, this becomes
/// fill or kill type of order.
///
/// # Events
/// * `StakeAdded`: On the successfully adding stake to a global account.
///
/// # Errors
/// * `NotEnoughBalanceToStake`: Not enough balance on the coldkey to add onto the global account.
///
/// * `NonAssociatedColdKey`: The calling coldkey is not associated with this hotkey.
///
/// * `BalanceWithdrawalError`: Errors stemming from transaction pallet.
///
#[pallet::call_index(88)]
#[pallet::weight(<T as crate::pallet::Config>::WeightInfo::add_stake_limit())]
pub fn add_stake_limit(
origin: OriginFor<T>,
hotkey: T::AccountId,
netuid: NetUid,
amount_staked: TaoBalance,
limit_price: TaoBalance,
allow_partial: bool,
) -> DispatchResult {
Self::do_add_stake_limit(
origin,
hotkey,
netuid,
amount_staked,
limit_price,
allow_partial,
)
.map(|_| ())
}
/// Removes stake from a hotkey on a subnet with a price limit.
/// This extrinsic allows to specify the limit price for alpha token
/// at which or better (higher) the staking should execute.
///
/// In case if slippage occurs and the price shall move beyond the limit
/// price, the staking order may execute only partially or not execute
/// at all.
///
/// # Arguments
/// * `origin`: The signature of the caller's coldkey.
///
/// * `hotkey`: The associated hotkey account.
///
/// * `netuid`: Subnetwork UID.
///
/// * `amount_unstaked`: The amount of stake to be added to the hotkey staking account.
///
/// * `limit_price`: The limit price expressed in units of RAO per one Alpha.
///
/// * `allow_partial`: Allows partial execution of the amount. If set to false, this becomes
/// fill or kill type of order.
///
/// # Events
/// * `StakeRemoved`: On the successfully removing stake from the hotkey account.
///
/// # Errors
/// * `NotRegistered`: Thrown if the account we are attempting to unstake from is non existent.
///
/// * `NonAssociatedColdKey`: Thrown if the coldkey does not own the hotkey we are unstaking from.
///
/// * `NotEnoughStakeToWithdraw`: Thrown if there is not enough stake on the hotkey to withdwraw this amount.
///
#[pallet::call_index(89)]
#[pallet::weight(<T as crate::pallet::Config>::WeightInfo::remove_stake_limit())]
pub fn remove_stake_limit(
origin: OriginFor<T>,
hotkey: T::AccountId,
netuid: NetUid,
amount_unstaked: AlphaBalance,
limit_price: TaoBalance,
allow_partial: bool,
) -> DispatchResult {
Self::do_remove_stake_limit(
origin,
hotkey,
netuid,
amount_unstaked,
limit_price,
allow_partial,
)
}
/// Swaps a specified amount of stake from one subnet to another, while keeping the same coldkey and hotkey.
///
/// # Arguments
/// * `origin`: The origin of the transaction, which must be signed by the coldkey that owns the `hotkey`.
/// * `hotkey`: The hotkey whose stake is being swapped.
/// * `origin_netuid`: The network/subnet ID from which stake is removed.
/// * `destination_netuid`: The network/subnet ID to which stake is added.
/// * `alpha_amount`: The amount of stake to swap.
/// * `limit_price`: The limit price expressed in units of RAO per one Alpha.
/// * `allow_partial`: Allows partial execution of the amount. If set to false, this becomes fill or kill type of order.
///
/// # Errors
/// * `BadOrigin`: The transaction is not signed.
/// * `SameNetuid`: `origin_netuid` and `destination_netuid` are the same.
/// * `SubnetNotExists`: Either `origin_netuid` or `destination_netuid` does not exist.
/// * `SubtokenDisabled`: The subtoken is disabled on the origin or destination subnet.
/// * `HotKeyAccountNotExists`: The `hotkey` account does not exist.
/// * `NotEnoughStakeToWithdraw`: The `(coldkey, hotkey, origin_netuid)` position has less stake than `alpha_amount`.
/// * `InsufficientLiquidity`: The swap simulation on the origin subnet fails.
/// * `AmountTooLow`: The TAO-equivalent of the swap is below the minimum stake requirement.
/// * `SlippageTooHigh`: `allow_partial` is false and the amount would cross the limit price.
/// * `StakeUnavailable`: The remaining stake would not cover the locked amount on the origin subnet.
///
/// # Events
/// May emit a `StakeSwapped` event on success.
#[pallet::call_index(90)]
#[pallet::weight(<T as crate::pallet::Config>::WeightInfo::swap_stake_limit())]
pub fn swap_stake_limit(
origin: OriginFor<T>,
hotkey: T::AccountId,
origin_netuid: NetUid,
destination_netuid: NetUid,
alpha_amount: AlphaBalance,
limit_price: TaoBalance,
allow_partial: bool,
) -> DispatchResult {
Self::do_swap_stake_limit(
origin,
hotkey,
origin_netuid,
destination_netuid,
alpha_amount,
limit_price,
allow_partial,
)
}
/// Attempts to associate a hotkey with a coldkey.
///
/// # Arguments
/// * `origin`: The origin of the transaction, which must be signed by the coldkey that owns the `hotkey`.
/// * `hotkey`: The hotkey to associate with the coldkey.
///
/// # Note
/// Will charge based on the weight even if the hotkey is already associated with a coldkey.
#[pallet::call_index(91)]
#[pallet::weight(<T as crate::pallet::Config>::WeightInfo::try_associate_hotkey())]
pub fn try_associate_hotkey(origin: OriginFor<T>, hotkey: T::AccountId) -> DispatchResult {
let coldkey = ensure_signed(origin)?;
Self::do_try_associate_hotkey(&coldkey, &hotkey)?;
Ok(())
}
/// Initiates a call on a subnet.
///
/// # Arguments
/// * `origin`: The origin of the call, which must be signed by the subnet owner.
/// * `netuid`: The unique identifier of the subnet on which the call is being initiated.
///
/// # Events
/// Emits a `FirstEmissionBlockNumberSet` event on success.
#[pallet::call_index(92)]
#[pallet::weight(<T as crate::pallet::Config>::WeightInfo::start_call())]
pub fn start_call(origin: OriginFor<T>, netuid: NetUid) -> DispatchResult {
Self::do_start_call(origin, netuid)?;
Ok(())
}
/// Attempts to associate a hotkey with an EVM key.
///
/// The signature will be checked to see if the recovered public key matches the `evm_key` provided.
///
/// The EVM key is expected to sign the message according to this formula to produce the signature:
/// ```text
/// keccak_256(hotkey ++ keccak_256(block_number))
/// ```
///
/// # Arguments
/// * `origin`: The origin of the transaction, which must be signed by the `hotkey`.
/// * `netuid`: The netuid that the `hotkey` belongs to.
/// * `evm_key`: The EVM key to associate with the `hotkey`.
/// * `block_number`: The block number used in the `signature`.
/// * `signature`: A signed message by the `evm_key` containing the `hotkey` and the hashed `block_number`.
///
/// # Errors
/// * `BadOrigin`: The transaction is not signed.
/// * `SubnetNotExists`: The subnet identified by `netuid` does not exist.
/// * `NonAssociatedColdKey`: The hotkey is not associated with a coldkey.
/// * `HotKeyNotRegisteredInSubNet`: The hotkey is not registered on the subnet identified by `netuid`.
/// * `EvmKeyAssociateRateLimitExceeded`: The association rate limit has not elapsed since the last association.
/// * `InvalidIdentity`: The public key cannot be recovered from the signature.
/// * `UnableToRecoverPublicKey`: The recovered public key cannot be parsed.
/// * `InvalidRecoveredPublicKey`: The EVM key recovered from the signature does not match the given `evm_key`.
/// * `EvmKeyAssociationLimitExceeded`: The EVM key is already associated with the maximum number of UIDs.
///
/// # Events
/// May emit a `EvmKeyAssociated` event on success
#[pallet::call_index(93)]
#[pallet::weight((
<T as crate::pallet::Config>::WeightInfo::associate_evm_key(),
DispatchClass::Normal,
Pays::No
))]
pub fn associate_evm_key(
origin: OriginFor<T>,
netuid: NetUid,
evm_key: H160,
block_number: u64,
signature: Signature,
) -> DispatchResult {
Self::do_associate_evm_key(origin, netuid, evm_key, block_number, signature)
}
/// Recycles alpha from a cold/hot key pair, reducing AlphaOut on a subnet
///
/// # Arguments
/// * `origin`: The origin of the call (must be signed by the coldkey)
/// * `hotkey`: The hotkey account
/// * `amount`: The amount of alpha to recycle
/// * `netuid`: The subnet ID
///
/// # Events
/// Emits a `TokensRecycled` event on success.
#[pallet::call_index(101)]
#[pallet::weight(<T as crate::pallet::Config>::WeightInfo::recycle_alpha())]
pub fn recycle_alpha(
origin: OriginFor<T>,
hotkey: T::AccountId,
amount: AlphaBalance,
netuid: NetUid,
) -> DispatchResult {
Self::do_recycle_alpha(origin, hotkey, amount, netuid).map(|_| ())
}
/// Burns alpha from a cold/hot key pair without reducing `AlphaOut`
///
/// # Arguments
/// * `origin`: The origin of the call (must be signed by the coldkey)
/// * `hotkey`: The hotkey account
/// * `amount`: The amount of alpha to burn
/// * `netuid`: The subnet ID
///
/// # Events
/// Emits a `TokensBurned` event on success.
#[pallet::call_index(102)]
#[pallet::weight(<T as crate::pallet::Config>::WeightInfo::burn_alpha())]
pub fn burn_alpha(
origin: OriginFor<T>,
hotkey: T::AccountId,
amount: AlphaBalance,
netuid: NetUid,
) -> DispatchResult {
Self::do_burn_alpha(origin, hotkey, amount, netuid).map(|_| ())
}
/// Sets the pending childkey cooldown (in blocks). Root only.
#[pallet::call_index(109)]
#[pallet::weight(Weight::from_parts(3_000_000, 0)
.saturating_add(T::DbWeight::get().writes(1_u64)))]
pub fn set_pending_childkey_cooldown(
origin: OriginFor<T>,
cooldown: u64,
) -> DispatchResult {
ensure_root(origin)?;
PendingChildKeyCooldown::<T>::put(cooldown);
Ok(())
}
/// Removes all stake from a hotkey on a subnet with a price limit.
/// This extrinsic allows to specify the limit price for alpha token
/// at which or better (higher) the staking should execute.
/// Without limit_price it remove all the stake similar to `remove_stake` extrinsic
#[pallet::call_index(103)]
#[pallet::weight(<T as crate::pallet::Config>::WeightInfo::remove_stake_full_limit())]
pub fn remove_stake_full_limit(
origin: OriginFor<T>,
hotkey: T::AccountId,
netuid: NetUid,
limit_price: Option<TaoBalance>,
) -> DispatchResult {
Self::do_remove_stake_full_limit(origin, hotkey, netuid, limit_price)
}
/// Register a new leased network.
///
/// The crowdloan's contributions are used to compute the share of the emissions that the contributors
/// will receive as dividends.
///
/// The leftover cap is refunded to the contributors and the beneficiary.
///
/// # Arguments
/// * `origin`: The signature of the caller's coldkey.
///
/// * `emissions_share`: The share of the emissions that the contributors will receive as dividends.
///
/// * `end_block`: The block at which the lease will end. If not defined, the lease is perpetual.
#[pallet::call_index(110)]
#[pallet::weight(<T as crate::pallet::Config>::WeightInfo::register_leased_network(T::MaxContributors::get()))]
pub fn register_leased_network(
origin: OriginFor<T>,
emissions_share: Percent,
end_block: Option<BlockNumberFor<T>>,
) -> DispatchResultWithPostInfo {
Self::do_register_leased_network(origin, emissions_share, end_block)
}
/// Terminate a lease.
///
/// The beneficiary can terminate the lease after the end block has passed and get the subnet ownership.
/// The subnet is transferred to the beneficiary and the lease is removed from storage.
///
/// **The hotkey must be owned by the beneficiary coldkey.**
///
/// # Arguments
/// * `origin`: The signature of the caller's coldkey.
///
/// * `lease_id`: The ID of the lease to terminate.
///
/// * `hotkey`: The hotkey of the beneficiary to mark as subnet owner hotkey.
#[pallet::call_index(111)]
#[pallet::weight(<T as crate::pallet::Config>::WeightInfo::terminate_lease(T::MaxContributors::get()))]
pub fn terminate_lease(
origin: OriginFor<T>,
lease_id: LeaseId,
hotkey: T::AccountId,
) -> DispatchResultWithPostInfo {
Self::do_terminate_lease(origin, lease_id, hotkey)
}
/// Updates the symbol for a subnet.
///
/// # Arguments
/// * `origin`: The origin of the call, which must be the subnet owner or root.
/// * `netuid`: The unique identifier of the subnet on which the symbol is being set.
/// * `symbol`: The symbol to set for the subnet.
///
/// # Errors
/// * `BadOrigin`: The transaction is not signed by the subnet owner or root.
/// * `SubnetNotExists`: The subnet identified by `netuid` does not exist.
/// * `SymbolDoesNotExist`: The symbol is not one of the recognized symbols.
/// * `SymbolAlreadyInUse`: The symbol is already in use by another subnet.
///
/// # Events
/// Emits a `SymbolUpdated` event on success.
#[pallet::call_index(112)]
#[pallet::weight(<T as crate::pallet::Config>::WeightInfo::update_symbol())]
pub fn update_symbol(
origin: OriginFor<T>,
netuid: NetUid,
symbol: Vec<u8>,
) -> DispatchResult {
Self::ensure_subnet_owner_or_root(origin, netuid)?;
ensure!(Self::if_subnet_exist(netuid), Error::<T>::SubnetNotExists);
Self::ensure_symbol_exists(&symbol)?;
Self::ensure_symbol_available(&symbol)?;
TokenSymbol::<T>::insert(netuid, symbol.clone());
Self::deposit_event(Event::SymbolUpdated { netuid, symbol });
Ok(())
}
/// Used to commit timelock encrypted commit-reveal weight values to later be revealed.
///
/// # Arguments
/// * `origin`: The committing hotkey.
///
/// * `netuid`: The u16 network identifier.
///
/// * `commit`: The encrypted compressed commit.
/// The steps for this are:
/// 1. Instantiate [`WeightsTlockPayload`]
/// 2. Serialize it using the `parity_scale_codec::Encode` trait
/// 3. Encrypt it following the steps (here)[https://github.com/ideal-lab5/tle/blob/f8e6019f0fb02c380ebfa6b30efb61786dede07b/timelock/src/tlock.rs#L283-L336]
/// to produce a [`TLECiphertext<TinyBLS381>`] type.
/// 4. Serialize and compress using the `ark-serialize` `CanonicalSerialize` trait.
///
/// * `reveal_round`: The drand reveal round which will be avaliable during epoch `n+1` from the current
/// epoch.
///
/// * `commit_reveal_version`: The client (bittensor-drand) version.
#[pallet::call_index(113)]
#[pallet::weight((<T as crate::pallet::Config>::WeightInfo::commit_timelocked_weights(), DispatchClass::Normal, Pays::No))]
pub fn commit_timelocked_weights(
origin: OriginFor<T>,
netuid: NetUid,
commit: BoundedVec<u8, ConstU32<MAX_CRV3_COMMIT_SIZE_BYTES>>,
reveal_round: u64,
commit_reveal_version: u16,
) -> DispatchResult {
Self::do_commit_timelocked_weights(
origin,
netuid,
commit,
reveal_round,
commit_reveal_version,
)
}
/// Set the autostake destination hotkey for a coldkey.
///
/// The caller selects a hotkey where all future rewards
/// will be automatically staked.
///
/// # Arguments
/// * `origin`: The signature of the caller's coldkey.
///
/// * `hotkey`: The hotkey account to designate as the autostake destination.
#[pallet::call_index(114)]
#[pallet::weight(<T as crate::pallet::Config>::WeightInfo::set_coldkey_auto_stake_hotkey())]
pub fn set_coldkey_auto_stake_hotkey(
origin: OriginFor<T>,
netuid: NetUid,
hotkey: T::AccountId,
) -> DispatchResult {
let coldkey = ensure_signed(origin)?;
ensure!(Self::if_subnet_exist(netuid), Error::<T>::SubnetNotExists);
ensure!(
Uids::<T>::contains_key(netuid, &hotkey),
Error::<T>::HotKeyNotRegisteredInSubNet
);
let current_hotkey = AutoStakeDestination::<T>::get(coldkey.clone(), netuid);
if let Some(current_hotkey) = current_hotkey {
ensure!(
current_hotkey != hotkey,
Error::<T>::SameAutoStakeHotkeyAlreadySet
);
// Remove the coldkey from the old hotkey (if present)
AutoStakeDestinationColdkeys::<T>::mutate(current_hotkey.clone(), netuid, |v| {
v.retain(|c| c != &coldkey);
});
}
// Add the coldkey to the new hotkey (if not already present)
AutoStakeDestination::<T>::insert(coldkey.clone(), netuid, hotkey.clone());
AutoStakeDestinationColdkeys::<T>::mutate(hotkey.clone(), netuid, |v| {
if !v.contains(&coldkey) {
v.push(coldkey.clone());
}
});
Self::deposit_event(Event::AutoStakeDestinationSet {
coldkey,
netuid,
hotkey,
});
Ok(())
}
/// Used to commit timelock encrypted commit-reveal weight values to later be revealed for
/// a mechanism.
///
/// # Arguments
/// * `origin`: The committing hotkey.
///
/// * `netuid`: The u16 network identifier.
///
/// * `mecid`: The u8 mechanism identifier.
///
/// * `commit`: The encrypted compressed commit.
/// The steps for this are:
/// 1. Instantiate [`WeightsTlockPayload`]
/// 2. Serialize it using the `parity_scale_codec::Encode` trait
/// 3. Encrypt it following the steps (here)[https://github.com/ideal-lab5/tle/blob/f8e6019f0fb02c380ebfa6b30efb61786dede07b/timelock/src/tlock.rs#L283-L336]
/// to produce a [`TLECiphertext<TinyBLS381>`] type.
/// 4. Serialize and compress using the `ark-serialize` `CanonicalSerialize` trait.
///
/// * `reveal_round`: The drand reveal round which will be avaliable during epoch `n+1` from the current
/// epoch.
///
/// * `commit_reveal_version`: The client (bittensor-drand) version.
#[pallet::call_index(118)]
#[pallet::weight((Weight::from_parts(84_020_000, 0)
.saturating_add(T::DbWeight::get().reads(9_u64))
.saturating_add(T::DbWeight::get().writes(2)), DispatchClass::Normal, Pays::No))]
pub fn commit_timelocked_mechanism_weights(
origin: OriginFor<T>,
netuid: NetUid,
mecid: MechId,
commit: BoundedVec<u8, ConstU32<MAX_CRV3_COMMIT_SIZE_BYTES>>,
reveal_round: u64,
commit_reveal_version: u16,
) -> DispatchResult {
Self::do_commit_timelocked_mechanism_weights(
origin,
netuid,
mecid,
commit,
reveal_round,
commit_reveal_version,
)
}
/// Remove a subnetwork
/// The caller must be root
#[pallet::call_index(120)]
#[pallet::weight(Weight::from_parts(119_000_000, 0)
.saturating_add(T::DbWeight::get().reads(6))
.saturating_add(T::DbWeight::get().writes(31)))]
pub fn root_dissolve_network(origin: OriginFor<T>, netuid: NetUid) -> DispatchResult {
ensure_root(origin)?;
Self::do_dissolve_network(netuid)
}
/// Claims the root emissions for a coldkey.
/// # Arguments
/// * `origin`: The signature of the caller's coldkey.
///
/// # Events
/// * `RootClaimed`: On the successfully claiming the root emissions for a coldkey.
///
/// # Errors
/// * `InvalidSubnetNumber`: The subnet set is empty or exceeds the maximum number of claims.
///
#[pallet::call_index(121)]
#[pallet::weight(<T as crate::pallet::Config>::WeightInfo::claim_root())]
pub fn claim_root(
origin: OriginFor<T>,
subnets: BTreeSet<NetUid>,
) -> DispatchResultWithPostInfo {
let coldkey: T::AccountId = ensure_signed(origin)?;
ensure!(!subnets.is_empty(), Error::<T>::InvalidSubnetNumber);
ensure!(
subnets.len() <= MAX_SUBNET_CLAIMS,
Error::<T>::InvalidSubnetNumber
);
Self::maybe_add_coldkey_index(&coldkey);
let weight = Self::do_root_claim(coldkey, Some(subnets))?;
Ok((Some(weight), Pays::Yes).into())
}
/// Sets the root claim type for the coldkey.
/// # Arguments
/// * `origin`: The signature of the caller's coldkey.
///
/// # Events
/// * `RootClaimTypeSet`: On the successfully setting the root claim type for the coldkey.
///
#[pallet::call_index(122)]
#[pallet::weight(<T as crate::pallet::Config>::WeightInfo::set_root_claim_type())]
pub fn set_root_claim_type(
origin: OriginFor<T>,
new_root_claim_type: RootClaimTypeEnum,
) -> DispatchResult {
let coldkey: T::AccountId = ensure_signed(origin)?;
if let RootClaimTypeEnum::KeepSubnets { subnets } = &new_root_claim_type {
ensure!(!subnets.is_empty(), Error::<T>::InvalidSubnetNumber);
}
Self::maybe_add_coldkey_index(&coldkey);
Self::change_root_claim_type(&coldkey, new_root_claim_type);
Ok(())
}
/// Sets root claim number (sudo extrinsic). Zero disables auto-claim.
#[pallet::call_index(123)]
#[pallet::weight(<T as crate::pallet::Config>::WeightInfo::sudo_set_num_root_claims())]
pub fn sudo_set_num_root_claims(origin: OriginFor<T>, new_value: u64) -> DispatchResult {
ensure_root(origin)?;
ensure!(
new_value <= MAX_NUM_ROOT_CLAIMS,
Error::<T>::InvalidNumRootClaim
);
NumRootClaim::<T>::set(new_value);
Ok(())
}
/// Sets root claim threshold for subnet (sudo or owner origin).
#[pallet::call_index(124)]
#[pallet::weight(<T as crate::pallet::Config>::WeightInfo::sudo_set_root_claim_threshold())]
pub fn sudo_set_root_claim_threshold(
origin: OriginFor<T>,
netuid: NetUid,
new_value: u64,
) -> DispatchResult {
Self::ensure_subnet_owner_or_root(origin, netuid)?;
ensure!(Self::if_subnet_exist(netuid), Error::<T>::SubnetNotExists);
ensure!(
new_value <= I96F32::from(MAX_ROOT_CLAIM_THRESHOLD),
Error::<T>::InvalidRootClaimThreshold
);
RootClaimableThreshold::<T>::set(netuid, new_value.into());
Ok(())
}
/// Announces a coldkey swap using BlakeTwo256 hash of the new coldkey.
///
/// This is required before the coldkey swap can be performed
/// after the delay period.
///
/// It can be reannounced after a delay of `ColdkeySwapReannouncementDelay` following
/// the first valid execution block of the original announcement.
///
/// The dispatch origin of this call must be the original coldkey that made the announcement.
///
/// * `new_coldkey_hash`: The hash of the new coldkey using BlakeTwo256.
///
/// The `ColdkeySwapAnnounced` event is emitted on successful announcement.
///
#[pallet::call_index(125)]
#[pallet::weight(<T as crate::pallet::Config>::WeightInfo::announce_coldkey_swap())]
pub fn announce_coldkey_swap(
origin: OriginFor<T>,
new_coldkey_hash: T::Hash,
) -> DispatchResult {
let who = ensure_signed(origin)?;
let now = <frame_system::Pallet<T>>::block_number();
if let Some((when, _)) = ColdkeySwapAnnouncements::<T>::get(who.clone()) {
let reannouncement_delay = ColdkeySwapReannouncementDelay::<T>::get();
let min_when = when.saturating_add(reannouncement_delay);
ensure!(now >= min_when, Error::<T>::ColdkeySwapReannouncedTooEarly);
} else {
// Only charge the swap cost on the first announcement
let swap_cost = Self::get_key_swap_cost();
Self::charge_swap_cost(&who, swap_cost)?;
}
let delay = ColdkeySwapAnnouncementDelay::<T>::get();
let when = now.saturating_add(delay);
ColdkeySwapAnnouncements::<T>::insert(who.clone(), (when, new_coldkey_hash.clone()));
Self::deposit_event(Event::ColdkeySwapAnnounced {
who,
new_coldkey_hash,
});
Ok(())
}
/// Performs a coldkey swap if an announcement has been made.
///
/// The dispatch origin of this call must be the original coldkey that made the announcement.
///
/// * `new_coldkey`: The new coldkey to swap to. The BlakeTwo256 hash of the new coldkey must be
/// the same as the announced coldkey hash.
///
/// The `ColdkeySwapped` event is emitted on successful swap.
#[pallet::call_index(126)]
#[pallet::weight(<T as crate::pallet::Config>::WeightInfo::swap_coldkey_announced())]
pub fn swap_coldkey_announced(
origin: OriginFor<T>,
new_coldkey: T::AccountId,
) -> DispatchResult {
let who = ensure_signed(origin)?;
let (when, new_coldkey_hash) = ColdkeySwapAnnouncements::<T>::take(who.clone())
.ok_or(Error::<T>::ColdkeySwapAnnouncementNotFound)?;
ensure!(
new_coldkey_hash == T::Hashing::hash_of(&new_coldkey),
Error::<T>::AnnouncedColdkeyHashDoesNotMatch
);
let now = <frame_system::Pallet<T>>::block_number();
ensure!(now >= when, Error::<T>::ColdkeySwapTooEarly);
Self::do_swap_coldkey(&who, &new_coldkey)?;
Ok(())
}
/// Dispute a coldkey swap.
///
/// This will prevent any further actions on the coldkey swap
/// until triumvirate step in to resolve the issue.
///
/// * `coldkey`: The coldkey to dispute the swap for.
///
#[pallet::call_index(127)]
#[pallet::weight(<T as crate::pallet::Config>::WeightInfo::dispute_coldkey_swap())]
pub fn dispute_coldkey_swap(origin: OriginFor<T>) -> DispatchResult {
let coldkey = ensure_signed(origin)?;
ensure!(
ColdkeySwapAnnouncements::<T>::contains_key(&coldkey),
Error::<T>::ColdkeySwapAnnouncementNotFound
);
ensure!(
!ColdkeySwapDisputes::<T>::contains_key(&coldkey),
Error::<T>::ColdkeySwapAlreadyDisputed
);
let now = <frame_system::Pallet<T>>::block_number();
ColdkeySwapDisputes::<T>::insert(&coldkey, now);
Self::deposit_event(Event::ColdkeySwapDisputed { coldkey });
Ok(())
}
/// Reset a coldkey swap by clearing the announcement and dispute status.
///
/// The dispatch origin of this call must be root.
///
/// * `coldkey`: The coldkey to reset the swap for.
///
#[pallet::call_index(128)]
#[pallet::weight(<T as crate::pallet::Config>::WeightInfo::reset_coldkey_swap())]
pub fn reset_coldkey_swap(origin: OriginFor<T>, coldkey: T::AccountId) -> DispatchResult {
ensure_root(origin)?;
ColdkeySwapAnnouncements::<T>::remove(&coldkey);
ColdkeySwapDisputes::<T>::remove(&coldkey);
Self::deposit_event(Event::ColdkeySwapReset { who: coldkey });
Ok(())
}
/// Enables voting power tracking for a subnet.
///
/// This function can be called by the subnet owner or root.
/// When enabled, voting power EMA is updated every epoch for all validators.
/// Voting power starts at 0 and increases over epochs.
///
/// # Arguments
/// * `origin`: The origin of the call, must be subnet owner or root.
/// * `netuid`: The subnet to enable voting power tracking for.
///
/// # Errors
/// * `SubnetNotExist`: If the subnet does not exist.
/// * `NotSubnetOwner`: If the caller is not the subnet owner or root.
#[pallet::call_index(129)]
#[pallet::weight(Weight::from_parts(10_000, 0)
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(2)))]
pub fn enable_voting_power_tracking(
origin: OriginFor<T>,
netuid: NetUid,
) -> DispatchResult {
Self::ensure_subnet_owner_or_root(origin, netuid)?;
Self::do_enable_voting_power_tracking(netuid)
}
/// Schedules disabling of voting power tracking for a subnet.
///
/// This function can be called by the subnet owner or root.
/// Voting power tracking will continue for 14 days (grace period) after this call,
/// then automatically disable and clear all VotingPower entries for the subnet.
///
/// # Arguments
/// * `origin`: The origin of the call, must be subnet owner or root.
/// * `netuid`: The subnet to schedule disabling voting power tracking for.
///
/// # Errors
/// * `SubnetNotExist`: If the subnet does not exist.
/// * `NotSubnetOwner`: If the caller is not the subnet owner or root.
/// * `VotingPowerTrackingNotEnabled`: If voting power tracking is not enabled.
#[pallet::call_index(130)]
#[pallet::weight(Weight::from_parts(10_000, 0)
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(1)))]
pub fn disable_voting_power_tracking(
origin: OriginFor<T>,
netuid: NetUid,
) -> DispatchResult {
Self::ensure_subnet_owner_or_root(origin, netuid)?;
Self::do_disable_voting_power_tracking(netuid)
}
/// Sets the EMA alpha value for voting power calculation on a subnet.
///
/// This function can only be called by root (sudo).
/// Higher alpha = faster response to stake changes.
/// Alpha is stored as u64 with 18 decimal precision (1.0 = 10^18).
///
/// # Arguments
/// * `origin`: The origin of the call, must be root.
/// * `netuid`: The subnet to set the alpha for.
/// * `alpha`: The new alpha value (u64 with 18 decimal precision).
///
/// # Errors
/// * `BadOrigin`: If the origin is not root.
/// * `SubnetNotExist`: If the subnet does not exist.
/// * `InvalidVotingPowerEmaAlpha`: If alpha is greater than 10^18 (1.0).
#[pallet::call_index(131)]
#[pallet::weight(Weight::from_parts(6_000, 0)
.saturating_add(T::DbWeight::get().reads(1))
.saturating_add(T::DbWeight::get().writes(1)))]
pub fn sudo_set_voting_power_ema_alpha(
origin: OriginFor<T>,
netuid: NetUid,
alpha: u64,
) -> DispatchResult {
ensure_root(origin)?;
Self::do_set_voting_power_ema_alpha(netuid, alpha)
}
/// The extrinsic is a combination of add_stake(add_stake_limit) and burn_alpha. We buy
/// alpha token first and immediately burn the acquired amount of alpha (aka Subnet buyback).
#[pallet::call_index(132)]
#[pallet::weight(<T as crate::pallet::Config>::WeightInfo::add_stake_burn())]
pub fn add_stake_burn(
origin: OriginFor<T>,
hotkey: T::AccountId,
netuid: NetUid,
amount: TaoBalance,
limit: Option<TaoBalance>,
) -> DispatchResult {
Self::do_add_stake_burn(origin, hotkey, netuid, amount, limit)
}
/// Clears a coldkey swap announcement after the reannouncement delay if
/// it has not been disputed.
///
/// The `ColdkeySwapCleared` event is emitted on successful clear.
#[pallet::call_index(133)]
#[pallet::weight(Weight::from_parts(17_890_000, 0)
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(1)))]
pub fn clear_coldkey_swap_announcement(origin: OriginFor<T>) -> DispatchResult {
let who = ensure_signed(origin)?;
let now = <frame_system::Pallet<T>>::block_number();
let Some((when, _)) = ColdkeySwapAnnouncements::<T>::get(who.clone()) else {
return Err(Error::<T>::ColdkeySwapAnnouncementNotFound.into());
};
let delay = ColdkeySwapReannouncementDelay::<T>::get();
let min_when = when.saturating_add(delay);
ensure!(now >= min_when, Error::<T>::ColdkeySwapClearTooEarly);
ColdkeySwapAnnouncements::<T>::remove(&who);
Self::deposit_event(Event::ColdkeySwapCleared { who });
Ok(())
}
/// User register a new subnetwork via burning token, but only if the
/// on-chain burn price for this block is <= `limit_price`.
///
/// `limit_price` is expressed in the same TaoCurrency/u64 units as `Burn`.
#[pallet::call_index(134)]
#[pallet::weight((
Weight::from_parts(354_200_000, 0)
.saturating_add(T::DbWeight::get().reads(47_u64))
.saturating_add(T::DbWeight::get().writes(40_u64)),
DispatchClass::Normal,
Pays::Yes
))]
pub fn register_limit(
origin: OriginFor<T>,
netuid: NetUid,
hotkey: T::AccountId,
limit_price: u64,
) -> DispatchResult {
Self::do_register_limit(origin, netuid, hotkey, limit_price)
}
/// Allows a root validator to toggle auto parent delegation
/// for new subnets owner hotkey
#[pallet::call_index(135)]
#[pallet::weight((<T as crate::pallet::Config>::WeightInfo::set_auto_parent_delegation_enabled(), DispatchClass::Normal, Pays::Yes))]
pub fn set_auto_parent_delegation_enabled(
origin: OriginFor<T>,
hotkey: T::AccountId,
enabled: bool,
) -> DispatchResult {
let coldkey = ensure_signed(origin)?;
ensure!(
Self::coldkey_owns_hotkey(&coldkey, &hotkey),
Error::<T>::NonAssociatedColdKey
);
ensure!(
Self::is_hotkey_registered_on_network(NetUid::ROOT, &hotkey),
Error::<T>::HotKeyNotRegisteredInSubNet
);
AutoParentDelegationEnabled::<T>::insert(&hotkey, enabled);
Self::deposit_event(Event::AutoParentDelegationEnabledSet { hotkey, enabled });
Ok(())
}
/// Locks stake on a subnet to a specific hotkey, building conviction over time.
///
/// If no lock exists for (coldkey, subnet), a new one is created.
/// If a lock exists, the destination hotkey must match the existing lock's hotkey.
/// Top-up adds to the locked amount after rolling the lock state forward.
///
/// # Arguments
/// * `origin`: Must be signed by the coldkey.
/// * `hotkey`: The hotkey to lock stake to.
/// * `netuid`: The subnet on which to lock.
/// * `amount`: The alpha amount to lock.
#[pallet::call_index(136)]
#[pallet::weight(<T as crate::pallet::Config>::WeightInfo::lock_stake())]
pub fn lock_stake(
origin: OriginFor<T>,
hotkey: T::AccountId,
netuid: NetUid,
amount: AlphaBalance,
) -> DispatchResult {
let coldkey = ensure_signed(origin)?;
Self::do_lock_stake(&coldkey, netuid, &hotkey, amount)
}
/// Moves an existing lock for a coldkey on a subnet from one hotkey to another.
///
/// The lock is rolled forward to the current block before switching the
/// associated hotkey, preserving the decayed locked mass. The conviction is
/// reset to zero.
///
/// # Arguments
/// * `origin`: Must be signed by the coldkey that owns the lock.
/// * `destination_hotkey`: The hotkey the lock should target after the move.
/// * `netuid`: The subnet on which the lock exists.
/// # Errors
/// * `Error::<T>::NoExistingLock`: If no lock exists for the given coldkey and subnet.
#[pallet::call_index(137)]
#[pallet::weight(<T as crate::pallet::Config>::WeightInfo::move_lock())]
pub fn move_lock(
origin: OriginFor<T>,
destination_hotkey: T::AccountId,
netuid: NetUid,
) -> DispatchResult {
let coldkey = ensure_signed(origin)?;
Self::do_move_lock(&coldkey, &destination_hotkey, netuid)
}
/// Sets or clears the caller's perpetual lock flag for a subnet.
///
/// Locks decay by default. When enabled, the caller's individual lock
/// does not unlock through locked-mass decay. Passing `false` returns
/// the caller's lock to normal decay.
#[pallet::call_index(138)]
#[pallet::weight(<T as frame_system::Config>::DbWeight::get().reads_writes(4, 3))]
pub fn set_perpetual_lock(
origin: OriginFor<T>,
netuid: NetUid,
enabled: bool,
) -> DispatchResult {
let coldkey = ensure_signed(origin)?;
Self::do_set_perpetual_lock(&coldkey, netuid, enabled)
}
/// Deprecated compatibility entry point retained for call-index stability.
/// This call charges a fee, returns success, and does not modify state.
/// Use `AdminUtils::sudo_set_tempo` to change subnet tempo.
#[deprecated(note = "Use `AdminUtils::sudo_set_tempo` instead")]
#[pallet::call_index(139)]
#[pallet::weight((Weight::from_parts(0, 0), DispatchClass::Normal, Pays::Yes))]
pub fn set_tempo(_origin: OriginFor<T>, _netuid: NetUid, _tempo: u16) -> DispatchResult {
Ok(())
}
/// Deprecated compatibility entry point retained for call-index stability.
/// This call charges a fee, returns success, and does not modify state.
/// Use `AdminUtils::sudo_set_activity_cutoff_factor` to change the factor.
#[deprecated(note = "Use `AdminUtils::sudo_set_activity_cutoff_factor` instead")]
#[pallet::call_index(140)]
#[pallet::weight((Weight::from_parts(0, 0), DispatchClass::Normal, Pays::Yes))]
pub fn set_activity_cutoff_factor(
_origin: OriginFor<T>,
_netuid: NetUid,
_factor_milli: u32,
) -> DispatchResult {
Ok(())
}
/// Owner-side `trigger_epoch`. Schedules an epoch to fire after `AdminFreezeWindow`
/// blocks. Rate-limited via the existing `OwnerHyperparamUpdate` pattern.
#[pallet::call_index(141)]
#[pallet::weight(<T as crate::pallet::Config>::WeightInfo::trigger_epoch())]
pub fn trigger_epoch(origin: OriginFor<T>, netuid: NetUid) -> DispatchResult {
Self::do_trigger_epoch(origin, netuid)
}
/// Sets or clears whether the caller rejects incoming locked alpha.
///
/// Coldkeys reject locked alpha by default. Passing `false` opts the
/// caller into receiving locked alpha from stake transfers or coldkey
/// swaps.
#[pallet::call_index(142)]
#[pallet::weight((
<T as frame_system::Config>::DbWeight::get().reads_writes(1, 1),
DispatchClass::Normal,
Pays::Yes
))]
pub fn set_reject_locked_alpha(origin: OriginFor<T>, enabled: bool) -> DispatchResult {
let coldkey = ensure_signed(origin)?;
Self::set_accept_locked_alpha(&coldkey, !enabled);
Self::deposit_event(Event::RejectLockedAlphaUpdated { coldkey, enabled });
Ok(())
}
}
}