Skip to content

Commit 621725f

Browse files
author
Samuel Dare
committed
chore: unstake + transfer
1 parent 0855df7 commit 621725f

File tree

6 files changed

+448
-3
lines changed

6 files changed

+448
-3
lines changed

justfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,11 @@ clippy:
3131

3232
clippy-fix:
3333
@echo "Running cargo clippy with automatic fixes on potentially dirty code..."
34-
cargo +{{RUSTV}} clippy --fix --allow-dirty --workspace --all-targets -- \
34+
cargo +{{RUSTV}} clippy --fix --allow-dirty --allow-staged --workspace --all-targets -- \
3535
-A clippy::todo \
3636
-A clippy::unimplemented \
3737
-A clippy::indexing_slicing
38-
38+
3939
fix:
4040
@echo "Running cargo fix..."
4141
cargo +{{RUSTV}} fix --workspace

pallets/subtensor/src/errors.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,5 +142,9 @@ mod errors {
142142
NotExistColdkey,
143143
/// The coldkey balance is not enough to pay for the swap
144144
NotEnoughBalanceToPaySwapColdKey,
145+
/// No balance to transfer
146+
NoBalanceToTransfer,
147+
/// Same coldkey
148+
SameColdkey,
145149
}
146150
}

pallets/subtensor/src/events.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,5 +139,20 @@ mod events {
139139
/// the account ID of new coldkey
140140
new_coldkey: T::AccountId,
141141
},
142+
/// All balance of a hotkey has been unstaked and transferred to a new coldkey
143+
AllBalanceUnstakedAndTransferredToNewColdkey {
144+
/// The account ID of the current coldkey
145+
current_coldkey: T::AccountId,
146+
/// The account ID of the new coldkey
147+
new_coldkey: T::AccountId,
148+
/// The account ID of the hotkey
149+
hotkey: T::AccountId,
150+
/// The current stake of the hotkey
151+
current_stake: u64,
152+
/// The total balance of the hotkey
153+
total_balance: <<T as Config>::Currency as fungible::Inspect<
154+
<T as frame_system::Config>::AccountId,
155+
>>::Balance,
156+
},
142157
}
143158
}

pallets/subtensor/src/lib.rs

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1982,7 +1982,21 @@ pub mod pallet {
19821982
Self::do_swap_hotkey(origin, &hotkey, &new_hotkey)
19831983
}
19841984

1985-
/// The extrinsic for user to change the coldkey
1985+
/// The extrinsic for user to change the coldkey associated with their account.
1986+
///
1987+
/// # Arguments
1988+
///
1989+
/// * `origin` - The origin of the call, must be signed by the old coldkey.
1990+
/// * `old_coldkey` - The current coldkey associated with the account.
1991+
/// * `new_coldkey` - The new coldkey to be associated with the account.
1992+
///
1993+
/// # Returns
1994+
///
1995+
/// Returns a `DispatchResultWithPostInfo` indicating success or failure of the operation.
1996+
///
1997+
/// # Weight
1998+
///
1999+
/// Weight is calculated based on the number of database reads and writes.
19862000
#[pallet::call_index(71)]
19872001
#[pallet::weight((Weight::from_parts(1_940_000_000, 0)
19882002
.saturating_add(T::DbWeight::get().reads(272))
@@ -1995,6 +2009,34 @@ pub mod pallet {
19952009
Self::do_swap_coldkey(origin, &old_coldkey, &new_coldkey)
19962010
}
19972011

2012+
/// Unstakes all tokens associated with a hotkey and transfers them to a new coldkey.
2013+
///
2014+
/// # Arguments
2015+
///
2016+
/// * `origin` - The origin of the call, must be signed by the current coldkey.
2017+
/// * `hotkey` - The hotkey associated with the stakes to be unstaked.
2018+
/// * `new_coldkey` - The new coldkey to receive the unstaked tokens.
2019+
///
2020+
/// # Returns
2021+
///
2022+
/// Returns a `DispatchResult` indicating success or failure of the operation.
2023+
///
2024+
/// # Weight
2025+
///
2026+
/// Weight is calculated based on the number of database reads and writes.
2027+
#[pallet::call_index(72)]
2028+
#[pallet::weight((Weight::from_parts(1_940_000_000, 0)
2029+
.saturating_add(T::DbWeight::get().reads(272))
2030+
.saturating_add(T::DbWeight::get().writes(527)), DispatchClass::Operational, Pays::No))]
2031+
pub fn unstake_all_and_transfer_to_new_coldkey(
2032+
origin: OriginFor<T>,
2033+
hotkey: T::AccountId,
2034+
new_coldkey: T::AccountId,
2035+
) -> DispatchResult {
2036+
let current_coldkey = ensure_signed(origin)?;
2037+
Self::do_unstake_all_and_transfer_to_new_coldkey(current_coldkey, hotkey, new_coldkey)
2038+
}
2039+
19982040
// ---- SUDO ONLY FUNCTIONS ------------------------------------------------------------
19992041

20002042
// ==================================

pallets/subtensor/src/staking.rs

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use super::*;
2+
use dispatch::RawOrigin;
23
use frame_support::{
34
storage::IterableStorageDoubleMap,
45
traits::{
@@ -9,6 +10,7 @@ use frame_support::{
910
Imbalance,
1011
},
1112
};
13+
use num_traits::Zero;
1214

1315
impl<T: Config> Pallet<T> {
1416
/// ---- The implementation for the extrinsic become_delegate: signals that this hotkey allows delegated stake.
@@ -827,4 +829,96 @@ impl<T: Config> Pallet<T> {
827829
Self::add_balance_to_coldkey_account(&delegate_coldkey_i, stake_i);
828830
}
829831
}
832+
833+
/// Unstakes all tokens associated with a hotkey and transfers them to a new coldkey.
834+
///
835+
/// This function performs the following operations:
836+
/// 1. Verifies that the hotkey exists and is owned by the current coldkey.
837+
/// 2. Ensures that the new coldkey is different from the current one.
838+
/// 3. Unstakes all balance if there's any stake.
839+
/// 4. Transfers the entire balance of the hotkey to the new coldkey.
840+
/// 5. Verifies the success of the transfer and handles partial transfers if necessary.
841+
///
842+
/// # Arguments
843+
///
844+
/// * `current_coldkey` - The AccountId of the current coldkey.
845+
/// * `hotkey` - The AccountId of the hotkey whose balance is being unstaked and transferred.
846+
/// * `new_coldkey` - The AccountId of the new coldkey to receive the unstaked tokens.
847+
///
848+
/// # Returns
849+
///
850+
/// Returns a `DispatchResult` indicating success or failure of the operation.
851+
///
852+
/// # Errors
853+
///
854+
/// This function will return an error if:
855+
/// * The hotkey account does not exist.
856+
/// * The current coldkey does not own the hotkey.
857+
/// * The new coldkey is the same as the current coldkey.
858+
/// * There is no balance to transfer.
859+
/// * The transfer fails or is only partially successful.
860+
///
861+
/// # Events
862+
///
863+
/// Emits an `AllBalanceUnstakedAndTransferredToNewColdkey` event upon successful execution.
864+
/// Emits a `PartialBalanceTransferredToNewColdkey` event if only a partial transfer is successful.
865+
///
866+
pub fn do_unstake_all_and_transfer_to_new_coldkey(
867+
current_coldkey: T::AccountId,
868+
hotkey: T::AccountId,
869+
new_coldkey: T::AccountId,
870+
) -> DispatchResult {
871+
// Ensure the hotkey exists and is owned by the current coldkey
872+
ensure!(
873+
Self::hotkey_account_exists(&hotkey),
874+
Error::<T>::HotKeyAccountNotExists
875+
);
876+
ensure!(
877+
Self::coldkey_owns_hotkey(&current_coldkey, &hotkey),
878+
Error::<T>::NonAssociatedColdKey
879+
);
880+
881+
// Ensure the new coldkey is different from the current one
882+
ensure!(current_coldkey != new_coldkey, Error::<T>::SameColdkey);
883+
884+
// Get the current stake
885+
let current_stake: u64 = Self::get_stake_for_coldkey_and_hotkey(&current_coldkey, &hotkey);
886+
887+
// Unstake all balance if there's any stake
888+
if current_stake > 0 {
889+
Self::do_remove_stake(
890+
RawOrigin::Signed(current_coldkey.clone()).into(),
891+
hotkey.clone(),
892+
current_stake,
893+
)?;
894+
}
895+
896+
// Get the total balance of the current coldkey account
897+
// let total_balance: <<T as Config>::Currency as fungible::Inspect<<T as system::Config>::AccountId>>::Balance = T::Currency::total_balance(&current_coldkey);
898+
899+
let total_balance = Self::get_coldkey_balance(&current_coldkey);
900+
log::info!("Total Bank Balance: {:?}", total_balance);
901+
902+
// Ensure there's a balance to transfer
903+
ensure!(!total_balance.is_zero(), Error::<T>::NoBalanceToTransfer);
904+
905+
// Attempt to transfer the entire total balance to the new coldkey
906+
T::Currency::transfer(
907+
&current_coldkey,
908+
&new_coldkey,
909+
total_balance,
910+
Preservation::Expendable,
911+
)?;
912+
913+
// Emit the event
914+
Self::deposit_event(Event::AllBalanceUnstakedAndTransferredToNewColdkey {
915+
current_coldkey: current_coldkey.clone(),
916+
new_coldkey: new_coldkey.clone(),
917+
hotkey: hotkey.clone(),
918+
current_stake,
919+
total_balance,
920+
});
921+
922+
Ok(())
923+
}
830924
}

0 commit comments

Comments
 (0)