@@ -5,7 +5,10 @@ use frame_support::{
55 pallet_prelude:: * ,
66 traits:: {
77 Imbalance , IsSubType , OnUnbalanced ,
8- fungible:: { Balanced , Credit , Debt , Inspect } ,
8+ fungible:: {
9+ Balanced , Credit , Debt , DecreaseIssuance , Imbalance as FungibleImbalance ,
10+ IncreaseIssuance , Inspect ,
11+ } ,
912 tokens:: { Precision , WithdrawConsequence } ,
1013 } ,
1114 weights:: { WeightToFeeCoefficient , WeightToFeeCoefficients , WeightToFeePolynomial } ,
@@ -21,11 +24,13 @@ use sp_runtime::{
2124use pallet_subtensor:: Call as SubtensorCall ;
2225use pallet_transaction_payment:: Config as PTPConfig ;
2326use pallet_transaction_payment:: OnChargeTransaction ;
27+ use subtensor_swap_interface:: SwapHandler ;
2428
2529// Misc
2630use core:: marker:: PhantomData ;
2731use smallvec:: smallvec;
2832use sp_std:: vec:: Vec ;
33+ use substrate_fixed:: types:: U96F32 ;
2934use subtensor_runtime_common:: { Balance , NetUid } ;
3035
3136// Tests
@@ -55,16 +60,130 @@ impl WeightToFeePolynomial for LinearWeightToFee {
5560pub trait AlphaFeeHandler < T : frame_system:: Config > {
5661 fn can_withdraw_in_alpha (
5762 coldkey : & AccountIdOf < T > ,
58- alpha_vec : & Vec < ( AccountIdOf < T > , NetUid ) > ,
63+ alpha_vec : & [ ( AccountIdOf < T > , NetUid ) ] ,
5964 tao_amount : u64 ,
6065 ) -> bool ;
6166 fn withdraw_in_alpha (
6267 coldkey : & AccountIdOf < T > ,
63- alpha_vec : & Vec < ( AccountIdOf < T > , NetUid ) > ,
68+ alpha_vec : & [ ( AccountIdOf < T > , NetUid ) ] ,
6469 tao_amount : u64 ,
6570 ) ;
6671}
6772
73+ /// Deduct the transaction fee from the Subtensor Pallet TotalIssuance when charging the transaction
74+ /// fee.
75+ pub struct TransactionFeeHandler < T > ( core:: marker:: PhantomData < T > ) ;
76+ impl < T > Default for TransactionFeeHandler < T > {
77+ fn default ( ) -> Self {
78+ Self ( core:: marker:: PhantomData )
79+ }
80+ }
81+
82+ impl < T >
83+ OnUnbalanced <
84+ FungibleImbalance <
85+ u64 ,
86+ DecreaseIssuance < AccountIdOf < T > , pallet_balances:: Pallet < T > > ,
87+ IncreaseIssuance < AccountIdOf < T > , pallet_balances:: Pallet < T > > ,
88+ > ,
89+ > for TransactionFeeHandler < T >
90+ where
91+ T : frame_system:: Config ,
92+ T : pallet_subtensor:: Config ,
93+ T : pallet_balances:: Config < Balance = u64 > ,
94+ {
95+ fn on_nonzero_unbalanced (
96+ imbalance : FungibleImbalance <
97+ u64 ,
98+ DecreaseIssuance < AccountIdOf < T > , pallet_balances:: Pallet < T > > ,
99+ IncreaseIssuance < AccountIdOf < T > , pallet_balances:: Pallet < T > > ,
100+ > ,
101+ ) {
102+ let ti_before = pallet_subtensor:: TotalIssuance :: < T > :: get ( ) ;
103+ pallet_subtensor:: TotalIssuance :: < T > :: put ( ti_before. saturating_sub ( imbalance. peek ( ) ) ) ;
104+ drop ( imbalance) ;
105+ }
106+ }
107+
108+ /// Handle Alpha fees
109+ impl < T > AlphaFeeHandler < T > for TransactionFeeHandler < T >
110+ where
111+ T : frame_system:: Config ,
112+ T : pallet_subtensor:: Config ,
113+ T : pallet_subtensor_swap:: Config ,
114+ {
115+ /// This function checks if tao_amount fee can be withdraw in Alpha currency
116+ /// by converting Alpha to TAO at the current price and ignoring slippage.
117+ ///
118+ /// If this function returns true, the transaction will be included in the block
119+ /// and Alpha will be withdraw from the account, no matter whether transaction
120+ /// is successful or not.
121+ ///
122+ /// If this function returns true, but at the time of execution the Alpha price
123+ /// changes and it becomes impossible to pay tx fee with the Alpha balance,
124+ /// the transaction still executes and all Alpha is withdrawn from the account.
125+ fn can_withdraw_in_alpha (
126+ coldkey : & AccountIdOf < T > ,
127+ alpha_vec : & [ ( AccountIdOf < T > , NetUid ) ] ,
128+ tao_amount : u64 ,
129+ ) -> bool {
130+ if alpha_vec. is_empty ( ) {
131+ // Alpha vector is empty, nothing to withdraw
132+ return false ;
133+ }
134+
135+ // Divide tao_amount among all alpha entries
136+ let tao_per_entry = tao_amount. checked_div ( alpha_vec. len ( ) as u64 ) . unwrap_or ( 0 ) ;
137+
138+ // The rule here is that we should be able to withdraw at least from one entry.
139+ // This is not ideal because it may not pay all fees, but UX is the priority
140+ // and this approach still provides spam protection.
141+ alpha_vec. iter ( ) . any ( |( hotkey, netuid) | {
142+ let alpha_balance = U96F32 :: saturating_from_num (
143+ pallet_subtensor:: Pallet :: < T > :: get_stake_for_hotkey_and_coldkey_on_subnet (
144+ hotkey, coldkey, * netuid,
145+ ) ,
146+ ) ;
147+ let alpha_price = pallet_subtensor_swap:: Pallet :: < T > :: current_alpha_price ( * netuid) ;
148+ alpha_price. saturating_mul ( alpha_balance) >= tao_per_entry
149+ } )
150+ }
151+
152+ fn withdraw_in_alpha (
153+ coldkey : & AccountIdOf < T > ,
154+ alpha_vec : & [ ( AccountIdOf < T > , NetUid ) ] ,
155+ tao_amount : u64 ,
156+ ) {
157+ if alpha_vec. is_empty ( ) {
158+ return ;
159+ }
160+
161+ let tao_per_entry = tao_amount. checked_div ( alpha_vec. len ( ) as u64 ) . unwrap_or ( 0 ) ;
162+
163+ alpha_vec. iter ( ) . for_each ( |( hotkey, netuid) | {
164+ // Divide tao_amount evenly among all alpha entries
165+ let alpha_balance = U96F32 :: saturating_from_num (
166+ pallet_subtensor:: Pallet :: < T > :: get_stake_for_hotkey_and_coldkey_on_subnet (
167+ hotkey, coldkey, * netuid,
168+ ) ,
169+ ) ;
170+ let alpha_price = pallet_subtensor_swap:: Pallet :: < T > :: current_alpha_price ( * netuid) ;
171+ let alpha_fee = U96F32 :: saturating_from_num ( tao_per_entry)
172+ . checked_div ( alpha_price)
173+ . unwrap_or ( alpha_balance)
174+ . min ( alpha_balance)
175+ . saturating_to_num :: < u64 > ( ) ;
176+
177+ pallet_subtensor:: Pallet :: < T > :: decrease_stake_for_hotkey_and_coldkey_on_subnet (
178+ hotkey,
179+ coldkey,
180+ * netuid,
181+ alpha_fee. into ( ) ,
182+ ) ;
183+ } ) ;
184+ }
185+ }
186+
68187/// Enum that describes either a withdrawn amount of transaction fee in TAO or the
69188/// fact that fee was charged in Alpha (without an amount because it is not needed)
70189pub enum WithdrawnFee < T : frame_system:: Config , F : Balanced < AccountIdOf < T > > > {
@@ -217,12 +336,12 @@ where
217336 Ok ( ( ) )
218337 }
219338
220- #[ cfg( feature = "runtime-benchmarks" ) ]
339+ // #[cfg(feature = "runtime-benchmarks")]
221340 fn endow_account ( who : & AccountIdOf < T > , amount : Self :: Balance ) {
222341 let _ = F :: deposit ( who, amount, Precision :: BestEffort ) ;
223342 }
224343
225- #[ cfg( feature = "runtime-benchmarks" ) ]
344+ // #[cfg(feature = "runtime-benchmarks")]
226345 fn minimum_balance ( ) -> Self :: Balance {
227346 F :: minimum_balance ( )
228347 }
0 commit comments