Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions pallets/subtensor/src/benchmarks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1355,4 +1355,58 @@ mod pallet_benchmarks {
#[extrinsic_call]
_(RawOrigin::Signed(coldkey), hotkey);
}

#[benchmark]
fn remove_stake_full_limit() {
let netuid = NetUid::from(1);
let tempo: u16 = 1;
let seed: u32 = 1;

// Set our total stake to 1000 TAO
Subtensor::<T>::increase_total_stake(1_000_000_000_000);

Subtensor::<T>::init_new_network(netuid, tempo);
Subtensor::<T>::set_network_registration_allowed(netuid, true);
SubtokenEnabled::<T>::insert(netuid, true);

Subtensor::<T>::set_max_allowed_uids(netuid, 4096);
assert_eq!(Subtensor::<T>::get_max_allowed_uids(netuid), 4096);

let coldkey: T::AccountId = account("Test", 0, seed);
let hotkey: T::AccountId = account("Alice", 0, seed);
Subtensor::<T>::set_burn(netuid, 1);

let limit: u64 = 1_000_000_000;
let tao_reserve = 150_000_000_000_u64;
let alpha_in = 100_000_000_000_u64;
SubnetTAO::<T>::insert(netuid, tao_reserve);
SubnetAlphaIn::<T>::insert(netuid, alpha_in);

let wallet_bal = 1000000u32.into();
Subtensor::<T>::add_balance_to_coldkey_account(&coldkey.clone(), wallet_bal);

assert_ok!(Subtensor::<T>::do_burned_registration(
RawOrigin::Signed(coldkey.clone()).into(),
netuid,
hotkey.clone()
));

let u64_staked_amt = 100_000_000_000;
Subtensor::<T>::add_balance_to_coldkey_account(&coldkey.clone(), u64_staked_amt);

assert_ok!(Subtensor::<T>::add_stake(
RawOrigin::Signed(coldkey.clone()).into(),
hotkey.clone(),
netuid,
u64_staked_amt
));

#[extrinsic_call]
_(
RawOrigin::Signed(coldkey.clone()),
hotkey.clone(),
netuid,
Some(limit),
);
}
}
17 changes: 17 additions & 0 deletions pallets/subtensor/src/macros/dispatches.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2069,5 +2069,22 @@ mod dispatches {
PendingChildKeyCooldown::<T>::put(cooldown);
Ok(())
}

/// Removes all stake from a hotkey on a subnet with a price limit.
/// This extrinsic allows to specify the limit price for alpha token
/// at which or better (higher) the staking should execute.
/// Without limit_price it remove all the stake similar to `remove_stake` extrinsic
#[pallet::call_index(103)]
#[pallet::weight((Weight::from_parts(398_000_000, 10142)
.saturating_add(T::DbWeight::get().reads(29_u64))
.saturating_add(T::DbWeight::get().writes(14_u64)), DispatchClass::Normal, Pays::No))]
pub fn remove_stake_full_limit(
origin: T::RuntimeOrigin,
hotkey: T::AccountId,
netuid: NetUid,
limit_price: Option<u64>,
) -> DispatchResult {
Self::do_remove_stake_full_limit(origin, hotkey, netuid, limit_price)
}
}
}
18 changes: 18 additions & 0 deletions pallets/subtensor/src/staking/remove_stake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -403,4 +403,22 @@ impl<T: Config> Pallet<T> {
Err(Error::ZeroMaxStakeAmount)
}
}

pub fn do_remove_stake_full_limit(
origin: T::RuntimeOrigin,
hotkey: T::AccountId,
netuid: NetUid,
limit_price: Option<u64>,
) -> DispatchResult {
let coldkey = ensure_signed(origin.clone())?;

let alpha_unstaked =
Self::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid);

if let Some(limit_price) = limit_price {
Self::do_remove_stake_limit(origin, hotkey, netuid, alpha_unstaked, limit_price, false)
} else {
Self::do_remove_stake(origin, hotkey, netuid, alpha_unstaked)
}
}
}
131 changes: 131 additions & 0 deletions pallets/subtensor/src/tests/staking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5055,6 +5055,137 @@ fn test_increase_stake_for_hotkey_and_coldkey_on_subnet_adds_to_staking_hotkeys_
});
}

#[test]
fn test_remove_stake_full_limit_ok() {
new_test_ext(1).execute_with(|| {
let hotkey_account_id = U256::from(1);
let coldkey_account_id = U256::from(2);
let stake_amount = 10_000_000_000;

// add network
let netuid = add_dynamic_network(&hotkey_account_id, &coldkey_account_id);

// Give the neuron some stake to remove
SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet(
&hotkey_account_id,
&coldkey_account_id,
netuid,
stake_amount,
);

let tao_reserve: U96F32 = U96F32::from_num(100_000_000_000_u64);
let alpha_in: U96F32 = U96F32::from_num(100_000_000_000_u64);
SubnetTAO::<Test>::insert(netuid, tao_reserve.to_num::<u64>());
SubnetAlphaIn::<Test>::insert(netuid, alpha_in.to_num::<u64>());

let limit_price = 90_000_000;

// Remove stake with slippage safety
assert_ok!(SubtensorModule::remove_stake_full_limit(
RuntimeOrigin::signed(coldkey_account_id),
hotkey_account_id,
netuid,
Some(limit_price),
));

// Check if stake has decreased to zero
assert_eq!(
SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(
&hotkey_account_id,
&coldkey_account_id,
netuid
),
0
);

let new_balance = SubtensorModule::get_coldkey_balance(&coldkey_account_id);
assert_abs_diff_eq!(new_balance, 9_066_000_000, epsilon = 1_000_000);
});
}

#[test]
fn test_remove_stake_full_limit_fails_slippage_too_high() {
new_test_ext(1).execute_with(|| {
let hotkey_account_id = U256::from(1);
let coldkey_account_id = U256::from(2);
let stake_amount = 10_000_000_000;

// add network
let netuid = add_dynamic_network(&hotkey_account_id, &coldkey_account_id);

// Give the neuron some stake to remove
SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet(
&hotkey_account_id,
&coldkey_account_id,
netuid,
stake_amount,
);

let tao_reserve: U96F32 = U96F32::from_num(100_000_000_000_u64);
let alpha_in: U96F32 = U96F32::from_num(100_000_000_000_u64);
SubnetTAO::<Test>::insert(netuid, tao_reserve.to_num::<u64>());
SubnetAlphaIn::<Test>::insert(netuid, alpha_in.to_num::<u64>());

let invalid_limit_price = 910_000_000;

// Remove stake with slippage safety
assert_err!(
SubtensorModule::remove_stake_full_limit(
RuntimeOrigin::signed(coldkey_account_id),
hotkey_account_id,
netuid,
Some(invalid_limit_price),
),
Error::<Test>::SlippageTooHigh
);
});
}

#[test]
fn test_remove_stake_full_limit_ok_with_no_limit_price() {
new_test_ext(1).execute_with(|| {
let hotkey_account_id = U256::from(1);
let coldkey_account_id = U256::from(2);
let stake_amount = 10_000_000_000;

// add network
let netuid = add_dynamic_network(&hotkey_account_id, &coldkey_account_id);

// Give the neuron some stake to remove
SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet(
&hotkey_account_id,
&coldkey_account_id,
netuid,
stake_amount,
);

let tao_reserve: U96F32 = U96F32::from_num(100_000_000_000_u64);
let alpha_in: U96F32 = U96F32::from_num(100_000_000_000_u64);
SubnetTAO::<Test>::insert(netuid, tao_reserve.to_num::<u64>());
SubnetAlphaIn::<Test>::insert(netuid, alpha_in.to_num::<u64>());

// Remove stake with slippage safety
assert_ok!(SubtensorModule::remove_stake_full_limit(
RuntimeOrigin::signed(coldkey_account_id),
hotkey_account_id,
netuid,
None,
));

// Check if stake has decreased to zero
assert_eq!(
SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(
&hotkey_account_id,
&coldkey_account_id,
netuid
),
0
);

let new_balance = SubtensorModule::get_coldkey_balance(&coldkey_account_id);
assert_abs_diff_eq!(new_balance, 9_066_000_000, epsilon = 1_000_000);
});
}
/// This test verifies that minimum stake amount is sufficient to move price and apply
/// non-zero staking fees
#[test]
Expand Down
33 changes: 8 additions & 25 deletions runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
// `spec_version`, and `authoring_version` are the same between Wasm and native.
// This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use
// the compatible custom types.
spec_version: 279,
spec_version: 280,
impl_version: 1,
apis: RUNTIME_API_VERSIONS,
transaction_version: 1,
Expand Down Expand Up @@ -708,18 +708,9 @@ impl InstanceFilter<RuntimeCall> for ProxyType {
| RuntimeCall::SubtensorModule(
pallet_subtensor::Call::remove_stake_limit { .. }
)
// | RuntimeCall::SubtensorModule(
// pallet_subtensor::Call::add_stake_aggregate { .. }
// )
// | RuntimeCall::SubtensorModule(
// pallet_subtensor::Call::add_stake_limit_aggregate { .. }
// )
// | RuntimeCall::SubtensorModule(
// pallet_subtensor::Call::remove_stake_aggregate { .. }
// )
// | RuntimeCall::SubtensorModule(
// pallet_subtensor::Call::remove_stake_limit_aggregate { .. }
// )
| RuntimeCall::SubtensorModule(
pallet_subtensor::Call::remove_stake_full_limit { .. }
)
| RuntimeCall::SubtensorModule(pallet_subtensor::Call::unstake_all { .. })
| RuntimeCall::SubtensorModule(
pallet_subtensor::Call::unstake_all_alpha { .. }
Expand Down Expand Up @@ -800,18 +791,10 @@ impl InstanceFilter<RuntimeCall> for ProxyType {
| RuntimeCall::SubtensorModule(pallet_subtensor::Call::add_stake_limit { .. })
| RuntimeCall::SubtensorModule(
pallet_subtensor::Call::remove_stake_limit { .. }
) // | RuntimeCall::SubtensorModule(
// pallet_subtensor::Call::add_stake_aggregate { .. }
// )
// | RuntimeCall::SubtensorModule(
// pallet_subtensor::Call::add_stake_limit_aggregate { .. }
// )
// | RuntimeCall::SubtensorModule(
// pallet_subtensor::Call::remove_stake_aggregate { .. }
// )
// | RuntimeCall::SubtensorModule(
// pallet_subtensor::Call::remove_stake_limit_aggregate { .. }
// )
)
| RuntimeCall::SubtensorModule(
pallet_subtensor::Call::remove_stake_full_limit { .. }
)
),
ProxyType::Registration => matches!(
c,
Expand Down
Loading