diff --git a/Cargo.lock b/Cargo.lock index b220a384dd..1a0d37f275 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1253,6 +1253,7 @@ dependencies = [ "log", "pallet-transaction-payment", "parity-scale-codec", + "paste", "serde", "sp-core", "sp-io", diff --git a/bin/node/runtime/pangolin/Cargo.toml b/bin/node/runtime/pangolin/Cargo.toml index aabe7e97b6..3def12e51a 100644 --- a/bin/node/runtime/pangolin/Cargo.toml +++ b/bin/node/runtime/pangolin/Cargo.toml @@ -176,6 +176,7 @@ with-tracing = ["frame-executive/with-tracing"] try-runtime = [ "frame-executive/try-runtime", "frame-try-runtime", + "darwinia-balances/try-runtime", "darwinia-crab-issuing/try-runtime", "darwinia-staking/try-runtime", ] diff --git a/bin/node/runtime/pangolin/src/lib.rs b/bin/node/runtime/pangolin/src/lib.rs index 349fd58c14..6f3af97bb6 100644 --- a/bin/node/runtime/pangolin/src/lib.rs +++ b/bin/node/runtime/pangolin/src/lib.rs @@ -283,7 +283,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_name: create_runtime_str!("Pangolin"), authoring_version: 1, // crate version ~2.2.0 := >=2.2.0, <2.3.0 - spec_version: 220, + spec_version: 221, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, @@ -798,11 +798,13 @@ pub struct CustomOnRuntimeUpgrade; impl OnRuntimeUpgrade for CustomOnRuntimeUpgrade { #[cfg(feature = "try-runtime")] fn pre_upgrade() -> Result<(), &'static str> { - darwinia_crab_issuing::migration::try_runtime::pre_migrate::()?; - darwinia_staking::migrations::v6::pre_migrate::() + darwinia_balances::migration::try_runtime::pre_migrate() } fn on_runtime_upgrade() -> Weight { - 0 + darwinia_balances::migration::migrate(b"Instance0DarwiniaBalances", b"Balances"); + darwinia_balances::migration::migrate(b"Instance1DarwiniaBalances", b"Kton"); + + RuntimeBlockWeights::get().max_block } } diff --git a/frame/balances/Cargo.toml b/frame/balances/Cargo.toml index 78258ca387..443e0f95f5 100644 --- a/frame/balances/Cargo.toml +++ b/frame/balances/Cargo.toml @@ -13,6 +13,7 @@ version = "2.2.0" # crates codec = { package = "parity-scale-codec", version = "2.0.1", default-features = false } log = { version = "0.4.14" } +paste = { version = "1.0.5", optional = true } serde = { version = "1.0.125", optional = true } # darwinia darwinia-balances-rpc-runtime-api = { default-features = false, path = "./rpc/runtime-api" } @@ -52,3 +53,8 @@ substrate-std = [ "sp-runtime/std", "sp-std/std", ] + +try-runtime = [ + "paste", + "frame-support/try-runtime", +] diff --git a/frame/balances/src/lib.rs b/frame/balances/src/lib.rs index 2705e98897..012f617486 100644 --- a/frame/balances/src/lib.rs +++ b/frame/balances/src/lib.rs @@ -16,17 +16,17 @@ // You should have received a copy of the GNU General Public License // along with Darwinia. If not, see . -//! # Balances Module +//! # Balances Pallet //! -//! The Balances module provides functionality for handling accounts and balances. +//! The Balances pallet provides functionality for handling accounts and balances. //! -//! - [`balances::Config`](./trait.Config.html) -//! - [`Call`](./enum.Call.html) -//! - [`Module`](./struct.Module.html) +//! - [`Config`] +//! - [`Call`] +//! - [`Pallet`] //! //! ## Overview //! -//! The Balances module provides functions for: +//! The Balances pallet provides functions for: //! //! - Getting and setting free balances. //! - Retrieving total, reserved and unreserved balances. @@ -44,7 +44,7 @@ //! fall below this, then the account is said to be dead; and it loses its functionality as well as any //! prior history and all information on it is removed from the chain's state. //! No account should ever have a total balance that is strictly between 0 and the existential -//! deposit (exclusive). If this ever happens, it indicates either a bug in this module or an +//! deposit (exclusive). If this ever happens, it indicates either a bug in this pallet or an //! erroneous raw mutation of storage. //! //! - **Total Issuance:** The total number of units in existence in a system. @@ -68,20 +68,18 @@ //! //! ### Implementations //! -//! The Balances module provides implementations for the following traits. If these traits provide the functionality -//! that you need, then you can avoid coupling with the Balances module. +//! The Balances pallet provides implementations for the following traits. If these traits provide the functionality +//! that you need, then you can avoid coupling with the Balances pallet. //! -//! - [`Currency`](../frame_support/traits/trait.Currency.html): Functions for dealing with a +//! - [`Currency`](frame_support::traits::Currency): Functions for dealing with a //! fungible assets system. -//! - [`ReservableCurrency`](../frame_support/traits/trait.ReservableCurrency.html): +//! - [`ReservableCurrency`](frame_support::traits::ReservableCurrency): //! Functions for dealing with assets that can be reserved from an account. -//! - [`LockableCurrency`](../frame_support/traits/trait.LockableCurrency.html): Functions for +//! - [`LockableCurrency`](darwinia_support::traits::LockableCurrency): Functions for //! dealing with accounts that allow liquidity restrictions. -//! - [`Imbalance`](../frame_support/traits/trait.Imbalance.html): Functions for handling +//! - [`Imbalance`](frame_support::traits::Imbalance): Functions for handling //! imbalances between total issuance in the system and account balances. Must be used when a function //! creates new funds (e.g. a reward) or destroys some funds (e.g. a system fee). -//! - [`IsDeadAccount`](../frame_support/traits/trait.IsDeadAccount.html): Determiner to say whether a -//! given account is unused. //! //! ## Interface //! @@ -92,11 +90,11 @@ //! //! ## Usage //! -//! The following examples show how to use the Balances module in your custom module. +//! The following examples show how to use the Balances pallet in your custom pallet. //! //! ### Examples from the FRAME //! -//! The Contract module uses the `Currency` trait to handle gas payment, and its types inherit from `Currency`: +//! The Contract pallet uses the `Currency` trait to handle gas payment, and its types inherit from `Currency`: //! //! ``` //! use frame_support::traits::Currency; @@ -110,11 +108,12 @@ //! # fn main() {} //! ``` //! -//! The Staking module uses the `LockableCurrency` trait to lock a stash account's funds: +//! The Staking pallet uses the `LockableCurrency` trait to lock a stash account's funds: //! //! ``` -//! use frame_support::traits::{WithdrawReasons, LockableCurrency}; +//! use frame_support::traits::WithdrawReasons; //! use sp_runtime::traits::Bounded; +//! use darwinia_support::balance::*; //! pub trait Config: frame_system::Config { //! type Currency: LockableCurrency; //! } @@ -132,7 +131,9 @@ //! T::Currency::set_lock( //! STAKING_ID, //! &ledger.stash, -//! ledger.total, +//! LockFor::Common { +//! amount: ledger.total +//! }, //! WithdrawReasons::all() //! ); //! // >::insert(controller, ledger); // Commented out as we don't have access to Staking's storage here. @@ -142,7 +143,7 @@ //! //! ## Genesis config //! -//! The Balances module depends on the [`GenesisConfig`](./struct.GenesisConfig.html). +//! The Balances pallet depends on the [`GenesisConfig`]. //! //! ## Assumptions //! @@ -157,159 +158,257 @@ mod tests; mod tests_local; pub mod weights; -// --- darwinia --- pub use weights::WeightInfo; -// --- darwinia --- -pub use imbalances::{NegativeImbalance, PositiveImbalance}; - -// --- crates --- -use codec::{Codec, EncodeLike}; -// --- substrate --- -use frame_support::{ - decl_error, decl_event, decl_module, decl_storage, ensure, - traits::{ - BalanceStatus as Status, Currency, ExistenceRequirement, ExistenceRequirement::AllowDeath, - ExistenceRequirement::KeepAlive, Get, Imbalance, OnUnbalanced, ReservableCurrency, - SignedImbalance, StoredMap, TryDrop, - }, - Parameter, StorageValue, -}; -use frame_system::{ensure_root, ensure_signed}; -use sp_runtime::{ - traits::{ - AtLeast32BitUnsigned, Bounded, CheckedAdd, CheckedSub, MaybeSerializeDeserialize, Member, - Saturating, StaticLookup, StoredMapError, Zero, - }, - DispatchError, DispatchResult, RuntimeDebug, -}; -use sp_std::{borrow::Borrow, cmp, fmt::Debug, mem, prelude::*}; -// --- darwinia --- -use darwinia_balances_rpc_runtime_api::RuntimeDispatchInfo; -use darwinia_support::{ - balance::{lock::*, *}, - impl_rpc, - traits::BalanceInfo, -}; - -pub trait Subtrait: frame_system::Config { - /// The balance of an account. - type Balance: Parameter - + Member - + AtLeast32BitUnsigned - + Codec - + Default - + Copy - + MaybeSerializeDeserialize - + Debug; - - /// The minimum amount required to keep an account open. - type ExistentialDeposit: Get; - - /// The means of storing the balances of an account. - type AccountStore: StoredMap; - - type BalanceInfo: BalanceInfo - + Into<::AccountData> - + Member - + Codec - + Clone - + Default - + EncodeLike; - - /// The maximum number of locks that should exist on an account. - /// Not strictly enforced, but used for weight estimation. - type MaxLocks: Get; - - /// Weight information for the extrinsics in this pallet. - type WeightInfo: WeightInfo; - - // A handle to check if other curencies drop below existential deposit - type OtherCurrencies: DustCollector; -} +#[frame_support::pallet] +pub mod pallet { + // wrapping these imbalances in a private module is necessary to ensure absolute privacy + // of the inner member. + pub mod imbalances { + // --- darwinia --- + use crate::pallet::*; + + /// Opaque, move-only struct with private fields that serves as a token denoting that + /// funds have been created without any equal and opposite accounting. + #[must_use] + #[derive(RuntimeDebug, PartialEq, Eq)] + pub struct PositiveImbalance, I: 'static>(T::Balance); + + impl, I: 'static> PositiveImbalance { + /// Create a new positive imbalance from a balance. + pub fn new(amount: T::Balance) -> Self { + PositiveImbalance(amount) + } + } -pub trait Config: frame_system::Config { - /// The balance of an account. - type Balance: Parameter - + Member - + AtLeast32BitUnsigned - + Codec - + Default - + Copy - + MaybeSerializeDeserialize - + Debug; - - /// Handler for the unbalanced reduction when removing a dust account. - type DustRemoval: OnUnbalanced>; - - /// The overarching event type. - type Event: From> + Into<::Event>; - - /// The minimum amount required to keep an account open. - type ExistentialDeposit: Get; - - type BalanceInfo: BalanceInfo - + Into<::AccountData> - + Member - + Codec - + Clone - + Default - + EncodeLike; - - /// The means of storing the balances of an account. - type AccountStore: StoredMap; - - /// The maximum number of locks that should exist on an account. - /// Not strictly enforced, but used for weight estimation. - type MaxLocks: Get; - - // A handle to check if other curencies drop below existential deposit - type OtherCurrencies: DustCollector; - - /// Weight information for the extrinsics in this pallet. - type WeightInfo: WeightInfo; -} + /// Opaque, move-only struct with private fields that serves as a token denoting that + /// funds have been destroyed without any equal and opposite accounting. + #[must_use] + #[derive(RuntimeDebug, PartialEq, Eq)] + pub struct NegativeImbalance, I: 'static>(T::Balance); -impl, I: Instance> Subtrait for T { - type Balance = T::Balance; - type ExistentialDeposit = T::ExistentialDeposit; - type AccountStore = T::AccountStore; - type BalanceInfo = T::BalanceInfo; - type MaxLocks = T::MaxLocks; - type OtherCurrencies = T::OtherCurrencies; - type WeightInfo = >::WeightInfo; -} + impl, I: 'static> NegativeImbalance { + /// Create a new negative imbalance from a balance. + pub fn new(amount: T::Balance) -> Self { + NegativeImbalance(amount) + } + } -decl_event!( - pub enum Event - where - ::AccountId, - >::Balance, - { - /// An account was created with some free balance. [account, free_balance] - Endowed(AccountId, Balance), + impl, I: 'static> TryDrop for PositiveImbalance { + fn try_drop(self) -> Result<(), Self> { + self.drop_zero() + } + } + + impl, I: 'static> Imbalance for PositiveImbalance { + type Opposite = NegativeImbalance; + + fn zero() -> Self { + Self(Zero::zero()) + } + fn drop_zero(self) -> Result<(), Self> { + if self.0.is_zero() { + Ok(()) + } else { + Err(self) + } + } + fn split(self, amount: T::Balance) -> (Self, Self) { + let first = self.0.min(amount); + let second = self.0 - first; + + mem::forget(self); + (Self(first), Self(second)) + } + fn merge(mut self, other: Self) -> Self { + self.0 = self.0.saturating_add(other.0); + mem::forget(other); + + self + } + fn subsume(&mut self, other: Self) { + self.0 = self.0.saturating_add(other.0); + mem::forget(other); + } + fn offset(self, other: Self::Opposite) -> Result { + let (a, b) = (self.0, other.0); + mem::forget((self, other)); + + if a >= b { + Ok(Self(a - b)) + } else { + Err(NegativeImbalance::new(b - a)) + } + } + fn peek(&self) -> T::Balance { + self.0.clone() + } + } + + impl, I: 'static> TryDrop for NegativeImbalance { + fn try_drop(self) -> Result<(), Self> { + self.drop_zero() + } + } + + impl, I: 'static> Imbalance for NegativeImbalance { + type Opposite = PositiveImbalance; + + fn zero() -> Self { + Self(Zero::zero()) + } + fn drop_zero(self) -> Result<(), Self> { + if self.0.is_zero() { + Ok(()) + } else { + Err(self) + } + } + fn split(self, amount: T::Balance) -> (Self, Self) { + let first = self.0.min(amount); + let second = self.0 - first; + + mem::forget(self); + (Self(first), Self(second)) + } + fn merge(mut self, other: Self) -> Self { + self.0 = self.0.saturating_add(other.0); + mem::forget(other); + + self + } + fn subsume(&mut self, other: Self) { + self.0 = self.0.saturating_add(other.0); + mem::forget(other); + } + fn offset(self, other: Self::Opposite) -> Result { + let (a, b) = (self.0, other.0); + mem::forget((self, other)); + + if a >= b { + Ok(Self(a - b)) + } else { + Err(PositiveImbalance::new(b - a)) + } + } + fn peek(&self) -> T::Balance { + self.0.clone() + } + } + + impl, I: 'static> Drop for PositiveImbalance { + /// Basic drop handler will just square up the total issuance. + fn drop(&mut self) { + >::mutate(|v| *v = v.saturating_add(self.0)); + } + } + + impl, I: 'static> Drop for NegativeImbalance { + /// Basic drop handler will just square up the total issuance. + fn drop(&mut self) { + >::mutate(|v| *v = v.saturating_sub(self.0)); + } + } + } + pub use imbalances::{NegativeImbalance, PositiveImbalance}; + + // --- crates --- + use codec::{Codec, EncodeLike}; + // --- substrate --- + use frame_support::{ + ensure, + pallet_prelude::*, + traits::{ + BalanceStatus, Currency, ExistenceRequirement, Imbalance, LockIdentifier, OnUnbalanced, + ReservableCurrency, SignedImbalance, StoredMap, TryDrop, WithdrawReasons, + }, + }; + use frame_system::pallet_prelude::*; + // --- substrate --- + use sp_runtime::{ + traits::{ + AtLeast32BitUnsigned, Bounded, CheckedAdd, CheckedSub, MaybeSerializeDeserialize, + Saturating, StaticLookup, StoredMapError, Zero, + }, + DispatchError, DispatchResult, RuntimeDebug, + }; + use sp_std::{borrow::Borrow, cmp, fmt::Debug, mem, prelude::*}; + // --- darwinia --- + use crate::weights::WeightInfo; + use darwinia_balances_rpc_runtime_api::RuntimeDispatchInfo; + use darwinia_support::{balance::*, impl_rpc, traits::BalanceInfo}; + + #[pallet::config] + pub trait Config: frame_system::Config { + /// The balance of an account. + type Balance: Parameter + + Member + + AtLeast32BitUnsigned + + Codec + + Default + + Copy + + MaybeSerializeDeserialize + + Debug; + + /// Handler for the unbalanced reduction when removing a dust account. + type DustRemoval: OnUnbalanced>; + + /// The overarching event type. + type Event: From> + IsType<::Event>; + + /// The minimum amount required to keep an account open. + #[pallet::constant] + type ExistentialDeposit: Get; + + /// The means of storing the balances of an account. + type AccountStore: StoredMap; + + /// Weight information for extrinsics in this pallet. + type WeightInfo: WeightInfo; + + /// The maximum number of locks that should exist on an account. + /// Not strictly enforced, but used for weight estimation. + type MaxLocks: Get; + + type BalanceInfo: BalanceInfo + + Into<::AccountData> + + Member + + Codec + + Clone + + Default + + EncodeLike; + + // A handle to check if other curencies drop below existential deposit + type OtherCurrencies: DustCollector; + } + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + #[pallet::metadata(T::AccountId = "AccountId", T::Balance = "Balance")] + pub enum Event, I: 'static = ()> { + /// An account was created with some free balance. \[account, free_balance\] + Endowed(T::AccountId, T::Balance), /// An account was removed whose balance was non-zero but below ExistentialDeposit, - /// resulting in an outright loss. [account, balance] - DustLost(AccountId, Balance), - /// Transfer succeeded. [from, to, value] - Transfer(AccountId, AccountId, Balance), - /// A balance was set by root. [who, free, reserved] - BalanceSet(AccountId, Balance, Balance), - /// Some amount was deposited (e.g. for transaction fees). [who, deposit] - Deposit(AccountId, Balance), - /// Some balance was reserved (moved from free to reserved). [who, value] - Reserved(AccountId, Balance), - /// Some balance was unreserved (moved from reserved to free). [who, value] - Unreserved(AccountId, Balance), + /// resulting in an outright loss. \[account, balance\] + DustLost(T::AccountId, T::Balance), + /// Transfer succeeded. \[from, to, value\] + Transfer(T::AccountId, T::AccountId, T::Balance), + /// A balance was set by root. \[who, free, reserved\] + BalanceSet(T::AccountId, T::Balance, T::Balance), + /// Some amount was deposited (e.g. for transaction fees). \[who, deposit\] + Deposit(T::AccountId, T::Balance), + /// Some balance was reserved (moved from free to reserved). \[who, value\] + Reserved(T::AccountId, T::Balance), + /// Some balance was unreserved (moved from reserved to free). \[who, value\] + Unreserved(T::AccountId, T::Balance), /// Some balance was moved from the reserve of the first account to the second account. /// Final argument indicates the destination balance type. - /// [from, to, balance, destination_status] - ReserveRepatriated(AccountId, AccountId, Balance, Status), + /// \[from, to, balance, destination_status\] + ReserveRepatriated(T::AccountId, T::AccountId, T::Balance, BalanceStatus), } -); -decl_error! { - pub enum Error for Module, I: Instance> { + #[pallet::error] + pub enum Error { /// Vesting balance too high to send value VestingBalance, /// Account liquidity restrictions prevent withdrawal @@ -329,35 +428,63 @@ decl_error! { /// Lock - POISONED LockP, } -} -decl_storage! { - trait Store for Module, I: Instance = DefaultInstance> as DarwiniaBalances { - /// The total units issued in the system. - pub TotalIssuance get(fn total_issuance) build(|config: &GenesisConfig| { - config + /// The total units issued in the system. + #[pallet::storage] + #[pallet::getter(fn total_issuance)] + pub type TotalIssuance, I: 'static = ()> = StorageValue<_, T::Balance, ValueQuery>; + + /// The balance of an account. + /// + /// NOTE: This is only used in the case that this pallet is used to store balances. + #[pallet::storage] + pub type Account, I: 'static = ()> = + StorageMap<_, Blake2_128Concat, T::AccountId, T::BalanceInfo, ValueQuery>; + + /// Any liquidity locks on some account balances. + /// NOTE: Should only be accessed when setting, changing and freeing a lock. + #[pallet::storage] + #[pallet::getter(fn locks)] + pub type Locks, I: 'static = ()> = StorageMap< + _, + Blake2_128Concat, + T::AccountId, + Vec>, + ValueQuery, + >; + + /// Storage version of the pallet. + /// + /// This is set to v2.0.0 for new networks. + #[pallet::storage] + pub(super) type StorageVersion, I: 'static = ()> = + StorageValue<_, Releases, ValueQuery>; + + #[pallet::genesis_config] + pub struct GenesisConfig, I: 'static = ()> { + pub balances: Vec<(T::AccountId, T::Balance)>, + } + + #[cfg(feature = "std")] + impl, I: 'static> Default for GenesisConfig { + fn default() -> Self { + Self { + balances: Default::default(), + } + } + } + #[pallet::genesis_build] + impl, I: 'static> GenesisBuild for GenesisConfig { + fn build(&self) { + let total = self .balances .iter() - .fold(Zero::zero(), |acc: T::Balance, &(_, n)| acc + n) - }): T::Balance; + .fold(Zero::zero(), |acc: T::Balance, &(_, n)| acc + n); + >::put(total); - /// The balance of an account. - /// - /// NOTE: This is only used in the case that this module is used to store balances. - pub Account: map hasher(blake2_128_concat) T::AccountId => T::BalanceInfo; - - /// Any liquidity locks on some account balances. - /// NOTE: Should only be accessed when setting, changing and freeing a lock. - pub Locks - get(fn locks) - : map hasher(blake2_128_concat) T::AccountId - => Vec>; - } - add_extra_genesis { - config(balances): Vec<(T::AccountId, T::Balance)>; - // ^^ begin, length, amount liquid at genesis - build(|config: &GenesisConfig| { - for (_, balance) in &config.balances { + >::put(Releases::V2_0_0); + + for (_, balance) in &self.balances { assert!( *balance >= >::ExistentialDeposit::get(), "the balance of any account should always be at least the existential deposit.", @@ -365,39 +492,34 @@ decl_storage! { } // ensure no duplicates exist. - let endowed_accounts = config.balances + let endowed_accounts = self + .balances .iter() .map(|(x, _)| x) .cloned() .collect::>(); assert!( - endowed_accounts.len() == config.balances.len(), + endowed_accounts.len() == self.balances.len(), "duplicate balances in genesis." ); - for &(ref who, free) in config.balances.iter() { + for &(ref who, free) in self.balances.iter() { let mut account_data = T::AccountStore::get(who); account_data.set_free(free); assert!(T::AccountStore::insert(who, account_data).is_ok()); } - }); + } } -} - -decl_module! { - pub struct Module, I: Instance = DefaultInstance> for enum Call - where - origin: T::Origin - { - type Error = Error; - - /// The minimum amount required to keep an account open. - const ExistentialDeposit: T::Balance = T::ExistentialDeposit::get(); - - fn deposit_event() = default; + #[pallet::pallet] + #[pallet::generate_store(pub(super) trait Store)] + pub struct Pallet(PhantomData<(T, I)>); + #[pallet::hooks] + impl, I: 'static> Hooks> for Pallet {} + #[pallet::call] + impl, I: 'static> Pallet { /// Transfer some liquid free balance to another account. /// /// `transfer` will set the `FreeBalance` of the sender and receiver. @@ -422,15 +544,21 @@ decl_module! { /// check that the transfer will not kill the origin account. /// /// # - #[weight = T::DbWeight::get().reads_writes(1, 1) + 200_000_000] + #[pallet::weight(T::WeightInfo::transfer())] pub fn transfer( - origin, + origin: OriginFor, dest: ::Source, - #[compact] value: T::Balance - ) { + #[pallet::compact] value: T::Balance, + ) -> DispatchResultWithPostInfo { let transactor = ensure_signed(origin)?; let dest = T::Lookup::lookup(dest)?; - >::transfer(&transactor, &dest, value, ExistenceRequirement::AllowDeath)?; + >::transfer( + &transactor, + &dest, + value, + ExistenceRequirement::AllowDeath, + )?; + Ok(().into()) } /// Set the balances of a given account. @@ -446,13 +574,16 @@ decl_module! { /// - Independent of the arguments. /// - Contains a limited number of reads and writes. /// # - #[weight = T::DbWeight::get().reads_writes(1, 1) + 100_000_000] - fn set_balance( - origin, + #[pallet::weight( + T::WeightInfo::set_balance_creating() // Creates a new account. + .max(T::WeightInfo::set_balance_killing()) // Kills an existing account. + )] + pub(super) fn set_balance( + origin: OriginFor, who: ::Source, - #[compact] new_free: T::Balance, - #[compact] new_reserved: T::Balance - ) { + #[pallet::compact] new_free: T::Balance, + #[pallet::compact] new_reserved: T::Balance, + ) -> DispatchResultWithPostInfo { ensure_root(origin)?; let who = T::Lookup::lookup(who)?; let existential_deposit = T::ExistentialDeposit::get(); @@ -467,15 +598,19 @@ decl_module! { let (free, reserved) = Self::mutate_account(&who, |account| { if new_free > account.free() { - mem::drop(PositiveImbalance::::new(new_free - account.free())); + mem::drop(>::new(new_free - account.free())); } else if new_free < account.free() { mem::drop(NegativeImbalance::::new(account.free() - new_free)); } if new_reserved > account.reserved() { - mem::drop(PositiveImbalance::::new(new_reserved - account.reserved())); + mem::drop(>::new( + new_reserved - account.reserved(), + )); } else if new_reserved < account.reserved() { - mem::drop(NegativeImbalance::::new(account.reserved() - new_reserved)); + mem::drop(>::new( + account.reserved() - new_reserved, + )); } account.set_free(new_free); @@ -483,7 +618,8 @@ decl_module! { (account.free(), account.reserved()) })?; - Self::deposit_event(RawEvent::BalanceSet(who, free, reserved)); + Self::deposit_event(Event::BalanceSet(who, free, reserved)); + Ok(().into()) } /// Exactly as `transfer`, except the origin must be root and the source account may be @@ -492,17 +628,23 @@ decl_module! { /// - Same as transfer, but additional read and write because the source account is /// not assumed to be in the overlay. /// # - #[weight = T::DbWeight::get().reads_writes(2, 2) + 200_000_000] + #[pallet::weight(T::WeightInfo::force_transfer())] pub fn force_transfer( - origin, + origin: OriginFor, source: ::Source, dest: ::Source, - #[compact] value: T::Balance - ) { + #[pallet::compact] value: T::Balance, + ) -> DispatchResultWithPostInfo { ensure_root(origin)?; let source = T::Lookup::lookup(source)?; let dest = T::Lookup::lookup(dest)?; - >::transfer(&source, &dest, value, ExistenceRequirement::AllowDeath)?; + >::transfer( + &source, + &dest, + value, + ExistenceRequirement::AllowDeath, + )?; + Ok(().into()) } /// Same as the [`transfer`] call, but with a check that the transfer will not kill the @@ -510,1040 +652,974 @@ decl_module! { /// /// 99% of the time you want [`transfer`] instead. /// - /// [`transfer`]: struct.Module.html#method.transfer - #[weight = T::DbWeight::get().reads_writes(1, 1) + 150_000_000] + /// [`transfer`]: struct.Pallet.html#method.transfer + #[pallet::weight(T::WeightInfo::transfer_keep_alive())] pub fn transfer_keep_alive( - origin, + origin: OriginFor, dest: ::Source, - #[compact] value: T::Balance - ) { + #[pallet::compact] value: T::Balance, + ) -> DispatchResultWithPostInfo { let transactor = ensure_signed(origin)?; let dest = T::Lookup::lookup(dest)?; - >::transfer(&transactor, &dest, value, KeepAlive)?; + >::transfer( + &transactor, + &dest, + value, + ExistenceRequirement::KeepAlive, + )?; + Ok(().into()) } } -} -impl, I: Instance> Module { - // PRIVATE MUTABLES + impl, I: 'static> Pallet { + // PRIVATE MUTABLES - /// Get the free balance of an account. - pub fn free_balance(who: impl Borrow) -> T::Balance { - Self::account(who.borrow()).free() - } + /// Get the free balance of an account. + pub fn free_balance(who: impl Borrow) -> T::Balance { + Self::account(who.borrow()).free() + } - /// Get the frozen balance of an account. - fn frozen_balance(who: impl Borrow) -> FrozenBalance { - let now = >::block_number(); - let mut frozen_balance = >::zero(); - for lock in Self::locks(who.borrow()).iter() { - let locked_amount = match &lock.lock_for { - LockFor::Common { amount } => *amount, - LockFor::Staking(staking_lock) => staking_lock.locked_amount(now), - }; - if lock.lock_reasons == LockReasons::All || lock.lock_reasons == LockReasons::Misc { - frozen_balance.misc = frozen_balance.misc.max(locked_amount); - } - if lock.lock_reasons == LockReasons::All || lock.lock_reasons == LockReasons::Fee { - frozen_balance.fee = frozen_balance.fee.max(locked_amount); + /// Get the frozen balance of an account. + fn frozen_balance(who: impl Borrow) -> FrozenBalance { + let now = >::block_number(); + let mut frozen_balance = >::zero(); + for lock in Self::locks(who.borrow()).iter() { + let locked_amount = match &lock.lock_for { + LockFor::Common { amount } => *amount, + LockFor::Staking(staking_lock) => staking_lock.locked_amount(now), + }; + if lock.lock_reasons == LockReasons::All || lock.lock_reasons == LockReasons::Misc { + frozen_balance.misc = frozen_balance.misc.max(locked_amount); + } + if lock.lock_reasons == LockReasons::All || lock.lock_reasons == LockReasons::Fee { + frozen_balance.fee = frozen_balance.fee.max(locked_amount); + } } - } - frozen_balance - } + frozen_balance + } - impl_rpc! { - fn usable_balance_rpc(who: impl Borrow) -> RuntimeDispatchInfo { - RuntimeDispatchInfo { - usable_balance: Self::usable_balance(who.borrow()), + impl_rpc! { + fn usable_balance_rpc(who: impl Borrow) -> RuntimeDispatchInfo { + RuntimeDispatchInfo { + usable_balance: Self::usable_balance(who.borrow()), + } } } - } - /// Get the reserved balance of an account. - pub fn reserved_balance(who: impl Borrow) -> T::Balance { - let account = Self::account(who.borrow()); - account.reserved() - } + /// Get the reserved balance of an account. + pub fn reserved_balance(who: impl Borrow) -> T::Balance { + let account = Self::account(who.borrow()); + account.reserved() + } - /// Get both the free and reserved balances of an account. - fn account(who: &T::AccountId) -> T::BalanceInfo { - T::AccountStore::get(&who) - } + /// Get both the free and reserved balances of an account. + fn account(who: &T::AccountId) -> T::BalanceInfo { + T::AccountStore::get(&who) + } - /// Places the `free` and `reserved` parts of `new` into `account`. Also does any steps needed - /// after mutating an account. This includes DustRemoval unbalancing, in the case than the `new` - /// account's total balance is non-zero but below ED. - /// - /// Returns the final free balance, iff the account was previously of total balance zero, known - /// as its "endowment". - fn post_mutation(who: &T::AccountId, new: T::BalanceInfo) -> Option { - let total = new.total(); + /// Places the `free` and `reserved` parts of `new` into `account`. Also does any steps needed + /// after mutating an account. This includes DustRemoval unbalancing, in the case than the `new` + /// account's total balance is non-zero but below ED. + /// + /// Returns the final free balance, iff the account was previously of total balance zero, known + /// as its "endowment". + fn post_mutation(who: &T::AccountId, new: T::BalanceInfo) -> Option { + let total = new.total(); - if total < T::ExistentialDeposit::get() && T::OtherCurrencies::is_dust(who) { - T::OtherCurrencies::collect(who); + if total < T::ExistentialDeposit::get() && T::OtherCurrencies::is_dust(who) { + T::OtherCurrencies::collect(who); - if !total.is_zero() { - T::DustRemoval::on_unbalanced(NegativeImbalance::new(total)); - Self::deposit_event(RawEvent::DustLost(who.clone(), total)); - } + if !total.is_zero() { + T::DustRemoval::on_unbalanced(NegativeImbalance::new(total)); + Self::deposit_event(Event::DustLost(who.clone(), total)); + } - None - } else { - Some(new) + None + } else { + Some(new) + } } - } - /// Mutate an account to some new value, or delete it entirely with `None`. Will enforce - /// `ExistentialDeposit` law, annulling the account as needed. - /// - /// NOTE: Doesn't do any preparatory work for creating a new account, so should only be used - /// when it is known that the account already exists. - /// - /// NOTE: LOW-LEVEL: This will not attempt to maintain total issuance. It is expected that - /// the caller will do this. - pub fn mutate_account( - who: &T::AccountId, - f: impl FnOnce(&mut T::BalanceInfo) -> R, - ) -> Result { - Self::try_mutate_account(who, |a, _| -> Result { Ok(f(a)) }) - } + /// Mutate an account to some new value, or delete it entirely with `None`. Will enforce + /// `ExistentialDeposit` law, annulling the account as needed. + /// + /// NOTE: Doesn't do any preparatory work for creating a new account, so should only be used + /// when it is known that the account already exists. + /// + /// NOTE: LOW-LEVEL: This will not attempt to maintain total issuance. It is expected that + /// the caller will do this. + pub fn mutate_account( + who: &T::AccountId, + f: impl FnOnce(&mut T::BalanceInfo) -> R, + ) -> Result { + Self::try_mutate_account(who, |a, _| -> Result { Ok(f(a)) }) + } - /// Mutate an account to some new value, or delete it entirely with `None`. Will enforce - /// `ExistentialDeposit` law, annulling the account as needed. This will do nothing if the - /// result of `f` is an `Err`. - /// - /// NOTE: Doesn't do any preparatory work for creating a new account, so should only be used - /// when it is known that the account already exists. - /// - /// NOTE: LOW-LEVEL: This will not attempt to maintain total issuance. It is expected that - /// the caller will do this. - fn try_mutate_account( - who: &T::AccountId, - f: impl FnOnce(&mut T::BalanceInfo, bool) -> Result, - ) -> Result - where - E: From, - { - T::AccountStore::try_mutate_exists(who, |maybe_account| { - let is_new = maybe_account.is_none(); - let mut account = maybe_account.take().unwrap_or_default(); - f(&mut account, is_new).map(move |result| { - let maybe_endowed = if is_new { Some(account.free()) } else { None }; - *maybe_account = Self::post_mutation(who, account); - (maybe_endowed, result) + /// Mutate an account to some new value, or delete it entirely with `None`. Will enforce + /// `ExistentialDeposit` law, annulling the account as needed. This will do nothing if the + /// result of `f` is an `Err`. + /// + /// NOTE: Doesn't do any preparatory work for creating a new account, so should only be used + /// when it is known that the account already exists. + /// + /// NOTE: LOW-LEVEL: This will not attempt to maintain total issuance. It is expected that + /// the caller will do this. + fn try_mutate_account( + who: &T::AccountId, + f: impl FnOnce(&mut T::BalanceInfo, bool) -> Result, + ) -> Result + where + E: From, + { + T::AccountStore::try_mutate_exists(who, |maybe_account| { + let is_new = maybe_account.is_none(); + let mut account = maybe_account.take().unwrap_or_default(); + f(&mut account, is_new).map(move |result| { + let maybe_endowed = if is_new { Some(account.free()) } else { None }; + *maybe_account = Self::post_mutation(who, account); + (maybe_endowed, result) + }) + }) + .map(|(maybe_endowed, result)| { + if let Some(endowed) = maybe_endowed { + Self::deposit_event(Event::Endowed(who.clone(), endowed)); + } + result }) - }) - .map(|(maybe_endowed, result)| { - if let Some(endowed) = maybe_endowed { - Self::deposit_event(RawEvent::Endowed(who.clone(), endowed)); - } - result - }) - } - - /// Update the account entry for `who`, given the locks. - fn update_locks(who: &T::AccountId, locks: &[BalanceLock]) { - if locks.len() as u32 > T::MaxLocks::get() { - log::warn!( - target: "runtime::balances", - "Warning: A user has more currency locks than expected. \ - A runtime configuration adjustment may be needed." - ); } - let existed = Locks::::contains_key(who); - if locks.is_empty() { - Locks::::remove(who); - if existed { - // TODO: use Locks::::hashed_key - // https://github.com/paritytech/substrate/issues/4969 - >::dec_consumers(who); + /// Update the account entry for `who`, given the locks. + fn update_locks(who: &T::AccountId, locks: &[BalanceLock]) { + if locks.len() as u32 > T::MaxLocks::get() { + log::warn!( + target: "runtime::balances", + "Warning: A user has more currency locks than expected. \ + A runtime configuration adjustment may be needed." + ); } - } else { - Locks::::insert(who, locks); - if !existed { - if >::inc_consumers(who).is_err() { - // No providers for the locks. This is impossible under normal circumstances - // since the funds that are under the lock will themselves be stored in the - // account and therefore will need a reference. - log::warn!( - target: "runtime::balances", - "Warning: Attempt to introduce lock consumer reference, yet no providers. \ - This is unexpected but should be safe." - ); + + let existed = Locks::::contains_key(who); + if locks.is_empty() { + Locks::::remove(who); + if existed { + // TODO: use Locks::::hashed_key + // https://github.com/paritytech/substrate/issues/4969 + >::dec_consumers(who); + } + } else { + Locks::::insert(who, locks); + if !existed { + if >::inc_consumers(who).is_err() { + // No providers for the locks. This is impossible under normal circumstances + // since the funds that are under the lock will themselves be stored in the + // account and therefore will need a reference. + log::warn!( + target: "runtime::balances", + "Warning: Attempt to introduce lock consumer reference, yet no providers. \ + This is unexpected but should be safe." + ); + } } } } } -} -// wrapping these imbalances in a private module is necessary to ensure absolute privacy -// of the inner member. -mod imbalances { - // --- substrate --- - use sp_std::mem; - // --- darwinia --- - use crate::*; - - /// Opaque, move-only struct with private fields that serves as a token denoting that - /// funds have been created without any equal and opposite accounting. - #[must_use] - #[derive(RuntimeDebug, PartialEq, Eq)] - pub struct PositiveImbalance, I: Instance = DefaultInstance>(T::Balance); - - impl, I: Instance> PositiveImbalance { - /// Create a new positive imbalance from a balance. - pub fn new(amount: T::Balance) -> Self { - PositiveImbalance(amount) - } - } - - /// Opaque, move-only struct with private fields that serves as a token denoting that - /// funds have been destroyed without any equal and opposite accounting. - #[must_use] - #[derive(RuntimeDebug, PartialEq, Eq)] - pub struct NegativeImbalance, I: Instance = DefaultInstance>(T::Balance); - - impl, I: Instance> NegativeImbalance { - /// Create a new negative imbalance from a balance. - pub fn new(amount: T::Balance) -> Self { - NegativeImbalance(amount) - } - } + impl, I: 'static> Currency for Pallet + where + T::Balance: MaybeSerializeDeserialize + Debug, + { + type Balance = T::Balance; + type PositiveImbalance = PositiveImbalance; + type NegativeImbalance = NegativeImbalance; - impl, I: Instance> TryDrop for PositiveImbalance { - fn try_drop(self) -> Result<(), Self> { - self.drop_zero() + fn total_balance(who: &T::AccountId) -> Self::Balance { + let account = Self::account(who); + account.total() } - } - impl, I: Instance> Imbalance for PositiveImbalance { - type Opposite = NegativeImbalance; - - fn zero() -> Self { - Self(Zero::zero()) - } - fn drop_zero(self) -> Result<(), Self> { - if self.0.is_zero() { - Ok(()) - } else { - Err(self) + // Check if `value` amount of free balance can be slashed from `who`. + fn can_slash(who: &T::AccountId, value: Self::Balance) -> bool { + if value.is_zero() { + return true; } + Self::free_balance(who) >= value } - fn split(self, amount: T::Balance) -> (Self, Self) { - let first = self.0.min(amount); - let second = self.0 - first; - mem::forget(self); - (Self(first), Self(second)) + fn total_issuance() -> Self::Balance { + >::get() } - fn merge(mut self, other: Self) -> Self { - self.0 = self.0.saturating_add(other.0); - mem::forget(other); - self + fn minimum_balance() -> Self::Balance { + T::ExistentialDeposit::get() } - fn subsume(&mut self, other: Self) { - self.0 = self.0.saturating_add(other.0); - mem::forget(other); - } - fn offset(self, other: Self::Opposite) -> Result { - let (a, b) = (self.0, other.0); - mem::forget((self, other)); - if a >= b { - Ok(Self(a - b)) - } else { - Err(NegativeImbalance::new(b - a)) + // Burn funds from the total issuance, returning a positive imbalance for the amount burned. + // Is a no-op if amount to be burned is zero. + fn burn(mut amount: Self::Balance) -> Self::PositiveImbalance { + if amount.is_zero() { + return PositiveImbalance::zero(); } + >::mutate(|issued| { + *issued = issued.checked_sub(&amount).unwrap_or_else(|| { + amount = *issued; + Zero::zero() + }); + }); + PositiveImbalance::new(amount) } - fn peek(&self) -> T::Balance { - self.0.clone() - } - } - - impl, I: Instance> TryDrop for NegativeImbalance { - fn try_drop(self) -> Result<(), Self> { - self.drop_zero() - } - } - - impl, I: Instance> Imbalance for NegativeImbalance { - type Opposite = PositiveImbalance; - fn zero() -> Self { - Self(Zero::zero()) - } - fn drop_zero(self) -> Result<(), Self> { - if self.0.is_zero() { - Ok(()) - } else { - Err(self) + // Create new funds into the total issuance, returning a negative imbalance + // for the amount issued. + // Is a no-op if amount to be issued it zero. + fn issue(mut amount: Self::Balance) -> Self::NegativeImbalance { + if amount.is_zero() { + return NegativeImbalance::zero(); } + >::mutate(|issued| { + *issued = issued.checked_add(&amount).unwrap_or_else(|| { + amount = Self::Balance::max_value() - *issued; + Self::Balance::max_value() + }) + }); + NegativeImbalance::new(amount) } - fn split(self, amount: T::Balance) -> (Self, Self) { - let first = self.0.min(amount); - let second = self.0 - first; - mem::forget(self); - (Self(first), Self(second)) + fn free_balance(who: &T::AccountId) -> Self::Balance { + Self::account(who).free() } - fn merge(mut self, other: Self) -> Self { - self.0 = self.0.saturating_add(other.0); - mem::forget(other); - self - } - fn subsume(&mut self, other: Self) { - self.0 = self.0.saturating_add(other.0); - mem::forget(other); + // Ensure that an account can withdraw from their free balance given any existing withdrawal + // restrictions like locks and vesting balance. + // Is a no-op if amount to be withdrawn is zero. + // + // # + // Despite iterating over a list of locks, they are limited by the number of + // lock IDs, which means the number of runtime modules that intend to use and create locks. + // # + fn ensure_can_withdraw( + who: &T::AccountId, + amount: T::Balance, + reasons: WithdrawReasons, + new_balance: T::Balance, + ) -> DispatchResult { + if amount.is_zero() { + return Ok(()); + } + let min_balance = Self::frozen_balance(who.borrow()).frozen_for(reasons.into()); + ensure!( + new_balance >= min_balance, + >::LiquidityRestrictions + ); + Ok(()) } - fn offset(self, other: Self::Opposite) -> Result { - let (a, b) = (self.0, other.0); - mem::forget((self, other)); - if a >= b { - Ok(Self(a - b)) - } else { - Err(PositiveImbalance::new(b - a)) + // Transfer some free balance from `transactor` to `dest`, respecting existence requirements. + // Is a no-op if value to be transferred is zero or the `transactor` is the same as `dest`. + fn transfer( + transactor: &T::AccountId, + dest: &T::AccountId, + value: Self::Balance, + existence_requirement: ExistenceRequirement, + ) -> DispatchResult { + if value.is_zero() || transactor == dest { + return Ok(()); } - } - fn peek(&self) -> T::Balance { - self.0.clone() - } - } - impl, I: Instance> Drop for PositiveImbalance { - /// Basic drop handler will just square up the total issuance. - fn drop(&mut self) { - >::mutate(|v| *v = v.saturating_add(self.0)); - } - } + Self::try_mutate_account(dest, |to_account, _| -> DispatchResult { + Self::try_mutate_account(transactor, |from_account, _| -> DispatchResult { + from_account.set_free( + from_account + .free() + .checked_sub(&value) + .ok_or(>::InsufficientBalance)?, + ); - impl, I: Instance> Drop for NegativeImbalance { - /// Basic drop handler will just square up the total issuance. - fn drop(&mut self) { - >::mutate(|v| *v = v.saturating_sub(self.0)); - } - } -} + // NOTE: total stake being stored in the same type means that this could never overflow + // but better to be safe than sorry. + to_account.set_free( + to_account + .free() + .checked_add(&value) + .ok_or(>::Overflow)?, + ); -impl, I: Instance> Currency for Module -where - T::Balance: MaybeSerializeDeserialize + Debug, -{ - type Balance = T::Balance; - type PositiveImbalance = PositiveImbalance; - type NegativeImbalance = NegativeImbalance; - - fn total_balance(who: &T::AccountId) -> Self::Balance { - let account = Self::account(who); - account.total() - } + let ed = T::ExistentialDeposit::get(); - // Check if `value` amount of free balance can be slashed from `who`. - fn can_slash(who: &T::AccountId, value: Self::Balance) -> bool { - if value.is_zero() { - return true; - } - Self::free_balance(who) >= value - } + ensure!( + to_account.total() >= ed || !T::OtherCurrencies::is_dust(dest), + >::ExistentialDeposit + ); - fn total_issuance() -> Self::Balance { - >::get() - } + Self::ensure_can_withdraw( + transactor, + value, + WithdrawReasons::TRANSFER, + from_account.free(), + ) + .map_err(|_| Error::::LiquidityRestrictions)?; + + // TODO: This is over-conservative. There may now be other providers, and this module + // may not even be a provider. + let allow_death = existence_requirement == ExistenceRequirement::AllowDeath; + let allow_death = + allow_death && !>::is_provider_required(transactor); + + ensure!( + allow_death + || from_account.free() >= ed || !T::OtherCurrencies::is_dust(transactor), + >::KeepAlive + ); - fn minimum_balance() -> Self::Balance { - T::ExistentialDeposit::get() - } + Ok(()) + }) + })?; - // Burn funds from the total issuance, returning a positive imbalance for the amount burned. - // Is a no-op if amount to be burned is zero. - fn burn(mut amount: Self::Balance) -> Self::PositiveImbalance { - if amount.is_zero() { - return PositiveImbalance::zero(); - } - >::mutate(|issued| { - *issued = issued.checked_sub(&amount).unwrap_or_else(|| { - amount = *issued; - Zero::zero() - }); - }); - PositiveImbalance::new(amount) - } + // Emit transfer event. + Self::deposit_event(Event::Transfer(transactor.clone(), dest.clone(), value)); - // Create new funds into the total issuance, returning a negative imbalance - // for the amount issued. - // Is a no-op if amount to be issued it zero. - fn issue(mut amount: Self::Balance) -> Self::NegativeImbalance { - if amount.is_zero() { - return NegativeImbalance::zero(); + Ok(()) } - >::mutate(|issued| { - *issued = issued.checked_add(&amount).unwrap_or_else(|| { - amount = Self::Balance::max_value() - *issued; - Self::Balance::max_value() - }) - }); - NegativeImbalance::new(amount) - } - fn free_balance(who: &T::AccountId) -> Self::Balance { - Self::account(who).free() - } + /// Slash a target account `who`, returning the negative imbalance created and any left over + /// amount that could not be slashed. + /// + /// Is a no-op if `value` to be slashed is zero or the account does not exist. + /// + /// NOTE: `slash()` prefers free balance, but assumes that reserve balance can be drawn + /// from in extreme circumstances. `can_slash()` should be used prior to `slash()` to avoid having + /// to draw from reserved funds, however we err on the side of punishment if things are inconsistent + /// or `can_slash` wasn't used appropriately. + fn slash( + who: &T::AccountId, + value: Self::Balance, + ) -> (Self::NegativeImbalance, Self::Balance) { + if value.is_zero() { + return (NegativeImbalance::zero(), Zero::zero()); + } + if Self::total_balance(&who).is_zero() { + return (NegativeImbalance::zero(), value); + } - // Ensure that an account can withdraw from their free balance given any existing withdrawal - // restrictions like locks and vesting balance. - // Is a no-op if amount to be withdrawn is zero. - // - // # - // Despite iterating over a list of locks, they are limited by the number of - // lock IDs, which means the number of runtime modules that intend to use and create locks. - // # - fn ensure_can_withdraw( - who: &T::AccountId, - amount: T::Balance, - reasons: WithdrawReasons, - new_balance: T::Balance, - ) -> DispatchResult { - if amount.is_zero() { - return Ok(()); + for attempt in 0..2 { + match Self::try_mutate_account( + who, + |account, + _is_new| + -> Result<(Self::NegativeImbalance, Self::Balance), StoredMapError> { + // Best value is the most amount we can slash following liveness rules. + let best_value = match attempt { + // First attempt we try to slash the full amount, and see if liveness issues happen. + 0 => value, + // If acting as a critical provider (i.e. first attempt failed), then slash + // as much as possible while leaving at least at ED. + _ => value.min( + (account.free() + account.reserved()) + .saturating_sub(T::ExistentialDeposit::get()), + ), + }; + + let free_slash = cmp::min(account.free(), best_value); + account.set_free(account.free() - free_slash); // Safe because of above check + let remaining_slash = best_value - free_slash; // Safe because of above check + + if !remaining_slash.is_zero() { + // If we have remaining slash, take it from reserved balance. + let reserved_slash = cmp::min(account.reserved(), remaining_slash); + account.set_reserved(account.reserved() - reserved_slash); // Safe because of above check + Ok(( + NegativeImbalance::new(free_slash + reserved_slash), + value - free_slash - reserved_slash, // Safe because value is gt or eq total slashed + )) + } else { + // Else we are done! + Ok(( + NegativeImbalance::new(free_slash), + value - free_slash, // Safe because value is gt or eq to total slashed + )) + } + }, + ) { + Ok(r) => return r, + Err(_) => (), + } + } + + // Should never get here. But we'll be defensive anyway. + (Self::NegativeImbalance::zero(), value) } - let min_balance = Self::frozen_balance(who.borrow()).frozen_for(reasons.into()); - ensure!( - new_balance >= min_balance, - >::LiquidityRestrictions - ); - Ok(()) - } - // Transfer some free balance from `transactor` to `dest`, respecting existence requirements. - // Is a no-op if value to be transferred is zero or the `transactor` is the same as `dest`. - fn transfer( - transactor: &T::AccountId, - dest: &T::AccountId, - value: Self::Balance, - existence_requirement: ExistenceRequirement, - ) -> DispatchResult { - if value.is_zero() || transactor == dest { - return Ok(()); + /// Deposit some `value` into the free balance of an existing target account `who`. + /// + /// Is a no-op if the `value` to be deposited is zero. + fn deposit_into_existing( + who: &T::AccountId, + value: Self::Balance, + ) -> Result { + if value.is_zero() { + return Ok(PositiveImbalance::zero()); + } + + Self::try_mutate_account( + who, + |account, is_new| -> Result { + ensure!( + !is_new || !T::OtherCurrencies::is_dust(who), + >::DeadAccount + ); + account.set_free( + account + .free() + .checked_add(&value) + .ok_or(>::Overflow)?, + ); + Ok(PositiveImbalance::new(value)) + }, + ) } - Self::try_mutate_account(dest, |to_account, _| -> DispatchResult { - Self::try_mutate_account(transactor, |from_account, _| -> DispatchResult { - from_account.set_free( - from_account - .free() - .checked_sub(&value) - .ok_or(>::InsufficientBalance)?, - ); + /// Deposit some `value` into the free balance of `who`, possibly creating a new account. + /// + /// This function is a no-op if: + /// - the `value` to be deposited is zero; or + /// - the `value` to be deposited is less than the required ED and the account does not yet exist; or + /// - the deposit would necessitate the account to exist and there are no provider references; or + /// - `value` is so large it would cause the balance of `who` to overflow. + fn deposit_creating(who: &T::AccountId, value: Self::Balance) -> Self::PositiveImbalance { + if value.is_zero() { + return Self::PositiveImbalance::zero(); + } - // NOTE: total stake being stored in the same type means that this could never overflow - // but better to be safe than sorry. - to_account.set_free( - to_account - .free() - .checked_add(&value) - .ok_or(>::Overflow)?, - ); + Self::try_mutate_account( + who, + |account, is_new| -> Result { + let ed = T::ExistentialDeposit::get(); + ensure!( + value >= ed || !is_new || !T::OtherCurrencies::is_dust(who), + >::ExistentialDeposit + ); - let ed = T::ExistentialDeposit::get(); + // defensive only: overflow should never happen, however in case it does, then this + // operation is a no-op. + account.set_free(match account.free().checked_add(&value) { + Some(x) => x, + None => return Ok(Self::PositiveImbalance::zero()), + }); - ensure!( - to_account.total() >= ed || !T::OtherCurrencies::is_dust(dest), - >::ExistentialDeposit - ); + Ok(PositiveImbalance::new(value)) + }, + ) + .unwrap_or_else(|_| Self::PositiveImbalance::zero()) + } - Self::ensure_can_withdraw( - transactor, - value, - WithdrawReasons::TRANSFER, - from_account.free(), - ) - .map_err(|_| Error::::LiquidityRestrictions)?; - - // TODO: This is over-conservative. There may now be other providers, and this module - // may not even be a provider. - let allow_death = existence_requirement == ExistenceRequirement::AllowDeath; - let allow_death = - allow_death && !>::is_provider_required(transactor); - - ensure!( - allow_death - || from_account.free() >= ed - || !T::OtherCurrencies::is_dust(transactor), - >::KeepAlive - ); + /// Withdraw some free balance from an account, respecting existence requirements. + /// + /// Is a no-op if value to be withdrawn is zero. + fn withdraw( + who: &T::AccountId, + value: Self::Balance, + reasons: WithdrawReasons, + liveness: ExistenceRequirement, + ) -> Result { + if value.is_zero() { + return Ok(NegativeImbalance::zero()); + } - Ok(()) - }) - })?; + Self::try_mutate_account( + who, + |account, _| -> Result { + let new_free_account = account + .free() + .checked_sub(&value) + .ok_or(>::InsufficientBalance)?; + + // bail if we need to keep the account alive and this would kill it. + let ed = T::ExistentialDeposit::get(); + let others_is_dust = T::OtherCurrencies::is_dust(who); + let would_be_dead = { + let new_total = new_free_account + account.reserved(); + new_total < ed && others_is_dust + }; + let would_kill = { + let old_total = account.free() + account.reserved(); + would_be_dead && (old_total >= ed || !others_is_dust) + }; + ensure!( + liveness == ExistenceRequirement::AllowDeath || !would_kill, + >::KeepAlive + ); - // Emit transfer event. - Self::deposit_event(RawEvent::Transfer(transactor.clone(), dest.clone(), value)); + Self::ensure_can_withdraw(who, value, reasons, new_free_account)?; - Ok(()) - } + account.set_free(new_free_account); - /// Slash a target account `who`, returning the negative imbalance created and any left over - /// amount that could not be slashed. - /// - /// Is a no-op if `value` to be slashed is zero or the account does not exist. - /// - /// NOTE: `slash()` prefers free balance, but assumes that reserve balance can be drawn - /// from in extreme circumstances. `can_slash()` should be used prior to `slash()` to avoid having - /// to draw from reserved funds, however we err on the side of punishment if things are inconsistent - /// or `can_slash` wasn't used appropriately. - fn slash(who: &T::AccountId, value: Self::Balance) -> (Self::NegativeImbalance, Self::Balance) { - if value.is_zero() { - return (NegativeImbalance::zero(), Zero::zero()); - } - if Self::total_balance(&who).is_zero() { - return (NegativeImbalance::zero(), value); + Ok(NegativeImbalance::new(value)) + }, + ) } - for attempt in 0..2 { - match Self::try_mutate_account( + /// Force the new free balance of a target account `who` to some new value `balance`. + fn make_free_balance_be( + who: &T::AccountId, + value: Self::Balance, + ) -> SignedImbalance { + Self::try_mutate_account( who, |account, - _is_new| - -> Result<(Self::NegativeImbalance, Self::Balance), StoredMapError> { - // Best value is the most amount we can slash following liveness rules. - let best_value = match attempt { - // First attempt we try to slash the full amount, and see if liveness issues happen. - 0 => value, - // If acting as a critical provider (i.e. first attempt failed), then slash - // as much as possible while leaving at least at ED. - _ => value.min( - (account.free() + account.reserved()) - .saturating_sub(T::ExistentialDeposit::get()), - ), - }; + is_new| + -> Result< + SignedImbalance, + DispatchError, + > { + let ed = T::ExistentialDeposit::get(); + let total = value.saturating_add(account.reserved()); + // If we're attempting to set an existing account to less than ED, then + // bypass the entire operation. It's a no-op if you follow it through, but + // since this is an instance where we might account for a negative imbalance + // (in the dust cleaner of set_account) before we account for its actual + // equal and opposite cause (returned as an Imbalance), then in the + // instance that there's no other accounts on the system at all, we might + // underflow the issuance and our arithmetic will be off. + ensure!( + total >= ed || !is_new || !T::OtherCurrencies::is_dust(who), + >::ExistentialDeposit + ); - let free_slash = cmp::min(account.free(), best_value); - account.set_free(account.free() - free_slash); // Safe because of above check - let remaining_slash = best_value - free_slash; // Safe because of above check - - if !remaining_slash.is_zero() { - // If we have remaining slash, take it from reserved balance. - let reserved_slash = cmp::min(account.reserved(), remaining_slash); - account.set_reserved(account.reserved() - reserved_slash); // Safe because of above check - Ok(( - NegativeImbalance::new(free_slash + reserved_slash), - value - free_slash - reserved_slash, // Safe because value is gt or eq total slashed - )) + let imbalance = if account.free() <= value { + SignedImbalance::Positive(PositiveImbalance::new(value - account.free())) } else { - // Else we are done! - Ok(( - NegativeImbalance::new(free_slash), - value - free_slash, // Safe because value is gt or eq to total slashed - )) - } + SignedImbalance::Negative(NegativeImbalance::new(account.free() - value)) + }; + account.set_free(value); + Ok(imbalance) }, - ) { - Ok(r) => return r, - Err(_) => (), - } + ) + .unwrap_or(SignedImbalance::Positive(Self::PositiveImbalance::zero())) } - - // Should never get here. But we'll be defensive anyway. - (Self::NegativeImbalance::zero(), value) } - /// Deposit some `value` into the free balance of an existing target account `who`. - /// - /// Is a no-op if the `value` to be deposited is zero. - fn deposit_into_existing( - who: &T::AccountId, - value: Self::Balance, - ) -> Result { - if value.is_zero() { - return Ok(PositiveImbalance::zero()); + impl, I: 'static> ReservableCurrency for Pallet + where + T::Balance: MaybeSerializeDeserialize + Debug, + { + /// Check if `who` can reserve `value` from their free balance. + /// + /// Always `true` if value to be reserved is zero. + fn can_reserve(who: &T::AccountId, value: Self::Balance) -> bool { + if value.is_zero() { + return true; + } + Self::account(who) + .free() + .checked_sub(&value) + .map_or(false, |new_balance| { + Self::ensure_can_withdraw(who, value, WithdrawReasons::RESERVE, new_balance) + .is_ok() + }) } - Self::try_mutate_account( - who, - |account, is_new| -> Result { - ensure!( - !is_new || !T::OtherCurrencies::is_dust(who), - >::DeadAccount - ); - account.set_free( - account - .free() - .checked_add(&value) - .ok_or(>::Overflow)?, - ); - Ok(PositiveImbalance::new(value)) - }, - ) - } + /// Slash from reserved balance, returning the negative imbalance created, + /// and any amount that was unable to be slashed. + /// + /// Is a no-op if the value to be slashed is zero or the account does not exist. + fn slash_reserved( + who: &T::AccountId, + value: Self::Balance, + ) -> (Self::NegativeImbalance, Self::Balance) { + if value.is_zero() { + return (NegativeImbalance::zero(), Zero::zero()); + } + if Self::total_balance(&who).is_zero() { + return (NegativeImbalance::zero(), value); + } - /// Deposit some `value` into the free balance of `who`, possibly creating a new account. - /// - /// This function is a no-op if: - /// - the `value` to be deposited is zero; or - /// - the `value` to be deposited is less than the required ED and the account does not yet exist; or - /// - the deposit would necessitate the account to exist and there are no provider references; or - /// - `value` is so large it would cause the balance of `who` to overflow. - fn deposit_creating(who: &T::AccountId, value: Self::Balance) -> Self::PositiveImbalance { - if value.is_zero() { - return Self::PositiveImbalance::zero(); - } + // NOTE: `mutate_account` may fail if it attempts to reduce the balance to the point that an + // account is attempted to be illegally destroyed. - Self::try_mutate_account( - who, - |account, is_new| -> Result { - let ed = T::ExistentialDeposit::get(); - ensure!( - value >= ed || !is_new || !T::OtherCurrencies::is_dust(who), - >::ExistentialDeposit - ); + for attempt in 0..2 { + match Self::mutate_account(who, |account| { + let best_value = match attempt { + 0 => value, + // If acting as a critical provider (i.e. first attempt failed), then ensure + // slash leaves at least the ED. + _ => value.min( + (account.free() + account.reserved()) + .saturating_sub(T::ExistentialDeposit::get()), + ), + }; - // defensive only: overflow should never happen, however in case it does, then this - // operation is a no-op. - account.set_free(match account.free().checked_add(&value) { - Some(x) => x, - None => return Ok(Self::PositiveImbalance::zero()), - }); + let actual = cmp::min(account.reserved(), best_value); + account.set_reserved(account.reserved() - actual); - Ok(PositiveImbalance::new(value)) - }, - ) - .unwrap_or_else(|_| Self::PositiveImbalance::zero()) - } + // underflow should never happen, but it if does, there's nothing to be done here. + (NegativeImbalance::new(actual), value - actual) + }) { + Ok(r) => return r, + Err(_) => (), + } + } + // Should never get here as we ensure that ED is left in the second attempt. + // In case we do, though, then we fail gracefully. + (Self::NegativeImbalance::zero(), value) + } - /// Withdraw some free balance from an account, respecting existence requirements. - /// - /// Is a no-op if value to be withdrawn is zero. - fn withdraw( - who: &T::AccountId, - value: Self::Balance, - reasons: WithdrawReasons, - liveness: ExistenceRequirement, - ) -> Result { - if value.is_zero() { - return Ok(NegativeImbalance::zero()); + fn reserved_balance(who: &T::AccountId) -> Self::Balance { + let account = Self::account(who); + account.reserved() } - Self::try_mutate_account( - who, - |account, _| -> Result { - let new_free_account = account + /// Move `value` from the free balance from `who` to their reserved balance. + /// + /// Is a no-op if value to be reserved is zero. + fn reserve(who: &T::AccountId, value: Self::Balance) -> DispatchResult { + if value.is_zero() { + return Ok(()); + } + + Self::try_mutate_account(who, |account, _| -> DispatchResult { + let new_free = account .free() .checked_sub(&value) .ok_or(>::InsufficientBalance)?; + account.set_free(new_free); - // bail if we need to keep the account alive and this would kill it. - let ed = T::ExistentialDeposit::get(); - let others_is_dust = T::OtherCurrencies::is_dust(who); - let would_be_dead = { - let new_total = new_free_account + account.reserved(); - new_total < ed && others_is_dust - }; - let would_kill = { - let old_total = account.free() + account.reserved(); - would_be_dead && (old_total >= ed || !others_is_dust) - }; - ensure!( - liveness == AllowDeath || !would_kill, - >::KeepAlive - ); + let new_reserved = account + .reserved() + .checked_add(&value) + .ok_or(>::Overflow)?; + account.set_reserved(new_reserved); + Self::ensure_can_withdraw( + &who, + value.clone(), + WithdrawReasons::RESERVE, + account.free(), + ) + })?; - Self::ensure_can_withdraw(who, value, reasons, new_free_account)?; + Self::deposit_event(Event::Reserved(who.clone(), value)); + Ok(()) + } - account.set_free(new_free_account); + /// Unreserve some funds, returning any amount that was unable to be unreserved. + /// + /// Is a no-op if the value to be unreserved is zero. + fn unreserve(who: &T::AccountId, value: Self::Balance) -> Self::Balance { + if value.is_zero() { + return Zero::zero(); + } + if Self::total_balance(&who).is_zero() { + return value; + } - Ok(NegativeImbalance::new(value)) - }, - ) - } + let actual = match Self::mutate_account(who, |account| { + let actual = cmp::min(account.reserved(), value); + let new_reserved = account.reserved() - actual; + account.set_reserved(new_reserved); + // defensive only: this can never fail since total issuance which is at least free+reserved + // fits into the same data type. + account.set_free(account.free().saturating_add(actual)); + actual + }) { + Ok(x) => x, + Err(_) => { + // This should never happen since we don't alter the total amount in the account. + // If it ever does, then we should fail gracefully though, indicating that nothing + // could be done. + return value; + } + }; - /// Force the new free balance of a target account `who` to some new value `balance`. - fn make_free_balance_be( - who: &T::AccountId, - value: Self::Balance, - ) -> SignedImbalance { - Self::try_mutate_account( - who, - |account, - is_new| - -> Result, DispatchError> { - let ed = T::ExistentialDeposit::get(); - let total = value.saturating_add(account.reserved()); - // If we're attempting to set an existing account to less than ED, then - // bypass the entire operation. It's a no-op if you follow it through, but - // since this is an instance where we might account for a negative imbalance - // (in the dust cleaner of set_account) before we account for its actual - // equal and opposite cause (returned as an Imbalance), then in the - // instance that there's no other accounts on the system at all, we might - // underflow the issuance and our arithmetic will be off. - ensure!( - total >= ed || !is_new || !T::OtherCurrencies::is_dust(who), - >::ExistentialDeposit - ); + Self::deposit_event(Event::Unreserved(who.clone(), actual.clone())); + value - actual + } - let imbalance = if account.free() <= value { - SignedImbalance::Positive(PositiveImbalance::new(value - account.free())) - } else { - SignedImbalance::Negative(NegativeImbalance::new(account.free() - value)) + /// Move the reserved balance of one account into the balance of another, according to `status`. + /// + /// Is a no-op if: + /// - the value to be moved is zero; or + /// - the `slashed` id equal to `beneficiary` and the `status` is `Reserved`. + fn repatriate_reserved( + slashed: &T::AccountId, + beneficiary: &T::AccountId, + value: Self::Balance, + status: BalanceStatus, + ) -> Result { + if value.is_zero() { + return Ok(Zero::zero()); + } + + if slashed == beneficiary { + return match status { + BalanceStatus::Free => Ok(Self::unreserve(slashed, value)), + BalanceStatus::Reserved => { + Ok(value.saturating_sub(Self::reserved_balance(slashed))) + } }; - account.set_free(value); - Ok(imbalance) - }, - ) - .unwrap_or(SignedImbalance::Positive(Self::PositiveImbalance::zero())) - } -} + } -impl, I: Instance> ReservableCurrency for Module -where - T::Balance: MaybeSerializeDeserialize + Debug, -{ - /// Check if `who` can reserve `value` from their free balance. - /// - /// Always `true` if value to be reserved is zero. - fn can_reserve(who: &T::AccountId, value: Self::Balance) -> bool { - if value.is_zero() { - return true; + let actual = Self::try_mutate_account( + beneficiary, + |to_account, is_new| -> Result { + ensure!( + !is_new || !T::OtherCurrencies::is_dust(beneficiary), + >::DeadAccount + ); + Self::try_mutate_account( + slashed, + |from_account, _| -> Result { + let actual = cmp::min(from_account.reserved(), value); + match status { + BalanceStatus::Free => to_account.set_free( + to_account + .free() + .checked_add(&actual) + .ok_or(>::Overflow)?, + ), + BalanceStatus::Reserved => to_account.set_reserved( + to_account + .reserved() + .checked_add(&actual) + .ok_or(>::Overflow)?, + ), + } + let new_reserved = from_account.reserved() - actual; + from_account.set_reserved(new_reserved); + Ok(actual) + }, + ) + }, + )?; + + Self::deposit_event(Event::ReserveRepatriated( + slashed.clone(), + beneficiary.clone(), + actual, + status, + )); + Ok(value - actual) } - Self::account(who) - .free() - .checked_sub(&value) - .map_or(false, |new_balance| { - Self::ensure_can_withdraw(who, value, WithdrawReasons::RESERVE, new_balance).is_ok() - }) } - /// Slash from reserved balance, returning the negative imbalance created, - /// and any amount that was unable to be slashed. - /// - /// Is a no-op if the value to be slashed is zero or the account does not exist. - fn slash_reserved( - who: &T::AccountId, - value: Self::Balance, - ) -> (Self::NegativeImbalance, Self::Balance) { - if value.is_zero() { - return (NegativeImbalance::zero(), Zero::zero()); - } - if Self::total_balance(&who).is_zero() { - return (NegativeImbalance::zero(), value); + impl, I: 'static> LockableCurrency for Pallet + where + T::Balance: MaybeSerializeDeserialize + Debug, + { + type Moment = T::BlockNumber; + type MaxLocks = T::MaxLocks; + + // Set a lock on the balance of `who`. + // Is a no-op if lock amount is zero or `reasons` `is_none()`. + fn set_lock( + id: LockIdentifier, + who: &T::AccountId, + lock_for: LockFor, + reasons: WithdrawReasons, + ) { + if match &lock_for { + LockFor::Common { amount } => *amount, + LockFor::Staking(staking_lock) => { + staking_lock.locked_amount(>::block_number()) + } + } + .is_zero() || reasons.is_empty() + { + return; + } + let mut new_lock = Some(BalanceLock { + id, + lock_for, + lock_reasons: reasons.into(), + }); + let mut locks = Self::locks(who) + .into_iter() + .filter_map(|l| if l.id == id { new_lock.take() } else { Some(l) }) + .collect::>(); + if let Some(lock) = new_lock { + locks.push(lock) + } + Self::update_locks(who, &locks); } - // NOTE: `mutate_account` may fail if it attempts to reduce the balance to the point that an - // account is attempted to be illegally destroyed. - - for attempt in 0..2 { - match Self::mutate_account(who, |account| { - let best_value = match attempt { - 0 => value, - // If acting as a critical provider (i.e. first attempt failed), then ensure - // slash leaves at least the ED. - _ => value.min( - (account.free() + account.reserved()) - .saturating_sub(T::ExistentialDeposit::get()), - ), - }; + // Extend a lock on the balance of `who`. + // Is a no-op if lock amount is zero or `reasons` `is_none()`. + fn extend_lock( + id: LockIdentifier, + who: &T::AccountId, + amount: T::Balance, + reasons: WithdrawReasons, + ) -> DispatchResult { + if amount.is_zero() || reasons.is_empty() { + return Ok(()); + } + + let mut new_lock = Some(BalanceLock { + id, + lock_for: LockFor::Common { amount }, + lock_reasons: reasons.into(), + }); + let mut poisoned = false; + let mut locks = Self::locks(who) + .into_iter() + .filter_map(|l| { + if l.id == id { + if let LockFor::Common { amount: a } = l.lock_for { + new_lock.take().map(|nl| BalanceLock { + id: l.id, + lock_for: { + match nl.lock_for { + // Only extend common lock type + LockFor::Common { amount: na } => LockFor::Common { + amount: (a).max(na), + }, + // Not allow to extend other combination/type lock + // + // And the lock is always with lock id + // it's impossiable to match a (other lock, common lock) + // under this if condition + _ => { + poisoned = true; + + nl.lock_for + } + } + }, + lock_reasons: l.lock_reasons | nl.lock_reasons, + }) + } else { + Some(l) + } + } else { + Some(l) + } + }) + .collect::>(); - let actual = cmp::min(account.reserved(), best_value); - account.set_reserved(account.reserved() - actual); + if poisoned { + Err(>::LockP)?; + } - // underflow should never happen, but it if does, there's nothing to be done here. - (NegativeImbalance::new(actual), value - actual) - }) { - Ok(r) => return r, - Err(_) => (), + if let Some(lock) = new_lock { + locks.push(lock) } - } - // Should never get here as we ensure that ED is left in the second attempt. - // In case we do, though, then we fail gracefully. - (Self::NegativeImbalance::zero(), value) - } - fn reserved_balance(who: &T::AccountId) -> Self::Balance { - let account = Self::account(who); - account.reserved() - } + Self::update_locks(who, &locks); - /// Move `value` from the free balance from `who` to their reserved balance. - /// - /// Is a no-op if value to be reserved is zero. - fn reserve(who: &T::AccountId, value: Self::Balance) -> DispatchResult { - if value.is_zero() { - return Ok(()); + Ok(()) } - Self::try_mutate_account(who, |account, _| -> DispatchResult { - let new_free = account - .free() - .checked_sub(&value) - .ok_or(>::InsufficientBalance)?; - account.set_free(new_free); - - let new_reserved = account - .reserved() - .checked_add(&value) - .ok_or(>::Overflow)?; - account.set_reserved(new_reserved); - Self::ensure_can_withdraw( - &who, - value.clone(), - WithdrawReasons::RESERVE, - account.free(), - ) - })?; + fn remove_lock(id: LockIdentifier, who: &T::AccountId) { + let mut locks = Self::locks(who); - Self::deposit_event(RawEvent::Reserved(who.clone(), value)); - Ok(()) - } + locks.retain(|l| l.id != id); - /// Unreserve some funds, returning any amount that was unable to be unreserved. - /// - /// Is a no-op if the value to be unreserved is zero. - fn unreserve(who: &T::AccountId, value: Self::Balance) -> Self::Balance { - if value.is_zero() { - return Zero::zero(); + Self::update_locks(who, &locks); } - if Self::total_balance(&who).is_zero() { - return value; + + /// Get the balance of an account that can be used for transfers, reservations, or any other + /// non-locking, non-transaction-fee activity. Will be at most `free_balance`. + fn usable_balance(who: &T::AccountId) -> Self::Balance { + let account = Self::account(who); + + account.usable(LockReasons::Misc, Self::frozen_balance(who)) } - let actual = match Self::mutate_account(who, |account| { - let actual = cmp::min(account.reserved(), value); - let new_reserved = account.reserved() - actual; - account.set_reserved(new_reserved); - // defensive only: this can never fail since total issuance which is at least free+reserved - // fits into the same data type. - account.set_free(account.free().saturating_add(actual)); - actual - }) { - Ok(x) => x, - Err(_) => { - // This should never happen since we don't alter the total amount in the account. - // If it ever does, then we should fail gracefully though, indicating that nothing - // could be done. - return value; - } - }; + /// Get the balance of an account that can be used for paying transaction fees (not tipping, + /// or any other kind of fees, though). Will be at most `free_balance`. + fn usable_balance_for_fees(who: &T::AccountId) -> Self::Balance { + let account = Self::account(who); - Self::deposit_event(RawEvent::Unreserved(who.clone(), actual.clone())); - value - actual + account.usable(LockReasons::Fee, Self::frozen_balance(who)) + } } - /// Move the reserved balance of one account into the balance of another, according to `status`. - /// - /// Is a no-op if: - /// - the value to be moved is zero; or - /// - the `slashed` id equal to `beneficiary` and the `status` is `Reserved`. - fn repatriate_reserved( - slashed: &T::AccountId, - beneficiary: &T::AccountId, - value: Self::Balance, - status: Status, - ) -> Result { - if value.is_zero() { - return Ok(Zero::zero()); - } + impl, I: 'static> DustCollector for Pallet { + fn is_dust(who: &T::AccountId) -> bool { + let total = Self::total_balance(who); - if slashed == beneficiary { - return match status { - Status::Free => Ok(Self::unreserve(slashed, value)), - Status::Reserved => Ok(value.saturating_sub(Self::reserved_balance(slashed))), - }; + total < T::ExistentialDeposit::get() || total.is_zero() } - let actual = Self::try_mutate_account( - beneficiary, - |to_account, is_new| -> Result { - ensure!( - !is_new || !T::OtherCurrencies::is_dust(beneficiary), - >::DeadAccount - ); - Self::try_mutate_account( - slashed, - |from_account, _| -> Result { - let actual = cmp::min(from_account.reserved(), value); - match status { - Status::Free => to_account.set_free( - to_account - .free() - .checked_add(&actual) - .ok_or(>::Overflow)?, - ), - Status::Reserved => to_account.set_reserved( - to_account - .reserved() - .checked_add(&actual) - .ok_or(>::Overflow)?, - ), - } - let new_reserved = from_account.reserved() - actual; - from_account.set_reserved(new_reserved); - Ok(actual) - }, - ) - }, - )?; - - Self::deposit_event(RawEvent::ReserveRepatriated( - slashed.clone(), - beneficiary.clone(), - actual, - status, - )); - Ok(value - actual) - } -} + fn collect(who: &T::AccountId) { + let dropped = Self::total_balance(who); -impl, I: Instance> LockableCurrency for Module -where - T::Balance: MaybeSerializeDeserialize + Debug, -{ - type Moment = T::BlockNumber; - type MaxLocks = T::MaxLocks; - - // Set a lock on the balance of `who`. - // Is a no-op if lock amount is zero or `reasons` `is_none()`. - fn set_lock( - id: LockIdentifier, - who: &T::AccountId, - lock_for: LockFor, - reasons: WithdrawReasons, - ) { - if match &lock_for { - LockFor::Common { amount } => *amount, - LockFor::Staking(staking_lock) => { - staking_lock.locked_amount(>::block_number()) + if !dropped.is_zero() { + T::DustRemoval::on_unbalanced(NegativeImbalance::new(dropped)); + if let Err(e) = >::dec_providers(who) { + log::error!("Logic error: Unexpected {:?}", e); + } + Self::deposit_event(Event::DustLost(who.clone(), dropped)); } } - .is_zero() || reasons.is_empty() - { - return; - } - let mut new_lock = Some(BalanceLock { - id, - lock_for, - lock_reasons: reasons.into(), - }); - let mut locks = Self::locks(who) - .into_iter() - .filter_map(|l| if l.id == id { new_lock.take() } else { Some(l) }) - .collect::>(); - if let Some(lock) = new_lock { - locks.push(lock) - } - Self::update_locks(who, &locks); } - // Extend a lock on the balance of `who`. - // Is a no-op if lock amount is zero or `reasons` `is_none()`. - fn extend_lock( - id: LockIdentifier, - who: &T::AccountId, - amount: T::Balance, - reasons: WithdrawReasons, - ) -> DispatchResult { - if amount.is_zero() || reasons.is_empty() { - return Ok(()); + // A value placed in storage that represents the current version of the Balances storage. + // This value is used by the `on_runtime_upgrade` logic to determine whether we run + // storage migration logic. This should match directly with the semantic versions of the Rust crate. + #[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug)] + pub enum Releases { + V1_0_0, + V2_0_0, + } + impl Default for Releases { + fn default() -> Self { + Releases::V1_0_0 } - - let mut new_lock = Some(BalanceLock { - id, - lock_for: LockFor::Common { amount }, - lock_reasons: reasons.into(), - }); - let mut poisoned = false; - let mut locks = Self::locks(who) - .into_iter() - .filter_map(|l| { - if l.id == id { - if let LockFor::Common { amount: a } = l.lock_for { - new_lock.take().map(|nl| BalanceLock { - id: l.id, - lock_for: { - match nl.lock_for { - // Only extend common lock type - LockFor::Common { amount: na } => LockFor::Common { - amount: (a).max(na), - }, - // Not allow to extend other combination/type lock - // - // And the lock is always with lock id - // it's impossiable to match a (other lock, common lock) - // under this if condition - _ => { - poisoned = true; - - nl.lock_for - } - } - }, - lock_reasons: l.lock_reasons | nl.lock_reasons, - }) - } else { - Some(l) + } +} +pub use pallet::{imbalances::*, *}; + +pub mod migration { + #[cfg(feature = "try-runtime")] + pub mod try_runtime { + // --- substrate --- + use frame_support::{pallet_prelude::*, traits::StorageInstance}; + // --- darwinia --- + use crate::*; + + macro_rules! generate_storage_types { + ($prefix:expr, $storage:expr, $name:ident => Value<$value:ty>) => { + paste::paste! { + type $name = StorageValue<[<$name Instance>], $value, ValueQuery>; + + struct [<$name Instance>]; + impl StorageInstance for [<$name Instance>] { + const STORAGE_PREFIX: &'static str = $storage; + + fn pallet_prefix() -> &'static str { $prefix } } - } else { - Some(l) } - }) - .collect::>(); - - if poisoned { - Err(>::LockP)?; - } - - if let Some(lock) = new_lock { - locks.push(lock) + }; } - Self::update_locks(who, &locks); + generate_storage_types!("Instance0DarwiniaBalances", "TotalIssuance", OldRingTotalIssuance => Value<()>); + generate_storage_types!("Balances", "TotalIssuance", NewRingTotalIssuance => Value<()>); + generate_storage_types!("Instance1DarwiniaBalances", "TotalIssuance", OldKtonTotalIssuance => Value<()>); + generate_storage_types!("Kton", "TotalIssuance", NewKtonTotalIssuance => Value<()>); - Ok(()) - } + pub fn pre_migrate() -> Result<(), &'static str> { + assert!(OldRingTotalIssuance::exists()); + assert!(!NewRingTotalIssuance::exists()); + assert!(OldKtonTotalIssuance::exists()); + assert!(!NewKtonTotalIssuance::exists()); - fn remove_lock(id: LockIdentifier, who: &T::AccountId) { - let mut locks = Self::locks(who); + log::info!("Migrating `Instance0Darwinia` to `Balances`..."); + migration::migrate(b"Instance0DarwiniaBalances", b"Balances"); - locks.retain(|l| l.id != id); + log::info!("Migrating `Instance1Darwinia` to `Kton`..."); + migration::migrate(b"Instance1DarwiniaBalances", b"Kton"); - Self::update_locks(who, &locks); - } + assert!(!OldRingTotalIssuance::exists()); + assert!(NewRingTotalIssuance::exists()); + assert!(!OldKtonTotalIssuance::exists()); + assert!(NewKtonTotalIssuance::exists()); - /// Get the balance of an account that can be used for transfers, reservations, or any other - /// non-locking, non-transaction-fee activity. Will be at most `free_balance`. - fn usable_balance(who: &T::AccountId) -> Self::Balance { - let account = Self::account(who); - - account.usable(LockReasons::Misc, Self::frozen_balance(who)) - } - - /// Get the balance of an account that can be used for paying transaction fees (not tipping, - /// or any other kind of fees, though). Will be at most `free_balance`. - fn usable_balance_for_fees(who: &T::AccountId) -> Self::Balance { - let account = Self::account(who); - - account.usable(LockReasons::Fee, Self::frozen_balance(who)) - } -} - -impl, I: Instance> DustCollector for Module { - fn is_dust(who: &T::AccountId) -> bool { - let total = Self::total_balance(who); - - total < T::ExistentialDeposit::get() || total.is_zero() + Ok(()) + } } - fn collect(who: &T::AccountId) { - let dropped = Self::total_balance(who); - - if !dropped.is_zero() { - T::DustRemoval::on_unbalanced(NegativeImbalance::new(dropped)); - if let Err(e) = >::dec_providers(who) { - log::error!("Logic error: Unexpected {:?}", e); - } - Self::deposit_event(RawEvent::DustLost(who.clone(), dropped)); - } + pub fn migrate(old_pallet_name: &[u8], new_pallet_name: &[u8]) { + // Balances: Instance0DarwiniaBalances + // Kton: Instance1DarwiniaBalances + frame_support::migration::move_pallet(old_pallet_name, new_pallet_name); } } diff --git a/frame/balances/src/tests.rs b/frame/balances/src/tests.rs index 885937e476..3858f6d80e 100644 --- a/frame/balances/src/tests.rs +++ b/frame/balances/src/tests.rs @@ -21,14 +21,6 @@ #[macro_export] macro_rules! decl_tests { ($test:ty, $ext_builder:ty, $existential_deposit:expr) => { - // --- substrate --- - use frame_support::{ - assert_err, assert_noop, assert_storage_noop, - traits::{Currency, ExistenceRequirement::AllowDeath, ReservableCurrency}, - }; - use pallet_transaction_payment::{ChargeTransactionPayment, Multiplier}; - use sp_runtime::{FixedPointNumber, traits::{SignedExtension, BadOrigin}}; - pub const CALL: &<$test as frame_system::Config>::Call = &Call::Ring(darwinia_balances::Call::transfer(0, 0)); @@ -65,7 +57,7 @@ macro_rules! decl_tests { assert_eq!(Ring::free_balance(1), 10); Ring::set_lock(ID_1, &1, LockFor::Common { amount: 9 }, WithdrawReasons::all()); assert_noop!( - >::transfer(&1, &2, 5, AllowDeath), + >::transfer(&1, &2, 5, ExistenceRequirement::AllowDeath), RingError::LiquidityRestrictions ); }); @@ -79,7 +71,7 @@ macro_rules! decl_tests { .build() .execute_with(|| { assert_eq!(Ring::free_balance(1), 10); - assert_ok!(>::transfer(&1, &2, 10, AllowDeath)); + assert_ok!(>::transfer(&1, &2, 10, ExistenceRequirement::AllowDeath)); // Check that the account is dead. assert!(!>::contains_key(&1)); }); @@ -93,7 +85,7 @@ macro_rules! decl_tests { .build() .execute_with(|| { Ring::set_lock(ID_1, &1, LockFor::Common { amount: 5 }, WithdrawReasons::all()); - assert_ok!(>::transfer(&1, &2, 1, AllowDeath)); + assert_ok!(>::transfer(&1, &2, 1, ExistenceRequirement::AllowDeath)); }); } @@ -113,7 +105,7 @@ macro_rules! decl_tests { WithdrawReasons::all(), ); Ring::remove_lock(ID_1, &1); - assert_ok!(>::transfer(&1, &2, 1, AllowDeath)); + assert_ok!(>::transfer(&1, &2, 1, ExistenceRequirement::AllowDeath)); }); } @@ -133,7 +125,7 @@ macro_rules! decl_tests { WithdrawReasons::all(), ); Ring::set_lock(ID_1, &1, LockFor::Common { amount: 5 }, WithdrawReasons::all()); - assert_ok!(>::transfer(&1, &2, 1, AllowDeath)); + assert_ok!(>::transfer(&1, &2, 1, ExistenceRequirement::AllowDeath)); }); } @@ -146,7 +138,7 @@ macro_rules! decl_tests { .execute_with(|| { Ring::set_lock(ID_1, &1, LockFor::Common { amount: 5 }, WithdrawReasons::all()); Ring::set_lock(ID_2, &1, LockFor::Common { amount: 5 }, WithdrawReasons::all()); - assert_ok!(>::transfer(&1, &2, 1, AllowDeath)); + assert_ok!(>::transfer(&1, &2, 1, ExistenceRequirement::AllowDeath)); }); } @@ -166,7 +158,7 @@ macro_rules! decl_tests { WithdrawReasons::empty(), ); Ring::set_lock(ID_2, &1, LockFor::Common { amount: 0 }, WithdrawReasons::all()); - assert_ok!(>::transfer(&1, &2, 1, AllowDeath)); + assert_ok!(>::transfer(&1, &2, 1, ExistenceRequirement::AllowDeath)); }); } @@ -179,17 +171,17 @@ macro_rules! decl_tests { .execute_with(|| { Ring::set_lock(ID_1, &1, LockFor::Common { amount: 5 }, WithdrawReasons::all()); assert_noop!( - >::transfer(&1, &2, 6, AllowDeath), + >::transfer(&1, &2, 6, ExistenceRequirement::AllowDeath), RingError::LiquidityRestrictions ); assert_ok!(Ring::extend_lock(ID_1, &1, 2, WithdrawReasons::all())); assert_noop!( - >::transfer(&1, &2, 6, AllowDeath), + >::transfer(&1, &2, 6, ExistenceRequirement::AllowDeath), RingError::LiquidityRestrictions ); assert_ok!(Ring::extend_lock(ID_1, &1, 8, WithdrawReasons::all())); assert_noop!( - >::transfer(&1, &2, 3, AllowDeath), + >::transfer(&1, &2, 3, ExistenceRequirement::AllowDeath), RingError::LiquidityRestrictions ); }); @@ -210,7 +202,7 @@ macro_rules! decl_tests { WithdrawReasons::RESERVE, ); assert_noop!( - >::transfer(&1, &2, 1, AllowDeath), + >::transfer(&1, &2, 1, ExistenceRequirement::AllowDeath), RingError::LiquidityRestrictions ); assert_noop!( @@ -244,7 +236,7 @@ macro_rules! decl_tests { LockFor::Common { amount: 10 }, WithdrawReasons::TRANSACTION_PAYMENT, ); - assert_ok!(>::transfer(&1, &2, 1, AllowDeath)); + assert_ok!(>::transfer(&1, &2, 1, ExistenceRequirement::AllowDeath)); assert_ok!(>::reserve(&1, 1)); assert!( as SignedExtension>::pre_dispatch( @@ -278,18 +270,18 @@ macro_rules! decl_tests { .execute_with(|| { Ring::set_lock(ID_1, &1, LockFor::Common { amount: 10 }, WithdrawReasons::all()); assert_noop!( - >::transfer(&1, &2, 6, AllowDeath), + >::transfer(&1, &2, 6, ExistenceRequirement::AllowDeath), RingError::LiquidityRestrictions ); Ring::extend_lock(ID_1, &1, 10, WithdrawReasons::all()).unwrap(); assert_noop!( - >::transfer(&1, &2, 6, AllowDeath), + >::transfer(&1, &2, 6, ExistenceRequirement::AllowDeath), RingError::LiquidityRestrictions ); System::set_block_number(2); Ring::extend_lock(ID_1, &1, 10, WithdrawReasons::all()).unwrap(); assert_noop!( - >::transfer(&1, &2, 3, AllowDeath), + >::transfer(&1, &2, 3, ExistenceRequirement::AllowDeath), RingError::LiquidityRestrictions ); }); @@ -309,17 +301,17 @@ macro_rules! decl_tests { WithdrawReasons::TRANSFER, ); assert_noop!( - >::transfer(&1, &2, 6, AllowDeath), + >::transfer(&1, &2, 6, ExistenceRequirement::AllowDeath), RingError::LiquidityRestrictions ); Ring::extend_lock(ID_1, &1, 10, WithdrawReasons::empty()).unwrap(); assert_noop!( - >::transfer(&1, &2, 6, AllowDeath), + >::transfer(&1, &2, 6, ExistenceRequirement::AllowDeath), RingError::LiquidityRestrictions ); Ring::extend_lock(ID_1, &1, 10, WithdrawReasons::RESERVE).unwrap(); assert_noop!( - >::transfer(&1, &2, 6, AllowDeath), + >::transfer(&1, &2, 6, ExistenceRequirement::AllowDeath), RingError::LiquidityRestrictions ); }); @@ -548,7 +540,7 @@ macro_rules! decl_tests { let _ = Ring::deposit_creating(&1, 110); let _ = Ring::deposit_creating(&2, 1); assert_ok!(Ring::reserve(&1, 110)); - assert_ok!(Ring::repatriate_reserved(&1, &2, 41, Status::Free), 0); + assert_ok!(Ring::repatriate_reserved(&1, &2, 41, BalanceStatus::Free), 0); assert_eq!(Ring::reserved_balance(1), 69); assert_eq!(Ring::free_balance(1), 0); assert_eq!(Ring::reserved_balance(2), 0); @@ -562,7 +554,7 @@ macro_rules! decl_tests { let _ = Ring::deposit_creating(&1, 110); let _ = Ring::deposit_creating(&2, 1); assert_ok!(Ring::reserve(&1, 110)); - assert_ok!(Ring::repatriate_reserved(&1, &2, 41, Status::Reserved), 0); + assert_ok!(Ring::repatriate_reserved(&1, &2, 41, BalanceStatus::Reserved), 0); assert_eq!(Ring::reserved_balance(1), 69); assert_eq!(Ring::free_balance(1), 0); assert_eq!(Ring::reserved_balance(2), 41); @@ -576,7 +568,7 @@ macro_rules! decl_tests { let _ = Ring::deposit_creating(&1, 111); assert_ok!(Ring::reserve(&1, 111)); assert_noop!( - Ring::repatriate_reserved(&1, &2, 42, Status::Free), + Ring::repatriate_reserved(&1, &2, 42, BalanceStatus::Free), RingError::DeadAccount ); }); @@ -588,7 +580,7 @@ macro_rules! decl_tests { let _ = Ring::deposit_creating(&1, 110); let _ = Ring::deposit_creating(&2, 1); assert_ok!(Ring::reserve(&1, 41)); - assert_ok!(Ring::repatriate_reserved(&1, &2, 69, Status::Free), 28); + assert_ok!(Ring::repatriate_reserved(&1, &2, 69, BalanceStatus::Free), 28); assert_eq!(Ring::reserved_balance(1), 0); assert_eq!(Ring::free_balance(1), 69); assert_eq!(Ring::reserved_balance(2), 0); @@ -785,7 +777,7 @@ macro_rules! decl_tests { assert_eq!( last_event(), - Event::darwinia_balances_Instance0(RawEvent::Reserved(1, 10)), + Event::darwinia_balances_Instance0(darwinia_balances::Event::Reserved(1, 10)), ); System::set_block_number(3); @@ -793,7 +785,7 @@ macro_rules! decl_tests { assert_eq!( last_event(), - Event::darwinia_balances_Instance0(RawEvent::Unreserved(1, 5)), + Event::darwinia_balances_Instance0(darwinia_balances::Event::Unreserved(1, 5)), ); System::set_block_number(4); @@ -802,7 +794,7 @@ macro_rules! decl_tests { // should only unreserve 5 assert_eq!( last_event(), - Event::darwinia_balances_Instance0(RawEvent::Unreserved(1, 5)), + Event::darwinia_balances_Instance0(darwinia_balances::Event::Unreserved(1, 5)), ); }); } @@ -819,8 +811,8 @@ macro_rules! decl_tests { events(), [ Event::frame_system(frame_system::Event::NewAccount(1)), - Event::darwinia_balances_Instance0(RawEvent::Endowed(1, 100)), - Event::darwinia_balances_Instance0(RawEvent::BalanceSet(1, 100, 0)), + Event::darwinia_balances_Instance0(darwinia_balances::Event::Endowed(1, 100)), + Event::darwinia_balances_Instance0(darwinia_balances::Event::BalanceSet(1, 100, 0)), ] ); @@ -829,7 +821,7 @@ macro_rules! decl_tests { assert_eq!( events(), [ - Event::darwinia_balances_Instance0(RawEvent::DustLost(1, 99)), + Event::darwinia_balances_Instance0(darwinia_balances::Event::DustLost(1, 99)), Event::frame_system(frame_system::Event::KilledAccount(1)) ] ); @@ -848,8 +840,8 @@ macro_rules! decl_tests { events(), [ Event::frame_system(frame_system::Event::NewAccount(1)), - Event::darwinia_balances_Instance0(RawEvent::Endowed(1, 100)), - Event::darwinia_balances_Instance0(RawEvent::BalanceSet(1, 100, 0)), + Event::darwinia_balances_Instance0(darwinia_balances::Event::Endowed(1, 100)), + Event::darwinia_balances_Instance0(darwinia_balances::Event::BalanceSet(1, 100, 0)), ] ); @@ -1053,7 +1045,7 @@ macro_rules! decl_tests { // Slash Reserve assert_storage_noop!(assert_eq!(Ring::slash_reserved(&1337, 42).1, 42)); // Repatriate Reserve - assert_noop!(Ring::repatriate_reserved(&1337, &1338, 42, Status::Free), RingError::DeadAccount); + assert_noop!(Ring::repatriate_reserved(&1337, &1338, 42, BalanceStatus::Free), RingError::DeadAccount); // Slash assert_storage_noop!(assert_eq!(Ring::slash(&1337, 42).1, 42)); }); diff --git a/frame/balances/src/tests_local.rs b/frame/balances/src/tests_local.rs index 8af440876b..a6364edaf2 100644 --- a/frame/balances/src/tests_local.rs +++ b/frame/balances/src/tests_local.rs @@ -22,20 +22,25 @@ use codec::{Decode, Encode}; // --- substrate --- use frame_support::{ - assert_ok, parameter_types, - traits::StorageMapShim, + assert_err, assert_noop, assert_ok, assert_storage_noop, parameter_types, + traits::{ + BalanceStatus, Currency, ExistenceRequirement, GenesisBuild, LockIdentifier, + ReservableCurrency, StorageMapShim, WithdrawReasons, + }, weights::{DispatchInfo, IdentityFee, Weight}, + StorageValue, }; use frame_system::{mocking::*, RawOrigin}; -use pallet_transaction_payment::CurrencyAdapter; +use pallet_transaction_payment::{ChargeTransactionPayment, CurrencyAdapter, Multiplier}; use sp_core::H256; use sp_runtime::{ testing::Header, - traits::{BlakeTwo256, IdentityLookup}, - RuntimeDebug, + traits::{BadOrigin, BlakeTwo256, IdentityLookup, SignedExtension, Zero}, + FixedPointNumber, RuntimeDebug, }; // --- darwinia --- -use crate::{self as darwinia_balances, *}; +use crate::{self as darwinia_balances, pallet::*}; +use darwinia_support::balance::*; type Block = MockBlock; type UncheckedExtrinsic = MockUncheckedExtrinsic; @@ -200,8 +205,10 @@ fn emit_events_with_no_existential_deposit_suicide_with_dust() { events(), [ Event::frame_system(frame_system::Event::NewAccount(1)), - Event::darwinia_balances_Instance0(RawEvent::Endowed(1, 100)), - Event::darwinia_balances_Instance0(RawEvent::BalanceSet(1, 100, 0)), + Event::darwinia_balances_Instance0(darwinia_balances::Event::Endowed(1, 100)), + Event::darwinia_balances_Instance0(darwinia_balances::Event::BalanceSet( + 1, 100, 0 + )), ] ); @@ -215,7 +222,7 @@ fn emit_events_with_no_existential_deposit_suicide_with_dust() { assert_eq!( events(), [ - Event::darwinia_balances_Instance0(RawEvent::DustLost(1, 1)), + Event::darwinia_balances_Instance0(darwinia_balances::Event::DustLost(1, 1)), Event::frame_system(frame_system::Event::KilledAccount(1)) ] ); @@ -224,7 +231,7 @@ fn emit_events_with_no_existential_deposit_suicide_with_dust() { #[test] fn dust_collector_should_work() { - type AnotherBalance = Module; + type AnotherBalance = Pallet; ::default() .existential_deposit(100) @@ -236,8 +243,10 @@ fn dust_collector_should_work() { events(), [ Event::frame_system(frame_system::Event::NewAccount(1)), - Event::darwinia_balances_Instance0(RawEvent::Endowed(1, 100)), - Event::darwinia_balances_Instance0(RawEvent::BalanceSet(1, 100, 0)), + Event::darwinia_balances_Instance0(darwinia_balances::Event::Endowed(1, 100)), + Event::darwinia_balances_Instance0(darwinia_balances::Event::BalanceSet( + 1, 100, 0 + )), ] ); @@ -246,7 +255,7 @@ fn dust_collector_should_work() { assert_eq!( events(), [ - Event::darwinia_balances_Instance0(RawEvent::DustLost(1, 99)), + Event::darwinia_balances_Instance0(darwinia_balances::Event::DustLost(1, 99)), Event::frame_system(frame_system::Event::KilledAccount(1)) ] ); @@ -265,10 +274,14 @@ fn dust_collector_should_work() { events(), [ Event::frame_system(frame_system::Event::NewAccount(1)), - Event::darwinia_balances_Instance0(RawEvent::Endowed(1, 100)), - Event::darwinia_balances_Instance0(RawEvent::BalanceSet(1, 100, 0)), - Event::darwinia_balances_Instance1(RawEvent::Endowed(1, 100)), - Event::darwinia_balances_Instance1(RawEvent::BalanceSet(1, 100, 0)), + Event::darwinia_balances_Instance0(darwinia_balances::Event::Endowed(1, 100)), + Event::darwinia_balances_Instance0(darwinia_balances::Event::BalanceSet( + 1, 100, 0 + )), + Event::darwinia_balances_Instance1(darwinia_balances::Event::Endowed(1, 100)), + Event::darwinia_balances_Instance1(darwinia_balances::Event::BalanceSet( + 1, 100, 0 + )), ] ); @@ -281,8 +294,8 @@ fn dust_collector_should_work() { assert_eq!( events(), [ - Event::darwinia_balances_Instance0(RawEvent::DustLost(1, 99)), - Event::darwinia_balances_Instance1(RawEvent::DustLost(1, 99)), + Event::darwinia_balances_Instance0(darwinia_balances::Event::DustLost(1, 99)), + Event::darwinia_balances_Instance1(darwinia_balances::Event::DustLost(1, 99)), Event::frame_system(frame_system::Event::KilledAccount(1)), ] ); diff --git a/frame/bridge/ethereum/backing/src/lib.rs b/frame/bridge/ethereum/backing/src/lib.rs index 9beca84c84..85064128ce 100644 --- a/frame/bridge/ethereum/backing/src/lib.rs +++ b/frame/bridge/ethereum/backing/src/lib.rs @@ -76,7 +76,7 @@ use sp_std::{convert::TryFrom, prelude::*}; // --- darwinia --- use darwinia_relay_primitives::relay_authorities::*; use darwinia_support::{ - balance::lock::*, + balance::*, traits::{EthereumReceipt, OnDepositRedeem}, }; use ethereum_primitives::{ diff --git a/frame/bridge/ethereum/backing/src/test_with_linear_relay.rs b/frame/bridge/ethereum/backing/src/test_with_linear_relay.rs index 278b2ec473..0d5e7bde54 100644 --- a/frame/bridge/ethereum/backing/src/test_with_linear_relay.rs +++ b/frame/bridge/ethereum/backing/src/test_with_linear_relay.rs @@ -25,7 +25,7 @@ use sp_runtime::{traits::Dispatchable, AccountId32}; // --- darwinia --- use crate::*; use darwinia_staking::{RewardDestination, StakingBalance, StakingLedger, TimeDepositItem}; -use darwinia_support::balance::lock::StakingLock; +use darwinia_support::balance::*; use ethereum_primitives::{ header::EthereumHeader, receipt::EthereumReceiptProof, EthereumNetworkType, }; diff --git a/frame/bridge/ethereum/backing/src/test_with_relay.rs b/frame/bridge/ethereum/backing/src/test_with_relay.rs index fd3b15927a..a1d818ee38 100644 --- a/frame/bridge/ethereum/backing/src/test_with_relay.rs +++ b/frame/bridge/ethereum/backing/src/test_with_relay.rs @@ -27,7 +27,7 @@ use crate::*; use darwinia_ethereum_relay::{EthereumRelayHeaderParcel, EthereumRelayProofs, MMRProof}; use darwinia_relay_primitives::relayer_game::*; use darwinia_staking::{RewardDestination, StakingBalance, StakingLedger, TimeDepositItem}; -use darwinia_support::balance::lock::StakingLock; +use darwinia_support::balance::*; use ethereum_primitives::{ header::EthereumHeader, receipt::EthereumReceiptProof, EthereumBlockNumber, EthereumNetworkType, }; diff --git a/frame/bridge/ethereum/issuing/src/lib.rs b/frame/bridge/ethereum/issuing/src/lib.rs index 4ea7915f5f..abbb9acaa8 100644 --- a/frame/bridge/ethereum/issuing/src/lib.rs +++ b/frame/bridge/ethereum/issuing/src/lib.rs @@ -58,7 +58,7 @@ use darwinia_ethereum_issuing_contract::{ }; use darwinia_evm::{GasWeightMapping, IssuingHandler}; use darwinia_relay_primitives::relay_authorities::*; -use darwinia_support::{balance::lock::*, traits::EthereumReceipt}; +use darwinia_support::{balance::*, traits::EthereumReceipt}; use dp_evm::CallOrCreateInfo; use ethereum_primitives::{ receipt::{EthereumTransactionIndex, LogEntry}, diff --git a/frame/bridge/ethereum/linear-relay/src/lib.rs b/frame/bridge/ethereum/linear-relay/src/lib.rs index 60dc91ca72..cb3cb8940b 100644 --- a/frame/bridge/ethereum/linear-relay/src/lib.rs +++ b/frame/bridge/ethereum/linear-relay/src/lib.rs @@ -86,9 +86,7 @@ use sp_runtime::{ }; use sp_std::prelude::*; // --- darwinia --- -use darwinia_support::{ - balance::lock::LockableCurrency, traits::EthereumReceipt as EthereumReceiptT, -}; +use darwinia_support::{balance::*, traits::EthereumReceipt as EthereumReceiptT}; use ethereum_primitives::{ ethashproof::EthashProof, header::EthereumHeader, diff --git a/frame/bridge/ethereum/relay/src/lib.rs b/frame/bridge/ethereum/relay/src/lib.rs index 540bde1376..a82094b16e 100644 --- a/frame/bridge/ethereum/relay/src/lib.rs +++ b/frame/bridge/ethereum/relay/src/lib.rs @@ -73,9 +73,7 @@ use sp_std::{convert::From, marker::PhantomData, prelude::*}; // --- darwinia --- use crate::mmr::{leaf_index_to_mmr_size, leaf_index_to_pos, MMRMerge, MerkleProof}; use darwinia_relay_primitives::relayer_game::*; -use darwinia_support::{ - balance::lock::LockableCurrency, traits::EthereumReceipt as EthereumReceiptT, -}; +use darwinia_support::{balance::*, traits::EthereumReceipt as EthereumReceiptT}; use ethereum_primitives::{ ethashproof::EthashProof, header::EthereumHeader, diff --git a/frame/bridge/relay-authorities/src/lib.rs b/frame/bridge/relay-authorities/src/lib.rs index 499e3cb1cc..ff5ae76eb5 100644 --- a/frame/bridge/relay-authorities/src/lib.rs +++ b/frame/bridge/relay-authorities/src/lib.rs @@ -59,7 +59,7 @@ use codec::Encode; // --- substrate --- use frame_support::{ decl_error, decl_event, decl_module, decl_storage, ensure, - traits::{Currency, EnsureOrigin, Get, LockIdentifier}, + traits::{Currency, EnsureOrigin, Get, LockIdentifier, WithdrawReasons}, weights::Weight, StorageValue, }; @@ -73,7 +73,7 @@ use sp_std::borrow::ToOwned; use sp_std::prelude::*; // --- darwinia --- use darwinia_relay_primitives::relay_authorities::*; -use darwinia_support::balance::lock::*; +use darwinia_support::balance::*; use types::*; pub trait Config: frame_system::Config { diff --git a/frame/bridge/relay-authorities/src/mock.rs b/frame/bridge/relay-authorities/src/mock.rs index 2e02957a95..5211c9cf0e 100644 --- a/frame/bridge/relay-authorities/src/mock.rs +++ b/frame/bridge/relay-authorities/src/mock.rs @@ -21,7 +21,7 @@ // --- crates --- use codec::{Decode, Encode}; // --- substrate --- -use frame_support::traits::OnInitialize; +use frame_support::traits::{GenesisBuild, OnInitialize}; use frame_system::{mocking::*, EnsureRoot}; use sp_core::H256; use sp_io::{hashing, TestExternalities}; diff --git a/frame/bridge/relayer-game/src/lib.rs b/frame/bridge/relayer-game/src/lib.rs index 4f1f92d3bd..eb698bcd35 100644 --- a/frame/bridge/relayer-game/src/lib.rs +++ b/frame/bridge/relayer-game/src/lib.rs @@ -59,7 +59,7 @@ mod types { // --- substrate --- use frame_support::{ decl_error, decl_module, decl_storage, ensure, - traits::{Currency, Get, OnUnbalanced}, + traits::{Currency, Get, LockIdentifier, OnUnbalanced, WithdrawReasons}, }; use sp_runtime::{ traits::{Saturating, Zero}, @@ -70,7 +70,7 @@ use sp_std::borrow::ToOwned; use sp_std::{collections::btree_map::BTreeMap, prelude::*}; // --- darwinia --- use darwinia_relay_primitives::relayer_game::*; -use darwinia_support::balance::lock::*; +use darwinia_support::balance::*; use types::*; pub trait Config: frame_system::Config { diff --git a/frame/bridge/relayer-game/src/mock.rs b/frame/bridge/relayer-game/src/mock.rs index 361eac50ae..81e7344d04 100644 --- a/frame/bridge/relayer-game/src/mock.rs +++ b/frame/bridge/relayer-game/src/mock.rs @@ -241,7 +241,7 @@ use std::time::Instant; // --- crates --- use codec::{Decode, Encode}; // --- substrate --- -use frame_support::traits::OnFinalize; +use frame_support::traits::{GenesisBuild, OnFinalize}; use frame_system::mocking::*; use sp_runtime::RuntimeDebug; // --- darwinia --- diff --git a/frame/bridge/relayer-game/src/tests.rs b/frame/bridge/relayer-game/src/tests.rs index dd72aeae2b..b4e4b8e4b6 100644 --- a/frame/bridge/relayer-game/src/tests.rs +++ b/frame/bridge/relayer-game/src/tests.rs @@ -23,7 +23,7 @@ use crate::{ mock::{mock_relay::*, BlockNumber, *}, *, }; -use darwinia_support::balance::lock::*; +use darwinia_support::balance::*; // #[test] // fn events_should_work() { diff --git a/frame/claims/src/lib.rs b/frame/claims/src/lib.rs index f89c6e662e..924a0cadbf 100644 --- a/frame/claims/src/lib.rs +++ b/frame/claims/src/lib.rs @@ -62,7 +62,7 @@ use sp_runtime::{ }; use sp_std::prelude::*; // --- darwinia --- -use darwinia_support::balance::lock::*; +use darwinia_support::balance::*; use types::*; pub trait Config: frame_system::Config { @@ -583,7 +583,7 @@ mod tests { // --- substrate --- use frame_support::{ assert_err, assert_noop, assert_ok, dispatch::DispatchError::BadOrigin, - ord_parameter_types, parameter_types, + ord_parameter_types, parameter_types, traits::GenesisBuild, }; use frame_system::mocking::*; use sp_core::H256; diff --git a/frame/democracy/src/lib.rs b/frame/democracy/src/lib.rs index cac954c48f..7351b2d96e 100644 --- a/frame/democracy/src/lib.rs +++ b/frame/democracy/src/lib.rs @@ -148,7 +148,8 @@ use frame_support::{ ensure, traits::{ schedule::{DispatchTime, Named as ScheduleNamed}, - BalanceStatus, Currency, EnsureOrigin, Get, OnUnbalanced, ReservableCurrency, + BalanceStatus, Currency, EnsureOrigin, Get, LockIdentifier, OnUnbalanced, + ReservableCurrency, WithdrawReasons, }, weights::{DispatchClass, Pays, Weight}, Parameter, @@ -160,7 +161,7 @@ use sp_runtime::{ }; use sp_std::prelude::*; // --- darwinia --- -use darwinia_support::balance::lock::*; +use darwinia_support::balance::*; mod conviction; mod types; diff --git a/frame/democracy/src/tests.rs b/frame/democracy/src/tests.rs index 77e84f982f..363a579930 100644 --- a/frame/democracy/src/tests.rs +++ b/frame/democracy/src/tests.rs @@ -5,7 +5,7 @@ use codec::Encode; // --- substrate --- use frame_support::{ assert_noop, assert_ok, ord_parameter_types, - traits::{Contains, Filter, OnInitialize}, + traits::{Contains, Filter, GenesisBuild, OnInitialize}, weights::Weight, }; use frame_system::{mocking::*, EnsureRoot, EnsureSignedBy}; diff --git a/frame/democracy/src/tests/lock_voting.rs b/frame/democracy/src/tests/lock_voting.rs index 5674a44c12..185c704974 100644 --- a/frame/democracy/src/tests/lock_voting.rs +++ b/frame/democracy/src/tests/lock_voting.rs @@ -27,7 +27,7 @@ fn the_lock(amount: u64) -> BalanceLock { BalanceLock { id: DEMOCRACY_ID, lock_for: LockFor::Common { amount }, - lock_reasons: darwinia_support::balance::lock::LockReasons::Misc, + lock_reasons: darwinia_support::balance::LockReasons::Misc, } } diff --git a/frame/dvm/src/mock.rs b/frame/dvm/src/mock.rs index 5d5a93d1b9..ee235e7d65 100644 --- a/frame/dvm/src/mock.rs +++ b/frame/dvm/src/mock.rs @@ -24,7 +24,7 @@ use crate::{ use codec::{Decode, Encode}; use darwinia_evm::{AddressMapping, EnsureAddressTruncated, FeeCalculator, IssuingHandler}; use ethereum::{TransactionAction, TransactionSignature}; -use frame_support::ConsensusEngineId; +use frame_support::{traits::GenesisBuild, ConsensusEngineId}; use frame_system::mocking::*; use rlp::*; use sp_core::{H160, H256, U256}; diff --git a/frame/elections-phragmen/src/lib.rs b/frame/elections-phragmen/src/lib.rs index 45a6d1e545..fd8c035964 100644 --- a/frame/elections-phragmen/src/lib.rs +++ b/frame/elections-phragmen/src/lib.rs @@ -89,7 +89,7 @@ use frame_support::{ storage::{IterableStorageMap, StorageMap}, traits::{ ChangeMembers, Contains, ContainsLengthBound, Currency, CurrencyToVote, Get, - InitializeMembers, OnUnbalanced, ReservableCurrency, + InitializeMembers, LockIdentifier, OnUnbalanced, ReservableCurrency, WithdrawReasons, }, weights::Weight, }; @@ -101,7 +101,7 @@ use sp_runtime::{ }; use sp_std::{cmp::Ordering, prelude::*}; -use darwinia_support::balance::lock::*; +use darwinia_support::balance::*; pub mod weights; pub use weights::WeightInfo; diff --git a/frame/evm/src/tests.rs b/frame/evm/src/tests.rs index 947f24f1e4..537b1ea1ba 100644 --- a/frame/evm/src/tests.rs +++ b/frame/evm/src/tests.rs @@ -1,7 +1,7 @@ #![cfg(test)] use crate::{self as darwinia_evm, *}; -use frame_support::assert_ok; +use frame_support::{assert_ok, traits::GenesisBuild}; use frame_system::mocking::*; use sp_core::{Blake2Hasher, H256}; use sp_runtime::{ diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index ae4bf55b70..d03ff7a68a 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -419,7 +419,7 @@ use frame_support::{ storage::IterableStorageMap, traits::{ Currency, EnsureOrigin, EstimateNextNewSession, ExistenceRequirement::KeepAlive, Get, - Imbalance, OnUnbalanced, UnixTime, + Imbalance, LockIdentifier, OnUnbalanced, UnixTime, WithdrawReasons, }, weights::{ constants::{WEIGHT_PER_MICROS, WEIGHT_PER_NANOS}, @@ -447,7 +447,7 @@ use sp_std::{collections::btree_map::BTreeMap, convert::TryInto, marker::Phantom // --- darwinia --- use darwinia_staking_rpc_runtime_api::RuntimeDispatchInfo; use darwinia_support::{ - balance::lock::*, + balance::*, impl_rpc, traits::{OnDepositRedeem, OnUnbalancedKton}, }; diff --git a/frame/staking/src/mock.rs b/frame/staking/src/mock.rs index 16402fe313..c51e3494f1 100644 --- a/frame/staking/src/mock.rs +++ b/frame/staking/src/mock.rs @@ -27,7 +27,9 @@ use frame_election_provider_support::onchain; use frame_support::{ assert_ok, parameter_types, storage::IterableStorageMap, - traits::{Currency, FindAuthor, Get, OnFinalize, OnInitialize, OneSessionHandler}, + traits::{ + Currency, FindAuthor, GenesisBuild, Get, OnFinalize, OnInitialize, OneSessionHandler, + }, weights::constants::RocksDbWeight, StorageValue, }; diff --git a/frame/staking/src/substrate_tests.rs b/frame/staking/src/substrate_tests.rs index 35ddcb3922..6e4252d240 100644 --- a/frame/staking/src/substrate_tests.rs +++ b/frame/staking/src/substrate_tests.rs @@ -43,7 +43,7 @@ use crate::{ mock::{AccountId, Balance, *}, *, }; -use darwinia_support::balance::lock::*; +use darwinia_support::balance::*; #[test] fn force_unstake_works() { diff --git a/frame/support/src/lib.rs b/frame/support/src/lib.rs index 5c40ee6f9f..ad01df50b2 100644 --- a/frame/support/src/lib.rs +++ b/frame/support/src/lib.rs @@ -24,15 +24,10 @@ pub mod testing; pub mod traits; pub mod balance { - pub mod lock { - pub use crate::structs::{BalanceLock, LockFor, LockReasons, StakingLock, Unbonding}; - pub use crate::traits::{ - LockIdentifier, LockableCurrency, VestingSchedule, WithdrawReasons, - }; - } - - pub use crate::structs::FrozenBalance; - pub use crate::traits::{BalanceInfo, DustCollector, OnUnbalancedKton}; + pub use crate::structs::{ + BalanceLock, FrozenBalance, LockFor, LockReasons, StakingLock, Unbonding, + }; + pub use crate::traits::{BalanceInfo, DustCollector, LockableCurrency, OnUnbalancedKton}; } pub mod evm { diff --git a/frame/support/src/macros.rs b/frame/support/src/macros.rs index a107f766c1..a3625ef464 100644 --- a/frame/support/src/macros.rs +++ b/frame/support/src/macros.rs @@ -70,7 +70,7 @@ macro_rules! impl_account_data { fn usable( &self, - reasons: darwinia_support::balance::lock::LockReasons, + reasons: darwinia_support::balance::LockReasons, frozen_balance: darwinia_support::balance::FrozenBalance<$btype>, ) -> $btype { self.free.saturating_sub(frozen_balance.frozen_for(reasons)) @@ -98,7 +98,7 @@ macro_rules! impl_account_data { fn usable( &self, - reasons: darwinia_support::balance::lock::LockReasons, + reasons: darwinia_support::balance::LockReasons, frozen_balance: darwinia_support::balance::FrozenBalance<$btype>, ) -> $btype { self.free_kton.saturating_sub(frozen_balance.frozen_for(reasons)) diff --git a/frame/support/src/structs.rs b/frame/support/src/structs.rs index abf6462cb7..3ae66c73ae 100644 --- a/frame/support/src/structs.rs +++ b/frame/support/src/structs.rs @@ -20,10 +20,9 @@ use codec::{Decode, Encode}; use num_traits::Zero; // --- substrate --- +use frame_support::traits::{LockIdentifier, WithdrawReasons}; use sp_runtime::{traits::AtLeast32BitUnsigned, RuntimeDebug}; use sp_std::{ops::BitOr, prelude::*}; -// --- darwinia --- -use crate::balance::lock::{LockIdentifier, WithdrawReasons}; /// Frozen balance information for an account. pub struct FrozenBalance { diff --git a/frame/support/src/traits.rs b/frame/support/src/traits.rs index 6477b3ca5e..7682ff0581 100644 --- a/frame/support/src/traits.rs +++ b/frame/support/src/traits.rs @@ -16,22 +16,17 @@ // You should have received a copy of the GNU General Public License // along with Darwinia. If not, see . -pub use frame_support::traits::{LockIdentifier, VestingSchedule, WithdrawReasons}; - // --- core --- use core::fmt::Debug; // --- crates --- use codec::FullCodec; use impl_trait_for_tuples::impl_for_tuples; // --- substrate --- -use frame_support::traits::{Currency, Get, TryDrop}; +use frame_support::traits::{Currency, Get, LockIdentifier, TryDrop, WithdrawReasons}; use sp_runtime::{DispatchError, DispatchResult}; use sp_std::prelude::*; // --- darwinia --- -use crate::balance::{ - lock::{LockFor, LockReasons}, - FrozenBalance, -}; +use crate::structs::{FrozenBalance, LockFor, LockReasons}; use ethereum_primitives::receipt::EthereumTransactionIndex; pub trait BalanceInfo { diff --git a/frame/treasury/src/lib.rs b/frame/treasury/src/lib.rs index 74c0822f77..c4acb48e75 100644 --- a/frame/treasury/src/lib.rs +++ b/frame/treasury/src/lib.rs @@ -191,7 +191,7 @@ use sp_runtime::{ }; use sp_std::prelude::*; // --- darwinia --- -use darwinia_support::balance::{lock::LockableCurrency, OnUnbalancedKton}; +use darwinia_support::balance::*; use types::*; pub trait Config: frame_system::Config { diff --git a/frame/treasury/src/mock.rs b/frame/treasury/src/mock.rs index 0840498237..9f1549e5d8 100644 --- a/frame/treasury/src/mock.rs +++ b/frame/treasury/src/mock.rs @@ -25,6 +25,7 @@ mod treasury { } // --- substrate --- +use frame_support::traits::GenesisBuild; use frame_system::mocking::*; use sp_core::H256; use sp_runtime::{ diff --git a/frame/treasury/src/tests.rs b/frame/treasury/src/tests.rs index 238e9de045..eee4348a33 100644 --- a/frame/treasury/src/tests.rs +++ b/frame/treasury/src/tests.rs @@ -19,7 +19,10 @@ //! Tests for treasury. // --- substrate --- -use frame_support::{assert_noop, assert_ok, traits::OnInitialize}; +use frame_support::{ + assert_noop, assert_ok, + traits::{GenesisBuild, OnInitialize}, +}; use sp_runtime::traits::BlakeTwo256; // --- darwinia --- use crate::{self as darwinia_treasury, mock::*, *}; diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index b06d2a17ef..6eaa1601cc 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -30,7 +30,7 @@ pub mod weights; use codec::{Decode, Encode}; -use darwinia_support::balance::lock::*; +use darwinia_support::balance::*; use frame_support::traits::{ Currency, ExistenceRequirement, Get, LockIdentifier, VestingSchedule, WithdrawReasons, };