Skip to content

Commit ecb6196

Browse files
authored
Merge pull request #1176 from opentensor/feat/add-staking-tx-fees-to-subnet-tao
Send staking and unstaking fees to SubnetTAO
2 parents a1d7e87 + af8cd5e commit ecb6196

File tree

13 files changed

+484
-168
lines changed

13 files changed

+484
-168
lines changed

pallets/subtensor/src/lib.rs

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@ use frame_support::{
1212
dispatch::{self, DispatchInfo, DispatchResult, DispatchResultWithPostInfo, PostDispatchInfo},
1313
ensure,
1414
pallet_macros::import_section,
15-
traits::{tokens::fungible, IsSubType},
15+
traits::{
16+
tokens::{fungible, Fortitude, Preservation},
17+
IsSubType,
18+
},
1619
};
1720

1821
use codec::{Decode, Encode};
@@ -21,6 +24,7 @@ use frame_support::sp_runtime::transaction_validity::ValidTransaction;
2124
use pallet_balances::Call as BalancesCall;
2225
// use pallet_scheduler as Scheduler;
2326
use scale_info::TypeInfo;
27+
use sp_core::Get;
2428
use sp_runtime::{
2529
traits::{DispatchInfoOf, Dispatchable, PostDispatchInfoOf, SignedExtension},
2630
transaction_validity::{TransactionValidity, TransactionValidityError},
@@ -697,9 +701,10 @@ pub mod pallet {
697701

698702
#[pallet::type_value]
699703
/// Default minimum stake.
700-
/// 2M rao matches $1 at $500/TAO
704+
/// 500k rao matches $0.25 at $500/TAO
705+
/// Also used as staking fee
701706
pub fn DefaultMinStake<T: Config>() -> u64 {
702-
2_000_000
707+
500_000
703708
}
704709

705710
#[pallet::type_value]
@@ -1532,12 +1537,14 @@ pub enum CallType {
15321537
#[derive(Debug, PartialEq)]
15331538
pub enum CustomTransactionError {
15341539
ColdkeyInSwapSchedule,
1540+
StakeAmountTooLow,
15351541
}
15361542

15371543
impl From<CustomTransactionError> for u8 {
15381544
fn from(variant: CustomTransactionError) -> u8 {
15391545
match variant {
15401546
CustomTransactionError::ColdkeyInSwapSchedule => 0,
1547+
CustomTransactionError::StakeAmountTooLow => 1,
15411548
}
15421549
}
15431550
}
@@ -1687,10 +1694,31 @@ where
16871694
Err(InvalidTransaction::Custom(7).into())
16881695
}
16891696
}
1690-
Some(Call::add_stake { .. }) => Ok(ValidTransaction {
1691-
priority: Self::get_priority_vanilla(),
1692-
..Default::default()
1693-
}),
1697+
Some(Call::add_stake {
1698+
hotkey: _,
1699+
netuid: _,
1700+
amount_staked,
1701+
}) => {
1702+
// Check that amount parameter is at least the min stake
1703+
// also check the coldkey balance
1704+
let coldkey_balance = <<T as Config>::Currency as fungible::Inspect<
1705+
<T as frame_system::Config>::AccountId,
1706+
>>::reducible_balance(
1707+
who, Preservation::Expendable, Fortitude::Polite
1708+
);
1709+
1710+
if (*amount_staked < DefaultMinStake::<T>::get())
1711+
|| (coldkey_balance < DefaultMinStake::<T>::get())
1712+
{
1713+
InvalidTransaction::Custom(CustomTransactionError::StakeAmountTooLow.into())
1714+
.into()
1715+
} else {
1716+
Ok(ValidTransaction {
1717+
priority: Self::get_priority_vanilla(),
1718+
..Default::default()
1719+
})
1720+
}
1721+
}
16941722
Some(Call::remove_stake { .. }) => Ok(ValidTransaction {
16951723
priority: Self::get_priority_vanilla(),
16961724
..Default::default()

pallets/subtensor/src/staking/add_stake.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,8 @@ impl<T: Config> Pallet<T> {
7474

7575
// 6. Swap the stake into alpha on the subnet and increase counters.
7676
// Emit the staking event.
77-
Self::stake_into_subnet(&hotkey, &coldkey, netuid, tao_staked);
77+
let fee = DefaultMinStake::<T>::get();
78+
Self::stake_into_subnet(&hotkey, &coldkey, netuid, tao_staked, fee);
7879

7980
// Ok and return.
8081
Ok(())

pallets/subtensor/src/staking/helpers.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,8 @@ impl<T: Config> Pallet<T> {
163163
// Log the clearing of a small nomination
164164
// Remove the stake from the nominator account. (this is a more forceful unstake operation which )
165165
// Actually deletes the staking account.
166-
let cleared_stake = Self::unstake_from_subnet(hotkey, coldkey, netuid, stake);
166+
// Do not apply any fees
167+
let cleared_stake = Self::unstake_from_subnet(hotkey, coldkey, netuid, stake, 0);
167168
// Add the stake to the coldkey account.
168169
Self::add_balance_to_coldkey_account(coldkey, cleared_stake);
169170
}

pallets/subtensor/src/staking/move_stake.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,11 +68,13 @@ impl<T: Config> Pallet<T> {
6868
);
6969

7070
// --- 7. Unstake the amount of alpha from the origin subnet, converting it to TAO
71+
let fee = DefaultMinStake::<T>::get().saturating_div(2); // fee is half of min stake because it is applied twice
7172
let origin_tao = Self::unstake_from_subnet(
7273
&origin_hotkey.clone(),
7374
&coldkey.clone(),
7475
origin_netuid,
7576
alpha_amount,
77+
fee,
7678
);
7779

7880
// Ensure origin_tao is at least DefaultMinStake
@@ -87,6 +89,7 @@ impl<T: Config> Pallet<T> {
8789
&coldkey.clone(),
8890
destination_netuid,
8991
origin_tao,
92+
fee,
9093
);
9194

9295
// --- 9. Log the event.
@@ -104,7 +107,7 @@ impl<T: Config> Pallet<T> {
104107
origin_netuid,
105108
destination_hotkey,
106109
destination_netuid,
107-
origin_tao,
110+
origin_tao.saturating_sub(fee),
108111
));
109112

110113
// -- 10. Ok and return.
@@ -178,7 +181,9 @@ impl<T: Config> Pallet<T> {
178181
);
179182

180183
// 6. Unstake from the origin coldkey; this returns an amount of TAO.
181-
let origin_tao = Self::unstake_from_subnet(&hotkey, &coldkey, origin_netuid, alpha_amount);
184+
let fee = DefaultMinStake::<T>::get().saturating_div(2);
185+
let origin_tao =
186+
Self::unstake_from_subnet(&hotkey, &coldkey, origin_netuid, alpha_amount, fee);
182187

183188
// 7. Ensure the returned TAO meets a minimum stake requirement (if required).
184189
ensure!(
@@ -193,6 +198,7 @@ impl<T: Config> Pallet<T> {
193198
&destination_coldkey,
194199
destination_netuid,
195200
origin_tao,
201+
fee,
196202
);
197203

198204
// 9. Emit an event for logging/monitoring.

pallets/subtensor/src/staking/remove_stake.rs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use super::*;
2+
use sp_core::Get;
23

34
impl<T: Config> Pallet<T> {
45
/// ---- The implementation for the extrinsic remove_stake: Removes stake from a hotkey account and adds it onto a coldkey.
@@ -65,8 +66,9 @@ impl<T: Config> Pallet<T> {
6566
);
6667

6768
// 6. Swap the alpba to tao and update counters for this subnet.
69+
let fee = DefaultMinStake::<T>::get();
6870
let tao_unstaked: u64 =
69-
Self::unstake_from_subnet(&hotkey, &coldkey, netuid, alpha_unstaked);
71+
Self::unstake_from_subnet(&hotkey, &coldkey, netuid, alpha_unstaked, fee);
7072

7173
// 7. We add the balance to the coldkey. If the above fails we will not credit this coldkey.
7274
Self::add_balance_to_coldkey_account(&coldkey, tao_unstaked);
@@ -81,10 +83,6 @@ impl<T: Config> Pallet<T> {
8183
})
8284
}
8385

84-
// TODO: Regression
85-
// Emit the unstaking event.
86-
// Self::deposit_event(Event::StakeRemoved(hotkey, stake_to_be_removed));
87-
8886
// Done and ok.
8987
Ok(())
9088
}
@@ -119,6 +117,8 @@ impl<T: Config> Pallet<T> {
119117
origin: T::RuntimeOrigin,
120118
hotkey: T::AccountId,
121119
) -> dispatch::DispatchResult {
120+
let fee = DefaultMinStake::<T>::get();
121+
122122
// 1. We check the transaction is signed by the caller and retrieve the T::AccountId coldkey information.
123123
let coldkey = ensure_signed(origin)?;
124124
log::info!("do_unstake_all( origin:{:?} hotkey:{:?} )", coldkey, hotkey);
@@ -141,7 +141,7 @@ impl<T: Config> Pallet<T> {
141141
if alpha_unstaked > 0 {
142142
// Swap the alpha to tao and update counters for this subnet.
143143
let tao_unstaked: u64 =
144-
Self::unstake_from_subnet(&hotkey, &coldkey, *netuid, alpha_unstaked);
144+
Self::unstake_from_subnet(&hotkey, &coldkey, *netuid, alpha_unstaked, fee);
145145

146146
// Add the balance to the coldkey. If the above fails we will not credit this coldkey.
147147
Self::add_balance_to_coldkey_account(&coldkey, tao_unstaked);
@@ -185,6 +185,8 @@ impl<T: Config> Pallet<T> {
185185
origin: T::RuntimeOrigin,
186186
hotkey: T::AccountId,
187187
) -> dispatch::DispatchResult {
188+
let fee = DefaultMinStake::<T>::get();
189+
188190
// 1. We check the transaction is signed by the caller and retrieve the T::AccountId coldkey information.
189191
let coldkey = ensure_signed(origin)?;
190192
log::info!("do_unstake_all( origin:{:?} hotkey:{:?} )", coldkey, hotkey);
@@ -210,7 +212,7 @@ impl<T: Config> Pallet<T> {
210212
if alpha_unstaked > 0 {
211213
// Swap the alpha to tao and update counters for this subnet.
212214
let tao_unstaked: u64 =
213-
Self::unstake_from_subnet(&hotkey, &coldkey, *netuid, alpha_unstaked);
215+
Self::unstake_from_subnet(&hotkey, &coldkey, *netuid, alpha_unstaked, fee);
214216

215217
// Increment total
216218
total_tao_unstaked = total_tao_unstaked.saturating_add(tao_unstaked);
@@ -227,6 +229,7 @@ impl<T: Config> Pallet<T> {
227229
&coldkey,
228230
Self::get_root_netuid(),
229231
total_tao_unstaked,
232+
0, // no fee for restaking
230233
);
231234

232235
// 5. Done and ok.

pallets/subtensor/src/staking/stake_utils.rs

Lines changed: 47 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -606,6 +606,7 @@ impl<T: Config> Pallet<T> {
606606
coldkey: &T::AccountId,
607607
netuid: u16,
608608
alpha: u64,
609+
fee: u64,
609610
) -> u64 {
610611
// Step 1: Swap the alpha for TAO.
611612
let tao: u64 = Self::swap_alpha_for_tao(netuid, alpha);
@@ -621,25 +622,35 @@ impl<T: Config> Pallet<T> {
621622
// });
622623
// }
623624

624-
// Step 4. Deposit and log the unstaking event.
625+
// Step 4. Reduce tao amount by staking fee and credit this fee to SubnetTAO
626+
let tao_unstaked = tao.saturating_sub(fee);
627+
let actual_fee = tao.saturating_sub(tao_unstaked);
628+
SubnetTAO::<T>::mutate(netuid, |total| {
629+
*total = total.saturating_add(actual_fee);
630+
});
631+
TotalStake::<T>::mutate(|total| {
632+
*total = total.saturating_add(actual_fee);
633+
});
634+
635+
// Step 5. Deposit and log the unstaking event.
625636
Self::deposit_event(Event::StakeRemoved(
626637
coldkey.clone(),
627638
hotkey.clone(),
628-
tao,
639+
tao_unstaked,
629640
alpha,
630641
netuid,
631642
));
632643
log::info!(
633644
"StakeRemoved( coldkey: {:?}, hotkey:{:?}, tao: {:?}, alpha:{:?}, netuid: {:?} )",
634645
coldkey.clone(),
635646
hotkey.clone(),
636-
tao,
647+
tao_unstaked,
637648
alpha,
638649
netuid
639650
);
640651

641-
// Step 5: Return the amount of TAO unstaked.
642-
tao
652+
// Step 6: Return the amount of TAO unstaked.
653+
tao_unstaked
643654
}
644655

645656
/// Stakes TAO into a subnet for a given hotkey and coldkey pair.
@@ -650,38 +661,54 @@ impl<T: Config> Pallet<T> {
650661
coldkey: &T::AccountId,
651662
netuid: u16,
652663
tao: u64,
664+
fee: u64,
653665
) -> u64 {
654-
// Step 1. Swap the tao to alpha.
655-
let alpha: u64 = Self::swap_tao_for_alpha(netuid, tao);
656-
657-
// Step 2: Increase the alpha on the hotkey account.
658-
Self::increase_stake_for_hotkey_and_coldkey_on_subnet(hotkey, coldkey, netuid, alpha);
659-
660-
// Step 4: Update the list of hotkeys staking for this coldkey
661-
let mut staking_hotkeys = StakingHotkeys::<T>::get(coldkey);
662-
if !staking_hotkeys.contains(hotkey) {
663-
staking_hotkeys.push(hotkey.clone());
664-
StakingHotkeys::<T>::insert(coldkey, staking_hotkeys.clone());
666+
// Step 1. Reduce tao amount by staking fee and credit this fee to SubnetTAO
667+
// At this point tao was already withdrawn from the user balance and is considered
668+
// available
669+
let tao_staked = tao.saturating_sub(fee);
670+
let actual_fee = tao.saturating_sub(tao_staked);
671+
672+
// Step 2. Swap the tao to alpha.
673+
let alpha: u64 = Self::swap_tao_for_alpha(netuid, tao_staked);
674+
if (tao_staked > 0) && (alpha > 0) {
675+
// Step 3: Increase the alpha on the hotkey account.
676+
Self::increase_stake_for_hotkey_and_coldkey_on_subnet(hotkey, coldkey, netuid, alpha);
677+
678+
// Step 4: Update the list of hotkeys staking for this coldkey
679+
let mut staking_hotkeys = StakingHotkeys::<T>::get(coldkey);
680+
if !staking_hotkeys.contains(hotkey) {
681+
staking_hotkeys.push(hotkey.clone());
682+
StakingHotkeys::<T>::insert(coldkey, staking_hotkeys.clone());
683+
}
665684
}
666685

667-
// Step 5. Deposit and log the staking event.
686+
// Step 5. Increase Tao reserves by the fee amount.
687+
SubnetTAO::<T>::mutate(netuid, |total| {
688+
*total = total.saturating_add(actual_fee);
689+
});
690+
TotalStake::<T>::mutate(|total| {
691+
*total = total.saturating_add(actual_fee);
692+
});
693+
694+
// Step 6. Deposit and log the staking event.
668695
Self::deposit_event(Event::StakeAdded(
669696
coldkey.clone(),
670697
hotkey.clone(),
671-
tao,
698+
tao_staked,
672699
alpha,
673700
netuid,
674701
));
675702
log::info!(
676703
"StakeAdded( coldkey: {:?}, hotkey:{:?}, tao: {:?}, alpha:{:?}, netuid: {:?} )",
677704
coldkey.clone(),
678705
hotkey.clone(),
679-
tao,
706+
tao_staked,
680707
alpha,
681708
netuid
682709
);
683710

684-
// Step 6: Return the amount of alpha staked
711+
// Step 7: Return the amount of alpha staked
685712
alpha
686713
}
687714

pallets/subtensor/src/tests/mock.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -726,7 +726,8 @@ pub fn increase_stake_on_coldkey_hotkey_account(
726726
tao_staked: u64,
727727
netuid: u16,
728728
) {
729-
SubtensorModule::stake_into_subnet(hotkey, coldkey, netuid, tao_staked);
729+
let fee = 0;
730+
SubtensorModule::stake_into_subnet(hotkey, coldkey, netuid, tao_staked, fee);
730731
}
731732

732733
/// Increases the stake on the hotkey account under its owning coldkey.

0 commit comments

Comments
 (0)