From ce9c9253933e6971fb22703da4fae4435da4b72e Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Thu, 28 Dec 2023 23:48:40 +0300 Subject: [PATCH 01/39] Move currencies implementation into separate mod --- frame/evm-balances/src/impl_currency.rs | 351 ++++++++++++++++++++++++ 1 file changed, 351 insertions(+) create mode 100644 frame/evm-balances/src/impl_currency.rs diff --git a/frame/evm-balances/src/impl_currency.rs b/frame/evm-balances/src/impl_currency.rs new file mode 100644 index 0000000000..eadfe7a6b2 --- /dev/null +++ b/frame/evm-balances/src/impl_currency.rs @@ -0,0 +1,351 @@ +//! Currency trait implementation. + +use super::*; + +impl, I: 'static> Currency<>::AccountId> for Pallet +where + T::Balance: MaybeSerializeDeserialize + Debug, +{ + type Balance = T::Balance; + type PositiveImbalance = PositiveImbalance; + type NegativeImbalance = NegativeImbalance; + + fn total_balance(who: &>::AccountId) -> Self::Balance { + Self::account(who).total() + } + + fn can_slash(who: &>::AccountId, value: Self::Balance) -> bool { + if value.is_zero() { + return true; + } + Self::free_balance(who) >= value + } + + fn total_issuance() -> Self::Balance { + TotalIssuance::::get() + } + + fn active_issuance() -> Self::Balance { + TotalIssuance::::get().saturating_sub(InactiveIssuance::::get()) + } + + fn deactivate(amount: Self::Balance) { + InactiveIssuance::::mutate(|b| b.saturating_accrue(amount)); + } + + fn reactivate(amount: Self::Balance) { + InactiveIssuance::::mutate(|b| b.saturating_reduce(amount)); + } + + fn minimum_balance() -> Self::Balance { + T::ExistentialDeposit::get() + } + + 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 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 free_balance(who: &>::AccountId) -> Self::Balance { + Self::account(who).free + } + + // We don't have any existing withdrawal restrictions like locked and reserved balance. + fn ensure_can_withdraw( + _who: &>::AccountId, + _amount: T::Balance, + _reasons: WithdrawReasons, + _new_balance: T::Balance, + ) -> DispatchResult { + Ok(()) + } + + fn transfer( + transactor: &>::AccountId, + dest: &>::AccountId, + value: Self::Balance, + existence_requirement: ExistenceRequirement, + ) -> DispatchResult { + if value.is_zero() || transactor == dest { + return Ok(()); + } + + Self::try_mutate_account_with_dust( + dest, + |to_account, _| -> Result, DispatchError> { + Self::try_mutate_account_with_dust( + transactor, + |from_account, _| -> DispatchResult { + from_account.free = from_account + .free + .checked_sub(&value) + .ok_or(Error::::InsufficientBalance)?; + + to_account.free = to_account + .free + .checked_add(&value) + .ok_or(ArithmeticError::Overflow)?; + + let ed = T::ExistentialDeposit::get(); + ensure!(to_account.total() >= ed, Error::::ExistentialDeposit); + + Self::ensure_can_withdraw( + transactor, + value, + WithdrawReasons::TRANSFER, + from_account.free, + ) + .map_err(|_| Error::::LiquidityRestrictions)?; + + let allow_death = existence_requirement == ExistenceRequirement::AllowDeath; + ensure!( + allow_death || from_account.total() >= ed, + Error::::KeepAlive + ); + + Ok(()) + }, + ) + .map(|(_, maybe_dust_cleaner)| maybe_dust_cleaner) + }, + )?; + + // Emit transfer event. + Self::deposit_event(Event::Transfer { + from: transactor.clone(), + to: dest.clone(), + amount: value, + }); + + Ok(()) + } + + /// 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: &>::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); + } + + for attempt in 0..2 { + match Self::try_mutate_account( + who, + |account, + _is_new| + -> Result<(Self::NegativeImbalance, Self::Balance), DispatchError> { + // 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.saturating_sub(T::ExistentialDeposit::get())), + }; + + let free_slash = cmp::min(account.free, best_value); + account.free -= free_slash; // Safe because of above check + + Ok(( + NegativeImbalance::new(free_slash), + value - free_slash, // Safe because value is gt or eq to total slashed + )) + }, + ) { + Ok((imbalance, not_slashed)) => { + Self::deposit_event(Event::Slashed { + who: who.clone(), + amount: value.saturating_sub(not_slashed), + }); + return (imbalance, not_slashed); + } + Err(_) => (), + } + } + + // 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: &>::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, Error::::DeadAccount); + account.free = account + .free + .checked_add(&value) + .ok_or(ArithmeticError::Overflow)?; + Self::deposit_event(Event::Deposit { + who: who.clone(), + amount: value, + }); + Ok(PositiveImbalance::new(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: &>::AccountId, + value: Self::Balance, + ) -> Self::PositiveImbalance { + if value.is_zero() { + return Self::PositiveImbalance::zero(); + } + + Self::try_mutate_account( + who, + |account, is_new| -> Result { + let ed = T::ExistentialDeposit::get(); + ensure!(value >= ed || !is_new, Error::::ExistentialDeposit); + + // defensive only: overflow should never happen, however in case it does, then this + // operation is a no-op. + account.free = match account.free.checked_add(&value) { + Some(x) => x, + None => return Ok(Self::PositiveImbalance::zero()), + }; + + Self::deposit_event(Event::Deposit { + who: who.clone(), + amount: value, + }); + Ok(PositiveImbalance::new(value)) + }, + ) + .unwrap_or_else(|_| Self::PositiveImbalance::zero()) + } + + /// Withdraw some free balance from an account, respecting existence requirements. + /// + /// Is a no-op if value to be withdrawn is zero. + fn withdraw( + who: &>::AccountId, + value: Self::Balance, + reasons: WithdrawReasons, + liveness: ExistenceRequirement, + ) -> result::Result { + if value.is_zero() { + return Ok(NegativeImbalance::zero()); + } + + Self::try_mutate_account( + who, + |account, _| -> Result { + let new_free_account = account + .free + .checked_sub(&value) + .ok_or(Error::::InsufficientBalance)?; + + // bail if we need to keep the account alive and this would kill it. + let ed = T::ExistentialDeposit::get(); + let would_be_dead = new_free_account < ed; + let would_kill = would_be_dead && account.free >= ed; + ensure!( + liveness == AllowDeath || !would_kill, + Error::::KeepAlive + ); + + Self::ensure_can_withdraw(who, value, reasons, new_free_account)?; + + account.free = new_free_account; + + Self::deposit_event(Event::Withdraw { + who: who.clone(), + amount: value, + }); + Ok(NegativeImbalance::new(value)) + }, + ) + } + + /// Force the new free balance of a target account `who` to some new value `balance`. + fn make_free_balance_be( + who: &>::AccountId, + value: Self::Balance, + ) -> SignedImbalance { + Self::try_mutate_account( + who, + |account, + is_new| + -> Result, DispatchError> { + let ed = T::ExistentialDeposit::get(); + let total = value; + // 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, Error::::ExistentialDeposit); + + let imbalance = if account.free <= value { + SignedImbalance::Positive(PositiveImbalance::new(value - account.free)) + } else { + SignedImbalance::Negative(NegativeImbalance::new(account.free - value)) + }; + account.free = value; + Self::deposit_event(Event::BalanceSet { + who: who.clone(), + free: account.free, + }); + Ok(imbalance) + }, + ) + .unwrap_or_else(|_| SignedImbalance::Positive(Self::PositiveImbalance::zero())) + } +} From 873e82a201547e36e8d4867829757c77a048a870 Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Fri, 29 Dec 2023 00:33:16 +0300 Subject: [PATCH 02/39] [substrate=apply] Deprecate Currency; introduce holds and freezing into fungible traits #12951 --- frame/evm-balances/src/lib.rs | 594 +++++++--------------------------- 1 file changed, 120 insertions(+), 474 deletions(-) diff --git a/frame/evm-balances/src/lib.rs b/frame/evm-balances/src/lib.rs index dbd5c744b0..75a8ab71e9 100644 --- a/frame/evm-balances/src/lib.rs +++ b/frame/evm-balances/src/lib.rs @@ -21,7 +21,10 @@ use sp_runtime::{ traits::{Bounded, CheckedAdd, CheckedSub, MaybeSerializeDeserialize, Zero}, ArithmeticError, DispatchError, DispatchResult, RuntimeDebug, Saturating, }; -use sp_std::{cmp, fmt::Debug, result}; +use sp_std::{fmt::Debug, result}; + +mod impl_currency; +mod impl_fungible; mod account_data; pub use account_data::{AccountData, Reasons}; @@ -143,40 +146,47 @@ pub mod pallet { who: >::AccountId, amount: T::Balance, }, + /// Some amount was minted into an account. + Minted { + who: >::AccountId, + amount: T::Balance + }, + /// Some amount was burned from an account. + Burned { + who: >::AccountId, + amount: T::Balance + }, + /// Some amount was suspended from an account (it can be restored later). + Suspended { + who: >::AccountId, + amount: T::Balance + }, + /// Some amount was restored into an account. + Restored { + who: >::AccountId, + amount: T::Balance + }, + /// Total issuance was increased by `amount`, creating a credit to be balanced. + Issued { amount: T::Balance }, + /// Total issuance was decreased by `amount`, creating a debt to be balanced. + Rescinded { amount: T::Balance }, } #[pallet::error] pub enum Error { - /// Account liquidity restrictions prevent withdrawal + /// Account liquidity restrictions prevent withdrawal. LiquidityRestrictions, /// Balance too low to send value. InsufficientBalance, - /// Value too low to create account due to existential deposit + /// Value too low to create account due to existential deposit. ExistentialDeposit, - /// Transfer/payment would kill account - KeepAlive, - /// Beneficiary account must pre-exist + /// Transfer/payment would kill account. + Expendability, + /// Beneficiary account must pre-exist. DeadAccount, } } -/// Removes a dust account whose balance was non-zero but below `ExistentialDeposit`. -pub struct DustCleaner, I: 'static = ()>( - Option<(>::AccountId, NegativeImbalance)>, -); - -impl, I: 'static> Drop for DustCleaner { - fn drop(&mut self) { - if let Some((who, dust)) = self.0.take() { - Pallet::::deposit_event(Event::DustLost { - account: who, - amount: dust.peek(), - }); - T::DustRemoval::on_unbalanced(dust); - } - } -} - impl, I: 'static> Pallet { /// Get the free balance of an account. pub fn free_balance( @@ -191,49 +201,117 @@ impl, I: 'static> Pallet { } /// 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`. + /// `ExistentialDeposit` law, annulling the account as needed. + /// + /// It returns the result from the closure. Any dust is handled through the low-level + /// `fungible::Unbalanced` trap-door for legacy dust management. /// /// 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>( + pub(crate) fn mutate_account_handling_dust( + who: &>::AccountId, + f: impl FnOnce(&mut AccountData) -> R, + ) -> Result { + let (r, maybe_dust) = Self::mutate_account(who, f)?; + if let Some(dust) = maybe_dust { + >::handle_raw_dust(dust); + } + Ok(r) + } + + /// Mutate an account to some new value, or delete it entirely with `None`. Will enforce + /// `ExistentialDeposit` law, annulling the account as needed. + /// + /// It returns the result from the closure. Any dust is handled through the low-level + /// `fungible::Unbalanced` trap-door for legacy dust management. + /// + /// 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(crate) fn try_mutate_account_handling_dust>( who: &>::AccountId, f: impl FnOnce(&mut AccountData, bool) -> Result, ) -> Result { - Self::try_mutate_account_with_dust(who, f).map(|(result, dust_cleaner)| { - drop(dust_cleaner); - result - }) + let (r, maybe_dust) = Self::try_mutate_account(who, f)?; + if let Some(dust) = maybe_dust { + >::handle_raw_dust(dust); + } + Ok(r) + } + + /// Mutate an account to some new value, or delete it entirely with `None`. Will enforce + /// `ExistentialDeposit` law, annulling the account as needed. + /// + /// It returns both the result from the closure, and an optional amount of dust + /// which should be handled once it is known that all nested mutates that could affect + /// storage items what the dust handler touches have completed. + /// + /// 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(crate) fn mutate_account( + who: &>::AccountId, + f: impl FnOnce(&mut AccountData) -> R, + ) -> Result<(R, Option), DispatchError> { + 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`. /// - /// It returns both the result from the closure, and an optional `DustCleaner` instance which - /// should be dropped once it is known that all nested mutates that could affect storage items - /// what the dust handler touches have completed. + /// It returns both the result from the closure, and an optional amount of dust + /// which should be handled once it is known that all nested mutates that could affect + /// storage items what the dust handler touches have completed. /// /// 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_with_dust>( + fn try_mutate_account>( who: &>::AccountId, f: impl FnOnce(&mut AccountData, bool) -> Result, - ) -> Result<(R, DustCleaner), E> { + ) -> Result<(R, Option), E> { let result = 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 }; - let maybe_account_maybe_dust = Self::post_mutation(who, account); - *maybe_account = maybe_account_maybe_dust.0; - (maybe_endowed, maybe_account_maybe_dust.1, result) + + // Handle 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. + // + // Updates `maybe_account` to `Some` iff the account has sufficient balance. + // Evaluates `maybe_dust`, which is `Some` containing the dust to be dropped, iff + // some dust should be dropped. + // + // We should never be dropping if reserved is non-zero. Reserved being non-zero + // should imply that we have a consumer ref, so this is economically safe. + let maybe_dust = if account.free < T::ExistentialDeposit::get() { + if account.free.is_zero() { + None + } else { + Some(account.free) + } + } else { + assert!( + account.free.is_zero() || account.free >= T::ExistentialDeposit::get() + ); + *maybe_account = Some(account); + None + }; + + (maybe_endowed, maybe_dust, result) }) }); result.map(|(maybe_endowed, maybe_dust, result)| { @@ -243,36 +321,11 @@ impl, I: 'static> Pallet { free_balance: endowed, }); } - let dust_cleaner = DustCleaner(maybe_dust.map(|dust| (who.clone(), dust))); - (result, dust_cleaner) - }) - } - - /// Handles 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 two values: - /// - `Some` containing the the `new` account, iff the account has sufficient balance. - /// - `Some` containing the dust to be dropped, iff some dust should be dropped. - fn post_mutation( - _who: &>::AccountId, - new: AccountData, - ) -> ( - Option>, - Option>, - ) { - let total = new.total(); - if total < T::ExistentialDeposit::get() { - if total.is_zero() { - (None, None) - } else { - (None, Some(NegativeImbalance::new(total))) + if let Some(amount) = maybe_dust { + Pallet::::deposit_event(Event::DustLost { account: who.clone(), amount }); } - } else { - (Some(new), None) - } + (result, maybe_dust) + }) } fn deposit_consequence( @@ -338,410 +391,3 @@ impl, I: 'static> Pallet { } } } - -impl, I: 'static> Currency<>::AccountId> for Pallet -where - T::Balance: MaybeSerializeDeserialize + Debug, -{ - type Balance = T::Balance; - type PositiveImbalance = PositiveImbalance; - type NegativeImbalance = NegativeImbalance; - - fn total_balance(who: &>::AccountId) -> Self::Balance { - Self::account(who).total() - } - - fn can_slash(who: &>::AccountId, value: Self::Balance) -> bool { - if value.is_zero() { - return true; - } - Self::free_balance(who) >= value - } - - fn total_issuance() -> Self::Balance { - TotalIssuance::::get() - } - - fn active_issuance() -> Self::Balance { - >::AccountId>>::active_issuance() - } - - fn deactivate(amount: Self::Balance) { - InactiveIssuance::::mutate(|b| b.saturating_accrue(amount)); - } - - fn reactivate(amount: Self::Balance) { - InactiveIssuance::::mutate(|b| b.saturating_reduce(amount)); - } - - fn minimum_balance() -> Self::Balance { - T::ExistentialDeposit::get() - } - - 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 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 free_balance(who: &>::AccountId) -> Self::Balance { - Self::account(who).free - } - - // We don't have any existing withdrawal restrictions like locked and reserved balance. - fn ensure_can_withdraw( - _who: &>::AccountId, - _amount: T::Balance, - _reasons: WithdrawReasons, - _new_balance: T::Balance, - ) -> DispatchResult { - Ok(()) - } - - fn transfer( - transactor: &>::AccountId, - dest: &>::AccountId, - value: Self::Balance, - existence_requirement: ExistenceRequirement, - ) -> DispatchResult { - if value.is_zero() || transactor == dest { - return Ok(()); - } - - Self::try_mutate_account_with_dust( - dest, - |to_account, _| -> Result, DispatchError> { - Self::try_mutate_account_with_dust( - transactor, - |from_account, _| -> DispatchResult { - from_account.free = from_account - .free - .checked_sub(&value) - .ok_or(Error::::InsufficientBalance)?; - - to_account.free = to_account - .free - .checked_add(&value) - .ok_or(ArithmeticError::Overflow)?; - - let ed = T::ExistentialDeposit::get(); - ensure!(to_account.total() >= ed, Error::::ExistentialDeposit); - - Self::ensure_can_withdraw( - transactor, - value, - WithdrawReasons::TRANSFER, - from_account.free, - ) - .map_err(|_| Error::::LiquidityRestrictions)?; - - let allow_death = existence_requirement == ExistenceRequirement::AllowDeath; - ensure!( - allow_death || from_account.total() >= ed, - Error::::KeepAlive - ); - - Ok(()) - }, - ) - .map(|(_, maybe_dust_cleaner)| maybe_dust_cleaner) - }, - )?; - - // Emit transfer event. - Self::deposit_event(Event::Transfer { - from: transactor.clone(), - to: dest.clone(), - amount: value, - }); - - Ok(()) - } - - /// 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: &>::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); - } - - for attempt in 0..2 { - match Self::try_mutate_account( - who, - |account, - _is_new| - -> Result<(Self::NegativeImbalance, Self::Balance), DispatchError> { - // 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.saturating_sub(T::ExistentialDeposit::get())), - }; - - let free_slash = cmp::min(account.free, best_value); - account.free -= free_slash; // Safe because of above check - - Ok(( - NegativeImbalance::new(free_slash), - value - free_slash, // Safe because value is gt or eq to total slashed - )) - }, - ) { - Ok((imbalance, not_slashed)) => { - Self::deposit_event(Event::Slashed { - who: who.clone(), - amount: value.saturating_sub(not_slashed), - }); - return (imbalance, not_slashed); - } - Err(_) => (), - } - } - - // 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: &>::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, Error::::DeadAccount); - account.free = account - .free - .checked_add(&value) - .ok_or(ArithmeticError::Overflow)?; - Self::deposit_event(Event::Deposit { - who: who.clone(), - amount: value, - }); - Ok(PositiveImbalance::new(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: &>::AccountId, - value: Self::Balance, - ) -> Self::PositiveImbalance { - if value.is_zero() { - return Self::PositiveImbalance::zero(); - } - - Self::try_mutate_account( - who, - |account, is_new| -> Result { - let ed = T::ExistentialDeposit::get(); - ensure!(value >= ed || !is_new, Error::::ExistentialDeposit); - - // defensive only: overflow should never happen, however in case it does, then this - // operation is a no-op. - account.free = match account.free.checked_add(&value) { - Some(x) => x, - None => return Ok(Self::PositiveImbalance::zero()), - }; - - Self::deposit_event(Event::Deposit { - who: who.clone(), - amount: value, - }); - Ok(PositiveImbalance::new(value)) - }, - ) - .unwrap_or_else(|_| Self::PositiveImbalance::zero()) - } - - /// Withdraw some free balance from an account, respecting existence requirements. - /// - /// Is a no-op if value to be withdrawn is zero. - fn withdraw( - who: &>::AccountId, - value: Self::Balance, - reasons: WithdrawReasons, - liveness: ExistenceRequirement, - ) -> result::Result { - if value.is_zero() { - return Ok(NegativeImbalance::zero()); - } - - Self::try_mutate_account( - who, - |account, _| -> Result { - let new_free_account = account - .free - .checked_sub(&value) - .ok_or(Error::::InsufficientBalance)?; - - // bail if we need to keep the account alive and this would kill it. - let ed = T::ExistentialDeposit::get(); - let would_be_dead = new_free_account < ed; - let would_kill = would_be_dead && account.free >= ed; - ensure!( - liveness == AllowDeath || !would_kill, - Error::::KeepAlive - ); - - Self::ensure_can_withdraw(who, value, reasons, new_free_account)?; - - account.free = new_free_account; - - Self::deposit_event(Event::Withdraw { - who: who.clone(), - amount: value, - }); - Ok(NegativeImbalance::new(value)) - }, - ) - } - - /// Force the new free balance of a target account `who` to some new value `balance`. - fn make_free_balance_be( - who: &>::AccountId, - value: Self::Balance, - ) -> SignedImbalance { - Self::try_mutate_account( - who, - |account, - is_new| - -> Result, DispatchError> { - let ed = T::ExistentialDeposit::get(); - let total = value; - // 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, Error::::ExistentialDeposit); - - let imbalance = if account.free <= value { - SignedImbalance::Positive(PositiveImbalance::new(value - account.free)) - } else { - SignedImbalance::Negative(NegativeImbalance::new(account.free - value)) - }; - account.free = value; - Self::deposit_event(Event::BalanceSet { - who: who.clone(), - free: account.free, - }); - Ok(imbalance) - }, - ) - .unwrap_or_else(|_| SignedImbalance::Positive(Self::PositiveImbalance::zero())) - } -} - -impl, I: 'static> fungible::Inspect<>::AccountId> for Pallet { - type Balance = T::Balance; - - fn total_issuance() -> Self::Balance { - TotalIssuance::::get() - } - - fn active_issuance() -> Self::Balance { - TotalIssuance::::get().saturating_sub(InactiveIssuance::::get()) - } - - fn minimum_balance() -> Self::Balance { - T::ExistentialDeposit::get() - } - - fn total_balance(who: &>::AccountId) -> Self::Balance { - Self::account(who).total() - } - - fn balance(who: &>::AccountId) -> Self::Balance { - Self::account(who).total() - } - - fn reducible_balance( - who: &>::AccountId, - preservation: Preservation, - _force: Fortitude, - ) -> Self::Balance { - let a = Self::account(who); - // Liquid balance is what is neither reserved nor locked/frozen. - let liquid = a.free; - match preservation { - Preservation::Expendable => liquid, - _ => { - // `must_remain_to_exist` is the part of liquid balance which must remain - // to keep total over ED. - let must_remain_to_exist = - T::ExistentialDeposit::get().saturating_sub(a.total() - liquid); - liquid.saturating_sub(must_remain_to_exist) - } - } - } - - fn can_deposit( - who: &>::AccountId, - amount: Self::Balance, - provenance: Provenance, - ) -> DepositConsequence { - Self::deposit_consequence(who, amount, provenance) - } - - fn can_withdraw( - who: &>::AccountId, - amount: Self::Balance, - ) -> WithdrawConsequence { - Self::withdraw_consequence(who, amount) - } -} From 067eeb654377a6618c1bcbe8e6e53d969fa2c5d0 Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Fri, 29 Dec 2023 00:34:25 +0300 Subject: [PATCH 03/39] Apply new balances logic to currency trait implementation --- frame/evm-balances/src/impl_currency.rs | 176 +++++++----------------- 1 file changed, 52 insertions(+), 124 deletions(-) diff --git a/frame/evm-balances/src/impl_currency.rs b/frame/evm-balances/src/impl_currency.rs index eadfe7a6b2..4d3a3a510f 100644 --- a/frame/evm-balances/src/impl_currency.rs +++ b/frame/evm-balances/src/impl_currency.rs @@ -26,21 +26,23 @@ where } fn active_issuance() -> Self::Balance { - TotalIssuance::::get().saturating_sub(InactiveIssuance::::get()) + >::active_issuance() } fn deactivate(amount: Self::Balance) { - InactiveIssuance::::mutate(|b| b.saturating_accrue(amount)); + >::deactivate(amount); } fn reactivate(amount: Self::Balance) { - InactiveIssuance::::mutate(|b| b.saturating_reduce(amount)); + >::reactivate(amount); } fn minimum_balance() -> Self::Balance { T::ExistentialDeposit::get() } + // 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(); @@ -54,6 +56,9 @@ where PositiveImbalance::new(amount) } + // 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(); @@ -72,6 +77,7 @@ where } // We don't have any existing withdrawal restrictions like locked and reserved balance. + // Is a no-op if amount to be withdrawn is zero. fn ensure_can_withdraw( _who: &>::AccountId, _amount: T::Balance, @@ -81,6 +87,8 @@ where 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: &>::AccountId, dest: &>::AccountId, @@ -88,56 +96,13 @@ where existence_requirement: ExistenceRequirement, ) -> DispatchResult { if value.is_zero() || transactor == dest { - return Ok(()); + return Ok(()) } - - Self::try_mutate_account_with_dust( - dest, - |to_account, _| -> Result, DispatchError> { - Self::try_mutate_account_with_dust( - transactor, - |from_account, _| -> DispatchResult { - from_account.free = from_account - .free - .checked_sub(&value) - .ok_or(Error::::InsufficientBalance)?; - - to_account.free = to_account - .free - .checked_add(&value) - .ok_or(ArithmeticError::Overflow)?; - - let ed = T::ExistentialDeposit::get(); - ensure!(to_account.total() >= ed, Error::::ExistentialDeposit); - - Self::ensure_can_withdraw( - transactor, - value, - WithdrawReasons::TRANSFER, - from_account.free, - ) - .map_err(|_| Error::::LiquidityRestrictions)?; - - let allow_death = existence_requirement == ExistenceRequirement::AllowDeath; - ensure!( - allow_death || from_account.total() >= ed, - Error::::KeepAlive - ); - - Ok(()) - }, - ) - .map(|(_, maybe_dust_cleaner)| maybe_dust_cleaner) - }, - )?; - - // Emit transfer event. - Self::deposit_event(Event::Transfer { - from: transactor.clone(), - to: dest.clone(), - amount: value, - }); - + let keep_alive = match existence_requirement { + ExistenceRequirement::KeepAlive => Preservation::Preserve, + ExistenceRequirement::AllowDeath => Preservation::Expendable, + }; + >::transfer(transactor, dest, value, keep_alive)?; Ok(()) } @@ -157,48 +122,32 @@ where if value.is_zero() { return (NegativeImbalance::zero(), Zero::zero()); } + if Self::total_balance(who).is_zero() { return (NegativeImbalance::zero(), value); } - for attempt in 0..2 { - match Self::try_mutate_account( - who, - |account, - _is_new| - -> Result<(Self::NegativeImbalance, Self::Balance), DispatchError> { - // 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.saturating_sub(T::ExistentialDeposit::get())), - }; - - let free_slash = cmp::min(account.free, best_value); - account.free -= free_slash; // Safe because of above check - - Ok(( - NegativeImbalance::new(free_slash), - value - free_slash, // Safe because value is gt or eq to total slashed - )) - }, - ) { - Ok((imbalance, not_slashed)) => { - Self::deposit_event(Event::Slashed { - who: who.clone(), - amount: value.saturating_sub(not_slashed), - }); - return (imbalance, not_slashed); - } - Err(_) => (), - } - } - - // Should never get here. But we'll be defensive anyway. - (Self::NegativeImbalance::zero(), value) + let result = match Self::try_mutate_account_handling_dust( + who, + |account, _is_new| -> Result<(Self::NegativeImbalance, Self::Balance), DispatchError> { + // Best value is the most amount we can slash following liveness rules. + let ed = T::ExistentialDeposit::get(); + let actual = value.min(account.free.saturating_sub(ed)); + account.free.saturating_reduce(actual); + let remaining = value.saturating_sub(actual); + Ok((NegativeImbalance::new(actual), remaining)) + }, + ) { + Ok((imbalance, remaining)) => { + Self::deposit_event(Event::Slashed { + who: who.clone(), + amount: value.saturating_sub(remaining), + }); + (imbalance, remaining) + }, + Err(_) => (Self::NegativeImbalance::zero(), value), + }; + result } /// Deposit some `value` into the free balance of an existing target account `who`. @@ -212,18 +161,12 @@ where return Ok(PositiveImbalance::zero()); } - Self::try_mutate_account( + Self::try_mutate_account_handling_dust( who, |account, is_new| -> Result { ensure!(!is_new, Error::::DeadAccount); - account.free = account - .free - .checked_add(&value) - .ok_or(ArithmeticError::Overflow)?; - Self::deposit_event(Event::Deposit { - who: who.clone(), - amount: value, - }); + account.free = account.free.checked_add(&value).ok_or(ArithmeticError::Overflow)?; + Self::deposit_event(Event::Deposit { who: who.clone(), amount: value }); Ok(PositiveImbalance::new(value)) }, ) @@ -246,7 +189,7 @@ where return Self::PositiveImbalance::zero(); } - Self::try_mutate_account( + Self::try_mutate_account_handling_dust( who, |account, is_new| -> Result { let ed = T::ExistentialDeposit::get(); @@ -259,10 +202,7 @@ where None => return Ok(Self::PositiveImbalance::zero()), }; - Self::deposit_event(Event::Deposit { - who: who.clone(), - amount: value, - }); + Self::deposit_event(Event::Deposit { who: who.clone(), amount: value }); Ok(PositiveImbalance::new(value)) }, ) @@ -282,31 +222,23 @@ where return Ok(NegativeImbalance::zero()); } - Self::try_mutate_account( + Self::try_mutate_account_handling_dust( who, |account, _| -> Result { - let new_free_account = account - .free - .checked_sub(&value) - .ok_or(Error::::InsufficientBalance)?; + let new_free_account = + account.free.checked_sub(&value).ok_or(Error::::InsufficientBalance)?; // bail if we need to keep the account alive and this would kill it. let ed = T::ExistentialDeposit::get(); let would_be_dead = new_free_account < ed; let would_kill = would_be_dead && account.free >= ed; - ensure!( - liveness == AllowDeath || !would_kill, - Error::::KeepAlive - ); + ensure!(liveness == AllowDeath || !would_kill, Error::::Expendability); Self::ensure_can_withdraw(who, value, reasons, new_free_account)?; account.free = new_free_account; - Self::deposit_event(Event::Withdraw { - who: who.clone(), - amount: value, - }); + Self::deposit_event(Event::Withdraw { who: who.clone(), amount: value }); Ok(NegativeImbalance::new(value)) }, ) @@ -317,13 +249,12 @@ where who: &>::AccountId, value: Self::Balance, ) -> SignedImbalance { - Self::try_mutate_account( + Self::try_mutate_account_handling_dust( who, |account, is_new| -> Result, DispatchError> { let ed = T::ExistentialDeposit::get(); - let total = value; // 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 @@ -331,7 +262,7 @@ where // 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, Error::::ExistentialDeposit); + ensure!(value >= ed || !is_new, Error::::ExistentialDeposit); let imbalance = if account.free <= value { SignedImbalance::Positive(PositiveImbalance::new(value - account.free)) @@ -339,10 +270,7 @@ where SignedImbalance::Negative(NegativeImbalance::new(account.free - value)) }; account.free = value; - Self::deposit_event(Event::BalanceSet { - who: who.clone(), - free: account.free, - }); + Self::deposit_event(Event::BalanceSet { who: who.clone(), free: account.free }); Ok(imbalance) }, ) From 94d7bb6b0e3b88efbfd0924b19dcb5deace1973e Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Fri, 29 Dec 2023 00:49:48 +0300 Subject: [PATCH 04/39] Use DustRemoval over Credit instead of NegativeImbalance --- frame/evm-balances/src/lib.rs | 26 ++------------------------ 1 file changed, 2 insertions(+), 24 deletions(-) diff --git a/frame/evm-balances/src/lib.rs b/frame/evm-balances/src/lib.rs index 75a8ab71e9..049bb5c775 100644 --- a/frame/evm-balances/src/lib.rs +++ b/frame/evm-balances/src/lib.rs @@ -8,7 +8,7 @@ use frame_support::{ ensure, traits::{ - fungible, + fungible::{self, Credit}, tokens::{DepositConsequence, WithdrawConsequence, Preservation, Fortitude, Provenance}, Currency, ExistenceRequirement, ExistenceRequirement::AllowDeath, @@ -92,7 +92,7 @@ pub mod pallet { type AccountStore: StoredMap<>::AccountId, AccountData>; /// Handler for the unbalanced reduction when removing a dust account. - type DustRemoval: OnUnbalanced>; + type DustRemoval: OnUnbalanced>::AccountId, Pallet>>; } /// The total units issued. @@ -200,28 +200,6 @@ impl, I: 'static> Pallet { T::AccountStore::get(who) } - /// Mutate an account to some new value, or delete it entirely with `None`. Will enforce - /// `ExistentialDeposit` law, annulling the account as needed. - /// - /// It returns the result from the closure. Any dust is handled through the low-level - /// `fungible::Unbalanced` trap-door for legacy dust management. - /// - /// 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(crate) fn mutate_account_handling_dust( - who: &>::AccountId, - f: impl FnOnce(&mut AccountData) -> R, - ) -> Result { - let (r, maybe_dust) = Self::mutate_account(who, f)?; - if let Some(dust) = maybe_dust { - >::handle_raw_dust(dust); - } - Ok(r) - } - /// Mutate an account to some new value, or delete it entirely with `None`. Will enforce /// `ExistentialDeposit` law, annulling the account as needed. /// From bb889629b9686e54f54109f4c3333c66c38d6232 Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Fri, 29 Dec 2023 00:50:25 +0300 Subject: [PATCH 05/39] Implement Balanced, Unbalanced, Mutate fungible traits for pallet --- frame/evm-balances/src/impl_fungible.rs | 144 ++++++++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 frame/evm-balances/src/impl_fungible.rs diff --git a/frame/evm-balances/src/impl_fungible.rs b/frame/evm-balances/src/impl_fungible.rs new file mode 100644 index 0000000000..7bdf54c753 --- /dev/null +++ b/frame/evm-balances/src/impl_fungible.rs @@ -0,0 +1,144 @@ +//! Fungible related traits implementation. + +use super::*; + +impl, I: 'static> fungible::Inspect<>::AccountId> for Pallet { + type Balance = T::Balance; + + fn total_issuance() -> Self::Balance { + TotalIssuance::::get() + } + + fn active_issuance() -> Self::Balance { + TotalIssuance::::get().saturating_sub(InactiveIssuance::::get()) + } + + fn minimum_balance() -> Self::Balance { + T::ExistentialDeposit::get() + } + + fn total_balance(who: &>::AccountId) -> Self::Balance { + Self::account(who).total() + } + + fn balance(who: &>::AccountId) -> Self::Balance { + Self::account(who).total() + } + + fn reducible_balance( + who: &>::AccountId, + preservation: Preservation, + _force: Fortitude, + ) -> Self::Balance { + let a = Self::account(who); + // Liquid balance is what is neither reserved nor locked/frozen. + let liquid = a.free; + match preservation { + Preservation::Expendable => liquid, + _ => { + // `must_remain_to_exist` is the part of liquid balance which must remain + // to keep total over ED. + let must_remain_to_exist = + T::ExistentialDeposit::get().saturating_sub(a.total() - liquid); + liquid.saturating_sub(must_remain_to_exist) + } + } + } + + fn can_deposit( + who: &>::AccountId, + amount: Self::Balance, + provenance: Provenance, + ) -> DepositConsequence { + Self::deposit_consequence(who, amount, provenance) + } + + fn can_withdraw( + who: &>::AccountId, + amount: Self::Balance, + ) -> WithdrawConsequence { + Self::withdraw_consequence(who, amount) + } +} + +impl, I: 'static> fungible::Unbalanced<>::AccountId> for Pallet { + fn handle_dust(dust: fungible::Dust<>::AccountId, Self>) { + T::DustRemoval::on_unbalanced(dust.into_credit()); + } + fn write_balance( + who: &>::AccountId, + amount: Self::Balance, + ) -> Result, DispatchError> { + let max_reduction = + >::reducible_balance(who, Preservation::Expendable, Fortitude::Force); + let (result, maybe_dust) = Self::mutate_account(who, |account| -> DispatchResult { + // Make sure the reduction (if there is one) is no more than the maximum allowed. + let reduction = account.free.saturating_sub(amount); + ensure!(reduction <= max_reduction, Error::::InsufficientBalance); + + account.free = amount; + Ok(()) + })?; + result?; + Ok(maybe_dust) + } + + fn set_total_issuance(amount: Self::Balance) { + TotalIssuance::::mutate(|t| *t = amount); + } + + fn deactivate(amount: Self::Balance) { + InactiveIssuance::::mutate(|b| b.saturating_accrue(amount)); + } + + fn reactivate(amount: Self::Balance) { + InactiveIssuance::::mutate(|b| b.saturating_reduce(amount)); + } +} + +impl, I: 'static> fungible::Mutate<>::AccountId> for Pallet { + fn done_mint_into(who: &>::AccountId, amount: Self::Balance) { + Self::deposit_event(Event::Minted { who: who.clone(), amount }); + } + + fn done_burn_from(who: &>::AccountId, amount: Self::Balance) { + Self::deposit_event(Event::Burned { who: who.clone(), amount }); + } + + fn done_shelve(who: &>::AccountId, amount: Self::Balance) { + Self::deposit_event(Event::Suspended { who: who.clone(), amount }); + } + + fn done_restore(who: &>::AccountId, amount: Self::Balance) { + Self::deposit_event(Event::Restored { who: who.clone(), amount }); + } + + fn done_transfer(source: &>::AccountId, dest: &>::AccountId, amount: Self::Balance) { + Self::deposit_event(Event::Transfer { + from: source.clone(), + to: dest.clone(), + amount, + }); + } +} + +impl, I: 'static> fungible::Balanced<>::AccountId> for Pallet { + type OnDropCredit = fungible::DecreaseIssuance<>::AccountId, Self>; + type OnDropDebt = fungible::IncreaseIssuance<>::AccountId, Self>; + + fn done_deposit(who: &>::AccountId, amount: Self::Balance) { + Self::deposit_event(Event::Deposit { who: who.clone(), amount }); + } + + fn done_withdraw(who: &>::AccountId, amount: Self::Balance) { + Self::deposit_event(Event::Withdraw { who: who.clone(), amount }); + } + + fn done_issue(amount: Self::Balance) { + Self::deposit_event(Event::Issued { amount }); + } + + fn done_rescind(amount: Self::Balance) { + Self::deposit_event(Event::Rescinded { amount }); + } +} From 869e90b7f035d109d34fd030e51ee3df48ce8f04 Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Fri, 29 Dec 2023 12:39:26 +0300 Subject: [PATCH 06/39] Revert using negative imbalance --- frame/evm-balances/src/impl_fungible.rs | 2 +- frame/evm-balances/src/lib.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frame/evm-balances/src/impl_fungible.rs b/frame/evm-balances/src/impl_fungible.rs index 7bdf54c753..ed4ec70686 100644 --- a/frame/evm-balances/src/impl_fungible.rs +++ b/frame/evm-balances/src/impl_fungible.rs @@ -63,7 +63,7 @@ impl, I: 'static> fungible::Inspect<>::AccountId> fo impl, I: 'static> fungible::Unbalanced<>::AccountId> for Pallet { fn handle_dust(dust: fungible::Dust<>::AccountId, Self>) { - T::DustRemoval::on_unbalanced(dust.into_credit()); + T::DustRemoval::on_unbalanced(NegativeImbalance::new(dust.0)); } fn write_balance( who: &>::AccountId, diff --git a/frame/evm-balances/src/lib.rs b/frame/evm-balances/src/lib.rs index 049bb5c775..d443c8f6d1 100644 --- a/frame/evm-balances/src/lib.rs +++ b/frame/evm-balances/src/lib.rs @@ -8,7 +8,7 @@ use frame_support::{ ensure, traits::{ - fungible::{self, Credit}, + fungible, tokens::{DepositConsequence, WithdrawConsequence, Preservation, Fortitude, Provenance}, Currency, ExistenceRequirement, ExistenceRequirement::AllowDeath, @@ -92,7 +92,7 @@ pub mod pallet { type AccountStore: StoredMap<>::AccountId, AccountData>; /// Handler for the unbalanced reduction when removing a dust account. - type DustRemoval: OnUnbalanced>::AccountId, Pallet>>; + type DustRemoval: OnUnbalanced>; } /// The total units issued. From 99e4094212830ae418f4144f88d4fc9e1891be2e Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Fri, 29 Dec 2023 13:51:35 +0300 Subject: [PATCH 07/39] Apply formatter --- frame/evm-balances/src/account_data.rs | 24 +++++++++++-- frame/evm-balances/src/impl_currency.rs | 42 ++++++++++++++++------ frame/evm-balances/src/impl_fungible.rs | 48 +++++++++++++++++++------ frame/evm-balances/src/lib.rs | 28 ++++++++------- 4 files changed, 107 insertions(+), 35 deletions(-) diff --git a/frame/evm-balances/src/account_data.rs b/frame/evm-balances/src/account_data.rs index 3b70e21bc7..4bd3fffdd9 100644 --- a/frame/evm-balances/src/account_data.rs +++ b/frame/evm-balances/src/account_data.rs @@ -5,7 +5,17 @@ use frame_support::traits::WithdrawReasons; use super::*; /// All balance information for an account. -#[derive(Encode, Decode, Clone, PartialEq, Eq, Default, RuntimeDebug, MaxEncodedLen, TypeInfo)] +#[derive( + Encode, + Decode, + Clone, + PartialEq, + Eq, + Default, + RuntimeDebug, + MaxEncodedLen, + TypeInfo +)] pub struct AccountData { /// Non-reserved part of the balance. There may still be restrictions on this, but it is the /// total pool what may in principle be transferred, reserved and used for tipping. @@ -23,7 +33,17 @@ impl AccountData { } /// Simplified reasons for withdrawing balance. -#[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug, MaxEncodedLen, TypeInfo)] +#[derive( + Encode, + Decode, + Clone, + Copy, + PartialEq, + Eq, + RuntimeDebug, + MaxEncodedLen, + TypeInfo +)] pub enum Reasons { /// Paying system transaction fees. Fee = 0, diff --git a/frame/evm-balances/src/impl_currency.rs b/frame/evm-balances/src/impl_currency.rs index 4d3a3a510f..3e98167f1b 100644 --- a/frame/evm-balances/src/impl_currency.rs +++ b/frame/evm-balances/src/impl_currency.rs @@ -1,5 +1,7 @@ //! Currency trait implementation. +use frame_support::traits::Currency; + use super::*; impl, I: 'static> Currency<>::AccountId> for Pallet @@ -96,7 +98,7 @@ where existence_requirement: ExistenceRequirement, ) -> DispatchResult { if value.is_zero() || transactor == dest { - return Ok(()) + return Ok(()); } let keep_alive = match existence_requirement { ExistenceRequirement::KeepAlive => Preservation::Preserve, @@ -144,7 +146,7 @@ where amount: value.saturating_sub(remaining), }); (imbalance, remaining) - }, + } Err(_) => (Self::NegativeImbalance::zero(), value), }; result @@ -165,8 +167,14 @@ where who, |account, is_new| -> Result { ensure!(!is_new, Error::::DeadAccount); - account.free = account.free.checked_add(&value).ok_or(ArithmeticError::Overflow)?; - Self::deposit_event(Event::Deposit { who: who.clone(), amount: value }); + account.free = account + .free + .checked_add(&value) + .ok_or(ArithmeticError::Overflow)?; + Self::deposit_event(Event::Deposit { + who: who.clone(), + amount: value, + }); Ok(PositiveImbalance::new(value)) }, ) @@ -202,7 +210,10 @@ where None => return Ok(Self::PositiveImbalance::zero()), }; - Self::deposit_event(Event::Deposit { who: who.clone(), amount: value }); + Self::deposit_event(Event::Deposit { + who: who.clone(), + amount: value, + }); Ok(PositiveImbalance::new(value)) }, ) @@ -225,20 +236,28 @@ where Self::try_mutate_account_handling_dust( who, |account, _| -> Result { - let new_free_account = - account.free.checked_sub(&value).ok_or(Error::::InsufficientBalance)?; + let new_free_account = account + .free + .checked_sub(&value) + .ok_or(Error::::InsufficientBalance)?; // bail if we need to keep the account alive and this would kill it. let ed = T::ExistentialDeposit::get(); let would_be_dead = new_free_account < ed; let would_kill = would_be_dead && account.free >= ed; - ensure!(liveness == AllowDeath || !would_kill, Error::::Expendability); + ensure!( + liveness == ExistenceRequirement::AllowDeath || !would_kill, + Error::::Expendability + ); Self::ensure_can_withdraw(who, value, reasons, new_free_account)?; account.free = new_free_account; - Self::deposit_event(Event::Withdraw { who: who.clone(), amount: value }); + Self::deposit_event(Event::Withdraw { + who: who.clone(), + amount: value, + }); Ok(NegativeImbalance::new(value)) }, ) @@ -270,7 +289,10 @@ where SignedImbalance::Negative(NegativeImbalance::new(account.free - value)) }; account.free = value; - Self::deposit_event(Event::BalanceSet { who: who.clone(), free: account.free }); + Self::deposit_event(Event::BalanceSet { + who: who.clone(), + free: account.free, + }); Ok(imbalance) }, ) diff --git a/frame/evm-balances/src/impl_fungible.rs b/frame/evm-balances/src/impl_fungible.rs index ed4ec70686..c9a54cc212 100644 --- a/frame/evm-balances/src/impl_fungible.rs +++ b/frame/evm-balances/src/impl_fungible.rs @@ -69,12 +69,18 @@ impl, I: 'static> fungible::Unbalanced<>::AccountId> who: &>::AccountId, amount: Self::Balance, ) -> Result, DispatchError> { - let max_reduction = - >::reducible_balance(who, Preservation::Expendable, Fortitude::Force); + let max_reduction = >::reducible_balance( + who, + Preservation::Expendable, + Fortitude::Force, + ); let (result, maybe_dust) = Self::mutate_account(who, |account| -> DispatchResult { // Make sure the reduction (if there is one) is no more than the maximum allowed. let reduction = account.free.saturating_sub(amount); - ensure!(reduction <= max_reduction, Error::::InsufficientBalance); + ensure!( + reduction <= max_reduction, + Error::::InsufficientBalance + ); account.free = amount; Ok(()) @@ -98,22 +104,38 @@ impl, I: 'static> fungible::Unbalanced<>::AccountId> impl, I: 'static> fungible::Mutate<>::AccountId> for Pallet { fn done_mint_into(who: &>::AccountId, amount: Self::Balance) { - Self::deposit_event(Event::Minted { who: who.clone(), amount }); + Self::deposit_event(Event::Minted { + who: who.clone(), + amount, + }); } fn done_burn_from(who: &>::AccountId, amount: Self::Balance) { - Self::deposit_event(Event::Burned { who: who.clone(), amount }); + Self::deposit_event(Event::Burned { + who: who.clone(), + amount, + }); } fn done_shelve(who: &>::AccountId, amount: Self::Balance) { - Self::deposit_event(Event::Suspended { who: who.clone(), amount }); + Self::deposit_event(Event::Suspended { + who: who.clone(), + amount, + }); } fn done_restore(who: &>::AccountId, amount: Self::Balance) { - Self::deposit_event(Event::Restored { who: who.clone(), amount }); + Self::deposit_event(Event::Restored { + who: who.clone(), + amount, + }); } - fn done_transfer(source: &>::AccountId, dest: &>::AccountId, amount: Self::Balance) { + fn done_transfer( + source: &>::AccountId, + dest: &>::AccountId, + amount: Self::Balance, + ) { Self::deposit_event(Event::Transfer { from: source.clone(), to: dest.clone(), @@ -127,11 +149,17 @@ impl, I: 'static> fungible::Balanced<>::AccountId> f type OnDropDebt = fungible::IncreaseIssuance<>::AccountId, Self>; fn done_deposit(who: &>::AccountId, amount: Self::Balance) { - Self::deposit_event(Event::Deposit { who: who.clone(), amount }); + Self::deposit_event(Event::Deposit { + who: who.clone(), + amount, + }); } fn done_withdraw(who: &>::AccountId, amount: Self::Balance) { - Self::deposit_event(Event::Withdraw { who: who.clone(), amount }); + Self::deposit_event(Event::Withdraw { + who: who.clone(), + amount, + }); } fn done_issue(amount: Self::Balance) { diff --git a/frame/evm-balances/src/lib.rs b/frame/evm-balances/src/lib.rs index d443c8f6d1..ff87806302 100644 --- a/frame/evm-balances/src/lib.rs +++ b/frame/evm-balances/src/lib.rs @@ -9,10 +9,9 @@ use frame_support::{ ensure, traits::{ fungible, - tokens::{DepositConsequence, WithdrawConsequence, Preservation, Fortitude, Provenance}, - Currency, ExistenceRequirement, - ExistenceRequirement::AllowDeath, - Get, Imbalance, OnUnbalanced, SignedImbalance, StorageVersion, StoredMap, WithdrawReasons, + tokens::{DepositConsequence, Fortitude, Preservation, Provenance, WithdrawConsequence}, + ExistenceRequirement, Get, Imbalance, OnUnbalanced, SignedImbalance, StorageVersion, + StoredMap, WithdrawReasons, }, }; use scale_codec::{Codec, Decode, Encode, MaxEncodedLen}; @@ -149,22 +148,22 @@ pub mod pallet { /// Some amount was minted into an account. Minted { who: >::AccountId, - amount: T::Balance + amount: T::Balance, }, /// Some amount was burned from an account. Burned { who: >::AccountId, - amount: T::Balance + amount: T::Balance, }, /// Some amount was suspended from an account (it can be restored later). Suspended { who: >::AccountId, - amount: T::Balance + amount: T::Balance, }, /// Some amount was restored into an account. Restored { who: >::AccountId, - amount: T::Balance + amount: T::Balance, }, /// Total issuance was increased by `amount`, creating a credit to be balanced. Issued { amount: T::Balance }, @@ -282,9 +281,7 @@ impl, I: 'static> Pallet { Some(account.free) } } else { - assert!( - account.free.is_zero() || account.free >= T::ExistentialDeposit::get() - ); + assert!(account.free.is_zero() || account.free >= T::ExistentialDeposit::get()); *maybe_account = Some(account); None }; @@ -300,7 +297,10 @@ impl, I: 'static> Pallet { }); } if let Some(amount) = maybe_dust { - Pallet::::deposit_event(Event::DustLost { account: who.clone(), amount }); + Pallet::::deposit_event(Event::DustLost { + account: who.clone(), + amount, + }); } (result, maybe_dust) }) @@ -315,7 +315,9 @@ impl, I: 'static> Pallet { return DepositConsequence::Success; } - if provenance == Provenance::Minted && TotalIssuance::::get().checked_add(&amount).is_none() { + if provenance == Provenance::Minted + && TotalIssuance::::get().checked_add(&amount).is_none() + { return DepositConsequence::Overflow; } From 24a9359c06a470af294aaf7cdf9f18576efda7b8 Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Fri, 29 Dec 2023 15:16:27 +0300 Subject: [PATCH 08/39] Fix withdraw_consequence method --- frame/evm-balances/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/evm-balances/src/lib.rs b/frame/evm-balances/src/lib.rs index ff87806302..e25ad19e1e 100644 --- a/frame/evm-balances/src/lib.rs +++ b/frame/evm-balances/src/lib.rs @@ -361,7 +361,7 @@ impl, I: 'static> Pallet { // then this will need to adapt accordingly. let ed = T::ExistentialDeposit::get(); if new_total_balance < ed { - return WithdrawConsequence::WouldDie; + return WithdrawConsequence::ReducedToZero(new_total_balance); } // Enough free funds to have them be reduced. From e79dcd2b6c2f75de232fb0ef99508cf2f15ee829 Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Fri, 29 Dec 2023 16:10:58 +0300 Subject: [PATCH 09/39] Move currency related tests to separate mod --- .../src/{tests.rs => tests/currency_test.rs} | 273 +++--------------- frame/evm-balances/src/tests/mod.rs | 224 ++++++++++++++ 2 files changed, 263 insertions(+), 234 deletions(-) rename frame/evm-balances/src/{tests.rs => tests/currency_test.rs} (50%) create mode 100644 frame/evm-balances/src/tests/mod.rs diff --git a/frame/evm-balances/src/tests.rs b/frame/evm-balances/src/tests/currency_test.rs similarity index 50% rename from frame/evm-balances/src/tests.rs rename to frame/evm-balances/src/tests/currency_test.rs index 4466176420..f9e3cfb76b 100644 --- a/frame/evm-balances/src/tests.rs +++ b/frame/evm-balances/src/tests/currency_test.rs @@ -1,33 +1,21 @@ -//! Unit tests. +//! Tests regarding the functionality of the `Currency` trait set implementations. -use frame_support::{assert_noop, assert_ok, weights::Weight}; -use pallet_evm::{FeeCalculator, Runner, FixedGasWeightMapping, GasWeightMapping}; -use sp_core::{H160, U256}; -use sp_runtime::traits::UniqueSaturatedInto; +use frame_support::{assert_noop, assert_ok, traits::Currency}; +use sp_core::H160; use sp_std::str::FromStr; use crate::{mock::*, *}; #[test] -fn basic_setup_works() { +fn total_issuance_works() { new_test_ext().execute_with_ext(|_| { - // Check the accounts. - assert_eq!( - ::account(&alice()), - account_data::AccountData { free: INIT_BALANCE } - ); - assert_eq!( - ::account(&bob()), - account_data::AccountData { free: INIT_BALANCE } - ); - - // Check the total balance value. + // Check the total issuance value. assert_eq!(EvmBalances::total_issuance(), 2 * INIT_BALANCE); }); } #[test] -fn currency_total_balance_works() { +fn total_balance_works() { new_test_ext().execute_with_ext(|_| { // Check the total balance value. assert_eq!(EvmBalances::total_balance(&alice()), INIT_BALANCE); @@ -35,23 +23,29 @@ fn currency_total_balance_works() { } #[test] -fn currency_can_slash_works() { +fn free_balance_works() { new_test_ext().execute_with_ext(|_| { - // Check possible slashing. - assert!(EvmBalances::can_slash(&alice(), 100)); + // Check the free balance value. + assert_eq!(EvmBalances::free_balance(&alice()), INIT_BALANCE); }); } #[test] -fn currency_total_issuance_works() { +fn can_slash_works() { new_test_ext().execute_with_ext(|_| { - // Check the total issuance value. - assert_eq!(EvmBalances::total_issuance(), 2 * INIT_BALANCE); + // Check possible slashing if slashing value is less than current balance. + assert!(EvmBalances::can_slash(&alice(), 100)); + + // Check possible slashing if slashing value is equal to current balance. + assert!(EvmBalances::can_slash(&alice(), INIT_BALANCE)); + + // Check slashing restriction if slashing value that is greater than current balance. + assert!(!EvmBalances::can_slash(&alice(), INIT_BALANCE + 1)); }); } #[test] -fn currency_active_issuance_works() { +fn active_issuance_works() { new_test_ext().execute_with_ext(|_| { // Check the active issuance value. assert_eq!(EvmBalances::active_issuance(), 2 * INIT_BALANCE); @@ -59,7 +53,7 @@ fn currency_active_issuance_works() { } #[test] -fn currency_deactivate_reactivate_works() { +fn deactivate_reactivate_works() { new_test_ext().execute_with_ext(|_| { // Check test preconditions. assert_eq!(>::get(), 0); @@ -68,6 +62,7 @@ fn currency_deactivate_reactivate_works() { EvmBalances::deactivate(100); // Assert state changes. assert_eq!(>::get(), 100); + // Reactivate some balance. EvmBalances::reactivate(40); // Assert state changes. @@ -76,7 +71,15 @@ fn currency_deactivate_reactivate_works() { } #[test] -fn currency_burn_works() { +fn minimum_balance_works() { + new_test_ext().execute_with_ext(|_| { + // Check the minimum balance value. + assert_eq!(EvmBalances::minimum_balance(), 1); + }); +} + +#[test] +fn burn_works() { new_test_ext().execute_with_ext(|_| { // Check test preconditions. assert_eq!(EvmBalances::total_issuance(), 2 * INIT_BALANCE); @@ -92,7 +95,7 @@ fn currency_burn_works() { } #[test] -fn currency_issue_works() { +fn issue_works() { new_test_ext().execute_with_ext(|_| { // Check test preconditions. assert_eq!(EvmBalances::total_issuance(), 2 * INIT_BALANCE); @@ -108,7 +111,7 @@ fn currency_issue_works() { } #[test] -fn currency_transfer_works() { +fn transfer_works() { new_test_ext().execute_with_ext(|_| { // Check test preconditions. assert_eq!(EvmBalances::total_balance(&alice()), INIT_BALANCE); @@ -144,7 +147,7 @@ fn currency_transfer_works() { } #[test] -fn currency_slash_works() { +fn slash_works() { new_test_ext().execute_with_ext(|_| { // Check test preconditions. assert_eq!(EvmBalances::total_balance(&alice()), INIT_BALANCE); @@ -155,7 +158,7 @@ fn currency_slash_works() { System::set_block_number(1); // Invoke the function under test. - assert!(EvmBalances::slash(&alice(), 1000).1.is_zero()); + assert!(EvmBalances::slash(&alice(), slashed_amount).1.is_zero()); // Assert state changes. assert_eq!( @@ -170,7 +173,7 @@ fn currency_slash_works() { } #[test] -fn currency_deposit_into_existing_works() { +fn deposit_into_existing_works() { new_test_ext().execute_with_ext(|_| { // Check test preconditions. assert_eq!(EvmBalances::total_balance(&alice()), INIT_BALANCE); @@ -199,7 +202,7 @@ fn currency_deposit_into_existing_works() { } #[test] -fn currency_deposit_creating_works() { +fn deposit_creating_works() { new_test_ext().execute_with_ext(|_| { // Prepare test preconditions. let charlie = H160::from_str("1000000000000000000000000000000000000003").unwrap(); @@ -226,7 +229,7 @@ fn currency_deposit_creating_works() { } #[test] -fn currency_withdraw_works() { +fn withdraw_works() { new_test_ext().execute_with_ext(|_| { // Check test preconditions. assert_eq!(EvmBalances::total_balance(&alice()), INIT_BALANCE); @@ -257,7 +260,7 @@ fn currency_withdraw_works() { } #[test] -fn currency_make_free_balance_be_works() { +fn make_free_balance_be_works() { new_test_ext().execute_with(|| { // Prepare test preconditions. let charlie = H160::from_str("1000000000000000000000000000000000000003").unwrap(); @@ -301,7 +304,7 @@ fn evm_system_account_should_be_reaped() { } #[test] -fn evm_balances_transferring_too_high_value_should_not_panic() { +fn transferring_too_high_value_should_not_panic() { new_test_ext().execute_with(|| { // Prepare test preconditions. let charlie = H160::from_str("1000000000000000000000000000000000000003").unwrap(); @@ -316,201 +319,3 @@ fn evm_balances_transferring_too_high_value_should_not_panic() { ); }); } - -#[test] -fn evm_fee_deduction() { - new_test_ext().execute_with(|| { - let charlie = H160::from_str("1000000000000000000000000000000000000003").unwrap(); - - // Seed account - let _ = ::Currency::deposit_creating(&charlie, 100); - assert_eq!(EvmBalances::free_balance(&charlie), 100); - - // Deduct fees as 10 units - let imbalance = - <::OnChargeTransaction as pallet_evm::OnChargeEVMTransaction>::withdraw_fee( - &charlie, - U256::from(10), - ) - .unwrap(); - assert_eq!(EvmBalances::free_balance(&charlie), 90); - - // Refund fees as 5 units - <::OnChargeTransaction as pallet_evm::OnChargeEVMTransaction>::correct_and_deposit_fee(&charlie, U256::from(5), U256::from(5), imbalance); - assert_eq!(EvmBalances::free_balance(&charlie), 95); - }); -} - -#[test] -fn evm_issuance_after_tip() { - new_test_ext().execute_with(|| { - let before_tip = ::Currency::total_issuance(); - - let gas_limit: u64 = 1_000_000; - let weight_limit = FixedGasWeightMapping::::gas_to_weight(gas_limit, true); - - assert_ok!(::Runner::call( - alice(), - bob(), - Vec::new(), - U256::from(1), - gas_limit, - Some(U256::from(2_000_000_000)), - None, - None, - Vec::new(), - true, - true, - Some(weight_limit), - Some(0), - ::config(), - )); - - // Only base fee is burned - let base_fee: u64 = ::FeeCalculator::min_gas_price() - .0 - .unique_saturated_into(); - - let after_tip = ::Currency::total_issuance(); - - assert_eq!(after_tip, (before_tip - (base_fee * 21_000))); - }); -} - -#[test] -fn evm_refunds_should_work() { - new_test_ext().execute_with(|| { - let before_call = EVM::account_basic(&alice()).0.balance; - // Gas price is not part of the actual fee calculations anymore, only the base fee. - // - // Because we first deduct max_fee_per_gas * gas_limit (2_000_000_000 * 1000000) we need - // to ensure that the difference (max fee VS base fee) is refunded. - - let gas_limit: u64 = 1_000_000; - let weight_limit = FixedGasWeightMapping::::gas_to_weight(gas_limit, true); - - let _ = ::Runner::call( - alice(), - bob(), - Vec::new(), - U256::from(1), - gas_limit, - Some(U256::from(2_000_000_000)), - None, - None, - Vec::new(), - true, - true, - Some(weight_limit), - Some(0), - ::config(), - ); - - let (base_fee, _) = ::FeeCalculator::min_gas_price(); - let total_cost = (U256::from(21_000) * base_fee) + U256::from(1); - let after_call = EVM::account_basic(&alice()).0.balance; - assert_eq!(after_call, before_call - total_cost); - }); -} - -#[test] -fn evm_refunds_and_priority_should_work() { - new_test_ext().execute_with(|| { - let before_call = EVM::account_basic(&alice()).0.balance; - // We deliberately set a base fee + max tip > max fee. - // The effective priority tip will be 1GWEI instead 1.5GWEI: - // (max_fee_per_gas - base_fee).min(max_priority_fee) - // (2 - 1).min(1.5) - let tip = U256::from(1_500_000_000); - let max_fee_per_gas = U256::from(2_000_000_000); - let used_gas = U256::from(21_000); - - let gas_limit: u64 = 1_000_000; - let weight_limit = FixedGasWeightMapping::::gas_to_weight(gas_limit, true); - - let _ = ::Runner::call( - alice(), - bob(), - Vec::new(), - U256::from(1), - gas_limit, - Some(max_fee_per_gas), - Some(tip), - None, - Vec::new(), - true, - true, - Some(weight_limit), - Some(0), - ::config(), - ); - - let (base_fee, _) = ::FeeCalculator::min_gas_price(); - let actual_tip = (max_fee_per_gas - base_fee).min(tip) * used_gas; - let total_cost = (used_gas * base_fee) + U256::from(actual_tip) + U256::from(1); - let after_call = EVM::account_basic(&alice()).0.balance; - // The tip is deducted but never refunded to the caller. - assert_eq!(after_call, before_call - total_cost); - }); -} - -#[test] -fn evm_call_should_fail_with_priority_greater_than_max_fee() { - new_test_ext().execute_with(|| { - // Max priority greater than max fee should fail. - let tip: u128 = 1_100_000_000; - - let gas_limit: u64 = 1_000_000; - let weight_limit = FixedGasWeightMapping::::gas_to_weight(gas_limit, true); - - let result = ::Runner::call( - alice(), - bob(), - Vec::new(), - U256::from(1), - gas_limit, - Some(U256::from(1_000_000_000)), - Some(U256::from(tip)), - None, - Vec::new(), - true, - true, - Some(weight_limit), - Some(0), - ::config(), - ); - assert!(result.is_err()); - // Some used weight is returned as part of the error. - assert_eq!(result.unwrap_err().weight, Weight::from_parts(7, 0)); - }); -} - -#[test] -fn evm_call_should_succeed_with_priority_equal_to_max_fee() { - new_test_ext().execute_with(|| { - let tip: u128 = 1_000_000_000; - - let gas_limit: u64 = 1_000_000; - let weight_limit = FixedGasWeightMapping::::gas_to_weight(gas_limit, true); - - // Mimics the input for pre-eip-1559 transaction types where `gas_price` - // is used for both `max_fee_per_gas` and `max_priority_fee_per_gas`. - let result = ::Runner::call( - alice(), - bob(), - Vec::new(), - U256::from(1), - gas_limit, - Some(U256::from(1_000_000_000)), - Some(U256::from(tip)), - None, - Vec::new(), - true, - true, - Some(weight_limit), - Some(0), - ::config(), - ); - assert!(result.is_ok()); - }); -} diff --git a/frame/evm-balances/src/tests/mod.rs b/frame/evm-balances/src/tests/mod.rs new file mode 100644 index 0000000000..efb4a3aec4 --- /dev/null +++ b/frame/evm-balances/src/tests/mod.rs @@ -0,0 +1,224 @@ +//! Unit tests. + +use frame_support::{assert_ok, traits::Currency, weights::Weight}; +use pallet_evm::{FeeCalculator, FixedGasWeightMapping, GasWeightMapping, Runner}; +use sp_core::{H160, U256}; +use sp_runtime::traits::UniqueSaturatedInto; +use sp_std::str::FromStr; + +use crate::{mock::*, *}; + +mod currency_test; + +#[test] +fn basic_setup_works() { + new_test_ext().execute_with_ext(|_| { + // Check the accounts. + assert_eq!( + ::account(&alice()), + account_data::AccountData { free: INIT_BALANCE } + ); + assert_eq!( + ::account(&bob()), + account_data::AccountData { free: INIT_BALANCE } + ); + }); +} + +#[test] +fn evm_fee_deduction() { + new_test_ext().execute_with(|| { + let charlie = H160::from_str("1000000000000000000000000000000000000003").unwrap(); + + // Seed account + let _ = ::Currency::deposit_creating(&charlie, 100); + assert_eq!(EvmBalances::free_balance(&charlie), 100); + + // Deduct fees as 10 units + let imbalance = + <::OnChargeTransaction as pallet_evm::OnChargeEVMTransaction>::withdraw_fee( + &charlie, + U256::from(10), + ) + .unwrap(); + assert_eq!(EvmBalances::free_balance(&charlie), 90); + + // Refund fees as 5 units + <::OnChargeTransaction as pallet_evm::OnChargeEVMTransaction>::correct_and_deposit_fee(&charlie, U256::from(5), U256::from(5), imbalance); + assert_eq!(EvmBalances::free_balance(&charlie), 95); + }); +} + +#[test] +fn evm_issuance_after_tip() { + new_test_ext().execute_with(|| { + let before_tip = ::Currency::total_issuance(); + + let gas_limit: u64 = 1_000_000; + let weight_limit = FixedGasWeightMapping::::gas_to_weight(gas_limit, true); + + assert_ok!(::Runner::call( + alice(), + bob(), + Vec::new(), + U256::from(1), + gas_limit, + Some(U256::from(2_000_000_000)), + None, + None, + Vec::new(), + true, + true, + Some(weight_limit), + Some(0), + ::config(), + )); + + // Only base fee is burned + let base_fee: u64 = ::FeeCalculator::min_gas_price() + .0 + .unique_saturated_into(); + + let after_tip = ::Currency::total_issuance(); + + assert_eq!(after_tip, (before_tip - (base_fee * 21_000))); + }); +} + +#[test] +fn evm_refunds_should_work() { + new_test_ext().execute_with(|| { + let before_call = EVM::account_basic(&alice()).0.balance; + // Gas price is not part of the actual fee calculations anymore, only the base fee. + // + // Because we first deduct max_fee_per_gas * gas_limit (2_000_000_000 * 1000000) we need + // to ensure that the difference (max fee VS base fee) is refunded. + + let gas_limit: u64 = 1_000_000; + let weight_limit = FixedGasWeightMapping::::gas_to_weight(gas_limit, true); + + let _ = ::Runner::call( + alice(), + bob(), + Vec::new(), + U256::from(1), + gas_limit, + Some(U256::from(2_000_000_000)), + None, + None, + Vec::new(), + true, + true, + Some(weight_limit), + Some(0), + ::config(), + ); + + let (base_fee, _) = ::FeeCalculator::min_gas_price(); + let total_cost = (U256::from(21_000) * base_fee) + U256::from(1); + let after_call = EVM::account_basic(&alice()).0.balance; + assert_eq!(after_call, before_call - total_cost); + }); +} + +#[test] +fn evm_refunds_and_priority_should_work() { + new_test_ext().execute_with(|| { + let before_call = EVM::account_basic(&alice()).0.balance; + // We deliberately set a base fee + max tip > max fee. + // The effective priority tip will be 1GWEI instead 1.5GWEI: + // (max_fee_per_gas - base_fee).min(max_priority_fee) + // (2 - 1).min(1.5) + let tip = U256::from(1_500_000_000); + let max_fee_per_gas = U256::from(2_000_000_000); + let used_gas = U256::from(21_000); + + let gas_limit: u64 = 1_000_000; + let weight_limit = FixedGasWeightMapping::::gas_to_weight(gas_limit, true); + + let _ = ::Runner::call( + alice(), + bob(), + Vec::new(), + U256::from(1), + gas_limit, + Some(max_fee_per_gas), + Some(tip), + None, + Vec::new(), + true, + true, + Some(weight_limit), + Some(0), + ::config(), + ); + + let (base_fee, _) = ::FeeCalculator::min_gas_price(); + let actual_tip = (max_fee_per_gas - base_fee).min(tip) * used_gas; + let total_cost = (used_gas * base_fee) + U256::from(actual_tip) + U256::from(1); + let after_call = EVM::account_basic(&alice()).0.balance; + // The tip is deducted but never refunded to the caller. + assert_eq!(after_call, before_call - total_cost); + }); +} + +#[test] +fn evm_call_should_fail_with_priority_greater_than_max_fee() { + new_test_ext().execute_with(|| { + // Max priority greater than max fee should fail. + let tip: u128 = 1_100_000_000; + + let gas_limit: u64 = 1_000_000; + let weight_limit = FixedGasWeightMapping::::gas_to_weight(gas_limit, true); + + let result = ::Runner::call( + alice(), + bob(), + Vec::new(), + U256::from(1), + gas_limit, + Some(U256::from(1_000_000_000)), + Some(U256::from(tip)), + None, + Vec::new(), + true, + true, + Some(weight_limit), + Some(0), + ::config(), + ); + assert!(result.is_err()); + // Some used weight is returned as part of the error. + assert_eq!(result.unwrap_err().weight, Weight::from_parts(7, 0)); + }); +} + +#[test] +fn evm_call_should_succeed_with_priority_equal_to_max_fee() { + new_test_ext().execute_with(|| { + let tip: u128 = 1_000_000_000; + + let gas_limit: u64 = 1_000_000; + let weight_limit = FixedGasWeightMapping::::gas_to_weight(gas_limit, true); + + // Mimics the input for pre-eip-1559 transaction types where `gas_price` + // is used for both `max_fee_per_gas` and `max_priority_fee_per_gas`. + let result = ::Runner::call( + alice(), + bob(), + Vec::new(), + U256::from(1), + gas_limit, + Some(U256::from(1_000_000_000)), + Some(U256::from(tip)), + None, + Vec::new(), + true, + true, + Some(weight_limit), + Some(0), + ::config(), + ); + assert!(result.is_ok()); + }); +} From df46391a62c0add182b79e8d3606080691d9625f Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Sat, 30 Dec 2023 23:01:03 +0300 Subject: [PATCH 10/39] Add transfer_fails_funds_unavailable test to currency tests --- frame/evm-balances/src/tests/currency_test.rs | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/frame/evm-balances/src/tests/currency_test.rs b/frame/evm-balances/src/tests/currency_test.rs index f9e3cfb76b..5722d0f679 100644 --- a/frame/evm-balances/src/tests/currency_test.rs +++ b/frame/evm-balances/src/tests/currency_test.rs @@ -2,6 +2,7 @@ use frame_support::{assert_noop, assert_ok, traits::Currency}; use sp_core::H160; +use sp_runtime::TokenError; use sp_std::str::FromStr; use crate::{mock::*, *}; @@ -146,6 +147,30 @@ fn transfer_works() { }); } +#[test] +fn transfer_fails_funds_unavailable() { + new_test_ext().execute_with_ext(|_| { + // Check test preconditions. + assert_eq!(EvmBalances::total_balance(&alice()), INIT_BALANCE); + + let transfered_amount = INIT_BALANCE + 1; + + // Set block number to enable events. + System::set_block_number(1); + + // Invoke the function under test. + assert_noop!( + EvmBalances::transfer( + &alice(), + &bob(), + transfered_amount, + ExistenceRequirement::KeepAlive + ), + TokenError::FundsUnavailable + ); + }); +} + #[test] fn slash_works() { new_test_ext().execute_with_ext(|_| { From fd00da79b0cbe34ccf1fb66e16f290e9831f7ccf Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Sat, 30 Dec 2023 23:04:16 +0300 Subject: [PATCH 11/39] Add transfer_works_full_balance test to currency tests --- frame/evm-balances/src/tests/currency_test.rs | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/frame/evm-balances/src/tests/currency_test.rs b/frame/evm-balances/src/tests/currency_test.rs index 5722d0f679..29b6badb6c 100644 --- a/frame/evm-balances/src/tests/currency_test.rs +++ b/frame/evm-balances/src/tests/currency_test.rs @@ -147,6 +147,46 @@ fn transfer_works() { }); } +#[test] +fn transfer_works_full_balance() { + new_test_ext().execute_with_ext(|_| { + // Check test preconditions. + assert_eq!(EvmBalances::total_balance(&alice()), INIT_BALANCE); + + let transfered_amount = INIT_BALANCE; + + // Set block number to enable events. + System::set_block_number(1); + + // Invoke the function under test. + assert_ok!(EvmBalances::transfer( + &alice(), + &bob(), + transfered_amount, + ExistenceRequirement::AllowDeath + )); + + // Assert state changes. + assert_eq!( + EvmBalances::total_balance(&alice()), + INIT_BALANCE - transfered_amount + ); + assert_eq!( + EvmBalances::total_balance(&bob()), + INIT_BALANCE + transfered_amount + ); + System::assert_has_event(RuntimeEvent::EvmBalances(crate::Event::Transfer { + from: alice(), + to: bob(), + amount: transfered_amount, + })); + assert!(!EvmSystem::account_exists(&alice())); + System::assert_has_event(RuntimeEvent::EvmSystem( + pallet_evm_system::Event::KilledAccount { account: alice() }, + )); + }); +} + #[test] fn transfer_fails_funds_unavailable() { new_test_ext().execute_with_ext(|_| { @@ -171,6 +211,30 @@ fn transfer_fails_funds_unavailable() { }); } +#[test] +fn transfer_fails_not_expendable() { + new_test_ext().execute_with_ext(|_| { + // Check test preconditions. + assert_eq!(EvmBalances::total_balance(&alice()), INIT_BALANCE); + + let transfered_amount = INIT_BALANCE; + + // Set block number to enable events. + System::set_block_number(1); + + // Invoke the function under test. + assert_noop!( + EvmBalances::transfer( + &alice(), + &bob(), + transfered_amount, + ExistenceRequirement::KeepAlive + ), + TokenError::NotExpendable + ); + }); +} + #[test] fn slash_works() { new_test_ext().execute_with_ext(|_| { From a103598cc0b8502e3c151b01a8cc73f4ebb60941 Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Sat, 30 Dec 2023 23:22:11 +0300 Subject: [PATCH 12/39] Fix slashing conditions --- frame/evm-balances/src/impl_currency.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frame/evm-balances/src/impl_currency.rs b/frame/evm-balances/src/impl_currency.rs index 3e98167f1b..9bb70f92e3 100644 --- a/frame/evm-balances/src/impl_currency.rs +++ b/frame/evm-balances/src/impl_currency.rs @@ -133,8 +133,7 @@ where who, |account, _is_new| -> Result<(Self::NegativeImbalance, Self::Balance), DispatchError> { // Best value is the most amount we can slash following liveness rules. - let ed = T::ExistentialDeposit::get(); - let actual = value.min(account.free.saturating_sub(ed)); + let actual = value.min(account.free); account.free.saturating_reduce(actual); let remaining = value.saturating_sub(actual); Ok((NegativeImbalance::new(actual), remaining)) From de59b8c2f1233ad5d7ce4d29d35a58c4f460958a Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Sat, 30 Dec 2023 23:22:28 +0300 Subject: [PATCH 13/39] Add slash_works_full_balance test to currency tests --- frame/evm-balances/src/tests/currency_test.rs | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/frame/evm-balances/src/tests/currency_test.rs b/frame/evm-balances/src/tests/currency_test.rs index 29b6badb6c..1f389d8937 100644 --- a/frame/evm-balances/src/tests/currency_test.rs +++ b/frame/evm-balances/src/tests/currency_test.rs @@ -261,6 +261,32 @@ fn slash_works() { }); } +#[test] +fn slash_works_full_balance() { + new_test_ext().execute_with_ext(|_| { + // Check test preconditions. + assert_eq!(EvmBalances::total_balance(&alice()), INIT_BALANCE); + + let slashed_amount = INIT_BALANCE; + + // Set block number to enable events. + System::set_block_number(1); + + // Invoke the function under test. + assert!(EvmBalances::slash(&alice(), slashed_amount).1.is_zero()); + + // Assert state changes. + assert_eq!( + EvmBalances::total_balance(&alice()), + INIT_BALANCE - slashed_amount + ); + System::assert_has_event(RuntimeEvent::EvmBalances(crate::Event::Slashed { + who: alice(), + amount: slashed_amount, + })); + }); +} + #[test] fn deposit_into_existing_works() { new_test_ext().execute_with_ext(|_| { From ae350ffcb113bc511c2346d82115bb24c5f1bf8e Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Sat, 30 Dec 2023 23:28:30 +0300 Subject: [PATCH 14/39] Add deposit_into_existing related fails tests --- frame/evm-balances/src/tests/currency_test.rs | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/frame/evm-balances/src/tests/currency_test.rs b/frame/evm-balances/src/tests/currency_test.rs index 1f389d8937..c1fc812310 100644 --- a/frame/evm-balances/src/tests/currency_test.rs +++ b/frame/evm-balances/src/tests/currency_test.rs @@ -316,6 +316,40 @@ fn deposit_into_existing_works() { }); } +#[test] +fn deposit_into_existing_fails_overflow() { + new_test_ext().execute_with_ext(|_| { + // Check test preconditions. + assert_eq!(EvmBalances::total_balance(&alice()), INIT_BALANCE); + + let deposited_amount = u64::MAX; + + // Invoke the function under test. + assert_noop!( + EvmBalances::deposit_into_existing(&alice(), deposited_amount), + ArithmeticError::Overflow + ); + }); +} + +#[test] +fn deposit_into_existing_fails_dead_account() { + new_test_ext().execute_with_ext(|_| { + let charlie = H160::from_str("1000000000000000000000000000000000000003").unwrap(); + + // Check test preconditions. + assert_eq!(EvmBalances::total_balance(&charlie), 0); + + let deposited_amount = 10; + + // Invoke the function under test. + assert_noop!( + EvmBalances::deposit_into_existing(&charlie, deposited_amount), + Error::::DeadAccount + ); + }); +} + #[test] fn deposit_creating_works() { new_test_ext().execute_with_ext(|_| { From 13af349f98014c5a51703265487376a92ab04615 Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Sat, 30 Dec 2023 23:34:16 +0300 Subject: [PATCH 15/39] Add withdraw_works_full_balance test to currency tests --- frame/evm-balances/src/tests/currency_test.rs | 41 ++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/frame/evm-balances/src/tests/currency_test.rs b/frame/evm-balances/src/tests/currency_test.rs index c1fc812310..1607f0467a 100644 --- a/frame/evm-balances/src/tests/currency_test.rs +++ b/frame/evm-balances/src/tests/currency_test.rs @@ -284,6 +284,10 @@ fn slash_works_full_balance() { who: alice(), amount: slashed_amount, })); + assert!(!EvmSystem::account_exists(&alice())); + System::assert_has_event(RuntimeEvent::EvmSystem( + pallet_evm_system::Event::KilledAccount { account: alice() }, + )); }); } @@ -391,7 +395,7 @@ fn withdraw_works() { // Invoke the function under test. assert_ok!(EvmBalances::withdraw( &alice(), - 1000, + withdrawed_amount, WithdrawReasons::FEE, ExistenceRequirement::KeepAlive )); @@ -408,6 +412,41 @@ fn withdraw_works() { }); } +#[test] +fn withdraw_works_full_balance() { + new_test_ext().execute_with_ext(|_| { + // Check test preconditions. + assert_eq!(EvmBalances::total_balance(&alice()), INIT_BALANCE); + + let withdrawed_amount = INIT_BALANCE; + + // Set block number to enable events. + System::set_block_number(1); + + // Invoke the function under test. + assert_ok!(EvmBalances::withdraw( + &alice(), + withdrawed_amount, + WithdrawReasons::TRANSFER, + ExistenceRequirement::AllowDeath + )); + + // Assert state changes. + assert_eq!( + EvmBalances::total_balance(&alice()), + INIT_BALANCE - withdrawed_amount + ); + System::assert_has_event(RuntimeEvent::EvmBalances(crate::Event::Withdraw { + who: alice(), + amount: withdrawed_amount, + })); + assert!(!EvmSystem::account_exists(&alice())); + System::assert_has_event(RuntimeEvent::EvmSystem( + pallet_evm_system::Event::KilledAccount { account: alice() }, + )); + }); +} + #[test] fn make_free_balance_be_works() { new_test_ext().execute_with(|| { From d90822e969f8e3a05707a96f4ad6849ab2cc31b4 Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Sat, 30 Dec 2023 23:37:42 +0300 Subject: [PATCH 16/39] Add withdraw fails related tests to currency tests --- frame/evm-balances/src/tests/currency_test.rs | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/frame/evm-balances/src/tests/currency_test.rs b/frame/evm-balances/src/tests/currency_test.rs index 1607f0467a..918496e513 100644 --- a/frame/evm-balances/src/tests/currency_test.rs +++ b/frame/evm-balances/src/tests/currency_test.rs @@ -447,6 +447,48 @@ fn withdraw_works_full_balance() { }); } +#[test] +fn withdraw_fails_insufficient_balance() { + new_test_ext().execute_with_ext(|_| { + // Check test preconditions. + assert_eq!(EvmBalances::total_balance(&alice()), INIT_BALANCE); + + let withdrawed_amount = INIT_BALANCE + 1; + + // Invoke the function under test. + assert_noop!( + EvmBalances::withdraw( + &alice(), + withdrawed_amount, + WithdrawReasons::TRANSFER, + ExistenceRequirement::AllowDeath + ), + Error::::InsufficientBalance + ); + }); +} + +#[test] +fn withdraw_fails_expendability() { + new_test_ext().execute_with_ext(|_| { + // Check test preconditions. + assert_eq!(EvmBalances::total_balance(&alice()), INIT_BALANCE); + + let withdrawed_amount = INIT_BALANCE; + + // Invoke the function under test. + assert_noop!( + EvmBalances::withdraw( + &alice(), + withdrawed_amount, + WithdrawReasons::TRANSFER, + ExistenceRequirement::KeepAlive + ), + Error::::Expendability + ); + }); +} + #[test] fn make_free_balance_be_works() { new_test_ext().execute_with(|| { From 4c387f742f9b958a427c6ea702fc8026a2a53138 Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Sat, 30 Dec 2023 23:46:52 +0300 Subject: [PATCH 17/39] Add basic fungible tests --- .../tests/{currency_test.rs => currency.rs} | 0 frame/evm-balances/src/tests/fungible.rs | 48 +++++++++++++++++++ frame/evm-balances/src/tests/mod.rs | 3 +- 3 files changed, 50 insertions(+), 1 deletion(-) rename frame/evm-balances/src/tests/{currency_test.rs => currency.rs} (100%) create mode 100644 frame/evm-balances/src/tests/fungible.rs diff --git a/frame/evm-balances/src/tests/currency_test.rs b/frame/evm-balances/src/tests/currency.rs similarity index 100% rename from frame/evm-balances/src/tests/currency_test.rs rename to frame/evm-balances/src/tests/currency.rs diff --git a/frame/evm-balances/src/tests/fungible.rs b/frame/evm-balances/src/tests/fungible.rs new file mode 100644 index 0000000000..504fbce78c --- /dev/null +++ b/frame/evm-balances/src/tests/fungible.rs @@ -0,0 +1,48 @@ +//! Tests regarding the functionality of the fungible trait set implementations. + +use frame_support::{assert_noop, assert_ok, traits::fungible::Inspect}; +use sp_core::H160; +use sp_runtime::TokenError; +use sp_std::str::FromStr; + +use crate::{mock::*, *}; + +#[test] +fn total_issuance_works() { + new_test_ext().execute_with_ext(|_| { + // Check the total issuance value. + assert_eq!(EvmBalances::total_issuance(), 2 * INIT_BALANCE); + }); +} + +#[test] +fn active_issuance_works() { + new_test_ext().execute_with_ext(|_| { + // Check the active issuance value. + assert_eq!(EvmBalances::active_issuance(), 2 * INIT_BALANCE); + }); +} + +#[test] +fn minimum_balance_works() { + new_test_ext().execute_with_ext(|_| { + // Check the minimum balance value. + assert_eq!(EvmBalances::minimum_balance(), 1); + }); +} + +#[test] +fn total_balance_works() { + new_test_ext().execute_with_ext(|_| { + // Check the total balance value. + assert_eq!(EvmBalances::total_balance(&alice()), INIT_BALANCE); + }); +} + +#[test] +fn balance_works() { + new_test_ext().execute_with_ext(|_| { + // Check the balance value. + assert_eq!(EvmBalances::balance(&alice()), INIT_BALANCE); + }); +} diff --git a/frame/evm-balances/src/tests/mod.rs b/frame/evm-balances/src/tests/mod.rs index efb4a3aec4..628904b400 100644 --- a/frame/evm-balances/src/tests/mod.rs +++ b/frame/evm-balances/src/tests/mod.rs @@ -8,7 +8,8 @@ use sp_std::str::FromStr; use crate::{mock::*, *}; -mod currency_test; +mod currency; +mod fungible; #[test] fn basic_setup_works() { From 43b165268af122fbab148ca1dda2196a88d08b83 Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Sun, 31 Dec 2023 00:04:34 +0300 Subject: [PATCH 18/39] Add reducable balance test to fungible tests --- frame/evm-balances/src/tests/fungible.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/frame/evm-balances/src/tests/fungible.rs b/frame/evm-balances/src/tests/fungible.rs index 504fbce78c..f8929b044e 100644 --- a/frame/evm-balances/src/tests/fungible.rs +++ b/frame/evm-balances/src/tests/fungible.rs @@ -46,3 +46,26 @@ fn balance_works() { assert_eq!(EvmBalances::balance(&alice()), INIT_BALANCE); }); } + +#[test] +fn reducable_balance_works() { + new_test_ext().execute_with_ext(|_| { + // Check the reducable balance value in `Expendable` case. + assert_eq!( + EvmBalances::reducible_balance(&alice(), Preservation::Expendable, Fortitude::Polite), + INIT_BALANCE + ); + + // Check the reducable balance value in `Preserve` case. + assert_eq!( + EvmBalances::reducible_balance(&alice(), Preservation::Preserve, Fortitude::Polite), + INIT_BALANCE - 1 + ); + + // Check the reducable balance value in `Protect` case. + assert_eq!( + EvmBalances::reducible_balance(&alice(), Preservation::Protect, Fortitude::Polite), + INIT_BALANCE - 1 + ); + }); +} From 9c3f7b35bdd985e563fb74495fa6615b96244182 Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Sun, 31 Dec 2023 00:14:52 +0300 Subject: [PATCH 19/39] Add can deposit related tests --- frame/evm-balances/src/tests/fungible.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/frame/evm-balances/src/tests/fungible.rs b/frame/evm-balances/src/tests/fungible.rs index f8929b044e..820e83e114 100644 --- a/frame/evm-balances/src/tests/fungible.rs +++ b/frame/evm-balances/src/tests/fungible.rs @@ -69,3 +69,23 @@ fn reducable_balance_works() { ); }); } + +#[test] +fn can_deposit_works_success() { + new_test_ext().execute_with_ext(|_| { + assert_eq!( + EvmBalances::can_deposit(&alice(), 10, Provenance::Minted), + DepositConsequence::Success + ); + }); +} + +#[test] +fn can_deposit_works_overflow() { + new_test_ext().execute_with_ext(|_| { + assert_eq!( + EvmBalances::can_deposit(&alice(), u64::MAX, Provenance::Minted), + DepositConsequence::Overflow + ); + }); +} From e3b3c81e1191e858506e6671545ad6901577c36a Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Sun, 31 Dec 2023 00:23:42 +0300 Subject: [PATCH 20/39] Fix can_withdraw logic --- frame/evm-balances/src/lib.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/frame/evm-balances/src/lib.rs b/frame/evm-balances/src/lib.rs index e25ad19e1e..cd198f50b9 100644 --- a/frame/evm-balances/src/lib.rs +++ b/frame/evm-balances/src/lib.rs @@ -364,10 +364,6 @@ impl, I: 'static> Pallet { return WithdrawConsequence::ReducedToZero(new_total_balance); } - // Enough free funds to have them be reduced. - match account.free.checked_sub(&amount) { - Some(_) => WithdrawConsequence::Success, - None => WithdrawConsequence::BalanceLow, - } + WithdrawConsequence::Success } } From edfc99b5c86c7dd26ed674b15c0de08aa56738e3 Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Sun, 31 Dec 2023 00:23:56 +0300 Subject: [PATCH 21/39] Add can_withdraw related tests --- frame/evm-balances/src/tests/fungible.rs | 40 ++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/frame/evm-balances/src/tests/fungible.rs b/frame/evm-balances/src/tests/fungible.rs index 820e83e114..66bd817c9b 100644 --- a/frame/evm-balances/src/tests/fungible.rs +++ b/frame/evm-balances/src/tests/fungible.rs @@ -89,3 +89,43 @@ fn can_deposit_works_overflow() { ); }); } + +#[test] +fn can_withdraw_works_success() { + new_test_ext().execute_with_ext(|_| { + assert_eq!( + EvmBalances::can_withdraw(&alice(), 10), + WithdrawConsequence::Success + ); + }); +} + +#[test] +fn can_withdraw_works_underflow() { + new_test_ext().execute_with_ext(|_| { + assert_eq!( + EvmBalances::can_withdraw(&alice(), u64::MAX), + WithdrawConsequence::Underflow + ); + }); +} + +#[test] +fn can_withdraw_works_balance_low() { + new_test_ext().execute_with_ext(|_| { + assert_eq!( + EvmBalances::can_withdraw(&alice(), INIT_BALANCE + 1), + WithdrawConsequence::BalanceLow + ); + }); +} + +#[test] +fn can_withdraw_works_reduced_to_zero() { + new_test_ext().execute_with_ext(|_| { + assert_eq!( + EvmBalances::can_withdraw(&alice(), INIT_BALANCE), + WithdrawConsequence::ReducedToZero(0) + ); + }); +} From 751b3aacffbc36f3371f7d8ff692e66791efa747 Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Thu, 4 Jan 2024 12:53:24 +0300 Subject: [PATCH 22/39] Add write_balance_works test --- frame/evm-balances/src/impl_fungible.rs | 1 + frame/evm-balances/src/tests/fungible.rs | 24 +++++++++++++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/frame/evm-balances/src/impl_fungible.rs b/frame/evm-balances/src/impl_fungible.rs index c9a54cc212..73b529c4e0 100644 --- a/frame/evm-balances/src/impl_fungible.rs +++ b/frame/evm-balances/src/impl_fungible.rs @@ -65,6 +65,7 @@ impl, I: 'static> fungible::Unbalanced<>::AccountId> fn handle_dust(dust: fungible::Dust<>::AccountId, Self>) { T::DustRemoval::on_unbalanced(NegativeImbalance::new(dust.0)); } + fn write_balance( who: &>::AccountId, amount: Self::Balance, diff --git a/frame/evm-balances/src/tests/fungible.rs b/frame/evm-balances/src/tests/fungible.rs index 66bd817c9b..6aa77670f8 100644 --- a/frame/evm-balances/src/tests/fungible.rs +++ b/frame/evm-balances/src/tests/fungible.rs @@ -1,6 +1,9 @@ //! Tests regarding the functionality of the fungible trait set implementations. -use frame_support::{assert_noop, assert_ok, traits::fungible::Inspect}; +use frame_support::{ + assert_noop, assert_ok, + traits::fungible::{Inspect, Unbalanced}, +}; use sp_core::H160; use sp_runtime::TokenError; use sp_std::str::FromStr; @@ -129,3 +132,22 @@ fn can_withdraw_works_reduced_to_zero() { ); }); } + +#[test] +fn write_balance_works() { + new_test_ext().execute_with_ext(|_| { + // Check test preconditions. + assert_eq!(EvmBalances::total_balance(&alice()), INIT_BALANCE); + + let write_balance = 10; + + // Invoke the function under test. + assert_eq!( + EvmBalances::write_balance(&alice(), write_balance), + Ok(None) + ); + + // Assert state changes. + assert_eq!(EvmBalances::total_balance(&alice()), write_balance); + }); +} From 8dc698a50e41392cf317c03d874a4f718d1248d6 Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Thu, 4 Jan 2024 13:03:44 +0300 Subject: [PATCH 23/39] Add set_total_issuance_works test --- frame/evm-balances/src/tests/fungible.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/frame/evm-balances/src/tests/fungible.rs b/frame/evm-balances/src/tests/fungible.rs index 6aa77670f8..96f295acf2 100644 --- a/frame/evm-balances/src/tests/fungible.rs +++ b/frame/evm-balances/src/tests/fungible.rs @@ -151,3 +151,19 @@ fn write_balance_works() { assert_eq!(EvmBalances::total_balance(&alice()), write_balance); }); } + +#[test] +fn set_total_issuance_works() { + new_test_ext().execute_with_ext(|_| { + // Check test preconditions. + assert_eq!(EvmBalances::total_issuance(), 2 * INIT_BALANCE); + + let set_total_issuance_balance = 100; + + // Invoke the function under test. + EvmBalances::set_total_issuance(set_total_issuance_balance); + + // Assert state changes. + assert_eq!(EvmBalances::total_issuance(), set_total_issuance_balance); + }); +} From 8a08cce577072d88009f8ad1b282037ab878d8c4 Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Thu, 4 Jan 2024 13:17:33 +0300 Subject: [PATCH 24/39] Add decrease_balance related tests --- frame/evm-balances/src/tests/fungible.rs | 103 ++++++++++++++++++++++- 1 file changed, 102 insertions(+), 1 deletion(-) diff --git a/frame/evm-balances/src/tests/fungible.rs b/frame/evm-balances/src/tests/fungible.rs index 96f295acf2..e57b405476 100644 --- a/frame/evm-balances/src/tests/fungible.rs +++ b/frame/evm-balances/src/tests/fungible.rs @@ -2,7 +2,10 @@ use frame_support::{ assert_noop, assert_ok, - traits::fungible::{Inspect, Unbalanced}, + traits::{ + fungible::{Inspect, Unbalanced}, + tokens::Precision, + }, }; use sp_core::H160; use sp_runtime::TokenError; @@ -167,3 +170,101 @@ fn set_total_issuance_works() { assert_eq!(EvmBalances::total_issuance(), set_total_issuance_balance); }); } + +#[test] +fn decrease_balance_works_exact_expendable() { + new_test_ext().execute_with_ext(|_| { + // Check test preconditions. + assert_eq!(EvmBalances::total_balance(&alice()), INIT_BALANCE); + + let decreased_balance = 100; + + // Invoke the function under test. + assert_ok!(EvmBalances::decrease_balance( + &alice(), + decreased_balance, + Precision::Exact, + Preservation::Expendable, + Fortitude::Polite + )); + + // Assert state changes. + assert_eq!( + EvmBalances::total_balance(&alice()), + INIT_BALANCE - decreased_balance + ); + }); +} + +#[test] +fn decrease_balance_works_best_effort_preserve() { + new_test_ext().execute_with_ext(|_| { + // Check test preconditions. + assert_eq!(EvmBalances::total_balance(&alice()), INIT_BALANCE); + + let decreased_balance = INIT_BALANCE + 1; + + // Invoke the function under test. + assert_ok!(EvmBalances::decrease_balance( + &alice(), + decreased_balance, + Precision::BestEffort, + Preservation::Preserve, + Fortitude::Polite + )); + + // Assert state changes. + assert_eq!(EvmBalances::total_balance(&alice()), 1); + }); +} + +#[test] +fn decrease_balance_works_full_balance() { + new_test_ext().execute_with_ext(|_| { + // Check test preconditions. + assert_eq!(EvmBalances::total_balance(&alice()), INIT_BALANCE); + + // Set block number to enable events. + System::set_block_number(1); + + let decreased_balance = INIT_BALANCE; + + // Invoke the function under test. + assert_ok!(EvmBalances::decrease_balance( + &alice(), + decreased_balance, + Precision::Exact, + Preservation::Expendable, + Fortitude::Polite + )); + + // Assert state changes. + assert_eq!(EvmBalances::total_balance(&alice()), 0); + assert!(!EvmSystem::account_exists(&alice())); + System::assert_has_event(RuntimeEvent::EvmSystem( + pallet_evm_system::Event::KilledAccount { account: alice() }, + )); + }); +} + +#[test] +fn decrease_balance_fails_funds_unavailable() { + new_test_ext().execute_with_ext(|_| { + // Check test preconditions. + assert_eq!(EvmBalances::total_balance(&alice()), INIT_BALANCE); + + let decreased_balance = INIT_BALANCE + 1; + + // Invoke the function under test. + assert_noop!( + EvmBalances::decrease_balance( + &alice(), + decreased_balance, + Precision::Exact, + Preservation::Preserve, + Fortitude::Polite + ), + TokenError::FundsUnavailable + ); + }); +} From ebbfa85ccda0c65a6ec7c8b09950661afd2cc425 Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Thu, 4 Jan 2024 13:24:53 +0300 Subject: [PATCH 25/39] Add increase_balance related tests --- frame/evm-balances/src/tests/fungible.rs | 59 ++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/frame/evm-balances/src/tests/fungible.rs b/frame/evm-balances/src/tests/fungible.rs index e57b405476..c49ae2a3da 100644 --- a/frame/evm-balances/src/tests/fungible.rs +++ b/frame/evm-balances/src/tests/fungible.rs @@ -268,3 +268,62 @@ fn decrease_balance_fails_funds_unavailable() { ); }); } + +#[test] +fn increase_balance_works() { + new_test_ext().execute_with_ext(|_| { + // Check test preconditions. + assert_eq!(EvmBalances::total_balance(&alice()), INIT_BALANCE); + + let increased_balance = 100; + + // Invoke the function under test. + assert_ok!(EvmBalances::increase_balance( + &alice(), + increased_balance, + Precision::Exact, + )); + + // Assert state changes. + assert_eq!( + EvmBalances::total_balance(&alice()), + INIT_BALANCE + increased_balance + ); + }); +} + +#[test] +fn increase_balance_works_best_effort() { + new_test_ext().execute_with_ext(|_| { + // Check test preconditions. + assert_eq!(EvmBalances::total_balance(&alice()), INIT_BALANCE); + + let increased_balance = u64::MAX; + + // Invoke the function under test. + assert_ok!(EvmBalances::increase_balance( + &alice(), + increased_balance, + Precision::BestEffort, + )); + + // Assert state changes. + assert_eq!(EvmBalances::total_balance(&alice()), u64::MAX); + }); +} + +#[test] +fn increase_balance_fails_overflow() { + new_test_ext().execute_with_ext(|_| { + // Check test preconditions. + assert_eq!(EvmBalances::total_balance(&alice()), INIT_BALANCE); + + let increased_balance = u64::MAX; + + // Invoke the function under test. + assert_noop!( + EvmBalances::increase_balance(&alice(), increased_balance, Precision::Exact), + ArithmeticError::Overflow + ); + }); +} From f1f7d5f7ed9c2b0e07248f4a5095c911e7c215f4 Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Thu, 4 Jan 2024 13:25:51 +0300 Subject: [PATCH 26/39] Add deactivate_reactivate_works test --- frame/evm-balances/src/tests/fungible.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/frame/evm-balances/src/tests/fungible.rs b/frame/evm-balances/src/tests/fungible.rs index c49ae2a3da..c31aa960e1 100644 --- a/frame/evm-balances/src/tests/fungible.rs +++ b/frame/evm-balances/src/tests/fungible.rs @@ -327,3 +327,21 @@ fn increase_balance_fails_overflow() { ); }); } + +#[test] +fn deactivate_reactivate_works() { + new_test_ext().execute_with_ext(|_| { + // Check test preconditions. + assert_eq!(>::get(), 0); + + // Deactivate some balance. + EvmBalances::deactivate(100); + // Assert state changes. + assert_eq!(>::get(), 100); + + // Reactivate some balance. + EvmBalances::reactivate(40); + // Assert state changes. + assert_eq!(>::get(), 60); + }); +} From 3338d190019214bdd65c0f35a55b6999e3f9670e Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Thu, 4 Jan 2024 13:36:02 +0300 Subject: [PATCH 27/39] Add mint_into related tests --- frame/evm-balances/src/tests/fungible.rs | 50 +++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/frame/evm-balances/src/tests/fungible.rs b/frame/evm-balances/src/tests/fungible.rs index c31aa960e1..3e406adf0b 100644 --- a/frame/evm-balances/src/tests/fungible.rs +++ b/frame/evm-balances/src/tests/fungible.rs @@ -3,8 +3,9 @@ use frame_support::{ assert_noop, assert_ok, traits::{ - fungible::{Inspect, Unbalanced}, + fungible::{Inspect, Mutate, Unbalanced}, tokens::Precision, + RankedMembers, }, }; use sp_core::H160; @@ -345,3 +346,50 @@ fn deactivate_reactivate_works() { assert_eq!(>::get(), 60); }); } + +#[test] +fn mint_into_works() { + new_test_ext().execute_with_ext(|_| { + // Check test preconditions. + assert_eq!(EvmBalances::total_balance(&alice()), INIT_BALANCE); + assert_eq!(EvmBalances::total_issuance(), 2 * INIT_BALANCE); + + // Set block number to enable events. + System::set_block_number(1); + + let minted_balance = 10; + + // Invoke the function under test. + assert_ok!(EvmBalances::mint_into(&alice(), minted_balance)); + + // Assert state changes. + assert_eq!( + EvmBalances::total_balance(&alice()), + INIT_BALANCE + minted_balance + ); + assert_eq!( + EvmBalances::total_issuance(), + 2 * INIT_BALANCE + minted_balance + ); + System::assert_has_event(RuntimeEvent::EvmBalances(Event::Minted { + who: alice(), + amount: minted_balance, + })); + }); +} + +#[test] +fn mint_into_fails_overflow() { + new_test_ext().execute_with_ext(|_| { + // Check test preconditions. + assert_eq!(EvmBalances::total_balance(&alice()), INIT_BALANCE); + + let minted_balance = u64::MAX; + + // Invoke the function under test. + assert_noop!( + EvmBalances::mint_into(&alice(), minted_balance), + ArithmeticError::Overflow + ); + }); +} From a866a108abf1d755c0d880a17e0a2b24813d59ab Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Thu, 4 Jan 2024 13:42:36 +0300 Subject: [PATCH 28/39] Add burn_from related tests --- frame/evm-balances/src/tests/fungible.rs | 129 +++++++++++++++++++++++ 1 file changed, 129 insertions(+) diff --git a/frame/evm-balances/src/tests/fungible.rs b/frame/evm-balances/src/tests/fungible.rs index 3e406adf0b..b97ac0df04 100644 --- a/frame/evm-balances/src/tests/fungible.rs +++ b/frame/evm-balances/src/tests/fungible.rs @@ -393,3 +393,132 @@ fn mint_into_fails_overflow() { ); }); } + +#[test] +fn burn_from_works() { + new_test_ext().execute_with_ext(|_| { + // Check test preconditions. + assert_eq!(EvmBalances::total_balance(&alice()), INIT_BALANCE); + assert_eq!(EvmBalances::total_issuance(), 2 * INIT_BALANCE); + + // Set block number to enable events. + System::set_block_number(1); + + let burned_balance = 10; + + // Invoke the function under test. + assert_ok!(EvmBalances::burn_from( + &alice(), + burned_balance, + Precision::Exact, + Fortitude::Polite + )); + + // Assert state changes. + assert_eq!( + EvmBalances::total_balance(&alice()), + INIT_BALANCE - burned_balance + ); + assert_eq!( + EvmBalances::total_issuance(), + 2 * INIT_BALANCE - burned_balance + ); + System::assert_has_event(RuntimeEvent::EvmBalances(Event::Burned { + who: alice(), + amount: burned_balance, + })); + }); +} + +#[test] +fn burn_from_works_best_effort() { + new_test_ext().execute_with_ext(|_| { + // Check test preconditions. + assert_eq!(EvmBalances::total_balance(&alice()), INIT_BALANCE); + assert_eq!(EvmBalances::total_issuance(), 2 * INIT_BALANCE); + + // Set block number to enable events. + System::set_block_number(1); + + let burned_balance = INIT_BALANCE + 1; + + // Invoke the function under test. + assert_ok!(EvmBalances::burn_from( + &alice(), + burned_balance, + Precision::BestEffort, + Fortitude::Polite + )); + + // Assert state changes. + assert_eq!(EvmBalances::total_balance(&alice()), 0); + assert_eq!(EvmBalances::total_issuance(), INIT_BALANCE); + System::assert_has_event(RuntimeEvent::EvmBalances(Event::Burned { + who: alice(), + amount: INIT_BALANCE, + })); + assert!(!EvmSystem::account_exists(&alice())); + System::assert_has_event(RuntimeEvent::EvmSystem( + pallet_evm_system::Event::KilledAccount { account: alice() }, + )); + }); +} + +#[test] +fn burn_from_works_exact_full_balance() { + new_test_ext().execute_with_ext(|_| { + // Check test preconditions. + assert_eq!(EvmBalances::total_balance(&alice()), INIT_BALANCE); + assert_eq!(EvmBalances::total_issuance(), 2 * INIT_BALANCE); + + // Set block number to enable events. + System::set_block_number(1); + + let burned_balance = INIT_BALANCE; + + // Invoke the function under test. + assert_ok!(EvmBalances::burn_from( + &alice(), + burned_balance, + Precision::Exact, + Fortitude::Polite + )); + + // Assert state changes. + assert_eq!(EvmBalances::total_balance(&alice()), 0); + assert_eq!(EvmBalances::total_issuance(), INIT_BALANCE); + System::assert_has_event(RuntimeEvent::EvmBalances(Event::Burned { + who: alice(), + amount: INIT_BALANCE, + })); + assert!(!EvmSystem::account_exists(&alice())); + System::assert_has_event(RuntimeEvent::EvmSystem( + pallet_evm_system::Event::KilledAccount { account: alice() }, + )); + }); +} + +#[test] +fn burn_from_works_fails_funds_unavailable() { + new_test_ext().execute_with_ext(|_| { + // Check test preconditions. + assert_eq!(EvmBalances::total_balance(&alice()), INIT_BALANCE); + assert_eq!(EvmBalances::total_issuance(), 2 * INIT_BALANCE); + + // Set block number to enable events. + System::set_block_number(1); + + let burned_balance = INIT_BALANCE + 1; + + // Invoke the function under test. + assert_noop!( + EvmBalances::burn_from( + &alice(), + burned_balance, + Precision::Exact, + Fortitude::Polite + ), + TokenError::FundsUnavailable + ); + }); +} From 68ab71aa7c125496181d14a4d7869c449b68bfdb Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Thu, 4 Jan 2024 13:55:16 +0300 Subject: [PATCH 29/39] Add shelve related tests --- frame/evm-balances/src/tests/fungible.rs | 82 +++++++++++++++++++++++- 1 file changed, 81 insertions(+), 1 deletion(-) diff --git a/frame/evm-balances/src/tests/fungible.rs b/frame/evm-balances/src/tests/fungible.rs index b97ac0df04..fecd5df88d 100644 --- a/frame/evm-balances/src/tests/fungible.rs +++ b/frame/evm-balances/src/tests/fungible.rs @@ -499,7 +499,7 @@ fn burn_from_works_exact_full_balance() { } #[test] -fn burn_from_works_fails_funds_unavailable() { +fn burn_from_fails_funds_unavailable() { new_test_ext().execute_with_ext(|_| { // Check test preconditions. assert_eq!(EvmBalances::total_balance(&alice()), INIT_BALANCE); @@ -522,3 +522,83 @@ fn burn_from_works_fails_funds_unavailable() { ); }); } + +#[test] +fn shelve_works() { + new_test_ext().execute_with_ext(|_| { + // Check test preconditions. + assert_eq!(EvmBalances::total_balance(&alice()), INIT_BALANCE); + assert_eq!(EvmBalances::total_issuance(), 2 * INIT_BALANCE); + + // Set block number to enable events. + System::set_block_number(1); + + let shelved_balance = 10; + + // Invoke the function under test. + assert_ok!(EvmBalances::shelve(&alice(), shelved_balance)); + + // Assert state changes. + assert_eq!( + EvmBalances::total_balance(&alice()), + INIT_BALANCE - shelved_balance + ); + assert_eq!( + EvmBalances::total_issuance(), + 2 * INIT_BALANCE - shelved_balance + ); + System::assert_has_event(RuntimeEvent::EvmBalances(Event::Suspended { + who: alice(), + amount: shelved_balance, + })); + }); +} + +#[test] +fn shelve_works_exact_full_balance() { + new_test_ext().execute_with_ext(|_| { + // Check test preconditions. + assert_eq!(EvmBalances::total_balance(&alice()), INIT_BALANCE); + assert_eq!(EvmBalances::total_issuance(), 2 * INIT_BALANCE); + + // Set block number to enable events. + System::set_block_number(1); + + let shelved_balance = INIT_BALANCE; + + // Invoke the function under test. + assert_ok!(EvmBalances::shelve(&alice(), shelved_balance)); + + // Assert state changes. + assert_eq!(EvmBalances::total_balance(&alice()), 0); + assert_eq!(EvmBalances::total_issuance(), INIT_BALANCE); + System::assert_has_event(RuntimeEvent::EvmBalances(Event::Suspended { + who: alice(), + amount: INIT_BALANCE, + })); + assert!(!EvmSystem::account_exists(&alice())); + System::assert_has_event(RuntimeEvent::EvmSystem( + pallet_evm_system::Event::KilledAccount { account: alice() }, + )); + }); +} + +#[test] +fn shelve_fails_funds_unavailable() { + new_test_ext().execute_with_ext(|_| { + // Check test preconditions. + assert_eq!(EvmBalances::total_balance(&alice()), INIT_BALANCE); + assert_eq!(EvmBalances::total_issuance(), 2 * INIT_BALANCE); + + // Set block number to enable events. + System::set_block_number(1); + + let shelved_balance = INIT_BALANCE + 1; + + // Invoke the function under test. + assert_noop!( + EvmBalances::shelve(&alice(), shelved_balance), + TokenError::FundsUnavailable + ); + }); +} From 293b45cdca2e0eb4574e53a59360e6542a1f6ce3 Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Thu, 4 Jan 2024 13:57:25 +0300 Subject: [PATCH 30/39] Add restored related tests --- frame/evm-balances/src/tests/fungible.rs | 47 ++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/frame/evm-balances/src/tests/fungible.rs b/frame/evm-balances/src/tests/fungible.rs index fecd5df88d..46cb1255e0 100644 --- a/frame/evm-balances/src/tests/fungible.rs +++ b/frame/evm-balances/src/tests/fungible.rs @@ -602,3 +602,50 @@ fn shelve_fails_funds_unavailable() { ); }); } + +#[test] +fn restore_works() { + new_test_ext().execute_with_ext(|_| { + // Check test preconditions. + assert_eq!(EvmBalances::total_balance(&alice()), INIT_BALANCE); + assert_eq!(EvmBalances::total_issuance(), 2 * INIT_BALANCE); + + // Set block number to enable events. + System::set_block_number(1); + + let restored_balance = 10; + + // Invoke the function under test. + assert_ok!(EvmBalances::restore(&alice(), restored_balance)); + + // Assert state changes. + assert_eq!( + EvmBalances::total_balance(&alice()), + INIT_BALANCE + restored_balance + ); + assert_eq!( + EvmBalances::total_issuance(), + 2 * INIT_BALANCE + restored_balance + ); + System::assert_has_event(RuntimeEvent::EvmBalances(Event::Restored { + who: alice(), + amount: restored_balance, + })); + }); +} + +#[test] +fn restore_fails_overflow() { + new_test_ext().execute_with_ext(|_| { + // Check test preconditions. + assert_eq!(EvmBalances::total_balance(&alice()), INIT_BALANCE); + + let restored_balance = u64::MAX; + + // Invoke the function under test. + assert_noop!( + EvmBalances::restore(&alice(), restored_balance), + ArithmeticError::Overflow + ); + }); +} From 028f051d71dfa0c7f067fb351874b4846b823a75 Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Thu, 4 Jan 2024 14:10:53 +0300 Subject: [PATCH 31/39] Add transfer related tests --- frame/evm-balances/src/tests/fungible.rs | 133 ++++++++++++++++++++++- 1 file changed, 132 insertions(+), 1 deletion(-) diff --git a/frame/evm-balances/src/tests/fungible.rs b/frame/evm-balances/src/tests/fungible.rs index 46cb1255e0..08c31eda19 100644 --- a/frame/evm-balances/src/tests/fungible.rs +++ b/frame/evm-balances/src/tests/fungible.rs @@ -5,7 +5,6 @@ use frame_support::{ traits::{ fungible::{Inspect, Mutate, Unbalanced}, tokens::Precision, - RankedMembers, }, }; use sp_core::H160; @@ -649,3 +648,135 @@ fn restore_fails_overflow() { ); }); } + +#[test] +fn transfer_works() { + new_test_ext().execute_with_ext(|_| { + // Check test preconditions. + assert_eq!(EvmBalances::total_balance(&alice()), INIT_BALANCE); + + let transfered_amount = 100; + + // Set block number to enable events. + System::set_block_number(1); + + // Invoke the function under test. + assert_ok!(EvmBalances::transfer( + &alice(), + &bob(), + transfered_amount, + Preservation::Preserve + )); + + // Assert state changes. + assert_eq!( + EvmBalances::total_balance(&alice()), + INIT_BALANCE - transfered_amount + ); + assert_eq!( + EvmBalances::total_balance(&bob()), + INIT_BALANCE + transfered_amount + ); + System::assert_has_event(RuntimeEvent::EvmBalances(Event::Transfer { + from: alice(), + to: bob(), + amount: transfered_amount, + })); + }); +} + +#[test] +fn transfer_works_full_balance() { + new_test_ext().execute_with_ext(|_| { + // Check test preconditions. + assert_eq!(EvmBalances::total_balance(&alice()), INIT_BALANCE); + + let transfered_amount = INIT_BALANCE; + + // Set block number to enable events. + System::set_block_number(1); + + // Invoke the function under test. + assert_ok!(EvmBalances::transfer( + &alice(), + &bob(), + transfered_amount, + Preservation::Expendable + )); + + // Assert state changes. + assert_eq!( + EvmBalances::total_balance(&alice()), + INIT_BALANCE - transfered_amount + ); + assert_eq!( + EvmBalances::total_balance(&bob()), + INIT_BALANCE + transfered_amount + ); + System::assert_has_event(RuntimeEvent::EvmBalances(Event::Transfer { + from: alice(), + to: bob(), + amount: transfered_amount, + })); + assert!(!EvmSystem::account_exists(&alice())); + System::assert_has_event(RuntimeEvent::EvmSystem( + pallet_evm_system::Event::KilledAccount { account: alice() }, + )); + }); +} + +#[test] +fn transfer_fails_funds_unavailable() { + new_test_ext().execute_with_ext(|_| { + // Check test preconditions. + assert_eq!(EvmBalances::total_balance(&alice()), INIT_BALANCE); + + let transfered_amount = INIT_BALANCE + 1; + + // Set block number to enable events. + System::set_block_number(1); + + // Invoke the function under test. + assert_noop!( + EvmBalances::transfer(&alice(), &bob(), transfered_amount, Preservation::Preserve), + TokenError::FundsUnavailable + ); + }); +} + +#[test] +fn transfer_fails_not_expendable() { + new_test_ext().execute_with_ext(|_| { + // Check test preconditions. + assert_eq!(EvmBalances::total_balance(&alice()), INIT_BALANCE); + + let transfered_amount = INIT_BALANCE; + + // Set block number to enable events. + System::set_block_number(1); + + // Invoke the function under test. + assert_noop!( + EvmBalances::transfer(&alice(), &bob(), transfered_amount, Preservation::Preserve), + TokenError::NotExpendable + ); + }); +} + +#[test] +fn transfer_fails_underflow() { + new_test_ext().execute_with(|| { + // Prepare test preconditions. + let charlie = H160::from_str("1000000000000000000000000000000000000003").unwrap(); + let eve = H160::from_str("1000000000000000000000000000000000000004").unwrap(); + EvmBalances::set_balance(&charlie, u64::MAX); + EvmBalances::set_balance(&eve, 1); + + // Invoke the function under test. + assert_noop!( + EvmBalances::transfer(&charlie, &eve, u64::MAX, Preservation::Expendable), + // Withdraw consequence is checked first by reducing total issuance. + ArithmeticError::Underflow, + ); + }); +} From 4bbd2b531c071507b1e0e9642732ba650e95d5e8 Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Thu, 4 Jan 2024 14:39:07 +0300 Subject: [PATCH 32/39] Add balanced related tests --- frame/evm-balances/src/tests/fungible.rs | 194 ++++++++++++++++++++++- 1 file changed, 193 insertions(+), 1 deletion(-) diff --git a/frame/evm-balances/src/tests/fungible.rs b/frame/evm-balances/src/tests/fungible.rs index 08c31eda19..aece51d3f3 100644 --- a/frame/evm-balances/src/tests/fungible.rs +++ b/frame/evm-balances/src/tests/fungible.rs @@ -3,7 +3,7 @@ use frame_support::{ assert_noop, assert_ok, traits::{ - fungible::{Inspect, Mutate, Unbalanced}, + fungible::{Balanced, Inspect, Mutate, Unbalanced}, tokens::Precision, }, }; @@ -780,3 +780,195 @@ fn transfer_fails_underflow() { ); }); } + +#[test] +fn rescind_works() { + new_test_ext().execute_with_ext(|_| { + // Check test preconditions. + assert_eq!(EvmBalances::total_issuance(), 2 * INIT_BALANCE); + + // Set block number to enable events. + System::set_block_number(1); + + let rescinded_balance = 100; + + // Burn some balance. + let imbalance = EvmBalances::rescind(rescinded_balance); + + // Assert state changes. + assert_eq!( + EvmBalances::total_issuance(), + 2 * INIT_BALANCE - rescinded_balance + ); + drop(imbalance); + assert_eq!(EvmBalances::total_issuance(), 2 * INIT_BALANCE); + System::assert_has_event(RuntimeEvent::EvmBalances(Event::Rescinded { + amount: rescinded_balance, + })); + }); +} + +#[test] +fn issue_works() { + new_test_ext().execute_with_ext(|_| { + // Check test preconditions. + assert_eq!(EvmBalances::total_issuance(), 2 * INIT_BALANCE); + + // Set block number to enable events. + System::set_block_number(1); + + let issued_balance = 100; + + // Burn some balance. + let imbalance = EvmBalances::issue(issued_balance); + + // Assert state changes. + assert_eq!( + EvmBalances::total_issuance(), + 2 * INIT_BALANCE + issued_balance + ); + drop(imbalance); + assert_eq!(EvmBalances::total_issuance(), 2 * INIT_BALANCE); + System::assert_has_event(RuntimeEvent::EvmBalances(Event::Issued { + amount: issued_balance, + })); + }); +} + +#[test] +fn deposit_flow_works() { + new_test_ext().execute_with_ext(|_| { + // Check test preconditions. + assert_eq!(EvmBalances::total_balance(&alice()), INIT_BALANCE); + + let deposited_amount = 10; + + // Set block number to enable events. + System::set_block_number(1); + + // Invoke the function under test. + let debt = EvmBalances::deposit(&alice(), deposited_amount, Precision::Exact).unwrap(); + + // Assert state changes. + assert_eq!( + EvmBalances::total_balance(&alice()), + INIT_BALANCE + deposited_amount + ); + System::assert_has_event(RuntimeEvent::EvmBalances(Event::Deposit { + who: alice(), + amount: deposited_amount, + })); + assert_eq!(EvmBalances::total_issuance(), 2 * INIT_BALANCE); + + let _ = EvmBalances::settle(&bob(), debt, Preservation::Expendable); + assert_eq!(EvmBalances::total_issuance(), 2 * INIT_BALANCE); + }); +} + +#[test] +fn deposit_works_new_account() { + new_test_ext().execute_with_ext(|_| { + let charlie = H160::from_str("1000000000000000000000000000000000000003").unwrap(); + + // Check test preconditions. + assert_eq!(EvmBalances::total_balance(&charlie), 0); + + let deposited_amount = 10; + + // Set block number to enable events. + System::set_block_number(1); + + // Invoke the function under test. + let debt = EvmBalances::deposit(&charlie, deposited_amount, Precision::Exact).unwrap(); + + // Assert state changes. + assert_eq!(EvmBalances::total_balance(&charlie), deposited_amount); + System::assert_has_event(RuntimeEvent::EvmBalances(Event::Deposit { + who: charlie, + amount: deposited_amount, + })); + assert_eq!(EvmBalances::total_issuance(), 2 * INIT_BALANCE); + let _ = EvmBalances::settle(&bob(), debt, Preservation::Expendable); + assert_eq!(EvmBalances::total_issuance(), 2 * INIT_BALANCE); + assert!(EvmSystem::account_exists(&charlie)); + System::assert_has_event(RuntimeEvent::EvmSystem( + pallet_evm_system::Event::NewAccount { account: charlie }, + )); + }); +} + +#[test] +fn withdraw_works() { + new_test_ext().execute_with_ext(|_| { + // Check test preconditions. + assert_eq!(EvmBalances::total_balance(&alice()), INIT_BALANCE); + + let withdrawed_amount = 1000; + + // Set block number to enable events. + System::set_block_number(1); + + // Invoke the function under test. + let credit = EvmBalances::withdraw( + &alice(), + withdrawed_amount, + Precision::Exact, + Preservation::Preserve, + Fortitude::Polite, + ) + .unwrap(); + + // Assert state changes. + assert_eq!( + EvmBalances::total_balance(&alice()), + INIT_BALANCE - withdrawed_amount + ); + System::assert_has_event(RuntimeEvent::EvmBalances(Event::Withdraw { + who: alice(), + amount: withdrawed_amount, + })); + assert_eq!(EvmBalances::total_issuance(), 2 * INIT_BALANCE); + let _ = EvmBalances::resolve(&bob(), credit); + assert_eq!(EvmBalances::total_issuance(), 2 * INIT_BALANCE); + }); +} + +#[test] +fn withdraw_works_full_balance() { + new_test_ext().execute_with_ext(|_| { + // Check test preconditions. + assert_eq!(EvmBalances::total_balance(&alice()), INIT_BALANCE); + + let withdrawed_amount = INIT_BALANCE; + + // Set block number to enable events. + System::set_block_number(1); + + // Invoke the function under test. + let credit = EvmBalances::withdraw( + &alice(), + withdrawed_amount, + Precision::Exact, + Preservation::Expendable, + Fortitude::Polite, + ) + .unwrap(); + + // Assert state changes. + assert_eq!( + EvmBalances::total_balance(&alice()), + INIT_BALANCE - withdrawed_amount + ); + System::assert_has_event(RuntimeEvent::EvmBalances(crate::Event::Withdraw { + who: alice(), + amount: withdrawed_amount, + })); + assert!(!EvmSystem::account_exists(&alice())); + System::assert_has_event(RuntimeEvent::EvmSystem( + pallet_evm_system::Event::KilledAccount { account: alice() }, + )); + assert_eq!(EvmBalances::total_issuance(), 2 * INIT_BALANCE); + let _ = EvmBalances::resolve(&bob(), credit); + assert_eq!(EvmBalances::total_issuance(), 2 * INIT_BALANCE); + }); +} From bf56f3b5d315a736f42f5bb569f04498c9b54880 Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Thu, 4 Jan 2024 14:46:05 +0300 Subject: [PATCH 33/39] Undo formatting --- frame/evm-balances/src/account_data.rs | 24 ++---------------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/frame/evm-balances/src/account_data.rs b/frame/evm-balances/src/account_data.rs index 4bd3fffdd9..3b70e21bc7 100644 --- a/frame/evm-balances/src/account_data.rs +++ b/frame/evm-balances/src/account_data.rs @@ -5,17 +5,7 @@ use frame_support::traits::WithdrawReasons; use super::*; /// All balance information for an account. -#[derive( - Encode, - Decode, - Clone, - PartialEq, - Eq, - Default, - RuntimeDebug, - MaxEncodedLen, - TypeInfo -)] +#[derive(Encode, Decode, Clone, PartialEq, Eq, Default, RuntimeDebug, MaxEncodedLen, TypeInfo)] pub struct AccountData { /// Non-reserved part of the balance. There may still be restrictions on this, but it is the /// total pool what may in principle be transferred, reserved and used for tipping. @@ -33,17 +23,7 @@ impl AccountData { } /// Simplified reasons for withdrawing balance. -#[derive( - Encode, - Decode, - Clone, - Copy, - PartialEq, - Eq, - RuntimeDebug, - MaxEncodedLen, - TypeInfo -)] +#[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug, MaxEncodedLen, TypeInfo)] pub enum Reasons { /// Paying system transaction fees. Fee = 0, From dca85498cdbf1de164214f93b8d131ae5d0444b4 Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Fri, 15 Mar 2024 10:59:46 +0300 Subject: [PATCH 34/39] Simplify reducible logic implementation --- frame/evm-balances/src/impl_fungible.rs | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/frame/evm-balances/src/impl_fungible.rs b/frame/evm-balances/src/impl_fungible.rs index 73b529c4e0..4019c4d930 100644 --- a/frame/evm-balances/src/impl_fungible.rs +++ b/frame/evm-balances/src/impl_fungible.rs @@ -31,18 +31,11 @@ impl, I: 'static> fungible::Inspect<>::AccountId> fo _force: Fortitude, ) -> Self::Balance { let a = Self::account(who); - // Liquid balance is what is neither reserved nor locked/frozen. - let liquid = a.free; - match preservation { - Preservation::Expendable => liquid, - _ => { - // `must_remain_to_exist` is the part of liquid balance which must remain - // to keep total over ED. - let must_remain_to_exist = - T::ExistentialDeposit::get().saturating_sub(a.total() - liquid); - liquid.saturating_sub(must_remain_to_exist) - } - } + let untouchable = match preservation { + Preservation::Expendable => Zero::zero(), + _ => T::ExistentialDeposit::get(), + }; + a.free.saturating_sub(untouchable) } fn can_deposit( From 069411cfd01326450468e6831deee369a40a58f4 Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Fri, 15 Mar 2024 11:04:56 +0300 Subject: [PATCH 35/39] Simplify can_deposit implementation --- frame/evm-balances/src/impl_fungible.rs | 22 +++++++++++++++++- frame/evm-balances/src/lib.rs | 31 ------------------------- 2 files changed, 21 insertions(+), 32 deletions(-) diff --git a/frame/evm-balances/src/impl_fungible.rs b/frame/evm-balances/src/impl_fungible.rs index 4019c4d930..e0a6c24e21 100644 --- a/frame/evm-balances/src/impl_fungible.rs +++ b/frame/evm-balances/src/impl_fungible.rs @@ -43,7 +43,27 @@ impl, I: 'static> fungible::Inspect<>::AccountId> fo amount: Self::Balance, provenance: Provenance, ) -> DepositConsequence { - Self::deposit_consequence(who, amount, provenance) + if amount.is_zero() { + return DepositConsequence::Success; + } + + if provenance == Provenance::Minted + && TotalIssuance::::get().checked_add(&amount).is_none() + { + return DepositConsequence::Overflow; + } + + let account = Self::account(who); + match account.total().checked_add(&amount) { + None => return DepositConsequence::Overflow, + Some(x) if x < T::ExistentialDeposit::get() => return DepositConsequence::BelowMinimum, + Some(x) => x, + }; + + // NOTE: We assume that we are a provider, so don't need to do any checks in the + // case of account creation. + + DepositConsequence::Success } fn can_withdraw( diff --git a/frame/evm-balances/src/lib.rs b/frame/evm-balances/src/lib.rs index cd198f50b9..bdeee8fca1 100644 --- a/frame/evm-balances/src/lib.rs +++ b/frame/evm-balances/src/lib.rs @@ -306,37 +306,6 @@ impl, I: 'static> Pallet { }) } - fn deposit_consequence( - who: &>::AccountId, - amount: T::Balance, - provenance: Provenance, - ) -> DepositConsequence { - if amount.is_zero() { - return DepositConsequence::Success; - } - - if provenance == Provenance::Minted - && TotalIssuance::::get().checked_add(&amount).is_none() - { - return DepositConsequence::Overflow; - } - - let account = Self::account(who); - let new_total_balance = match account.total().checked_add(&amount) { - Some(x) => x, - None => return DepositConsequence::Overflow, - }; - - if new_total_balance < T::ExistentialDeposit::get() { - return DepositConsequence::BelowMinimum; - } - - // NOTE: We assume that we are a provider, so don't need to do any checks in the - // case of account creation. - - DepositConsequence::Success - } - fn withdraw_consequence( who: &>::AccountId, amount: T::Balance, From 6a1e5ef91350b46a9df6159605d6f19b64ea2373 Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Fri, 15 Mar 2024 11:09:23 +0300 Subject: [PATCH 36/39] Simplify can_withdraw implementation --- frame/evm-balances/src/impl_fungible.rs | 25 ++++++++++++++++++++- frame/evm-balances/src/lib.rs | 30 ------------------------- 2 files changed, 24 insertions(+), 31 deletions(-) diff --git a/frame/evm-balances/src/impl_fungible.rs b/frame/evm-balances/src/impl_fungible.rs index e0a6c24e21..1bfee584ef 100644 --- a/frame/evm-balances/src/impl_fungible.rs +++ b/frame/evm-balances/src/impl_fungible.rs @@ -70,7 +70,30 @@ impl, I: 'static> fungible::Inspect<>::AccountId> fo who: &>::AccountId, amount: Self::Balance, ) -> WithdrawConsequence { - Self::withdraw_consequence(who, amount) + if amount.is_zero() { + return WithdrawConsequence::Success; + } + + if TotalIssuance::::get().checked_sub(&amount).is_none() { + return WithdrawConsequence::Underflow; + } + + let account = Self::account(who); + let new_free_balance = match account.free.checked_sub(&amount) { + Some(x) => x, + None => return WithdrawConsequence::BalanceLow, + }; + + // Provider restriction - total account balance cannot be reduced to zero if it cannot + // sustain the loss of a provider reference. + // NOTE: This assumes that the pallet is a provider (which is true). Is this ever changes, + // then this will need to adapt accordingly. + let ed = T::ExistentialDeposit::get(); + if new_free_balance < ed { + return WithdrawConsequence::ReducedToZero(new_free_balance); + } + + WithdrawConsequence::Success } } diff --git a/frame/evm-balances/src/lib.rs b/frame/evm-balances/src/lib.rs index bdeee8fca1..5cdee7374e 100644 --- a/frame/evm-balances/src/lib.rs +++ b/frame/evm-balances/src/lib.rs @@ -305,34 +305,4 @@ impl, I: 'static> Pallet { (result, maybe_dust) }) } - - fn withdraw_consequence( - who: &>::AccountId, - amount: T::Balance, - ) -> WithdrawConsequence { - if amount.is_zero() { - return WithdrawConsequence::Success; - } - - if TotalIssuance::::get().checked_sub(&amount).is_none() { - return WithdrawConsequence::Underflow; - } - - let account = Self::account(who); - let new_total_balance = match account.total().checked_sub(&amount) { - Some(x) => x, - None => return WithdrawConsequence::BalanceLow, - }; - - // Provider restriction - total account balance cannot be reduced to zero if it cannot - // sustain the loss of a provider reference. - // NOTE: This assumes that the pallet is a provider (which is true). Is this ever changes, - // then this will need to adapt accordingly. - let ed = T::ExistentialDeposit::get(); - if new_total_balance < ed { - return WithdrawConsequence::ReducedToZero(new_total_balance); - } - - WithdrawConsequence::Success - } } From beeb15dc253af398375c2dee717c3202574e7892 Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Fri, 15 Mar 2024 11:12:02 +0300 Subject: [PATCH 37/39] Properly use semantics of total and free balances --- frame/evm-balances/src/impl_fungible.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frame/evm-balances/src/impl_fungible.rs b/frame/evm-balances/src/impl_fungible.rs index 1bfee584ef..5c86426e39 100644 --- a/frame/evm-balances/src/impl_fungible.rs +++ b/frame/evm-balances/src/impl_fungible.rs @@ -22,7 +22,7 @@ impl, I: 'static> fungible::Inspect<>::AccountId> fo } fn balance(who: &>::AccountId) -> Self::Balance { - Self::account(who).total() + Self::account(who).free } fn reducible_balance( @@ -54,7 +54,7 @@ impl, I: 'static> fungible::Inspect<>::AccountId> fo } let account = Self::account(who); - match account.total().checked_add(&amount) { + match account.free.checked_add(&amount) { None => return DepositConsequence::Overflow, Some(x) if x < T::ExistentialDeposit::get() => return DepositConsequence::BelowMinimum, Some(x) => x, From f57fed2513c29fd9e05eceef06ef9f171558e455 Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Fri, 15 Mar 2024 19:21:33 +0300 Subject: [PATCH 38/39] Improve reducable_balance_works test --- frame/evm-balances/src/tests/fungible.rs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/frame/evm-balances/src/tests/fungible.rs b/frame/evm-balances/src/tests/fungible.rs index aece51d3f3..feb422734d 100644 --- a/frame/evm-balances/src/tests/fungible.rs +++ b/frame/evm-balances/src/tests/fungible.rs @@ -56,23 +56,35 @@ fn balance_works() { #[test] fn reducable_balance_works() { new_test_ext().execute_with_ext(|_| { - // Check the reducable balance value in `Expendable` case. assert_eq!( EvmBalances::reducible_balance(&alice(), Preservation::Expendable, Fortitude::Polite), INIT_BALANCE ); - // Check the reducable balance value in `Preserve` case. + assert_eq!( + EvmBalances::reducible_balance(&alice(), Preservation::Expendable, Fortitude::Force), + INIT_BALANCE + ); + assert_eq!( EvmBalances::reducible_balance(&alice(), Preservation::Preserve, Fortitude::Polite), INIT_BALANCE - 1 ); - // Check the reducable balance value in `Protect` case. + assert_eq!( + EvmBalances::reducible_balance(&alice(), Preservation::Preserve, Fortitude::Force), + INIT_BALANCE - 1 + ); + assert_eq!( EvmBalances::reducible_balance(&alice(), Preservation::Protect, Fortitude::Polite), INIT_BALANCE - 1 ); + + assert_eq!( + EvmBalances::reducible_balance(&alice(), Preservation::Protect, Fortitude::Force), + INIT_BALANCE - 1 + ); }); } From 4df109e2a48e625ecb09977c5d33a25e99d86dac Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Fri, 15 Mar 2024 19:36:36 +0300 Subject: [PATCH 39/39] Fix test race conditions --- frame/evm-balances/src/tests/currency.rs | 4 ++-- frame/evm-balances/src/tests/fungible.rs | 2 +- frame/evm-balances/src/tests/mod.rs | 12 ++++++------ 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/frame/evm-balances/src/tests/currency.rs b/frame/evm-balances/src/tests/currency.rs index 918496e513..80e1052d19 100644 --- a/frame/evm-balances/src/tests/currency.rs +++ b/frame/evm-balances/src/tests/currency.rs @@ -491,7 +491,7 @@ fn withdraw_fails_expendability() { #[test] fn make_free_balance_be_works() { - new_test_ext().execute_with(|| { + new_test_ext().execute_with_ext(|_| { // Prepare test preconditions. let charlie = H160::from_str("1000000000000000000000000000000000000003").unwrap(); let made_free_balance = 100; @@ -535,7 +535,7 @@ fn evm_system_account_should_be_reaped() { #[test] fn transferring_too_high_value_should_not_panic() { - new_test_ext().execute_with(|| { + new_test_ext().execute_with_ext(|_| { // Prepare test preconditions. let charlie = H160::from_str("1000000000000000000000000000000000000003").unwrap(); let eve = H160::from_str("1000000000000000000000000000000000000004").unwrap(); diff --git a/frame/evm-balances/src/tests/fungible.rs b/frame/evm-balances/src/tests/fungible.rs index feb422734d..6241c6322d 100644 --- a/frame/evm-balances/src/tests/fungible.rs +++ b/frame/evm-balances/src/tests/fungible.rs @@ -777,7 +777,7 @@ fn transfer_fails_not_expendable() { #[test] fn transfer_fails_underflow() { - new_test_ext().execute_with(|| { + new_test_ext().execute_with_ext(|_| { // Prepare test preconditions. let charlie = H160::from_str("1000000000000000000000000000000000000003").unwrap(); let eve = H160::from_str("1000000000000000000000000000000000000004").unwrap(); diff --git a/frame/evm-balances/src/tests/mod.rs b/frame/evm-balances/src/tests/mod.rs index 628904b400..a87abdd238 100644 --- a/frame/evm-balances/src/tests/mod.rs +++ b/frame/evm-balances/src/tests/mod.rs @@ -28,7 +28,7 @@ fn basic_setup_works() { #[test] fn evm_fee_deduction() { - new_test_ext().execute_with(|| { + new_test_ext().execute_with_ext(|_| { let charlie = H160::from_str("1000000000000000000000000000000000000003").unwrap(); // Seed account @@ -52,7 +52,7 @@ fn evm_fee_deduction() { #[test] fn evm_issuance_after_tip() { - new_test_ext().execute_with(|| { + new_test_ext().execute_with_ext(|_| { let before_tip = ::Currency::total_issuance(); let gas_limit: u64 = 1_000_000; @@ -88,7 +88,7 @@ fn evm_issuance_after_tip() { #[test] fn evm_refunds_should_work() { - new_test_ext().execute_with(|| { + new_test_ext().execute_with_ext(|_| { let before_call = EVM::account_basic(&alice()).0.balance; // Gas price is not part of the actual fee calculations anymore, only the base fee. // @@ -124,7 +124,7 @@ fn evm_refunds_should_work() { #[test] fn evm_refunds_and_priority_should_work() { - new_test_ext().execute_with(|| { + new_test_ext().execute_with_ext(|_| { let before_call = EVM::account_basic(&alice()).0.balance; // We deliberately set a base fee + max tip > max fee. // The effective priority tip will be 1GWEI instead 1.5GWEI: @@ -165,7 +165,7 @@ fn evm_refunds_and_priority_should_work() { #[test] fn evm_call_should_fail_with_priority_greater_than_max_fee() { - new_test_ext().execute_with(|| { + new_test_ext().execute_with_ext(|_| { // Max priority greater than max fee should fail. let tip: u128 = 1_100_000_000; @@ -196,7 +196,7 @@ fn evm_call_should_fail_with_priority_greater_than_max_fee() { #[test] fn evm_call_should_succeed_with_priority_equal_to_max_fee() { - new_test_ext().execute_with(|| { + new_test_ext().execute_with_ext(|_| { let tip: u128 = 1_000_000_000; let gas_limit: u64 = 1_000_000;