diff --git a/scripts/run-clippy.sh b/scripts/run-clippy.sh index a172ae411..e11fed87a 100755 --- a/scripts/run-clippy.sh +++ b/scripts/run-clippy.sh @@ -6,7 +6,7 @@ COMMAND=$1 ALLOW_CLIPPY_RULES=( "clippy::identity_op" # this helps the code to be consistant - "clippy::blacklisted-name" # TODO: allow them in test only + "clippy::disallowed_names" # TODO: allow them in test only "clippy::ptr-arg" # TODO: decide if we want to fix those "clippy::match_single_binding" # TODO: fix those ) diff --git a/tokens/src/lib.rs b/tokens/src/lib.rs index 18333052a..319d260af 100644 --- a/tokens/src/lib.rs +++ b/tokens/src/lib.rs @@ -343,6 +343,18 @@ pub mod module { currency_id: T::CurrencyId, who: T::AccountId, }, + /// Some free balance was locked. + Locked { + currency_id: T::CurrencyId, + who: T::AccountId, + amount: T::Balance, + }, + /// Some locked balance was freed. + Unlocked { + currency_id: T::CurrencyId, + who: T::AccountId, + amount: T::Balance, + }, } /// The total issuance of a token type. @@ -843,12 +855,18 @@ impl Pallet { who: &T::AccountId, locks: &[BalanceLock], ) -> DispatchResult { + // track lock delta + let mut total_frozen_prev = Zero::zero(); + let mut total_frozen_after = Zero::zero(); + // update account data Self::mutate_account(who, currency_id, |account, _| { + total_frozen_prev = account.frozen; account.frozen = Zero::zero(); for lock in locks.iter() { account.frozen = account.frozen.max(lock.amount); } + total_frozen_after = account.frozen; }); // update locks @@ -877,6 +895,22 @@ impl Pallet { } } + if total_frozen_prev < total_frozen_after { + let amount = total_frozen_after.saturating_sub(total_frozen_prev); + Self::deposit_event(Event::Locked { + currency_id, + who: who.clone(), + amount, + }); + } else if total_frozen_prev > total_frozen_after { + let amount = total_frozen_prev.saturating_sub(total_frozen_after); + Self::deposit_event(Event::Unlocked { + currency_id, + who: who.clone(), + amount, + }); + } + Ok(()) } diff --git a/tokens/src/tests_events.rs b/tokens/src/tests_events.rs index 07896f866..eca7e4c22 100644 --- a/tokens/src/tests_events.rs +++ b/tokens/src/tests_events.rs @@ -6,6 +6,12 @@ use super::*; use frame_support::assert_ok; use mock::*; +fn events() -> Vec { + let evt = System::events().into_iter().map(|evt| evt.event).collect::>(); + System::reset_events(); + evt +} + #[test] fn pallet_multicurrency_deposit_events() { ExtBuilder::default() @@ -300,3 +306,98 @@ fn currency_adapter_pallet_currency_deposit_events() { })); }); } + +#[test] +fn pallet_change_locks_events() { + ExtBuilder::default().build().execute_with(|| { + assert_ok!(Tokens::do_deposit(DOT, &ALICE, 100, false, false)); + assert_ok!(Tokens::do_deposit(BTC, &ALICE, 100, false, false)); + System::reset_events(); + + // Locks: [10/DOT] + assert_ok!(Tokens::set_lock(ID_1, DOT, &ALICE, 10)); + assert!(events().contains(&RuntimeEvent::Tokens(crate::Event::Locked { + currency_id: DOT, + who: ALICE, + amount: 10 + }))); + + // Locks: [15/DOT] + assert_ok!(Tokens::set_lock(ID_1, DOT, &ALICE, 15)); + assert!(events().contains(&RuntimeEvent::Tokens(crate::Event::Locked { + currency_id: DOT, + who: ALICE, + amount: 5 + }))); + + // Locks: [15/DOT, 20/BTC] + assert_ok!(Tokens::set_lock(ID_1, BTC, &ALICE, 20)); + assert!(events().contains(&RuntimeEvent::Tokens(crate::Event::Locked { + currency_id: BTC, + who: ALICE, + amount: 20 + }))); + + // Locks: [15/DOT, 20/BTC, 10/DOT] + assert_ok!(Tokens::set_lock(ID_2, DOT, &ALICE, 10)); + for event in events() { + match event { + RuntimeEvent::Tokens(crate::Event::Locked { .. }) => assert!(false, "unexpected lock event"), + RuntimeEvent::Tokens(crate::Event::Unlocked { .. }) => assert!(false, "unexpected unlock event"), + _ => continue, + } + } + + // Locks: [15/DOT, 20/BTC, 12/DOT] + assert_ok!(Tokens::set_lock(ID_2, DOT, &ALICE, 12)); + for event in events() { + match event { + RuntimeEvent::Tokens(crate::Event::Locked { .. }) => assert!(false, "unexpected lock event"), + RuntimeEvent::Tokens(crate::Event::Unlocked { .. }) => assert!(false, "unexpected unlock event"), + _ => continue, + } + } + + // Locks: [15/DOT, 20/BTC, 10/DOT] + assert_ok!(Tokens::set_lock(ID_2, DOT, &ALICE, 10)); + for event in events() { + match event { + RuntimeEvent::Tokens(crate::Event::Locked { .. }) => assert!(false, "unexpected lock event"), + RuntimeEvent::Tokens(crate::Event::Unlocked { .. }) => assert!(false, "unexpected unlock event"), + _ => continue, + } + } + + // Locks: [15/DOT, 20/BTC, 20/DOT] + assert_ok!(Tokens::set_lock(ID_2, DOT, &ALICE, 20)); + assert!(events().contains(&RuntimeEvent::Tokens(crate::Event::Locked { + currency_id: DOT, + who: ALICE, + amount: 5 + }))); + + // Locks: [15/DOT, 20/BTC, 16/DOT] + assert_ok!(Tokens::set_lock(ID_2, DOT, &ALICE, 16)); + assert!(events().contains(&RuntimeEvent::Tokens(crate::Event::Unlocked { + currency_id: DOT, + who: ALICE, + amount: 4 + }))); + + // Locks: [15/DOT, 12/BTC, 16/DOT] + assert_ok!(Tokens::set_lock(ID_1, BTC, &ALICE, 12)); + assert!(events().contains(&RuntimeEvent::Tokens(crate::Event::Unlocked { + currency_id: BTC, + who: ALICE, + amount: 8 + }))); + + // Locks: [15/DOT, 12/BTC] + assert_ok!(Tokens::remove_lock(ID_2, DOT, &ALICE)); + assert!(events().contains(&RuntimeEvent::Tokens(crate::Event::Unlocked { + currency_id: DOT, + who: ALICE, + amount: 1 + }))); + }); +}