Skip to content

Commit

Permalink
Merge pull request #1181 from opentensor/feat/swap-stake
Browse files Browse the repository at this point in the history
Swap Stake Between Subnets
  • Loading branch information
sam0x17 authored Jan 22, 2025
2 parents ecb6196 + 7d43c7e commit 8f4bcff
Show file tree
Hide file tree
Showing 4 changed files with 504 additions and 3 deletions.
44 changes: 41 additions & 3 deletions pallets/subtensor/src/macros/dispatches.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1618,9 +1618,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 @@ -1650,5 +1647,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 @@ -257,5 +257,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 @@ -223,4 +223,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

0 comments on commit 8f4bcff

Please sign in to comment.