Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 41 additions & 3 deletions pallets/subtensor/src/macros/dispatches.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1608,9 +1608,6 @@ mod dispatches {
/// * `destination_netuid` - The network/subnet ID to move stake to (for cross-subnet transfer).
/// * `alpha_amount` - The amount of stake to transfer.
///
/// # Weight
/// Uses a fixed weight of 3_000_000 (plus any DB write overhead).
///
/// # Errors
/// Returns an error if:
/// * The origin is not signed by the correct coldkey.
Expand Down Expand Up @@ -1640,5 +1637,46 @@ mod dispatches {
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
/// Returns an error if:
/// * The transaction is not signed by the correct coldkey (i.e., `coldkey_owns_hotkey` fails).
/// * Either `origin_netuid` or `destination_netuid` does not exist.
/// * The hotkey does not exist.
/// * There is insufficient stake on `(coldkey, hotkey, origin_netuid)`.
/// * The swap amount is below the minimum stake requirement.
///
/// # Events
/// May emit a `StakeSwapped` event on success.
#[pallet::call_index(87)]
#[pallet::weight((
Weight::from_parts(3_000_000, 0).saturating_add(T::DbWeight::get().writes(1)),
DispatchClass::Operational,
Pays::No
))]
pub fn swap_stake(
origin: T::RuntimeOrigin,
hotkey: T::AccountId,
origin_netuid: u16,
destination_netuid: u16,
alpha_amount: u64,
) -> DispatchResult {
Self::do_swap_stake(
origin,
hotkey,
origin_netuid,
destination_netuid,
alpha_amount,
)
}
}
}
6 changes: 6 additions & 0 deletions pallets/subtensor/src/macros/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,5 +253,11 @@ mod events {
/// Parameters:
/// (origin_coldkey, destination_coldkey, hotkey, origin_netuid, destination_netuid, amount)
StakeTransferred(T::AccountId, T::AccountId, T::AccountId, u16, u16, u64),

/// Stake has been swapped from one subnet to another for the same coldkey-hotkey pair.
///
/// Parameters:
/// (coldkey, hotkey, origin_netuid, destination_netuid, amount)
StakeSwapped(T::AccountId, T::AccountId, u16, u16, u64),
}
}
97 changes: 97 additions & 0 deletions pallets/subtensor/src/staking/move_stake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,4 +217,101 @@ impl<T: Config> Pallet<T> {
// 10. Return success.
Ok(())
}

/// Swaps a specified amount of stake for the same `(coldkey, hotkey)` pair from one subnet
/// (`origin_netuid`) to another (`destination_netuid`).
///
/// # 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 subnet ID from which stake is removed.
/// * `destination_netuid` - The subnet ID to which stake is added.
/// * `alpha_amount` - The amount of stake to swap.
///
/// # Returns
/// * `DispatchResult` - Indicates success or failure.
///
/// # Errors
/// This function returns an error if:
/// * The origin is not signed by the correct coldkey (i.e., not associated with `hotkey`).
/// * Either the `origin_netuid` or the `destination_netuid` does not exist.
/// * The specified `hotkey` does not exist.
/// * The `(coldkey, hotkey, origin_netuid)` does not have enough stake (`alpha_amount`).
/// * The unstaked amount is below `DefaultMinStake`.
///
/// # Events
/// Emits a `StakeSwapped` event upon successful completion.
pub fn do_swap_stake(
origin: T::RuntimeOrigin,
hotkey: T::AccountId,
origin_netuid: u16,
destination_netuid: u16,
alpha_amount: u64,
) -> dispatch::DispatchResult {
// 1. Ensure the extrinsic is signed by the coldkey.
let coldkey = ensure_signed(origin)?;

// 2. Check that both subnets exist.
ensure!(
Self::if_subnet_exist(origin_netuid),
Error::<T>::SubnetNotExists
);
ensure!(
Self::if_subnet_exist(destination_netuid),
Error::<T>::SubnetNotExists
);

// 3. Check that the hotkey exists.
ensure!(
Self::hotkey_account_exists(&hotkey),
Error::<T>::HotKeyAccountNotExists
);

// 4. Ensure this coldkey actually owns the hotkey.
ensure!(
Self::coldkey_owns_hotkey(&coldkey, &hotkey),
Error::<T>::NonAssociatedColdKey
);

// 5. Ensure there is enough stake in the origin subnet.
let origin_alpha =
Self::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, origin_netuid);
ensure!(
alpha_amount <= origin_alpha,
Error::<T>::NotEnoughStakeToWithdraw
);

// 6. Unstake from the origin subnet, returning TAO (or a 1:1 equivalent).
let tao_unstaked =
Self::unstake_from_subnet(&hotkey, &coldkey, origin_netuid, alpha_amount);

// 7. Check that the unstaked amount is above the minimum stake threshold.
ensure!(
tao_unstaked >= DefaultMinStake::<T>::get(),
Error::<T>::AmountTooLow
);

// 8. Stake the unstaked amount into the destination subnet, using the same coldkey/hotkey.
Self::stake_into_subnet(&hotkey, &coldkey, destination_netuid, tao_unstaked);

// 9. Emit an event for logging.
log::info!(
"StakeSwapped(coldkey: {:?}, hotkey: {:?}, origin_netuid: {:?}, destination_netuid: {:?}, amount: {:?})",
coldkey,
hotkey,
origin_netuid,
destination_netuid,
tao_unstaked
);
Self::deposit_event(Event::StakeSwapped(
coldkey,
hotkey,
origin_netuid,
destination_netuid,
tao_unstaked,
));

// 10. Return success.
Ok(())
}
}
Loading