diff --git a/frame/balances/src/lib.rs b/frame/balances/src/lib.rs index 029a170535a10..f90c319d52452 100644 --- a/frame/balances/src/lib.rs +++ b/frame/balances/src/lib.rs @@ -334,6 +334,10 @@ pub mod pallet { Locked { who: T::AccountId, amount: T::Balance }, /// Some balance was unlocked. Unlocked { who: T::AccountId, amount: T::Balance }, + /// Some balance was frozen. + Frozen { who: T::AccountId, amount: T::Balance }, + /// Some balance was thawed. + Thawed { who: T::AccountId, amount: T::Balance }, } #[pallet::error] @@ -1081,7 +1085,10 @@ pub mod pallet { who: &T::AccountId, freezes: BoundedSlice, T::MaxFreezes>, ) -> DispatchResult { + let mut prev_frozen = Zero::zero(); + let mut after_frozen = Zero::zero(); let (_, maybe_dust) = Self::mutate_account(who, |b| { + prev_frozen = b.frozen; b.frozen = Zero::zero(); for l in Locks::::get(who).iter() { b.frozen = b.frozen.max(l.amount); @@ -1089,6 +1096,7 @@ pub mod pallet { for l in freezes.iter() { b.frozen = b.frozen.max(l.amount); } + after_frozen = b.frozen; })?; debug_assert!(maybe_dust.is_none(), "Not altering main balance; qed"); if freezes.is_empty() { @@ -1096,6 +1104,13 @@ pub mod pallet { } else { Freezes::::insert(who, freezes); } + if prev_frozen > after_frozen { + let amount = prev_frozen.saturating_sub(after_frozen); + Self::deposit_event(Event::Thawed { who: who.clone(), amount }); + } else if after_frozen > prev_frozen { + let amount = after_frozen.saturating_sub(prev_frozen); + Self::deposit_event(Event::Frozen { who: who.clone(), amount }); + } Ok(()) } diff --git a/frame/balances/src/tests/fungible_tests.rs b/frame/balances/src/tests/fungible_tests.rs index 128086885391f..185396019b13d 100644 --- a/frame/balances/src/tests/fungible_tests.rs +++ b/frame/balances/src/tests/fungible_tests.rs @@ -397,3 +397,49 @@ fn unholding_frees_hold_slot() { assert_ok!(Balances::hold(&TestId::Baz, &1, 10)); }); } + +#[test] +fn emit_events_with_changing_freezes() { + ExtBuilder::default().build_and_execute_with(|| { + let _ = Balances::set_balance(&1, 100); + System::reset_events(); + + // Freeze = [] --> [10] + assert_ok!(Balances::set_freeze(&TestId::Foo, &1, 10)); + assert_eq!(events(), [RuntimeEvent::Balances(crate::Event::Frozen { who: 1, amount: 10 })]); + + // Freeze = [10] --> [15] + assert_ok!(Balances::set_freeze(&TestId::Foo, &1, 15)); + assert_eq!(events(), [RuntimeEvent::Balances(crate::Event::Frozen { who: 1, amount: 5 })]); + + // Freeze = [15] --> [15, 20] + assert_ok!(Balances::set_freeze(&TestId::Bar, &1, 20)); + assert_eq!(events(), [RuntimeEvent::Balances(crate::Event::Frozen { who: 1, amount: 5 })]); + + // Freeze = [15, 20] --> [17, 20] + assert_ok!(Balances::set_freeze(&TestId::Foo, &1, 17)); + for event in events() { + match event { + RuntimeEvent::Balances(crate::Event::Frozen { .. }) => { + assert!(false, "unexpected freeze event") + }, + RuntimeEvent::Balances(crate::Event::Thawed { .. }) => { + assert!(false, "unexpected thaw event") + }, + _ => continue, + } + } + + // Freeze = [17, 20] --> [17, 15] + assert_ok!(Balances::set_freeze(&TestId::Bar, &1, 15)); + assert_eq!(events(), [RuntimeEvent::Balances(crate::Event::Thawed { who: 1, amount: 3 })]); + + // Freeze = [17, 15] --> [15] + assert_ok!(Balances::thaw(&TestId::Foo, &1)); + assert_eq!(events(), [RuntimeEvent::Balances(crate::Event::Thawed { who: 1, amount: 2 })]); + + // Freeze = [15] --> [] + assert_ok!(Balances::thaw(&TestId::Bar, &1)); + assert_eq!(events(), [RuntimeEvent::Balances(crate::Event::Thawed { who: 1, amount: 15 })]); + }); +}