From cb74089bf24ec0e479e5e9d175ec1b2d5b62ec63 Mon Sep 17 00:00:00 2001 From: Tsukasa Hayashi <4372044+altis0725@users.noreply.github.com> Date: Wed, 25 Feb 2026 20:32:12 +0900 Subject: [PATCH 1/3] feat: add origin AccountId to StakeAdded and StakeRemoved events Fixes #311 Co-Authored-By: Claude Opus 4.6 --- pallets/subtensor/src/macros/events.rs | 4 +++ pallets/subtensor/src/staking/add_stake.rs | 2 ++ pallets/subtensor/src/staking/helpers.rs | 1 + pallets/subtensor/src/staking/move_stake.rs | 3 ++ pallets/subtensor/src/staking/remove_stake.rs | 5 ++++ pallets/subtensor/src/staking/stake_utils.rs | 13 +++++++-- pallets/subtensor/src/subnets/leasing.rs | 2 ++ pallets/subtensor/src/tests/mock.rs | 1 + pallets/subtensor/src/tests/move_stake.rs | 29 +++++++++++++++++++ pallets/subtensor/src/tests/staking.rs | 9 ++++++ 10 files changed, 67 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/src/macros/events.rs b/pallets/subtensor/src/macros/events.rs index c86cc1a1e5..86661c35f5 100644 --- a/pallets/subtensor/src/macros/events.rs +++ b/pallets/subtensor/src/macros/events.rs @@ -14,7 +14,9 @@ mod events { /// a network is removed. NetworkRemoved(NetUid), /// stake has been transferred from the a coldkey account onto the hotkey staking account. + /// (origin, coldkey, hotkey, tao_amount, alpha_amount, netuid, fee) StakeAdded( + T::AccountId, T::AccountId, T::AccountId, TaoCurrency, @@ -23,7 +25,9 @@ mod events { u64, ), /// stake has been removed from the hotkey staking account onto the coldkey account. + /// (origin, coldkey, hotkey, tao_amount, alpha_amount, netuid, fee) StakeRemoved( + T::AccountId, T::AccountId, T::AccountId, TaoCurrency, diff --git a/pallets/subtensor/src/staking/add_stake.rs b/pallets/subtensor/src/staking/add_stake.rs index ea33912bf1..30a07450a1 100644 --- a/pallets/subtensor/src/staking/add_stake.rs +++ b/pallets/subtensor/src/staking/add_stake.rs @@ -70,6 +70,7 @@ impl Pallet { // 4. Swap the stake into alpha on the subnet and increase counters. // Emit the staking event. Self::stake_into_subnet( + &coldkey, &hotkey, &coldkey, netuid, @@ -166,6 +167,7 @@ impl Pallet { // 6. Swap the stake into alpha on the subnet and increase counters. // Emit the staking event. Self::stake_into_subnet( + &coldkey, &hotkey, &coldkey, netuid, diff --git a/pallets/subtensor/src/staking/helpers.rs b/pallets/subtensor/src/staking/helpers.rs index 099f8e26b6..811ebf4435 100644 --- a/pallets/subtensor/src/staking/helpers.rs +++ b/pallets/subtensor/src/staking/helpers.rs @@ -232,6 +232,7 @@ impl Pallet { // Actually deletes the staking account. // Do not apply any fees let maybe_cleared_stake = Self::unstake_from_subnet( + coldkey, hotkey, coldkey, netuid, diff --git a/pallets/subtensor/src/staking/move_stake.rs b/pallets/subtensor/src/staking/move_stake.rs index a1d9b46d5b..db7b3c04c9 100644 --- a/pallets/subtensor/src/staking/move_stake.rs +++ b/pallets/subtensor/src/staking/move_stake.rs @@ -356,6 +356,7 @@ impl Pallet { // do not pay remove fees to avoid double fees in moves transactions let tao_unstaked = Self::unstake_from_subnet( + origin_coldkey, origin_hotkey, origin_coldkey, origin_netuid, @@ -374,6 +375,7 @@ impl Pallet { } Self::stake_into_subnet( + origin_coldkey, destination_hotkey, destination_coldkey, destination_netuid, @@ -387,6 +389,7 @@ impl Pallet { Ok(tao_unstaked) } else { Self::transfer_stake_within_subnet( + origin_coldkey, origin_coldkey, origin_hotkey, destination_coldkey, diff --git a/pallets/subtensor/src/staking/remove_stake.rs b/pallets/subtensor/src/staking/remove_stake.rs index 74a6bf34a6..3a508fed41 100644 --- a/pallets/subtensor/src/staking/remove_stake.rs +++ b/pallets/subtensor/src/staking/remove_stake.rs @@ -68,6 +68,7 @@ impl Pallet { // 3. Swap the alpba to tao and update counters for this subnet. let tao_unstaked = Self::unstake_from_subnet( + &coldkey, &hotkey, &coldkey, netuid, @@ -163,6 +164,7 @@ impl Pallet { if !alpha_unstaked.is_zero() { // Swap the alpha to tao and update counters for this subnet. let tao_unstaked = Self::unstake_from_subnet( + &coldkey, &hotkey, &coldkey, netuid, @@ -256,6 +258,7 @@ impl Pallet { if !alpha_unstaked.is_zero() { // Swap the alpha to tao and update counters for this subnet. let tao_unstaked = Self::unstake_from_subnet( + &coldkey, &hotkey, &coldkey, netuid, @@ -275,6 +278,7 @@ impl Pallet { // Stake into root. Self::stake_into_subnet( + &coldkey, &hotkey, &coldkey, NetUid::ROOT, @@ -362,6 +366,7 @@ impl Pallet { // 4. Swap the alpha to tao and update counters for this subnet. let tao_unstaked = Self::unstake_from_subnet( + &coldkey, &hotkey, &coldkey, netuid, diff --git a/pallets/subtensor/src/staking/stake_utils.rs b/pallets/subtensor/src/staking/stake_utils.rs index 1aeeacc33c..0301d6cc5a 100644 --- a/pallets/subtensor/src/staking/stake_utils.rs +++ b/pallets/subtensor/src/staking/stake_utils.rs @@ -686,6 +686,7 @@ impl Pallet { /// /// We update the pools associated with a subnet as well as update hotkey alpha shares. pub fn unstake_from_subnet( + origin: &T::AccountId, hotkey: &T::AccountId, coldkey: &T::AccountId, netuid: NetUid, @@ -733,6 +734,7 @@ impl Pallet { // Deposit and log the unstaking event. Self::deposit_event(Event::StakeRemoved( + origin.clone(), coldkey.clone(), hotkey.clone(), swap_result.amount_paid_out.into(), @@ -742,7 +744,8 @@ impl Pallet { )); log::debug!( - "StakeRemoved( coldkey: {:?}, hotkey:{:?}, tao: {:?}, alpha:{:?}, netuid: {:?}, fee {} )", + "StakeRemoved( origin: {:?}, coldkey: {:?}, hotkey:{:?}, tao: {:?}, alpha:{:?}, netuid: {:?}, fee {} )", + origin.clone(), coldkey.clone(), hotkey.clone(), swap_result.amount_paid_out, @@ -758,6 +761,7 @@ impl Pallet { /// /// We update the pools associated with a subnet as well as update hotkey alpha shares. pub(crate) fn stake_into_subnet( + origin: &T::AccountId, hotkey: &T::AccountId, coldkey: &T::AccountId, netuid: NetUid, @@ -822,6 +826,7 @@ impl Pallet { // Deposit and log the staking event. Self::deposit_event(Event::StakeAdded( + origin.clone(), coldkey.clone(), hotkey.clone(), tao, @@ -831,7 +836,8 @@ impl Pallet { )); log::debug!( - "StakeAdded( coldkey: {:?}, hotkey:{:?}, tao: {:?}, alpha:{:?}, netuid: {:?}, fee {} )", + "StakeAdded( origin: {:?}, coldkey: {:?}, hotkey:{:?}, tao: {:?}, alpha:{:?}, netuid: {:?}, fee {} )", + origin.clone(), coldkey.clone(), hotkey.clone(), tao, @@ -848,6 +854,7 @@ impl Pallet { /// /// Does not incur any swapping nor fees pub fn transfer_stake_within_subnet( + origin: &T::AccountId, origin_coldkey: &T::AccountId, origin_hotkey: &T::AccountId, destination_coldkey: &T::AccountId, @@ -916,6 +923,7 @@ impl Pallet { // Deposit and log the unstaking event. Self::deposit_event(Event::StakeRemoved( + origin.clone(), origin_coldkey.clone(), origin_hotkey.clone(), tao_equivalent, @@ -924,6 +932,7 @@ impl Pallet { 0_u64, // 0 fee )); Self::deposit_event(Event::StakeAdded( + origin.clone(), destination_coldkey.clone(), destination_hotkey.clone(), tao_equivalent, diff --git a/pallets/subtensor/src/subnets/leasing.rs b/pallets/subtensor/src/subnets/leasing.rs index a202a22a45..e3fc02eb30 100644 --- a/pallets/subtensor/src/subnets/leasing.rs +++ b/pallets/subtensor/src/subnets/leasing.rs @@ -304,6 +304,7 @@ impl Pallet { .saturating_to_num::(); Self::transfer_stake_within_subnet( + &lease.coldkey, &lease.coldkey, &lease.hotkey, &contributor, @@ -324,6 +325,7 @@ impl Pallet { let beneficiary_cut_alpha = total_contributors_cut_alpha.saturating_sub(alpha_distributed); Self::transfer_stake_within_subnet( + &lease.coldkey, &lease.coldkey, &lease.hotkey, &lease.beneficiary, diff --git a/pallets/subtensor/src/tests/mock.rs b/pallets/subtensor/src/tests/mock.rs index b744c9b771..8da8964189 100644 --- a/pallets/subtensor/src/tests/mock.rs +++ b/pallets/subtensor/src/tests/mock.rs @@ -887,6 +887,7 @@ pub fn increase_stake_on_coldkey_hotkey_account( netuid: NetUid, ) { SubtensorModule::stake_into_subnet( + coldkey, hotkey, coldkey, netuid, diff --git a/pallets/subtensor/src/tests/move_stake.rs b/pallets/subtensor/src/tests/move_stake.rs index dfd9927da4..aff40fcaab 100644 --- a/pallets/subtensor/src/tests/move_stake.rs +++ b/pallets/subtensor/src/tests/move_stake.rs @@ -29,6 +29,7 @@ fn test_do_move_success() { SubtensorModule::create_account_if_non_existent(&coldkey, &origin_hotkey); SubtensorModule::create_account_if_non_existent(&coldkey, &destination_hotkey); SubtensorModule::stake_into_subnet( + &coldkey, &origin_hotkey, &coldkey, netuid.into(), @@ -106,6 +107,7 @@ fn test_do_move_different_subnets() { SubtensorModule::create_account_if_non_existent(&coldkey, &origin_hotkey); SubtensorModule::create_account_if_non_existent(&coldkey, &destination_hotkey); SubtensorModule::stake_into_subnet( + &coldkey, &origin_hotkey, &coldkey, origin_netuid, @@ -174,6 +176,7 @@ fn test_do_move_nonexistent_subnet() { // Set up initial stake SubtensorModule::stake_into_subnet( + &coldkey, &origin_hotkey, &coldkey, origin_netuid, @@ -278,6 +281,7 @@ fn test_do_move_nonexistent_destination_hotkey() { // Set up initial stake SubtensorModule::create_account_if_non_existent(&coldkey, &origin_hotkey); let alpha = SubtensorModule::stake_into_subnet( + &coldkey, &origin_hotkey, &coldkey, netuid, @@ -343,6 +347,7 @@ fn test_do_move_partial_stake() { // Set up initial stake SubtensorModule::stake_into_subnet( + &coldkey, &origin_hotkey, &coldkey, netuid, @@ -412,6 +417,7 @@ fn test_do_move_multiple_times() { SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey1); SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey2); SubtensorModule::stake_into_subnet( + &coldkey, &hotkey1, &coldkey, netuid, @@ -484,6 +490,7 @@ fn test_do_move_wrong_origin() { // Set up initial stake SubtensorModule::stake_into_subnet( + &coldkey, &origin_hotkey, &coldkey, netuid, @@ -551,6 +558,7 @@ fn test_do_move_same_hotkey_fails() { // Set up initial stake SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); SubtensorModule::stake_into_subnet( + &coldkey, &hotkey, &coldkey, netuid, @@ -602,6 +610,7 @@ fn test_do_move_event_emission() { SubtensorModule::create_account_if_non_existent(&coldkey, &origin_hotkey); SubtensorModule::create_account_if_non_existent(&coldkey, &destination_hotkey); SubtensorModule::stake_into_subnet( + &coldkey, &origin_hotkey, &coldkey, netuid, @@ -663,6 +672,7 @@ fn test_do_move_storage_updates() { // Set up initial stake SubtensorModule::stake_into_subnet( + &coldkey, &origin_hotkey, &coldkey, origin_netuid, @@ -730,6 +740,7 @@ fn test_move_full_amount_same_netuid() { // Set up initial stake SubtensorModule::stake_into_subnet( + &coldkey, &origin_hotkey, &coldkey, netuid, @@ -798,6 +809,7 @@ fn test_do_move_max_values() { mock::setup_reserves(netuid, reserve.into(), reserve.into()); SubtensorModule::stake_into_subnet( + &coldkey, &origin_hotkey, &coldkey, netuid, @@ -904,6 +916,7 @@ fn test_do_transfer_success() { SubtensorModule::create_account_if_non_existent(&origin_coldkey, &hotkey); SubtensorModule::create_account_if_non_existent(&destination_coldkey, &hotkey); SubtensorModule::stake_into_subnet( + &origin_coldkey, &hotkey, &origin_coldkey, netuid, @@ -1013,6 +1026,7 @@ fn test_do_transfer_insufficient_stake() { SubtensorModule::create_account_if_non_existent(&origin_coldkey, &hotkey); SubtensorModule::stake_into_subnet( + &origin_coldkey, &hotkey, &origin_coldkey, netuid, @@ -1054,6 +1068,7 @@ fn test_do_transfer_wrong_origin() { SubtensorModule::create_account_if_non_existent(&origin_coldkey, &hotkey); SubtensorModule::add_balance_to_coldkey_account(&origin_coldkey, stake_amount + fee); SubtensorModule::stake_into_subnet( + &origin_coldkey, &hotkey, &origin_coldkey, netuid, @@ -1092,6 +1107,7 @@ fn test_do_transfer_minimum_stake_check() { let stake_amount = DefaultMinStake::::get(); SubtensorModule::create_account_if_non_existent(&origin_coldkey, &hotkey); SubtensorModule::stake_into_subnet( + &origin_coldkey, &hotkey, &origin_coldkey, netuid, @@ -1140,6 +1156,7 @@ fn test_do_transfer_different_subnets() { // 5. Stake into the origin subnet. SubtensorModule::stake_into_subnet( + &origin_coldkey, &hotkey, &origin_coldkey, origin_netuid, @@ -1206,6 +1223,7 @@ fn test_do_swap_success() { SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); SubtensorModule::stake_into_subnet( + &coldkey, &hotkey, &coldkey, origin_netuid, @@ -1314,6 +1332,7 @@ fn test_do_swap_insufficient_stake() { SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); SubtensorModule::stake_into_subnet( + &coldkey, &hotkey, &coldkey, netuid1, @@ -1349,6 +1368,7 @@ fn test_do_swap_wrong_origin() { SubtensorModule::create_account_if_non_existent(&real_coldkey, &hotkey); SubtensorModule::stake_into_subnet( + &real_coldkey, &hotkey, &real_coldkey, netuid1, @@ -1387,6 +1407,7 @@ fn test_do_swap_minimum_stake_check() { SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); SubtensorModule::stake_into_subnet( + &coldkey, &hotkey, &coldkey, netuid1, @@ -1423,6 +1444,7 @@ fn test_do_swap_same_subnet() { SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); SubtensorModule::stake_into_subnet( + &coldkey, &hotkey, &coldkey, netuid, @@ -1468,6 +1490,7 @@ fn test_do_swap_partial_stake() { SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); SubtensorModule::stake_into_subnet( + &coldkey, &hotkey, &coldkey, origin_netuid, @@ -1520,6 +1543,7 @@ fn test_do_swap_storage_updates() { SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); SubtensorModule::stake_into_subnet( + &coldkey, &hotkey, &coldkey, origin_netuid, @@ -1580,6 +1604,7 @@ fn test_do_swap_multiple_times() { SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); SubtensorModule::stake_into_subnet( + &coldkey, &hotkey, &coldkey, netuid1, @@ -1651,6 +1676,7 @@ fn test_do_swap_allows_non_owned_hotkey() { SubtensorModule::create_account_if_non_existent(&foreign_coldkey, &hotkey); SubtensorModule::stake_into_subnet( + &coldkey, &hotkey, &coldkey, origin_netuid, @@ -1799,6 +1825,7 @@ fn test_transfer_stake_rate_limited() { SubtensorModule::create_account_if_non_existent(&origin_coldkey, &hotkey); SubtensorModule::create_account_if_non_existent(&destination_coldkey, &hotkey); SubtensorModule::stake_into_subnet( + &origin_coldkey, &hotkey, &origin_coldkey, netuid, @@ -1844,6 +1871,7 @@ fn test_transfer_stake_doesnt_limit_destination_coldkey() { SubtensorModule::create_account_if_non_existent(&origin_coldkey, &hotkey); SubtensorModule::create_account_if_non_existent(&destination_coldkey, &hotkey); SubtensorModule::stake_into_subnet( + &origin_coldkey, &hotkey, &origin_coldkey, netuid, @@ -1890,6 +1918,7 @@ fn test_swap_stake_limits_destination_netuid() { SubtensorModule::create_account_if_non_existent(&origin_coldkey, &hotkey); SubtensorModule::stake_into_subnet( + &origin_coldkey, &hotkey, &origin_coldkey, netuid, diff --git a/pallets/subtensor/src/tests/staking.rs b/pallets/subtensor/src/tests/staking.rs index 6c7e18b707..2a0238d1ae 100644 --- a/pallets/subtensor/src/tests/staking.rs +++ b/pallets/subtensor/src/tests/staking.rs @@ -864,6 +864,7 @@ fn test_remove_stake_insufficient_liquidity() { mock::setup_reserves(netuid, reserve.into(), reserve.into()); let alpha = SubtensorModule::stake_into_subnet( + &coldkey, &hotkey, &coldkey, netuid, @@ -4498,6 +4499,7 @@ fn test_stake_into_subnet_ok() { // Add stake with slippage safety and check if the result is ok assert_ok!(SubtensorModule::stake_into_subnet( + &coldkey, &hotkey, &coldkey, netuid, @@ -4552,6 +4554,7 @@ fn test_stake_into_subnet_low_amount() { // Add stake with slippage safety and check if the result is ok assert_ok!(SubtensorModule::stake_into_subnet( + &coldkey, &hotkey, &coldkey, netuid, @@ -4600,6 +4603,7 @@ fn test_unstake_from_subnet_low_amount() { // Add stake and check if the result is ok assert_ok!(SubtensorModule::stake_into_subnet( + &coldkey, &hotkey, &coldkey, netuid, @@ -4613,6 +4617,7 @@ fn test_unstake_from_subnet_low_amount() { let alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid); assert_ok!(SubtensorModule::unstake_from_subnet( + &coldkey, &hotkey, &coldkey, netuid, @@ -4714,6 +4719,7 @@ fn test_unstake_from_subnet_prohibitive_limit() { // Add stake and check if the result is ok assert_ok!(SubtensorModule::stake_into_subnet( + &coldkey, &owner_hotkey, &coldkey, netuid, @@ -4790,6 +4796,7 @@ fn test_unstake_full_amount() { // Add stake and check if the result is ok assert_ok!(SubtensorModule::stake_into_subnet( + &coldkey, &owner_hotkey, &coldkey, netuid, @@ -5606,6 +5613,7 @@ fn test_staking_records_flow() { // Add stake with slippage safety and check if the result is ok assert_ok!(SubtensorModule::stake_into_subnet( + &coldkey, &hotkey, &coldkey, netuid, @@ -5629,6 +5637,7 @@ fn test_staking_records_flow() { let alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid); assert_ok!(SubtensorModule::unstake_from_subnet( + &coldkey, &hotkey, &coldkey, netuid, From 7f3dba71f583335edad243a2b80c03737a299a0b Mon Sep 17 00:00:00 2001 From: Tsukasa Hayashi <4372044+altis0725@users.noreply.github.com> Date: Wed, 25 Feb 2026 21:03:32 +0900 Subject: [PATCH 2/3] Bump spec_version, clarify origin semantics, add event assertion test - Bump spec_version 380 -> 381 for event metadata breaking change - Clarify origin field documentation (coldkey from ensure_signed) - Add test_add_stake_event_has_origin to verify origin field in events Co-Authored-By: Claude Opus 4.6 --- pallets/subtensor/src/macros/events.rs | 8 +++- pallets/subtensor/src/tests/staking.rs | 60 ++++++++++++++++++++++++++ runtime/src/lib.rs | 2 +- 3 files changed, 67 insertions(+), 3 deletions(-) diff --git a/pallets/subtensor/src/macros/events.rs b/pallets/subtensor/src/macros/events.rs index 86661c35f5..028ffa74bb 100644 --- a/pallets/subtensor/src/macros/events.rs +++ b/pallets/subtensor/src/macros/events.rs @@ -13,7 +13,9 @@ mod events { NetworkAdded(NetUid, u16), /// a network is removed. NetworkRemoved(NetUid), - /// stake has been transferred from the a coldkey account onto the hotkey staking account. + /// Stake has been transferred from a coldkey account onto the hotkey staking account. + /// The origin is the account that initiated the action (coldkey from ensure_signed; + /// for proxy calls this is the proxied account, not the proxy itself). /// (origin, coldkey, hotkey, tao_amount, alpha_amount, netuid, fee) StakeAdded( T::AccountId, @@ -24,7 +26,9 @@ mod events { NetUid, u64, ), - /// stake has been removed from the hotkey staking account onto the coldkey account. + /// Stake has been removed from the hotkey staking account onto the coldkey account. + /// The origin is the account that initiated the action (coldkey from ensure_signed; + /// for proxy calls this is the proxied account, not the proxy itself). /// (origin, coldkey, hotkey, tao_amount, alpha_amount, netuid, fee) StakeRemoved( T::AccountId, diff --git a/pallets/subtensor/src/tests/staking.rs b/pallets/subtensor/src/tests/staking.rs index 2a0238d1ae..da167612ea 100644 --- a/pallets/subtensor/src/tests/staking.rs +++ b/pallets/subtensor/src/tests/staking.rs @@ -111,6 +111,66 @@ fn test_add_stake_ok_no_emission() { }); } +// Verify StakeAdded event contains the correct origin field (coldkey for direct calls) +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test staking -- test_add_stake_event_has_origin --exact --nocapture +#[test] +fn test_add_stake_event_has_origin() { + new_test_ext(1).execute_with(|| { + let hotkey_account_id = U256::from(533453); + let coldkey_account_id = U256::from(55453); + let amount = DefaultMinStake::::get().to_u64() * 10; + + let netuid = add_dynamic_network(&hotkey_account_id, &coldkey_account_id); + mock::setup_reserves( + netuid, + (amount * 1_000_000).into(), + (amount * 10_000_000).into(), + ); + SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, amount); + + System::reset_events(); + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(coldkey_account_id), + hotkey_account_id, + netuid, + amount.into() + )); + + // Find the StakeAdded event and assert origin == coldkey + let events = System::events(); + let stake_added = events.iter().find_map(|record| { + if let RuntimeEvent::SubtensorModule(Event::StakeAdded( + origin, + coldkey, + hotkey, + _tao, + _alpha, + ev_netuid, + _fee, + )) = &record.event + { + Some(( + origin.clone(), + coldkey.clone(), + hotkey.clone(), + *ev_netuid, + )) + } else { + None + } + }); + assert!(stake_added.is_some(), "StakeAdded event should be emitted"); + let (origin, coldkey, hotkey, ev_netuid) = stake_added.unwrap(); + assert_eq!( + origin, coldkey_account_id, + "Origin should equal coldkey for direct stake calls" + ); + assert_eq!(coldkey, coldkey_account_id); + assert_eq!(hotkey, hotkey_account_id); + assert_eq!(ev_netuid, netuid); + }); +} + #[test] fn test_dividends_with_run_to_block() { new_test_ext(1).execute_with(|| { diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 919468c1ef..4e5dbc8376 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -241,7 +241,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // `spec_version`, and `authoring_version` are the same between Wasm and native. // This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use // the compatible custom types. - spec_version: 377, + spec_version: 378, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From 8f099f5290a341eb5c3ab1014892281c33dbe2c9 Mon Sep 17 00:00:00 2001 From: Tsukasa Hayashi <4372044+altis0725@users.noreply.github.com> Date: Wed, 25 Feb 2026 21:36:48 +0900 Subject: [PATCH 3/3] Fix origin documentation and expand event assertion test coverage - Update event comments to accurately describe origin semantics for both extrinsic calls and automated distributions (leasing) - Add test_remove_stake_event_has_origin for StakeRemoved origin assertion - Add StakeRemoved/StakeAdded origin assertions to move_stake event test Co-Authored-By: Claude Opus 4.6 --- pallets/subtensor/src/macros/events.rs | 8 +-- pallets/subtensor/src/tests/move_stake.rs | 28 +++++++++ pallets/subtensor/src/tests/staking.rs | 71 +++++++++++++++++++++++ 3 files changed, 103 insertions(+), 4 deletions(-) diff --git a/pallets/subtensor/src/macros/events.rs b/pallets/subtensor/src/macros/events.rs index 028ffa74bb..679526e533 100644 --- a/pallets/subtensor/src/macros/events.rs +++ b/pallets/subtensor/src/macros/events.rs @@ -14,8 +14,8 @@ mod events { /// a network is removed. NetworkRemoved(NetUid), /// Stake has been transferred from a coldkey account onto the hotkey staking account. - /// The origin is the account that initiated the action (coldkey from ensure_signed; - /// for proxy calls this is the proxied account, not the proxy itself). + /// The origin is the account on behalf of which the staking operation was performed + /// (coldkey for extrinsic calls, lease coldkey for automated distributions). /// (origin, coldkey, hotkey, tao_amount, alpha_amount, netuid, fee) StakeAdded( T::AccountId, @@ -27,8 +27,8 @@ mod events { u64, ), /// Stake has been removed from the hotkey staking account onto the coldkey account. - /// The origin is the account that initiated the action (coldkey from ensure_signed; - /// for proxy calls this is the proxied account, not the proxy itself). + /// The origin is the account on behalf of which the unstaking operation was performed + /// (coldkey for extrinsic calls, lease coldkey for automated distributions). /// (origin, coldkey, hotkey, tao_amount, alpha_amount, netuid, fee) StakeRemoved( T::AccountId, diff --git a/pallets/subtensor/src/tests/move_stake.rs b/pallets/subtensor/src/tests/move_stake.rs index aff40fcaab..c2fb1ad55a 100644 --- a/pallets/subtensor/src/tests/move_stake.rs +++ b/pallets/subtensor/src/tests/move_stake.rs @@ -652,6 +652,34 @@ fn test_do_move_event_emission() { ) .into(), ); + + // Verify StakeRemoved and StakeAdded events have origin == coldkey + let events = System::events(); + let stake_removed = events.iter().find_map(|record| { + if let RuntimeEvent::SubtensorModule(Event::StakeRemoved( + origin, _, _, _, _, _, _, + )) = &record.event + { + Some(origin.clone()) + } else { + None + } + }); + assert!(stake_removed.is_some(), "StakeRemoved event should be emitted during move"); + assert_eq!(stake_removed.unwrap(), coldkey, "StakeRemoved origin should be coldkey"); + + let stake_added = events.iter().find_map(|record| { + if let RuntimeEvent::SubtensorModule(Event::StakeAdded( + origin, _, _, _, _, _, _, + )) = &record.event + { + Some(origin.clone()) + } else { + None + } + }); + assert!(stake_added.is_some(), "StakeAdded event should be emitted during move"); + assert_eq!(stake_added.unwrap(), coldkey, "StakeAdded origin should be coldkey"); }); } diff --git a/pallets/subtensor/src/tests/staking.rs b/pallets/subtensor/src/tests/staking.rs index da167612ea..9c2b52226f 100644 --- a/pallets/subtensor/src/tests/staking.rs +++ b/pallets/subtensor/src/tests/staking.rs @@ -171,6 +171,77 @@ fn test_add_stake_event_has_origin() { }); } +// Verify StakeRemoved event contains the correct origin field (coldkey for direct calls) +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test staking -- test_remove_stake_event_has_origin --exact --nocapture +#[test] +fn test_remove_stake_event_has_origin() { + new_test_ext(1).execute_with(|| { + let hotkey_account_id = U256::from(533453); + let coldkey_account_id = U256::from(55453); + let amount = DefaultMinStake::::get().to_u64() * 10; + + let netuid = add_dynamic_network(&hotkey_account_id, &coldkey_account_id); + mock::setup_reserves( + netuid, + (amount * 1_000_000).into(), + (amount * 10_000_000).into(), + ); + SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, amount); + + // Stake first + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(coldkey_account_id), + hotkey_account_id, + netuid, + amount.into() + )); + + let alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey_account_id, + &coldkey_account_id, + netuid, + ); + + System::reset_events(); + assert_ok!(SubtensorModule::remove_stake( + RuntimeOrigin::signed(coldkey_account_id), + hotkey_account_id, + netuid, + alpha, + )); + + // Find the StakeRemoved event and assert origin == coldkey + let events = System::events(); + let stake_removed = events.iter().find_map(|record| { + if let RuntimeEvent::SubtensorModule(Event::StakeRemoved( + origin, + coldkey, + _hotkey, + _tao, + _alpha, + ev_netuid, + _fee, + )) = &record.event + { + Some((origin.clone(), coldkey.clone(), *ev_netuid)) + } else { + None + } + }); + assert!( + stake_removed.is_some(), + "StakeRemoved event should be emitted" + ); + let (origin, coldkey, ev_netuid) = stake_removed.unwrap(); + assert_eq!( + origin, coldkey_account_id, + "Origin should equal coldkey for direct unstake calls" + ); + assert_eq!(coldkey, coldkey_account_id); + assert_eq!(ev_netuid, netuid); + }); +} + #[test] fn test_dividends_with_run_to_block() { new_test_ext(1).execute_with(|| {