@@ -1085,6 +1085,169 @@ fn add_stake_recycle_with_insufficient_balance_returns_error() {
10851085 } ) ;
10861086}
10871087
1088+ #[ test]
1089+ fn recycle_alpha_clamps_to_available_when_amount_exceeds_stake ( ) {
1090+ mock:: new_test_ext ( 1 ) . execute_with ( || {
1091+ let owner_hotkey = U256 :: from ( 11001 ) ;
1092+ let owner_coldkey = U256 :: from ( 11002 ) ;
1093+ let coldkey = U256 :: from ( 11101 ) ;
1094+ let hotkey = U256 :: from ( 11102 ) ;
1095+ let min_stake = DefaultMinStake :: < mock:: Test > :: get ( ) ;
1096+ let stake_amount_raw = min_stake. to_u64 ( ) . saturating_mul ( 200 ) ;
1097+
1098+ let netuid = mock:: add_dynamic_network ( & owner_hotkey, & owner_coldkey) ;
1099+ mock:: setup_reserves (
1100+ netuid,
1101+ TaoBalance :: from ( 130_000_000_000_u64 ) ,
1102+ AlphaBalance :: from ( 110_000_000_000_u64 ) ,
1103+ ) ;
1104+
1105+ mock:: register_ok_neuron ( netuid, hotkey, coldkey, 0 ) ;
1106+
1107+ pallet_subtensor:: Pallet :: < mock:: Test > :: add_balance_to_coldkey_account (
1108+ & coldkey,
1109+ TaoBalance :: from ( stake_amount_raw. saturating_add ( 1_000_000_000 ) ) ,
1110+ ) ;
1111+
1112+ assert_ok ! ( pallet_subtensor:: Pallet :: <mock:: Test >:: add_stake(
1113+ RawOrigin :: Signed ( coldkey) . into( ) ,
1114+ hotkey,
1115+ netuid,
1116+ stake_amount_raw. into( ) ,
1117+ ) ) ;
1118+
1119+ let alpha_before =
1120+ pallet_subtensor:: Pallet :: < mock:: Test > :: get_stake_for_hotkey_and_coldkey_on_subnet (
1121+ & hotkey, & coldkey, netuid,
1122+ ) ;
1123+ assert ! ( alpha_before > AlphaBalance :: ZERO ) ;
1124+
1125+ // Request way more than available — should clamp to alpha_before
1126+ let huge_amount = AlphaBalance :: from ( u64:: MAX ) ;
1127+
1128+ let expected_weight = Weight :: from_parts ( 113_400_000 , 0 )
1129+ . saturating_add ( <mock:: Test as frame_system:: Config >:: DbWeight :: get ( ) . reads ( 10 ) )
1130+ . saturating_add ( <mock:: Test as frame_system:: Config >:: DbWeight :: get ( ) . writes ( 4 ) ) ;
1131+
1132+ let mut env = MockEnv :: new (
1133+ FunctionId :: RecycleAlphaV1 ,
1134+ coldkey,
1135+ ( hotkey, huge_amount, netuid) . encode ( ) ,
1136+ )
1137+ . with_expected_weight ( expected_weight) ;
1138+
1139+ let ret = SubtensorChainExtension :: < mock:: Test > :: dispatch ( & mut env) . unwrap ( ) ;
1140+ assert_success ( ret) ;
1141+
1142+ let returned_amount = AlphaBalance :: decode ( & mut env. output ( ) ) . unwrap ( ) ;
1143+ assert_eq ! ( returned_amount, alpha_before, "should clamp to available alpha" ) ;
1144+
1145+ let alpha_after =
1146+ pallet_subtensor:: Pallet :: < mock:: Test > :: get_stake_for_hotkey_and_coldkey_on_subnet (
1147+ & hotkey, & coldkey, netuid,
1148+ ) ;
1149+ assert ! ( alpha_after. is_zero( ) , "all alpha should be recycled" ) ;
1150+ } ) ;
1151+ }
1152+
1153+ #[ test]
1154+ fn burn_alpha_on_root_subnet_returns_error ( ) {
1155+ mock:: new_test_ext ( 1 ) . execute_with ( || {
1156+ let coldkey = U256 :: from ( 11201 ) ;
1157+ let hotkey = U256 :: from ( 11202 ) ;
1158+
1159+ pallet_subtensor:: Owner :: < mock:: Test > :: insert ( hotkey, coldkey) ;
1160+
1161+ let expected_weight = Weight :: from_parts ( 112_200_000 , 0 )
1162+ . saturating_add ( <mock:: Test as frame_system:: Config >:: DbWeight :: get ( ) . reads ( 10 ) )
1163+ . saturating_add ( <mock:: Test as frame_system:: Config >:: DbWeight :: get ( ) . writes ( 3 ) ) ;
1164+
1165+ let mut env = MockEnv :: new (
1166+ FunctionId :: BurnAlphaV1 ,
1167+ coldkey,
1168+ ( hotkey, AlphaBalance :: from ( 1_000u64 ) , NetUid :: ROOT ) . encode ( ) ,
1169+ )
1170+ . with_expected_weight ( expected_weight) ;
1171+
1172+ let ret = SubtensorChainExtension :: < mock:: Test > :: dispatch ( & mut env) . unwrap ( ) ;
1173+ match ret {
1174+ RetVal :: Converging ( code) => {
1175+ assert_ne ! (
1176+ code,
1177+ Output :: Success as u32 ,
1178+ "should not succeed on root subnet"
1179+ )
1180+ }
1181+ _ => panic ! ( "unexpected return value" ) ,
1182+ }
1183+ } ) ;
1184+ }
1185+
1186+ #[ test]
1187+ fn burn_alpha_clamps_to_available_when_amount_exceeds_stake ( ) {
1188+ mock:: new_test_ext ( 1 ) . execute_with ( || {
1189+ let owner_hotkey = U256 :: from ( 11301 ) ;
1190+ let owner_coldkey = U256 :: from ( 11302 ) ;
1191+ let coldkey = U256 :: from ( 11401 ) ;
1192+ let hotkey = U256 :: from ( 11402 ) ;
1193+ let min_stake = DefaultMinStake :: < mock:: Test > :: get ( ) ;
1194+ let stake_amount_raw = min_stake. to_u64 ( ) . saturating_mul ( 200 ) ;
1195+
1196+ let netuid = mock:: add_dynamic_network ( & owner_hotkey, & owner_coldkey) ;
1197+ mock:: setup_reserves (
1198+ netuid,
1199+ TaoBalance :: from ( 130_000_000_000_u64 ) ,
1200+ AlphaBalance :: from ( 110_000_000_000_u64 ) ,
1201+ ) ;
1202+
1203+ mock:: register_ok_neuron ( netuid, hotkey, coldkey, 0 ) ;
1204+
1205+ pallet_subtensor:: Pallet :: < mock:: Test > :: add_balance_to_coldkey_account (
1206+ & coldkey,
1207+ TaoBalance :: from ( stake_amount_raw. saturating_add ( 1_000_000_000 ) ) ,
1208+ ) ;
1209+
1210+ assert_ok ! ( pallet_subtensor:: Pallet :: <mock:: Test >:: add_stake(
1211+ RawOrigin :: Signed ( coldkey) . into( ) ,
1212+ hotkey,
1213+ netuid,
1214+ stake_amount_raw. into( ) ,
1215+ ) ) ;
1216+
1217+ let alpha_before =
1218+ pallet_subtensor:: Pallet :: < mock:: Test > :: get_stake_for_hotkey_and_coldkey_on_subnet (
1219+ & hotkey, & coldkey, netuid,
1220+ ) ;
1221+ assert ! ( alpha_before > AlphaBalance :: ZERO ) ;
1222+
1223+ // Request way more than available — should clamp to alpha_before
1224+ let huge_amount = AlphaBalance :: from ( u64:: MAX ) ;
1225+
1226+ let expected_weight = Weight :: from_parts ( 112_200_000 , 0 )
1227+ . saturating_add ( <mock:: Test as frame_system:: Config >:: DbWeight :: get ( ) . reads ( 10 ) )
1228+ . saturating_add ( <mock:: Test as frame_system:: Config >:: DbWeight :: get ( ) . writes ( 3 ) ) ;
1229+
1230+ let mut env = MockEnv :: new (
1231+ FunctionId :: BurnAlphaV1 ,
1232+ coldkey,
1233+ ( hotkey, huge_amount, netuid) . encode ( ) ,
1234+ )
1235+ . with_expected_weight ( expected_weight) ;
1236+
1237+ let ret = SubtensorChainExtension :: < mock:: Test > :: dispatch ( & mut env) . unwrap ( ) ;
1238+ assert_success ( ret) ;
1239+
1240+ let returned_amount = AlphaBalance :: decode ( & mut env. output ( ) ) . unwrap ( ) ;
1241+ assert_eq ! ( returned_amount, alpha_before, "should clamp to available alpha" ) ;
1242+
1243+ let alpha_after =
1244+ pallet_subtensor:: Pallet :: < mock:: Test > :: get_stake_for_hotkey_and_coldkey_on_subnet (
1245+ & hotkey, & coldkey, netuid,
1246+ ) ;
1247+ assert ! ( alpha_after. is_zero( ) , "all alpha should be burned" ) ;
1248+ } ) ;
1249+ }
1250+
10881251impl MockEnv {
10891252 fn new ( func_id : FunctionId , caller : AccountId , input : Vec < u8 > ) -> Self {
10901253 Self {
0 commit comments