Skip to content

Commit bffbcd7

Browse files
committed
Implement remove_stake_limit
1 parent baf4a95 commit bffbcd7

File tree

4 files changed

+363
-33
lines changed

4 files changed

+363
-33
lines changed

pallets/subtensor/src/staking/add_stake.rs

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ impl<T: Config> Pallet<T> {
7979
/// - The amount of stake to be added to the hotkey staking account.
8080
///
8181
/// * 'limit_price' (u64):
82-
/// - The limit price expressed in units of RAO per one Alpha.
82+
/// - The limit price expressed in units of RAO per one Alpha.
8383
///
8484
/// # Event:
8585
/// * StakeAdded;
@@ -176,16 +176,13 @@ impl<T: Config> Pallet<T> {
176176
// This is the positive solution of quare equation for finding additional TAO from
177177
// limit_price.
178178
let zero: U96F32 = U96F32::saturating_from_num(0.0);
179-
let sqrt: U96F32 = checked_sqrt(
180-
limit_price_float
181-
.saturating_mul(tao_reserve_float)
182-
.saturating_mul(alpha_in_float),
183-
U96F32::saturating_from_num(0.1),
184-
)
185-
.unwrap_or(zero);
186-
187-
U96F32::saturating_from_num(sqrt)
188-
.saturating_sub(U96F32::saturating_from_num(tao_reserve_float))
179+
let epsilon: U96F32 = U96F32::saturating_from_num(0.1);
180+
let sqrt: U96F32 =
181+
checked_sqrt(limit_price_float.saturating_mul(tao_reserve_float), epsilon)
182+
.unwrap_or(zero)
183+
.saturating_mul(checked_sqrt(alpha_in_float, epsilon).unwrap_or(zero));
184+
185+
sqrt.saturating_sub(U96F32::saturating_from_num(tao_reserve_float))
189186
.saturating_to_num::<u64>()
190187
}
191188
}

pallets/subtensor/src/staking/remove_stake.rs

Lines changed: 95 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use super::*;
2+
use safe_math::*;
23
use sp_core::Get;
4+
use substrate_fixed::types::U96F32;
35

46
impl<T: Config> Pallet<T> {
57
/// ---- The implementation for the extrinsic remove_stake: Removes stake from a hotkey account and adds it onto a coldkey.
@@ -222,23 +224,107 @@ impl<T: Config> Pallet<T> {
222224
}
223225

224226
pub fn do_remove_stake_limit(
225-
_origin: T::RuntimeOrigin,
226-
_hotkey: T::AccountId,
227+
origin: T::RuntimeOrigin,
228+
hotkey: T::AccountId,
227229
netuid: u16,
228-
_stake_to_be_added: u64,
230+
alpha_unstaked: u64,
229231
limit_price: u64,
230232
) -> dispatch::DispatchResult {
231-
// TODO: Do all checks
233+
// 1. We check the transaction is signed by the caller and retrieve the T::AccountId coldkey information.
234+
let coldkey = ensure_signed(origin)?;
235+
log::info!(
236+
"do_remove_stake( origin:{:?} hotkey:{:?}, netuid: {:?}, alpha_unstaked:{:?} )",
237+
coldkey,
238+
hotkey,
239+
netuid,
240+
alpha_unstaked
241+
);
242+
243+
// 2. Validate the user input
244+
Self::validate_remove_stake(&coldkey, &hotkey, netuid, alpha_unstaked)?;
245+
246+
// 3. Calcaulate the maximum amount that can be executed with price limit
247+
let max_amount = Self::get_max_amount_remove(netuid, limit_price);
248+
let mut possible_alpha = alpha_unstaked;
249+
if possible_alpha > max_amount {
250+
possible_alpha = max_amount;
251+
}
252+
253+
// 4. Swap the alpba to tao and update counters for this subnet.
254+
let fee = DefaultStakingFee::<T>::get();
255+
let tao_unstaked: u64 =
256+
Self::unstake_from_subnet(&hotkey, &coldkey, netuid, possible_alpha, fee);
257+
258+
// 5. We add the balance to the coldkey. If the above fails we will not credit this coldkey.
259+
Self::add_balance_to_coldkey_account(&coldkey, tao_unstaked);
232260

233-
// Calcaulate the maximum amount that can be executed with price limit
234-
let _max_amount = Self::get_max_amount_remove(netuid, limit_price);
261+
// 6. If the stake is below the minimum, we clear the nomination from storage.
262+
Self::clear_small_nomination_if_required(&hotkey, &coldkey, netuid);
235263

236-
// Ok and return.
264+
// 7. Check if stake lowered below MinStake and remove Pending children if it did
265+
if Self::get_total_stake_for_hotkey(&hotkey) < StakeThreshold::<T>::get() {
266+
Self::get_all_subnet_netuids().iter().for_each(|netuid| {
267+
PendingChildKeys::<T>::remove(netuid, &hotkey);
268+
})
269+
}
270+
271+
// Done and ok.
237272
Ok(())
238273
}
239274

240275
// Returns the maximum amount of RAO that can be executed with price limit
241-
pub fn get_max_amount_remove(_netuid: u16, _limit_price: u64) -> u64 {
242-
0
276+
pub fn get_max_amount_remove(netuid: u16, limit_price: u64) -> u64 {
277+
// Corner case: root and stao
278+
// There's no slippage for root or stable subnets, so if limit price is 1e9 rao or
279+
// higher, then max_amount equals u64::MAX, otherwise it is 0.
280+
if (netuid == Self::get_root_netuid()) || (SubnetMechanism::<T>::get(netuid)) == 0 {
281+
if limit_price <= 1_000_000_000 {
282+
return u64::MAX;
283+
} else {
284+
return 0;
285+
}
286+
}
287+
288+
// Corner case: SubnetAlphaIn is zero. Staking can't happen, so max amount is zero.
289+
let alpha_in = SubnetAlphaIn::<T>::get(netuid);
290+
if alpha_in == 0 {
291+
return 0;
292+
}
293+
let alpha_in_float: U96F32 = U96F32::saturating_from_num(alpha_in);
294+
295+
// Corner case: SubnetTAO is zero. Staking can't happen, so max amount is zero.
296+
let tao_reserve = SubnetTAO::<T>::get(netuid);
297+
if tao_reserve == 0 {
298+
return 0;
299+
}
300+
let tao_reserve_float: U96F32 = U96F32::saturating_from_num(tao_reserve);
301+
302+
// Corner case: limit_price == 0 (because there's division by limit price)
303+
// => can sell all
304+
if limit_price == 0 {
305+
return u64::MAX;
306+
}
307+
308+
// Corner case: limit_price > current_price (price cannot increase with unstaking)
309+
let limit_price_float: U96F32 = U96F32::saturating_from_num(limit_price)
310+
.checked_div(U96F32::saturating_from_num(1_000_000_000))
311+
.unwrap_or(U96F32::saturating_from_num(0));
312+
if limit_price_float > Self::get_alpha_price(netuid) {
313+
return 0;
314+
}
315+
316+
// Main case: return SQRT(SubnetTAO * SubnetAlphaIn / limit_price) - SubnetAlphaIn
317+
// This is the positive solution of quare equation for finding Alpha amount from
318+
// limit_price.
319+
let zero: U96F32 = U96F32::saturating_from_num(0.0);
320+
let epsilon: U96F32 = U96F32::saturating_from_num(0.1);
321+
let sqrt: U96F32 = checked_sqrt(tao_reserve_float, epsilon)
322+
.unwrap_or(zero)
323+
.saturating_mul(
324+
checked_sqrt(alpha_in_float.safe_div(limit_price_float), epsilon).unwrap_or(zero),
325+
);
326+
327+
sqrt.saturating_sub(U96F32::saturating_from_num(alpha_in_float))
328+
.saturating_to_num::<u64>()
243329
}
244330
}

0 commit comments

Comments
 (0)