Skip to content

Commit 433be65

Browse files
authored
Merge pull request #1498 from opentensor/fix/unstake-all-should-not-give-zero
[fix] add remove validate to unstake_all
2 parents 1c61c73 + a3511db commit 433be65

File tree

2 files changed

+222
-1
lines changed

2 files changed

+222
-1
lines changed

Diff for: pallets/subtensor/src/staking/remove_stake.rs

+30
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,21 @@ impl<T: Config> Pallet<T> {
134134
// Ensure that the hotkey has enough stake to withdraw.
135135
let alpha_unstaked =
136136
Self::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid);
137+
138+
if Self::validate_remove_stake(
139+
&coldkey,
140+
&hotkey,
141+
netuid,
142+
alpha_unstaked,
143+
alpha_unstaked,
144+
false,
145+
)
146+
.is_err()
147+
{
148+
// Don't unstake from this netuid
149+
continue;
150+
}
151+
137152
let fee = Self::calculate_staking_fee(
138153
Some((&hotkey, netuid)),
139154
&coldkey,
@@ -211,6 +226,21 @@ impl<T: Config> Pallet<T> {
211226
// Ensure that the hotkey has enough stake to withdraw.
212227
let alpha_unstaked =
213228
Self::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid);
229+
230+
if Self::validate_remove_stake(
231+
&coldkey,
232+
&hotkey,
233+
netuid,
234+
alpha_unstaked,
235+
alpha_unstaked,
236+
false,
237+
)
238+
.is_err()
239+
{
240+
// Don't unstake from this netuid
241+
continue;
242+
}
243+
214244
let fee = Self::calculate_staking_fee(
215245
Some((&hotkey, netuid)),
216246
&coldkey,

Diff for: pallets/subtensor/src/tests/staking.rs

+192-1
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,16 @@
33

44
use frame_support::{assert_err, assert_noop, assert_ok, traits::Currency};
55
use frame_system::RawOrigin;
6+
use safe_math::SafeDiv;
7+
use substrate_fixed::traits::FromFixed;
68

79
use super::mock::*;
810
use crate::*;
911
use approx::assert_abs_diff_eq;
1012
use frame_support::dispatch::{DispatchClass, DispatchInfo, GetDispatchInfo, Pays};
1113
use frame_support::sp_runtime::DispatchError;
1214
use sp_core::{Get, H256, U256};
13-
use substrate_fixed::types::{I96F32, U64F64, U96F32};
15+
use substrate_fixed::types::{I96F32, I110F18, U64F64, U96F32};
1416

1517
/***********************************************************
1618
staking::add_stake() tests
@@ -4240,3 +4242,192 @@ fn test_move_stake_limit_partial() {
42404242
assert_abs_diff_eq!(new_alpha, 149_000_000_000, epsilon = 100_000_000,);
42414243
});
42424244
}
4245+
4246+
#[test]
4247+
fn test_unstake_all_hits_liquidity_min() {
4248+
new_test_ext(1).execute_with(|| {
4249+
let subnet_owner_coldkey = U256::from(1001);
4250+
let subnet_owner_hotkey = U256::from(1002);
4251+
let coldkey = U256::from(1);
4252+
let hotkey = U256::from(2);
4253+
4254+
let stake_amount = 190_000_000_000; // 190 Alpha
4255+
4256+
let netuid: u16 = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey);
4257+
register_ok_neuron(netuid, hotkey, coldkey, 192213123);
4258+
// Give the neuron some stake to remove
4259+
SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet(
4260+
&hotkey,
4261+
&coldkey,
4262+
netuid,
4263+
stake_amount,
4264+
);
4265+
4266+
// Setup the Alpha pool so that removing all the Alpha will bring liqudity below the minimum
4267+
let remaining_tao: I96F32 =
4268+
DefaultMinimumPoolLiquidity::<Test>::get().saturating_sub(I96F32::from(1));
4269+
let alpha_reserves: I110F18 = I110F18::from(stake_amount + 10_000_000);
4270+
let alpha = stake_amount;
4271+
4272+
let k: I110F18 = I110F18::from_fixed(remaining_tao)
4273+
.saturating_mul(alpha_reserves.saturating_add(I110F18::from(alpha)));
4274+
let tao_reserves: I110F18 = k.safe_div(alpha_reserves);
4275+
4276+
SubnetTAO::<Test>::insert(netuid, tao_reserves.to_num::<u64>());
4277+
SubnetAlphaIn::<Test>::insert(netuid, alpha_reserves.to_num::<u64>());
4278+
4279+
// Try to unstake, but we reduce liquidity too far
4280+
4281+
assert_ok!(SubtensorModule::unstake_all(
4282+
RuntimeOrigin::signed(coldkey),
4283+
hotkey,
4284+
));
4285+
4286+
// Expect nothing to be unstaked
4287+
let new_alpha =
4288+
SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid);
4289+
assert_abs_diff_eq!(new_alpha, stake_amount, epsilon = 0,);
4290+
});
4291+
}
4292+
4293+
#[test]
4294+
fn test_unstake_all_alpha_hits_liquidity_min() {
4295+
new_test_ext(1).execute_with(|| {
4296+
let subnet_owner_coldkey = U256::from(1001);
4297+
let subnet_owner_hotkey = U256::from(1002);
4298+
let coldkey = U256::from(1);
4299+
let hotkey = U256::from(2);
4300+
4301+
let stake_amount = 190_000_000_000; // 190 Alpha
4302+
4303+
let netuid: u16 = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey);
4304+
register_ok_neuron(netuid, hotkey, coldkey, 192213123);
4305+
// Give the neuron some stake to remove
4306+
SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet(
4307+
&hotkey,
4308+
&coldkey,
4309+
netuid,
4310+
stake_amount,
4311+
);
4312+
4313+
// Setup the Alpha pool so that removing all the Alpha will bring liqudity below the minimum
4314+
let remaining_tao: I96F32 =
4315+
DefaultMinimumPoolLiquidity::<Test>::get().saturating_sub(I96F32::from(1));
4316+
let alpha_reserves: I110F18 = I110F18::from(stake_amount + 10_000_000);
4317+
let alpha = stake_amount;
4318+
4319+
let k: I110F18 = I110F18::from_fixed(remaining_tao)
4320+
.saturating_mul(alpha_reserves.saturating_add(I110F18::from(alpha)));
4321+
let tao_reserves: I110F18 = k.safe_div(alpha_reserves);
4322+
4323+
SubnetTAO::<Test>::insert(netuid, tao_reserves.to_num::<u64>());
4324+
SubnetAlphaIn::<Test>::insert(netuid, alpha_reserves.to_num::<u64>());
4325+
4326+
// Try to unstake, but we reduce liquidity too far
4327+
4328+
assert_ok!(SubtensorModule::unstake_all_alpha(
4329+
RuntimeOrigin::signed(coldkey),
4330+
hotkey,
4331+
));
4332+
4333+
// Expect nothing to be unstaked
4334+
let new_alpha =
4335+
SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid);
4336+
assert_abs_diff_eq!(new_alpha, stake_amount, epsilon = 0,);
4337+
});
4338+
}
4339+
4340+
#[test]
4341+
fn test_unstake_all_alpha_works() {
4342+
new_test_ext(1).execute_with(|| {
4343+
let subnet_owner_coldkey = U256::from(1001);
4344+
let subnet_owner_hotkey = U256::from(1002);
4345+
let coldkey = U256::from(1);
4346+
let hotkey = U256::from(2);
4347+
4348+
let stake_amount = 190_000_000_000; // 190 Alpha
4349+
4350+
let netuid: u16 = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey);
4351+
register_ok_neuron(netuid, hotkey, coldkey, 192213123);
4352+
// Give the neuron some stake to remove
4353+
SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet(
4354+
&hotkey,
4355+
&coldkey,
4356+
netuid,
4357+
stake_amount,
4358+
);
4359+
4360+
// Setup the Alpha pool so that removing all the Alpha will keep liq above min
4361+
let remaining_tao: I96F32 =
4362+
DefaultMinimumPoolLiquidity::<Test>::get().saturating_add(I96F32::from(10_000_000));
4363+
let alpha_reserves: I110F18 = I110F18::from(stake_amount + 10_000_000);
4364+
let alpha = stake_amount;
4365+
4366+
let k: I110F18 = I110F18::from_fixed(remaining_tao)
4367+
.saturating_mul(alpha_reserves.saturating_add(I110F18::from(alpha)));
4368+
let tao_reserves: I110F18 = k.safe_div(alpha_reserves);
4369+
4370+
SubnetTAO::<Test>::insert(netuid, tao_reserves.to_num::<u64>());
4371+
SubnetAlphaIn::<Test>::insert(netuid, alpha_reserves.to_num::<u64>());
4372+
4373+
// Unstake all alpha to root
4374+
assert_ok!(SubtensorModule::unstake_all_alpha(
4375+
RuntimeOrigin::signed(coldkey),
4376+
hotkey,
4377+
));
4378+
4379+
let new_alpha =
4380+
SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid);
4381+
assert_abs_diff_eq!(new_alpha, 0, epsilon = 1_000,);
4382+
let new_root =
4383+
SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, 0);
4384+
assert!(new_root > 100_000);
4385+
});
4386+
}
4387+
4388+
#[test]
4389+
fn test_unstake_all_works() {
4390+
new_test_ext(1).execute_with(|| {
4391+
let subnet_owner_coldkey = U256::from(1001);
4392+
let subnet_owner_hotkey = U256::from(1002);
4393+
let coldkey = U256::from(1);
4394+
let hotkey = U256::from(2);
4395+
4396+
let stake_amount = 190_000_000_000; // 190 Alpha
4397+
4398+
let netuid: u16 = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey);
4399+
register_ok_neuron(netuid, hotkey, coldkey, 192213123);
4400+
// Give the neuron some stake to remove
4401+
SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet(
4402+
&hotkey,
4403+
&coldkey,
4404+
netuid,
4405+
stake_amount,
4406+
);
4407+
4408+
// Setup the Alpha pool so that removing all the Alpha will keep liq above min
4409+
let remaining_tao: I96F32 =
4410+
DefaultMinimumPoolLiquidity::<Test>::get().saturating_add(I96F32::from(10_000_000));
4411+
let alpha_reserves: I110F18 = I110F18::from(stake_amount + 10_000_000);
4412+
let alpha = stake_amount;
4413+
4414+
let k: I110F18 = I110F18::from_fixed(remaining_tao)
4415+
.saturating_mul(alpha_reserves.saturating_add(I110F18::from(alpha)));
4416+
let tao_reserves: I110F18 = k.safe_div(alpha_reserves);
4417+
4418+
SubnetTAO::<Test>::insert(netuid, tao_reserves.to_num::<u64>());
4419+
SubnetAlphaIn::<Test>::insert(netuid, alpha_reserves.to_num::<u64>());
4420+
4421+
// Unstake all alpha to root
4422+
assert_ok!(SubtensorModule::unstake_all(
4423+
RuntimeOrigin::signed(coldkey),
4424+
hotkey,
4425+
));
4426+
4427+
let new_alpha =
4428+
SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid);
4429+
assert_abs_diff_eq!(new_alpha, 0, epsilon = 1_000,);
4430+
let new_balance = SubtensorModule::get_coldkey_balance(&coldkey);
4431+
assert!(new_balance > 100_000);
4432+
});
4433+
}

0 commit comments

Comments
 (0)