diff --git a/pallets/subtensor/src/staking/stake_utils.rs b/pallets/subtensor/src/staking/stake_utils.rs index 894a5a913..53e8dbaa5 100644 --- a/pallets/subtensor/src/staking/stake_utils.rs +++ b/pallets/subtensor/src/staking/stake_utils.rs @@ -1097,7 +1097,7 @@ impl Pallet { DefaultStakingFee::::get() } else { // Otherwise, calculate the fee based on the alpha estimate - let fee = alpha_estimate + let mut fee = alpha_estimate .saturating_mul( I96F32::saturating_from_num(AlphaDividendsPerSubnet::::get( origin_netuid, @@ -1110,6 +1110,16 @@ impl Pallet { .saturating_mul(Self::get_alpha_price(origin_netuid)) // fee needs to be in TAO .saturating_to_num::(); + // 0.005% per epoch matches to 44% annual in compound interest. Do not allow the fee + // to be lower than that. (1.00005^(365*20) ~= 1.44) + let apr_20_percent = I96F32::saturating_from_num(0.00005); + fee = fee.max( + alpha_estimate + .saturating_mul(apr_20_percent) + .saturating_to_num::(), + ); + + // We should at least get DefaultStakingFee anyway fee.max(DefaultStakingFee::::get()) } } diff --git a/pallets/subtensor/src/tests/staking.rs b/pallets/subtensor/src/tests/staking.rs index 33d686a60..1fc4e7b59 100644 --- a/pallets/subtensor/src/tests/staking.rs +++ b/pallets/subtensor/src/tests/staking.rs @@ -2311,7 +2311,10 @@ fn test_remove_stake_fee_realistic_values() { ); // Estimate fees - let expected_fee: f64 = current_price * alpha_divs as f64; + let mut expected_fee: f64 = current_price * alpha_divs as f64; + if expected_fee < alpha_to_unstake as f64 * 0.00005 { + expected_fee = alpha_to_unstake as f64 * 0.00005; + } // Remove stake to measure fee let balance_before = SubtensorModule::get_coldkey_balance(&coldkey); @@ -3903,7 +3906,7 @@ fn test_remove_99_9991_per_cent_stake_removes_all() { let coldkey_account_id = U256::from(81337); let amount = 10_000_000_000; let netuid: u16 = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); - let fee = DefaultStakingFee::::get(); + let mut fee = DefaultStakingFee::::get(); register_ok_neuron(netuid, hotkey_account_id, coldkey_account_id, 192213123); // Give it some $$$ in his coldkey balance @@ -3923,17 +3926,19 @@ fn test_remove_99_9991_per_cent_stake_removes_all() { &coldkey_account_id, netuid, ); + let remove_amount = (U64F64::from_num(alpha) * U64F64::from_num(0.999991)).to_num::(); assert_ok!(SubtensorModule::remove_stake( RuntimeOrigin::signed(coldkey_account_id), hotkey_account_id, netuid, - (U64F64::from_num(alpha) * U64F64::from_num(0.999991)).to_num::() + remove_amount, )); // Check that all alpha was unstaked and all TAO balance was returned (less fees) + fee = fee + fee.max((remove_amount as f64 * 0.00005) as u64); assert_abs_diff_eq!( SubtensorModule::get_coldkey_balance(&coldkey_account_id), - amount - fee * 2, + amount - fee, epsilon = 10000, ); assert_eq!(