Skip to content

Commit e90205b

Browse files
authored
Merge pull request opentensor#1178 from opentensor/feat/transfer-stake
Transfer Coldkey Stake
2 parents 09e205b + f0f4c6b commit e90205b

File tree

4 files changed

+409
-0
lines changed

4 files changed

+409
-0
lines changed

pallets/subtensor/src/macros/dispatches.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1606,5 +1606,49 @@ mod dispatches {
16061606
alpha_amount,
16071607
)
16081608
}
1609+
1610+
/// Transfers a specified amount of stake from one coldkey to another, optionally across subnets,
1611+
/// while keeping the same hotkey.
1612+
///
1613+
/// # Arguments
1614+
/// * `origin` - The origin of the transaction, which must be signed by the `origin_coldkey`.
1615+
/// * `destination_coldkey` - The coldkey to which the stake is transferred.
1616+
/// * `hotkey` - The hotkey associated with the stake.
1617+
/// * `origin_netuid` - The network/subnet ID to move stake from.
1618+
/// * `destination_netuid` - The network/subnet ID to move stake to (for cross-subnet transfer).
1619+
/// * `alpha_amount` - The amount of stake to transfer.
1620+
///
1621+
/// # Weight
1622+
/// Uses a fixed weight of 3_000_000 (plus any DB write overhead).
1623+
///
1624+
/// # Errors
1625+
/// Returns an error if:
1626+
/// * The origin is not signed by the correct coldkey.
1627+
/// * Either subnet does not exist.
1628+
/// * The hotkey does not exist.
1629+
/// * There is insufficient stake on `(origin_coldkey, hotkey, origin_netuid)`.
1630+
/// * The transfer amount is below the minimum stake requirement.
1631+
///
1632+
/// # Events
1633+
/// May emit a `StakeTransferred` event on success.
1634+
#[pallet::call_index(86)]
1635+
#[pallet::weight((Weight::from_parts(3_000_000, 0).saturating_add(T::DbWeight::get().writes(1)), DispatchClass::Operational, Pays::No))]
1636+
pub fn transfer_stake(
1637+
origin: T::RuntimeOrigin,
1638+
destination_coldkey: T::AccountId,
1639+
hotkey: T::AccountId,
1640+
origin_netuid: u16,
1641+
destination_netuid: u16,
1642+
alpha_amount: u64,
1643+
) -> DispatchResult {
1644+
Self::do_transfer_stake(
1645+
origin,
1646+
destination_coldkey,
1647+
hotkey,
1648+
origin_netuid,
1649+
destination_netuid,
1650+
alpha_amount,
1651+
)
1652+
}
16091653
}
16101654
}

pallets/subtensor/src/macros/events.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,5 +252,10 @@ mod events {
252252
///
253253
/// - **error**: The dispatch error emitted by the failed item.
254254
BatchWeightItemFailed(sp_runtime::DispatchError),
255+
256+
/// Stake has been transferred from one coldkey to another on the same subnet.
257+
/// Parameters:
258+
/// (origin_coldkey, destination_coldkey, hotkey, origin_netuid, destination_netuid, amount)
259+
StakeTransferred(T::AccountId, T::AccountId, T::AccountId, u16, u16, u64),
255260
}
256261
}

pallets/subtensor/src/staking/move_stake.rs

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,4 +110,111 @@ impl<T: Config> Pallet<T> {
110110
// -- 10. Ok and return.
111111
Ok(())
112112
}
113+
114+
/// Transfers stake from one coldkey to another, optionally moving from one subnet to another,
115+
/// while keeping the same hotkey.
116+
///
117+
/// # Arguments
118+
/// * `origin` - The origin of the transaction, which must be signed by the `origin_coldkey`.
119+
/// * `destination_coldkey` - The account ID of the coldkey to which the stake is being transferred.
120+
/// * `hotkey` - The account ID of the hotkey associated with this stake.
121+
/// * `origin_netuid` - The network ID (subnet) from which the stake is being transferred.
122+
/// * `destination_netuid` - The network ID (subnet) to which the stake is being transferred.
123+
/// * `alpha_amount` - The amount of stake to transfer.
124+
///
125+
/// # Returns
126+
/// * `DispatchResult` - Indicates success or failure.
127+
///
128+
/// # Errors
129+
/// This function will return an error if:
130+
/// * The transaction is not signed by the `origin_coldkey`.
131+
/// * The subnet (`origin_netuid` or `destination_netuid`) does not exist.
132+
/// * The `hotkey` does not exist.
133+
/// * The `(origin_coldkey, hotkey, origin_netuid)` does not have enough stake for `alpha_amount`.
134+
/// * The amount to be transferred is below the minimum stake requirement.
135+
/// * There is a failure in staking or unstaking logic.
136+
///
137+
/// # Events
138+
/// Emits a `StakeTransferred` event upon successful completion of the transfer.
139+
pub fn do_transfer_stake(
140+
origin: T::RuntimeOrigin,
141+
destination_coldkey: T::AccountId,
142+
hotkey: T::AccountId,
143+
origin_netuid: u16,
144+
destination_netuid: u16,
145+
alpha_amount: u64,
146+
) -> dispatch::DispatchResult {
147+
// 1. Ensure the extrinsic is signed by the origin_coldkey.
148+
let coldkey = ensure_signed(origin)?;
149+
150+
// 2. Ensure both subnets exist.
151+
ensure!(
152+
Self::if_subnet_exist(origin_netuid),
153+
Error::<T>::SubnetNotExists
154+
);
155+
ensure!(
156+
Self::if_subnet_exist(destination_netuid),
157+
Error::<T>::SubnetNotExists
158+
);
159+
160+
// 3. Check that the hotkey exists.
161+
ensure!(
162+
Self::hotkey_account_exists(&hotkey),
163+
Error::<T>::HotKeyAccountNotExists
164+
);
165+
166+
// 4. Check that the signed coldkey actually owns the given hotkey.
167+
ensure!(
168+
Self::coldkey_owns_hotkey(&coldkey, &hotkey),
169+
Error::<T>::NonAssociatedColdKey
170+
);
171+
172+
// 5. Get current stake.
173+
let origin_alpha =
174+
Self::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, origin_netuid);
175+
ensure!(
176+
alpha_amount <= origin_alpha,
177+
Error::<T>::NotEnoughStakeToWithdraw
178+
);
179+
180+
// 6. Unstake from the origin coldkey; this returns an amount of TAO.
181+
let origin_tao = Self::unstake_from_subnet(&hotkey, &coldkey, origin_netuid, alpha_amount);
182+
183+
// 7. Ensure the returned TAO meets a minimum stake requirement (if required).
184+
ensure!(
185+
origin_tao >= DefaultMinStake::<T>::get(),
186+
Error::<T>::AmountTooLow
187+
);
188+
189+
// 8. Stake the TAO into `(destination_coldkey, hotkey)` on the destination subnet.
190+
// Create the account if it does not exist.
191+
Self::stake_into_subnet(
192+
&hotkey,
193+
&destination_coldkey,
194+
destination_netuid,
195+
origin_tao,
196+
);
197+
198+
// 9. Emit an event for logging/monitoring.
199+
log::info!(
200+
"StakeTransferred(origin_coldkey: {:?}, destination_coldkey: {:?}, hotkey: {:?}, origin_netuid: {:?}, destination_netuid: {:?}, amount: {:?})",
201+
coldkey,
202+
destination_coldkey,
203+
hotkey,
204+
origin_netuid,
205+
destination_netuid,
206+
origin_tao
207+
);
208+
Self::deposit_event(Event::StakeTransferred(
209+
coldkey,
210+
destination_coldkey,
211+
hotkey,
212+
origin_netuid,
213+
destination_netuid,
214+
origin_tao,
215+
));
216+
217+
// 10. Return success.
218+
Ok(())
219+
}
113220
}

0 commit comments

Comments
 (0)