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 06fefa10c..665769e0e 100644 --- a/tokens/src/lib.rs +++ b/tokens/src/lib.rs @@ -345,6 +345,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. @@ -840,12 +852,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 @@ -874,6 +892,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.rs b/tokens/src/tests.rs index 02b923293..a54ab3fcd 100644 --- a/tokens/src/tests.rs +++ b/tokens/src/tests.rs @@ -8,6 +8,12 @@ use frame_system::RawOrigin; use mock::{Event, *}; use sp_runtime::{traits::BadOrigin, TokenError}; +fn events() -> Vec { + let evt = System::events().into_iter().map(|evt| evt.event).collect::>(); + System::reset_events(); + evt +} + // ************************************************* // tests for genesis // ************************************************* @@ -990,6 +996,101 @@ fn do_deposit_report_existential_deposit_error() { }); } +#[test] +fn emit_events_when_changing_locks() { + 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(&Event::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(&Event::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(&Event::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 { + Event::Tokens(crate::Event::Locked { .. }) => assert!(false, "unexpected lock event"), + Event::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 { + Event::Tokens(crate::Event::Locked { .. }) => assert!(false, "unexpected lock event"), + Event::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 { + Event::Tokens(crate::Event::Locked { .. }) => assert!(false, "unexpected lock event"), + Event::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(&Event::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(&Event::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(&Event::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(&Event::Tokens(crate::Event::Unlocked { + currency_id: DOT, + who: ALICE, + amount: 1 + }))); + }); +} + // ************************************************* // tests for endowed account and remove account // *************************************************