Skip to content

Commit 8f4bcff

Browse files
authored
Merge pull request #1181 from opentensor/feat/swap-stake
Swap Stake Between Subnets
2 parents ecb6196 + 7d43c7e commit 8f4bcff

File tree

4 files changed

+504
-3
lines changed

4 files changed

+504
-3
lines changed

pallets/subtensor/src/macros/dispatches.rs

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1618,9 +1618,6 @@ mod dispatches {
16181618
/// * `destination_netuid` - The network/subnet ID to move stake to (for cross-subnet transfer).
16191619
/// * `alpha_amount` - The amount of stake to transfer.
16201620
///
1621-
/// # Weight
1622-
/// Uses a fixed weight of 3_000_000 (plus any DB write overhead).
1623-
///
16241621
/// # Errors
16251622
/// Returns an error if:
16261623
/// * The origin is not signed by the correct coldkey.
@@ -1650,5 +1647,46 @@ mod dispatches {
16501647
alpha_amount,
16511648
)
16521649
}
1650+
1651+
/// Swaps a specified amount of stake from one subnet to another, while keeping the same coldkey and hotkey.
1652+
///
1653+
/// # Arguments
1654+
/// * `origin` - The origin of the transaction, which must be signed by the coldkey that owns the `hotkey`.
1655+
/// * `hotkey` - The hotkey whose stake is being swapped.
1656+
/// * `origin_netuid` - The network/subnet ID from which stake is removed.
1657+
/// * `destination_netuid` - The network/subnet ID to which stake is added.
1658+
/// * `alpha_amount` - The amount of stake to swap.
1659+
///
1660+
/// # Errors
1661+
/// Returns an error if:
1662+
/// * The transaction is not signed by the correct coldkey (i.e., `coldkey_owns_hotkey` fails).
1663+
/// * Either `origin_netuid` or `destination_netuid` does not exist.
1664+
/// * The hotkey does not exist.
1665+
/// * There is insufficient stake on `(coldkey, hotkey, origin_netuid)`.
1666+
/// * The swap amount is below the minimum stake requirement.
1667+
///
1668+
/// # Events
1669+
/// May emit a `StakeSwapped` event on success.
1670+
#[pallet::call_index(87)]
1671+
#[pallet::weight((
1672+
Weight::from_parts(3_000_000, 0).saturating_add(T::DbWeight::get().writes(1)),
1673+
DispatchClass::Operational,
1674+
Pays::No
1675+
))]
1676+
pub fn swap_stake(
1677+
origin: T::RuntimeOrigin,
1678+
hotkey: T::AccountId,
1679+
origin_netuid: u16,
1680+
destination_netuid: u16,
1681+
alpha_amount: u64,
1682+
) -> DispatchResult {
1683+
Self::do_swap_stake(
1684+
origin,
1685+
hotkey,
1686+
origin_netuid,
1687+
destination_netuid,
1688+
alpha_amount,
1689+
)
1690+
}
16531691
}
16541692
}

pallets/subtensor/src/macros/events.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,5 +257,11 @@ mod events {
257257
/// Parameters:
258258
/// (origin_coldkey, destination_coldkey, hotkey, origin_netuid, destination_netuid, amount)
259259
StakeTransferred(T::AccountId, T::AccountId, T::AccountId, u16, u16, u64),
260+
261+
/// Stake has been swapped from one subnet to another for the same coldkey-hotkey pair.
262+
///
263+
/// Parameters:
264+
/// (coldkey, hotkey, origin_netuid, destination_netuid, amount)
265+
StakeSwapped(T::AccountId, T::AccountId, u16, u16, u64),
260266
}
261267
}

pallets/subtensor/src/staking/move_stake.rs

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,4 +223,101 @@ impl<T: Config> Pallet<T> {
223223
// 10. Return success.
224224
Ok(())
225225
}
226+
227+
/// Swaps a specified amount of stake for the same `(coldkey, hotkey)` pair from one subnet
228+
/// (`origin_netuid`) to another (`destination_netuid`).
229+
///
230+
/// # Arguments
231+
/// * `origin` - The origin of the transaction, which must be signed by the coldkey that owns the hotkey.
232+
/// * `hotkey` - The hotkey whose stake is being swapped.
233+
/// * `origin_netuid` - The subnet ID from which stake is removed.
234+
/// * `destination_netuid` - The subnet ID to which stake is added.
235+
/// * `alpha_amount` - The amount of stake to swap.
236+
///
237+
/// # Returns
238+
/// * `DispatchResult` - Indicates success or failure.
239+
///
240+
/// # Errors
241+
/// This function returns an error if:
242+
/// * The origin is not signed by the correct coldkey (i.e., not associated with `hotkey`).
243+
/// * Either the `origin_netuid` or the `destination_netuid` does not exist.
244+
/// * The specified `hotkey` does not exist.
245+
/// * The `(coldkey, hotkey, origin_netuid)` does not have enough stake (`alpha_amount`).
246+
/// * The unstaked amount is below `DefaultMinStake`.
247+
///
248+
/// # Events
249+
/// Emits a `StakeSwapped` event upon successful completion.
250+
pub fn do_swap_stake(
251+
origin: T::RuntimeOrigin,
252+
hotkey: T::AccountId,
253+
origin_netuid: u16,
254+
destination_netuid: u16,
255+
alpha_amount: u64,
256+
) -> dispatch::DispatchResult {
257+
// 1. Ensure the extrinsic is signed by the coldkey.
258+
let coldkey = ensure_signed(origin)?;
259+
260+
// 2. Check that both subnets exist.
261+
ensure!(
262+
Self::if_subnet_exist(origin_netuid),
263+
Error::<T>::SubnetNotExists
264+
);
265+
ensure!(
266+
Self::if_subnet_exist(destination_netuid),
267+
Error::<T>::SubnetNotExists
268+
);
269+
270+
// 3. Check that the hotkey exists.
271+
ensure!(
272+
Self::hotkey_account_exists(&hotkey),
273+
Error::<T>::HotKeyAccountNotExists
274+
);
275+
276+
// 4. Ensure this coldkey actually owns the hotkey.
277+
ensure!(
278+
Self::coldkey_owns_hotkey(&coldkey, &hotkey),
279+
Error::<T>::NonAssociatedColdKey
280+
);
281+
282+
// 5. Ensure there is enough stake in the origin subnet.
283+
let origin_alpha =
284+
Self::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, origin_netuid);
285+
ensure!(
286+
alpha_amount <= origin_alpha,
287+
Error::<T>::NotEnoughStakeToWithdraw
288+
);
289+
290+
// 6. Unstake from the origin subnet, returning TAO (or a 1:1 equivalent).
291+
let tao_unstaked =
292+
Self::unstake_from_subnet(&hotkey, &coldkey, origin_netuid, alpha_amount);
293+
294+
// 7. Check that the unstaked amount is above the minimum stake threshold.
295+
ensure!(
296+
tao_unstaked >= DefaultMinStake::<T>::get(),
297+
Error::<T>::AmountTooLow
298+
);
299+
300+
// 8. Stake the unstaked amount into the destination subnet, using the same coldkey/hotkey.
301+
Self::stake_into_subnet(&hotkey, &coldkey, destination_netuid, tao_unstaked);
302+
303+
// 9. Emit an event for logging.
304+
log::info!(
305+
"StakeSwapped(coldkey: {:?}, hotkey: {:?}, origin_netuid: {:?}, destination_netuid: {:?}, amount: {:?})",
306+
coldkey,
307+
hotkey,
308+
origin_netuid,
309+
destination_netuid,
310+
tao_unstaked
311+
);
312+
Self::deposit_event(Event::StakeSwapped(
313+
coldkey,
314+
hotkey,
315+
origin_netuid,
316+
destination_netuid,
317+
tao_unstaked,
318+
));
319+
320+
// 10. Return success.
321+
Ok(())
322+
}
226323
}

0 commit comments

Comments
 (0)