diff --git a/pallets/subtensor/src/staking/move_stake.rs b/pallets/subtensor/src/staking/move_stake.rs index b189c90968..e826f8484e 100644 --- a/pallets/subtensor/src/staking/move_stake.rs +++ b/pallets/subtensor/src/staking/move_stake.rs @@ -303,15 +303,6 @@ impl Pallet { maybe_allow_partial: Option, check_transfer_toggle: bool, ) -> Result { - // Cap the alpha_amount at available Alpha because user might be paying transaxtion fees - // in Alpha and their total is already reduced by now. - let alpha_available = Self::get_stake_for_hotkey_and_coldkey_on_subnet( - origin_hotkey, - origin_coldkey, - origin_netuid, - ); - let alpha_amount = alpha_amount.min(alpha_available); - // Calculate the maximum amount that can be executed let max_amount = if origin_netuid != destination_netuid { if let Some(limit_price) = maybe_limit_price { diff --git a/pallets/subtensor/src/tests/move_stake.rs b/pallets/subtensor/src/tests/move_stake.rs index 8265ec5e9d..67a37d1e07 100644 --- a/pallets/subtensor/src/tests/move_stake.rs +++ b/pallets/subtensor/src/tests/move_stake.rs @@ -1023,17 +1023,45 @@ fn test_do_transfer_insufficient_stake() { ) .unwrap(); - // Amount over available stake succeeds (because fees can be paid in Alpha, - // this limitation is removed) - let alpha = stake_amount * 2; - assert_ok!(SubtensorModule::do_transfer_stake( - RuntimeOrigin::signed(origin_coldkey), - destination_coldkey, - hotkey, + let origin_alpha_before = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &origin_coldkey, netuid, + ); + let destination_alpha_before = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &destination_coldkey, netuid, - alpha.into() - )); + ); + + let alpha = stake_amount * 2; + assert_noop!( + SubtensorModule::do_transfer_stake( + RuntimeOrigin::signed(origin_coldkey), + destination_coldkey, + hotkey, + netuid, + netuid, + alpha.into() + ), + Error::::NotEnoughStakeToWithdraw + ); + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &origin_coldkey, + netuid + ), + origin_alpha_before + ); + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &destination_coldkey, + netuid + ), + destination_alpha_before + ); }); } diff --git a/pallets/transaction-fee/src/tests/mod.rs b/pallets/transaction-fee/src/tests/mod.rs index f452634cfe..20b1247938 100644 --- a/pallets/transaction-fee/src/tests/mod.rs +++ b/pallets/transaction-fee/src/tests/mod.rs @@ -10,6 +10,7 @@ use sp_runtime::{ transaction_validity::{InvalidTransaction, TransactionValidityError}, }; use subtensor_runtime_common::AlphaBalance; +use subtensor_swap_interface::SwapHandler; use mock::*; mod mock; @@ -1258,6 +1259,135 @@ fn test_transfer_stake_fees_alpha() { }); } +// cargo test --package subtensor-transaction-fee --lib -- tests::test_transfer_stake_full_amount_fails_when_alpha_fee_reduces_available_stake --exact --show-output +#[test] +fn test_transfer_stake_full_amount_fails_when_alpha_fee_reduces_available_stake() { + new_test_ext().execute_with(|| { + let destination_coldkey = U256::from(100000); + let stake_amount = TAO; + let sn = setup_subnets(2, 2); + setup_stake( + sn.subnets[0].netuid, + &sn.coldkey, + &sn.hotkeys[0], + stake_amount, + ); + + let current_balance = Balances::free_balance(sn.coldkey); + remove_balance_from_coldkey_account(&sn.coldkey, current_balance); + assert_eq!(Balances::free_balance(sn.coldkey), TaoBalance::ZERO); + + let alpha_before = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &sn.hotkeys[0], + &sn.coldkey, + sn.subnets[0].netuid, + ); + let call = RuntimeCall::SubtensorModule(pallet_subtensor::Call::transfer_stake { + destination_coldkey, + hotkey: sn.hotkeys[0], + origin_netuid: sn.subnets[0].netuid, + destination_netuid: sn.subnets[1].netuid, + alpha_amount: alpha_before, + }); + let info = call.get_dispatch_info(); + let ext = pallet_transaction_payment::ChargeTransactionPayment::::from(0.into()); + + let inner = ext + .dispatch_transaction(RuntimeOrigin::signed(sn.coldkey).into(), call, &info, 0, 0) + .expect("alpha fee payment should validate"); + assert_eq!( + inner.unwrap_err().error, + Error::::NotEnoughStakeToWithdraw.into() + ); + + let alpha_after = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &sn.hotkeys[0], + &sn.coldkey, + sn.subnets[0].netuid, + ); + let actual_alpha_fee = alpha_before - alpha_after; + assert!(actual_alpha_fee > AlphaBalance::ZERO); + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &sn.hotkeys[0], + &destination_coldkey, + sn.subnets[1].netuid, + ), + AlphaBalance::ZERO + ); + }); + + new_test_ext().execute_with(|| { + let destination_coldkey = U256::from(100000); + let stake_amount = TAO; + let sn = setup_subnets(2, 2); + setup_stake( + sn.subnets[0].netuid, + &sn.coldkey, + &sn.hotkeys[0], + stake_amount, + ); + + let current_balance = Balances::free_balance(sn.coldkey); + remove_balance_from_coldkey_account(&sn.coldkey, current_balance); + assert_eq!(Balances::free_balance(sn.coldkey), TaoBalance::ZERO); + + let alpha_before = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &sn.hotkeys[0], + &sn.coldkey, + sn.subnets[0].netuid, + ); + let full_amount_call = + RuntimeCall::SubtensorModule(pallet_subtensor::Call::transfer_stake { + destination_coldkey, + hotkey: sn.hotkeys[0], + origin_netuid: sn.subnets[0].netuid, + destination_netuid: sn.subnets[1].netuid, + alpha_amount: alpha_before, + }); + let info = full_amount_call.get_dispatch_info(); + let tao_fee = pallet_transaction_payment::Pallet::::compute_fee(0, &info, 0.into()); + let alpha_fee = pallet_subtensor_swap::Pallet::::get_alpha_amount_for_tao( + sn.subnets[0].netuid, + tao_fee, + ); + assert!(alpha_fee > AlphaBalance::ZERO); + + let transfer_amount = alpha_before - alpha_fee; + let call = RuntimeCall::SubtensorModule(pallet_subtensor::Call::transfer_stake { + destination_coldkey, + hotkey: sn.hotkeys[0], + origin_netuid: sn.subnets[0].netuid, + destination_netuid: sn.subnets[1].netuid, + alpha_amount: transfer_amount, + }); + let info = call.get_dispatch_info(); + let ext = pallet_transaction_payment::ChargeTransactionPayment::::from(0.into()); + + let inner = ext + .dispatch_transaction(RuntimeOrigin::signed(sn.coldkey).into(), call, &info, 0, 0) + .expect("alpha fee payment should validate"); + assert_ok!(inner); + + assert_eq!(Balances::free_balance(sn.coldkey), TaoBalance::ZERO); + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &sn.hotkeys[0], + &sn.coldkey, + sn.subnets[0].netuid, + ), + AlphaBalance::ZERO + ); + assert!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &sn.hotkeys[0], + &destination_coldkey, + sn.subnets[1].netuid, + ) > AlphaBalance::ZERO + ); + }); +} + // cargo test --package subtensor-transaction-fee --lib -- tests::test_swap_stake_fees_alpha --exact --show-output #[test] fn test_swap_stake_fees_alpha() {