Skip to content

Commit f1886e2

Browse files
authored
Merge pull request #1213 from opentensor/feat/fill-or-kill-staking-orders
Add allow_partial parameter for add_stake_limit and remove_stake_limit
2 parents 84a27b4 + c3822c7 commit f1886e2

File tree

8 files changed

+248
-16
lines changed

8 files changed

+248
-16
lines changed

pallets/subtensor/src/lib.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1569,6 +1569,7 @@ pub enum CustomTransactionError {
15691569
NotEnoughStakeToWithdraw,
15701570
RateLimitExceeded,
15711571
InsufficientLiquidity,
1572+
SlippageTooHigh,
15721573
BadRequest,
15731574
}
15741575

@@ -1583,6 +1584,7 @@ impl From<CustomTransactionError> for u8 {
15831584
CustomTransactionError::NotEnoughStakeToWithdraw => 5,
15841585
CustomTransactionError::RateLimitExceeded => 6,
15851586
CustomTransactionError::InsufficientLiquidity => 7,
1587+
CustomTransactionError::SlippageTooHigh => 8,
15861588
CustomTransactionError::BadRequest => 255,
15871589
}
15881590
}
@@ -1654,6 +1656,10 @@ where
16541656
CustomTransactionError::InsufficientLiquidity.into(),
16551657
)
16561658
.into()),
1659+
Error::<T>::SlippageTooHigh => Err(InvalidTransaction::Custom(
1660+
CustomTransactionError::SlippageTooHigh.into(),
1661+
)
1662+
.into()),
16571663
_ => Err(
16581664
InvalidTransaction::Custom(CustomTransactionError::BadRequest.into()).into(),
16591665
),
@@ -1801,6 +1807,8 @@ where
18011807
hotkey,
18021808
*netuid,
18031809
*amount_staked,
1810+
*amount_staked,
1811+
false,
18041812
))
18051813
}
18061814
Some(Call::remove_stake {
@@ -1814,6 +1822,8 @@ where
18141822
hotkey,
18151823
*netuid,
18161824
*amount_unstaked,
1825+
*amount_unstaked,
1826+
false,
18171827
))
18181828
}
18191829
Some(Call::move_stake {

pallets/subtensor/src/macros/dispatches.rs

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1710,6 +1710,10 @@ mod dispatches {
17101710
/// * 'limit_price' (u64):
17111711
/// - The limit price expressed in units of RAO per one Alpha.
17121712
///
1713+
/// * 'allow_partial' (bool):
1714+
/// - Allows partial execution of the amount. If set to false, this becomes
1715+
/// fill or kill type or order.
1716+
///
17131717
/// # Event:
17141718
/// * StakeAdded;
17151719
/// - On the successfully adding stake to a global account.
@@ -1734,8 +1738,16 @@ mod dispatches {
17341738
netuid: u16,
17351739
amount_staked: u64,
17361740
limit_price: u64,
1741+
allow_partial: bool,
17371742
) -> DispatchResult {
1738-
Self::do_add_stake_limit(origin, hotkey, netuid, amount_staked, limit_price)
1743+
Self::do_add_stake_limit(
1744+
origin,
1745+
hotkey,
1746+
netuid,
1747+
amount_staked,
1748+
limit_price,
1749+
allow_partial,
1750+
)
17391751
}
17401752

17411753
/// --- Removes stake from a hotkey on a subnet with a price limit.
@@ -1759,6 +1771,10 @@ mod dispatches {
17591771
/// * 'limit_price' (u64):
17601772
/// - The limit price expressed in units of RAO per one Alpha.
17611773
///
1774+
/// * 'allow_partial' (bool):
1775+
/// - Allows partial execution of the amount. If set to false, this becomes
1776+
/// fill or kill type or order.
1777+
///
17621778
/// # Event:
17631779
/// * StakeRemoved;
17641780
/// - On the successfully removing stake from the hotkey account.
@@ -1784,8 +1800,16 @@ mod dispatches {
17841800
netuid: u16,
17851801
amount_unstaked: u64,
17861802
limit_price: u64,
1803+
allow_partial: bool,
17871804
) -> DispatchResult {
1788-
Self::do_remove_stake_limit(origin, hotkey, netuid, amount_unstaked, limit_price)
1805+
Self::do_remove_stake_limit(
1806+
origin,
1807+
hotkey,
1808+
netuid,
1809+
amount_unstaked,
1810+
limit_price,
1811+
allow_partial,
1812+
)
17891813
}
17901814
}
17911815
}

pallets/subtensor/src/macros/errors.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,5 +187,7 @@ mod errors {
187187
AmountTooLow,
188188
/// Not enough liquidity.
189189
InsufficientLiquidity,
190+
/// Slippage is too high for the transaction.
191+
SlippageTooHigh,
190192
}
191193
}

pallets/subtensor/src/staking/add_stake.rs

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,14 @@ impl<T: Config> Pallet<T> {
5050
);
5151

5252
// 2. Validate user input
53-
Self::validate_add_stake(&coldkey, &hotkey, netuid, stake_to_be_added)?;
53+
Self::validate_add_stake(
54+
&coldkey,
55+
&hotkey,
56+
netuid,
57+
stake_to_be_added,
58+
stake_to_be_added,
59+
false,
60+
)?;
5461

5562
// 3. Ensure the remove operation from the coldkey is a success.
5663
let tao_staked: u64 =
@@ -81,6 +88,10 @@ impl<T: Config> Pallet<T> {
8188
/// * 'limit_price' (u64):
8289
/// - The limit price expressed in units of RAO per one Alpha.
8390
///
91+
/// * 'allow_partial' (bool):
92+
/// - Allows partial execution of the amount. If set to false, this becomes
93+
/// fill or kill type or order.
94+
///
8495
/// # Event:
8596
/// * StakeAdded;
8697
/// - On the successfully adding stake to a global account.
@@ -104,6 +115,7 @@ impl<T: Config> Pallet<T> {
104115
netuid: u16,
105116
stake_to_be_added: u64,
106117
limit_price: u64,
118+
allow_partial: bool,
107119
) -> dispatch::DispatchResult {
108120
// 1. We check that the transaction is signed by the caller and retrieve the T::AccountId coldkey information.
109121
let coldkey = ensure_signed(origin)?;
@@ -115,16 +127,23 @@ impl<T: Config> Pallet<T> {
115127
stake_to_be_added
116128
);
117129

118-
// 2. Validate user input
119-
Self::validate_add_stake(&coldkey, &hotkey, netuid, stake_to_be_added)?;
120-
121-
// 3. Calcaulate the maximum amount that can be executed with price limit
130+
// 2. Calcaulate the maximum amount that can be executed with price limit
122131
let max_amount = Self::get_max_amount_add(netuid, limit_price);
123132
let mut possible_stake = stake_to_be_added;
124133
if possible_stake > max_amount {
125134
possible_stake = max_amount;
126135
}
127136

137+
// 3. Validate user input
138+
Self::validate_add_stake(
139+
&coldkey,
140+
&hotkey,
141+
netuid,
142+
stake_to_be_added,
143+
max_amount,
144+
allow_partial,
145+
)?;
146+
128147
// 4. Ensure the remove operation from the coldkey is a success.
129148
let tao_staked: u64 = Self::remove_balance_from_coldkey_account(&coldkey, possible_stake)?;
130149

pallets/subtensor/src/staking/remove_stake.rs

Lines changed: 59 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,14 @@ impl<T: Config> Pallet<T> {
5050
);
5151

5252
// 2. Validate the user input
53-
Self::validate_remove_stake(&coldkey, &hotkey, netuid, alpha_unstaked)?;
53+
Self::validate_remove_stake(
54+
&coldkey,
55+
&hotkey,
56+
netuid,
57+
alpha_unstaked,
58+
alpha_unstaked,
59+
false,
60+
)?;
5461

5562
// 3. Swap the alpba to tao and update counters for this subnet.
5663
let fee = DefaultStakingFee::<T>::get();
@@ -223,12 +230,51 @@ impl<T: Config> Pallet<T> {
223230
Ok(())
224231
}
225232

233+
/// ---- The implementation for the extrinsic remove_stake_limit: Removes stake from
234+
/// a hotkey on a subnet with a price limit.
235+
///
236+
/// In case if slippage occurs and the price shall move beyond the limit
237+
/// price, the staking order may execute only partially or not execute
238+
/// at all.
239+
///
240+
/// # Args:
241+
/// * 'origin': (<T as frame_system::Config>Origin):
242+
/// - The signature of the caller's coldkey.
243+
///
244+
/// * 'hotkey' (T::AccountId):
245+
/// - The associated hotkey account.
246+
///
247+
/// * 'amount_unstaked' (u64):
248+
/// - The amount of stake to be added to the hotkey staking account.
249+
///
250+
/// * 'limit_price' (u64):
251+
/// - The limit price expressed in units of RAO per one Alpha.
252+
///
253+
/// * 'allow_partial' (bool):
254+
/// - Allows partial execution of the amount. If set to false, this becomes
255+
/// fill or kill type or order.
256+
///
257+
/// # Event:
258+
/// * StakeRemoved;
259+
/// - On the successfully removing stake from the hotkey account.
260+
///
261+
/// # Raises:
262+
/// * 'NotRegistered':
263+
/// - Thrown if the account we are attempting to unstake from is non existent.
264+
///
265+
/// * 'NonAssociatedColdKey':
266+
/// - Thrown if the coldkey does not own the hotkey we are unstaking from.
267+
///
268+
/// * 'NotEnoughStakeToWithdraw':
269+
/// - Thrown if there is not enough stake on the hotkey to withdwraw this amount.
270+
///
226271
pub fn do_remove_stake_limit(
227272
origin: T::RuntimeOrigin,
228273
hotkey: T::AccountId,
229274
netuid: u16,
230275
alpha_unstaked: u64,
231276
limit_price: u64,
277+
allow_partial: bool,
232278
) -> dispatch::DispatchResult {
233279
// 1. We check the transaction is signed by the caller and retrieve the T::AccountId coldkey information.
234280
let coldkey = ensure_signed(origin)?;
@@ -240,17 +286,24 @@ impl<T: Config> Pallet<T> {
240286
alpha_unstaked
241287
);
242288

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
289+
// 2. Calcaulate the maximum amount that can be executed with price limit
247290
let max_amount = Self::get_max_amount_remove(netuid, limit_price);
248291
let mut possible_alpha = alpha_unstaked;
249292
if possible_alpha > max_amount {
250293
possible_alpha = max_amount;
251294
}
252295

253-
// 4. Swap the alpba to tao and update counters for this subnet.
296+
// 3. Validate the user input
297+
Self::validate_remove_stake(
298+
&coldkey,
299+
&hotkey,
300+
netuid,
301+
alpha_unstaked,
302+
max_amount,
303+
allow_partial,
304+
)?;
305+
306+
// 4. Swap the alpha to tao and update counters for this subnet.
254307
let fee = DefaultStakingFee::<T>::get();
255308
let tao_unstaked: u64 =
256309
Self::unstake_from_subnet(&hotkey, &coldkey, netuid, possible_alpha, fee);

pallets/subtensor/src/staking/stake_utils.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -729,6 +729,8 @@ impl<T: Config> Pallet<T> {
729729
hotkey: &T::AccountId,
730730
netuid: u16,
731731
stake_to_be_added: u64,
732+
max_amount: u64,
733+
allow_partial: bool,
732734
) -> Result<(), Error<T>> {
733735
// Ensure that the subnet exists.
734736
ensure!(Self::if_subnet_exist(netuid), Error::<T>::SubnetNotExists);
@@ -739,6 +741,12 @@ impl<T: Config> Pallet<T> {
739741
// Ensure that the stake_to_be_added is at least the min_amount
740742
ensure!(stake_to_be_added >= min_amount, Error::<T>::AmountTooLow);
741743

744+
// Ensure that if partial execution is not allowed, the amount will not cause
745+
// slippage over desired
746+
if !allow_partial {
747+
ensure!(stake_to_be_added <= max_amount, Error::<T>::SlippageTooHigh);
748+
}
749+
742750
// Ensure the callers coldkey has enough stake to perform the transaction.
743751
ensure!(
744752
Self::can_remove_balance_from_coldkey_account(coldkey, stake_to_be_added),
@@ -767,6 +775,8 @@ impl<T: Config> Pallet<T> {
767775
hotkey: &T::AccountId,
768776
netuid: u16,
769777
alpha_unstaked: u64,
778+
max_amount: u64,
779+
allow_partial: bool,
770780
) -> Result<(), Error<T>> {
771781
// Ensure that the subnet exists.
772782
ensure!(Self::if_subnet_exist(netuid), Error::<T>::SubnetNotExists);
@@ -781,6 +791,12 @@ impl<T: Config> Pallet<T> {
781791
return Err(Error::<T>::InsufficientLiquidity);
782792
};
783793

794+
// Ensure that if partial execution is not allowed, the amount will not cause
795+
// slippage over desired
796+
if !allow_partial {
797+
ensure!(alpha_unstaked <= max_amount, Error::<T>::SlippageTooHigh);
798+
}
799+
784800
// Ensure that the hotkey account exists this is only possible through registration.
785801
ensure!(
786802
Self::hotkey_account_exists(hotkey),

0 commit comments

Comments
 (0)