|
1 | 1 | use super::*;
|
| 2 | +use safe_math::*; |
2 | 3 | use sp_core::Get;
|
| 4 | +use substrate_fixed::types::U96F32; |
3 | 5 |
|
4 | 6 | impl<T: Config> Pallet<T> {
|
5 | 7 | /// ---- 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> {
|
222 | 224 | }
|
223 | 225 |
|
224 | 226 | pub fn do_remove_stake_limit(
|
225 |
| - _origin: T::RuntimeOrigin, |
226 |
| - _hotkey: T::AccountId, |
| 227 | + origin: T::RuntimeOrigin, |
| 228 | + hotkey: T::AccountId, |
227 | 229 | netuid: u16,
|
228 |
| - _stake_to_be_added: u64, |
| 230 | + alpha_unstaked: u64, |
229 | 231 | limit_price: u64,
|
230 | 232 | ) -> 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); |
232 | 260 |
|
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); |
235 | 263 |
|
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. |
237 | 272 | Ok(())
|
238 | 273 | }
|
239 | 274 |
|
240 | 275 | // 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>() |
243 | 329 | }
|
244 | 330 | }
|
0 commit comments