From 7dfc4a451c0944bf67892c2025db154669d963a0 Mon Sep 17 00:00:00 2001 From: lemunozm Date: Mon, 19 Aug 2024 10:00:39 +0200 Subject: [PATCH] num wrapper time --- Cargo.lock | 3 + libs/mocks/src/pools.rs | 3 +- libs/mocks/src/time.rs | 5 +- libs/primitives/Cargo.toml | 4 + libs/primitives/src/lib.rs | 35 +- libs/test-utils/src/mocks/nav.rs | 5 +- libs/traits/src/interest.rs | 4 +- libs/traits/src/lib.rs | 44 +- libs/traits/src/time.rs | 66 ++ libs/types/src/epoch.rs | 2 +- libs/types/src/permissions.rs | 68 +- libs/types/src/pools.rs | 91 +-- libs/types/src/portfolio.rs | 9 +- libs/utils/Cargo.toml | 2 + libs/utils/src/lib.rs | 3 + libs/utils/src/num_wrapper.rs | 657 ++++++++++++++++++ libs/utils/src/time.rs | 101 +++ pallets/block-rewards/src/lib.rs | 5 +- pallets/block-rewards/src/migrations.rs | 2 +- pallets/interest-accrual/Cargo.toml | 3 + pallets/interest-accrual/src/benchmarking.rs | 4 +- pallets/interest-accrual/src/lib.rs | 14 +- pallets/interest-accrual/src/mock.rs | 36 +- pallets/liquidity-pools/src/inbound.rs | 4 +- pallets/liquidity-pools/src/lib.rs | 7 +- pallets/liquidity-pools/src/message.rs | 3 +- pallets/loans/src/benchmarking.rs | 7 +- pallets/loans/src/entities/changes.rs | 3 +- pallets/loans/src/entities/interest.rs | 2 +- pallets/loans/src/entities/loans.rs | 6 +- .../loans/src/entities/pricing/external.rs | 7 +- .../loans/src/entities/pricing/internal.rs | 3 +- pallets/loans/src/lib.rs | 14 +- pallets/loans/src/tests/borrow_loan.rs | 16 +- pallets/loans/src/tests/close_loan.rs | 2 +- pallets/loans/src/tests/create_loan.rs | 4 +- pallets/loans/src/tests/mock.rs | 27 +- pallets/loans/src/tests/mod.rs | 4 +- pallets/loans/src/tests/mutate_loan.rs | 12 +- pallets/loans/src/tests/policy.rs | 22 +- .../loans/src/tests/portfolio_valuation.rs | 34 +- pallets/loans/src/tests/repay_loan.rs | 24 +- pallets/loans/src/tests/transfer_debt.rs | 6 +- pallets/loans/src/tests/util.rs | 18 +- pallets/loans/src/tests/write_off_loan.rs | 36 +- pallets/loans/src/types/cashflow.rs | 20 +- pallets/loans/src/types/policy.rs | 34 +- pallets/loans/src/types/valuation.rs | 4 +- pallets/pool-fees/src/lib.rs | 6 +- pallets/pool-fees/src/mock.rs | 8 +- pallets/pool-fees/src/tests.rs | 54 +- pallets/pool-system/src/impls.rs | 4 +- pallets/pool-system/src/lib.rs | 5 +- pallets/pool-system/src/pool_types.rs | 2 +- pallets/pool-system/src/tranches.rs | 3 +- runtime/altair/src/lib.rs | 5 +- runtime/centrifuge/src/lib.rs | 5 +- runtime/common/src/changes.rs | 2 +- runtime/common/src/oracle.rs | 3 +- runtime/development/src/lib.rs | 31 +- .../submodules/liquidity-pools | 2 +- 61 files changed, 1185 insertions(+), 430 deletions(-) create mode 100644 libs/traits/src/time.rs create mode 100644 libs/utils/src/num_wrapper.rs create mode 100644 libs/utils/src/time.rs diff --git a/Cargo.lock b/Cargo.lock index 8d26155359..df28f4d50b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1640,6 +1640,7 @@ dependencies = [ name = "cfg-primitives" version = "2.0.0" dependencies = [ + "cfg-utils", "cumulus-primitives-core", "frame-support", "frame-system", @@ -1728,6 +1729,7 @@ dependencies = [ "pallet-timestamp", "parity-scale-codec", "scale-info", + "serde", "sp-arithmetic", "sp-consensus-aura", "sp-runtime", @@ -8197,6 +8199,7 @@ name = "pallet-interest-accrual" version = "0.1.0" dependencies = [ "bitflags 1.3.2", + "cfg-mocks", "cfg-primitives", "cfg-traits", "cfg-types", diff --git a/libs/mocks/src/pools.rs b/libs/mocks/src/pools.rs index d1849b345b..d163707117 100644 --- a/libs/mocks/src/pools.rs +++ b/libs/mocks/src/pools.rs @@ -1,7 +1,8 @@ #[frame_support::pallet(dev_mode)] pub mod pallet { + use cfg_primitives::Seconds; use cfg_traits::{ - investments::InvestmentAccountant, PoolInspect, PoolReserve, Seconds, TrancheTokenPrice, + investments::InvestmentAccountant, PoolInspect, PoolReserve, TrancheTokenPrice, }; use cfg_types::investments::InvestmentInfo; use frame_support::pallet_prelude::*; diff --git a/libs/mocks/src/time.rs b/libs/mocks/src/time.rs index 15624bf654..2202cb2561 100644 --- a/libs/mocks/src/time.rs +++ b/libs/mocks/src/time.rs @@ -1,5 +1,6 @@ #[frame_support::pallet(dev_mode)] pub mod pallet { + use cfg_traits::time::TimeUnit; use frame_support::{pallet_prelude::*, traits::Time}; use mock_builder::{execute_call, register_call}; use sp_runtime::traits::AtLeast32Bit; @@ -31,10 +32,10 @@ pub mod pallet { impl frame_support::traits::UnixTime for Pallet where - T::Moment: Into, + ::Moment: TimeUnit, { fn now() -> std::time::Duration { - core::time::Duration::from_millis( as Time>::now().into()) + core::time::Duration::from_millis(::now().as_millis().into()) } } } diff --git a/libs/primitives/Cargo.toml b/libs/primitives/Cargo.toml index f8c83f3c8d..2dea224ae9 100644 --- a/libs/primitives/Cargo.toml +++ b/libs/primitives/Cargo.toml @@ -24,6 +24,7 @@ sp-core = { workspace = true } sp-io = { workspace = true } sp-runtime = { workspace = true } sp-std = { workspace = true } +cfg-utils = { workspace = true } # substrate frame dependencies frame-support = { workspace = true } @@ -57,6 +58,7 @@ std = [ "sp-std/std", "staging-xcm-executor/std", "staging-xcm/std", + "cfg-utils/std", ] runtime-benchmarks = [ "frame-support/runtime-benchmarks", @@ -65,6 +67,7 @@ runtime-benchmarks = [ "pallet-membership/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "staging-xcm-executor/runtime-benchmarks", + "cfg-utils/runtime-benchmarks", ] try-runtime = [ "frame-support/try-runtime", @@ -72,4 +75,5 @@ try-runtime = [ "pallet-collective/try-runtime", "pallet-membership/try-runtime", "sp-runtime/try-runtime", + "cfg-utils/try-runtime", ] diff --git a/libs/primitives/src/lib.rs b/libs/primitives/src/lib.rs index d7200cfa29..8150b8b773 100644 --- a/libs/primitives/src/lib.rs +++ b/libs/primitives/src/lib.rs @@ -161,6 +161,15 @@ pub mod types { /// The type for LP gateway message nonces. pub type LPGatewayQueueMessageNonce = u64; + + /// The type to represent milliseconds + pub type Millis = cfg_utils::time::Millis; + + /// The type to represent seconds + pub type Seconds = cfg_utils::time::Seconds; + + /// The type to represent days + pub type Days = cfg_utils::time::Days; } /// Common constants for all runtimes @@ -168,7 +177,7 @@ pub mod constants { use frame_support::weights::{constants::WEIGHT_REF_TIME_PER_SECOND, Weight}; use sp_runtime::Perbill; - use super::types::{Balance, BlockNumber}; + use super::types::{Balance, BlockNumber, Millis, Seconds}; /// This determines the average expected block time that we are targeting. /// Blocks will be produced at a minimum duration defined by @@ -177,24 +186,24 @@ pub mod constants { /// slot_duration()`. /// /// Change this to adjust the block time. - pub const MILLISECS_PER_BLOCK: u64 = 12000; - pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK; + pub const MILLISECS_PER_BLOCK: Millis = Millis::new(12000); + pub const SLOT_DURATION: Millis = MILLISECS_PER_BLOCK; // Time is measured by number of blocks. - pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber); + pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK.inner as BlockNumber); pub const HOURS: BlockNumber = MINUTES * 60; pub const DAYS: BlockNumber = HOURS * 24; - /// Milliseconds per day - pub const MILLISECS_PER_DAY: u64 = SECONDS_PER_DAY * 1000; - // Seconds units - pub const SECONDS_PER_MINUTE: u64 = 60; - pub const SECONDS_PER_HOUR: u64 = SECONDS_PER_MINUTE * 60; - pub const SECONDS_PER_DAY: u64 = SECONDS_PER_HOUR * 24; - pub const SECONDS_PER_WEEK: u64 = SECONDS_PER_DAY * 7; - pub const SECONDS_PER_MONTH: u64 = SECONDS_PER_DAY * 30; - pub const SECONDS_PER_YEAR: u64 = SECONDS_PER_DAY * 365; + pub const SECONDS_PER_MINUTE: Seconds = Seconds::new(60); + pub const SECONDS_PER_HOUR: Seconds = SECONDS_PER_MINUTE.mul_int(60); + pub const SECONDS_PER_DAY: Seconds = SECONDS_PER_HOUR.mul_int(24); + pub const SECONDS_PER_WEEK: Seconds = SECONDS_PER_DAY.mul_int(7); + pub const SECONDS_PER_MONTH: Seconds = SECONDS_PER_DAY.mul_int(30); + pub const SECONDS_PER_YEAR: Seconds = SECONDS_PER_DAY.mul_int(365); + + /// Milliseconds per day + pub const MILLISECS_PER_DAY: Millis = Millis::new(SECONDS_PER_DAY.inner * 1000); /// We assume that ~5% of the block weight is consumed by `on_initialize` /// handlers. This is used to limit the maximal weight of a single diff --git a/libs/test-utils/src/mocks/nav.rs b/libs/test-utils/src/mocks/nav.rs index ac0e3dd8d9..4bb0a6fe92 100644 --- a/libs/test-utils/src/mocks/nav.rs +++ b/libs/test-utils/src/mocks/nav.rs @@ -14,7 +14,8 @@ pub use pallet::*; #[frame_support::pallet] pub mod pallet { - use cfg_traits::{PoolNAV, Seconds}; + use cfg_primitives::Seconds; + use cfg_traits::PoolNAV; use frame_support::pallet_prelude::*; use parity_scale_codec::HasCompact; use sp_runtime::traits::{AtLeast32BitUnsigned, Zero}; @@ -53,7 +54,7 @@ pub mod pallet { } pub fn latest(pool_id: T::PoolId) -> (T::Balance, Seconds) { - Nav::::get(pool_id).unwrap_or((T::Balance::zero(), 0)) + Nav::::get(pool_id).unwrap_or((T::Balance::zero(), Seconds::zero())) } } diff --git a/libs/traits/src/interest.rs b/libs/traits/src/interest.rs index 5502263f61..78d330dc54 100644 --- a/libs/traits/src/interest.rs +++ b/libs/traits/src/interest.rs @@ -1,4 +1,4 @@ -use cfg_primitives::SECONDS_PER_YEAR; +use cfg_primitives::{Seconds, SECONDS_PER_YEAR}; use frame_support::{ dispatch::DispatchResult, pallet_prelude::{RuntimeDebug, TypeInfo}, @@ -14,8 +14,6 @@ use sp_runtime::{ DispatchError, }; -use crate::Seconds; - #[derive(Encode, Decode, Clone, PartialEq, Eq, TypeInfo, RuntimeDebug, MaxEncodedLen)] pub enum CompoundingSchedule { /// Interest compounds every second diff --git a/libs/traits/src/lib.rs b/libs/traits/src/lib.rs index d7eb9042b0..2d8046c411 100644 --- a/libs/traits/src/lib.rs +++ b/libs/traits/src/lib.rs @@ -18,10 +18,10 @@ // Ensure we're `no_std` when compiling for WebAssembly. #![cfg_attr(not(feature = "std"), no_std)] +use cfg_primitives::Seconds; use frame_support::{ dispatch::DispatchResult, pallet_prelude::{RuntimeDebug, TypeInfo}, - traits::UnixTime, Parameter, }; use impl_trait_for_tuples::impl_for_tuples; @@ -30,26 +30,17 @@ use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; use sp_runtime::{traits::Member, DispatchError}; use sp_std::{fmt::Debug, marker::PhantomData, vec::Vec}; -/// Traits related to checked changes. pub mod changes; -/// Traits related to data registry and collection. pub mod data; -/// Traits related to Ethereum/EVM. pub mod ethereum; -/// Traits related to pool fees. pub mod fee; -/// Traits related to fees payment. pub mod fees; -/// Traits related to interest rates. pub mod interest; -/// Traits related to investments. pub mod investments; -/// Traits related to liquidity pools. pub mod liquidity_pools; -/// Traits related to rewards. pub mod rewards; -/// Traits related to swaps. pub mod swaps; +pub mod time; #[cfg(feature = "runtime-benchmarks")] /// Traits related to benchmarking tooling. @@ -60,7 +51,7 @@ pub trait PoolNAV { type ClassId; type RuntimeOrigin; // nav returns the nav and the last time it was calculated - fn nav(pool_id: PoolId) -> Option<(Amount, u64)>; + fn nav(pool_id: PoolId) -> Option<(Amount, Seconds)>; fn update_nav(pool_id: PoolId) -> Result; fn initialise( origin: Self::RuntimeOrigin, @@ -380,35 +371,6 @@ pub trait TryConvert { fn try_convert(a: A) -> Result; } -// TODO: Probably these should be in a future cfg-utils. -// Issue: https://github.com/centrifuge/centrifuge-chain/issues/1380 - -/// Type to represent milliseconds -pub type Millis = u64; - -/// Type to represent seconds -pub type Seconds = u64; - -/// Trait to obtain the time as seconds -pub trait TimeAsSecs: UnixTime { - fn now() -> Seconds { - ::now().as_secs() - } -} - -impl TimeAsSecs for T {} - -/// Trait to convert into seconds -pub trait IntoSeconds { - fn into_seconds(self) -> Seconds; -} - -impl IntoSeconds for Millis { - fn into_seconds(self) -> Seconds { - self / 1000 - } -} - pub trait ValueProvider { type Value; diff --git a/libs/traits/src/time.rs b/libs/traits/src/time.rs new file mode 100644 index 0000000000..316462b6be --- /dev/null +++ b/libs/traits/src/time.rs @@ -0,0 +1,66 @@ +use cfg_primitives::{Days, Millis, Seconds}; +use frame_support::traits::UnixTime; + +/// Trait to obtain the unix time as seconds +pub trait UnixTimeSecs: UnixTime { + fn now() -> Seconds { + Seconds::from(::now().as_secs()) + } + + /// Same as now(), shortcut for cases where `now()` conflicts with + /// `UnixTime::now()` + fn now_secs() -> Seconds { + ::now() + } +} + +impl UnixTimeSecs for T {} + +/// Trait to handle an unknown time unit type +pub trait TimeUnit { + fn as_millis(self) -> Millis; + fn as_seconds(self) -> Seconds; + fn as_days(self) -> Days; +} + +impl TimeUnit for Millis { + fn as_millis(self) -> Millis { + self + } + + fn as_seconds(self) -> Seconds { + self.into_seconds() + } + + fn as_days(self) -> Days { + self.into_days() + } +} + +impl TimeUnit for Seconds { + fn as_millis(self) -> Millis { + self.into_millis() + } + + fn as_seconds(self) -> Seconds { + self + } + + fn as_days(self) -> Days { + self.into_days() + } +} + +impl TimeUnit for Days { + fn as_millis(self) -> Millis { + self.into_millis() + } + + fn as_seconds(self) -> Seconds { + self.into_seconds() + } + + fn as_days(self) -> Days { + self + } +} diff --git a/libs/types/src/epoch.rs b/libs/types/src/epoch.rs index 97b2f085aa..7aa4802f6f 100644 --- a/libs/types/src/epoch.rs +++ b/libs/types/src/epoch.rs @@ -10,7 +10,7 @@ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. -use cfg_traits::Seconds; +use cfg_primitives::Seconds; use frame_support::pallet_prelude::RuntimeDebug; use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; diff --git a/libs/types/src/permissions.rs b/libs/types/src/permissions.rs index 57eee60d24..7569a271ff 100644 --- a/libs/types/src/permissions.rs +++ b/libs/types/src/permissions.rs @@ -10,7 +10,8 @@ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. -use cfg_traits::{Properties, Seconds, TimeAsSecs}; +use cfg_primitives::Seconds; +use cfg_traits::{time::UnixTimeSecs, Properties}; use frame_support::{traits::Get, BoundedVec}; use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; @@ -136,7 +137,7 @@ pub struct PermissionRoles> { impl Default for PermissionedCurrencyHolders where - Now: TimeAsSecs, + Now: UnixTimeSecs, MinDelay: Get, { fn default() -> Self { @@ -150,7 +151,7 @@ where impl Default for TrancheInvestors where - Now: TimeAsSecs, + Now: UnixTimeSecs, MinDelay: Get, TrancheId: PartialEq + PartialOrd, MaxTranches: Get, @@ -166,7 +167,7 @@ where impl Default for PermissionRoles where - Now: TimeAsSecs, + Now: UnixTimeSecs, MinDelay: Get, TrancheId: PartialEq + PartialOrd, MaxTranches: Get, @@ -185,12 +186,12 @@ where /// care which Seconds is passed to the PoolRole::TrancheInvestor(TrancheId, /// Seconds) variant. This UNION shall reflect that and explain to the reader /// why it is passed here. -pub const UNION: Seconds = 0; +pub const UNION: Seconds = Seconds::new(0); impl Properties for PermissionRoles where - Now: TimeAsSecs, + Now: UnixTimeSecs, MinDelay: Get, TrancheId: PartialEq + PartialOrd, MaxTranches: Get, @@ -309,7 +310,7 @@ where impl PermissionedCurrencyHolders where - Now: TimeAsSecs, + Now: UnixTimeSecs, MinDelay: Get, { pub fn empty() -> Self { @@ -321,7 +322,7 @@ where } fn validity(&self, delta: Seconds) -> Result { - let now = ::now(); + let now = Now::now_secs(); let min_validity = now.saturating_add(MinDelay::get()); let req_validity = now.saturating_add(delta); @@ -334,7 +335,7 @@ where pub fn contains(&self) -> bool { if let Some(info) = &self.info { - info.permissioned_till >= ::now() + info.permissioned_till >= Now::now_secs() } else { false } @@ -344,7 +345,7 @@ where pub fn remove(&mut self, delta: Seconds) -> Result<(), ()> { if let Some(info) = &self.info { let valid_till = &info.permissioned_till; - let now = ::now(); + let now = Now::now_secs(); if *valid_till <= now { // The account is already invalid. Hence no more grace period @@ -379,7 +380,7 @@ where impl TrancheInvestors where - Now: TimeAsSecs, + Now: UnixTimeSecs, MinDelay: Get, TrancheId: PartialEq + PartialOrd, MaxTranches: Get, @@ -393,7 +394,7 @@ where } fn validity(&self, delta: Seconds) -> Result { - let now = ::now(); + let now = Now::now_secs(); let min_validity = now.saturating_add(MinDelay::get()); let req_validity = now.saturating_add(delta); @@ -405,16 +406,16 @@ where } pub fn contains(&self, tranche: TrancheId) -> bool { - self.info.iter().any(|info| { - info.tranche_id == tranche && info.permissioned_till >= ::now() - }) + self.info + .iter() + .any(|info| info.tranche_id == tranche && info.permissioned_till >= Now::now_secs()) } #[allow(clippy::result_unit_err)] pub fn remove(&mut self, tranche: TrancheId, delta: Seconds) -> Result<(), ()> { if let Some(index) = self.info.iter().position(|info| info.tranche_id == tranche) { let valid_till = &self.info[index].permissioned_till; - let now = ::now(); + let now = Now::now_secs(); if *valid_till <= now { // The account is already invalid. Hence no more grace period @@ -459,36 +460,33 @@ mod tests { use super::*; parameter_types! { - pub const MinDelay: u64 = 4; + pub const MinDelay: Seconds = Seconds::new(4); pub const MaxTranches: u32 = 5; } struct Now; impl Now { - fn pass(delta: u64) { + fn pass(delta: Seconds) { unsafe { let current = NOW_HOLDER; - NOW_HOLDER = current + delta; + NOW_HOLDER = current.add(delta); }; } - fn set(now: u64) { + fn set(now: Seconds) { unsafe { NOW_HOLDER = now; }; } } - static mut NOW_HOLDER: u64 = 0; + static mut NOW_HOLDER: Seconds = Seconds::new(0); impl frame_support::traits::UnixTime for Now { fn now() -> Duration { - unsafe { Duration::new(NOW_HOLDER, 0) } + unsafe { Duration::new(NOW_HOLDER.inner, 0) } } } - /// The exists call does not care what is passed as moment. This type shall - /// reflect that - const UNION: u64 = 0u64; /// The tranceh id type we use in our runtime-common. But we don't want a /// dependency here. type TrancheId = [u8; 16]; @@ -507,19 +505,19 @@ mod tests { assert!(roles .add(Role::PoolRole(PoolRole::TrancheInvestor( into_tranche_id(30), - 10 + Seconds::new(10) ))) .is_ok()); assert!(roles .add(Role::PoolRole(PoolRole::TrancheInvestor( into_tranche_id(30), - 9 + Seconds::new(9) ))) .is_err()); assert!(roles .add(Role::PoolRole(PoolRole::TrancheInvestor( into_tranche_id(30), - 11 + Seconds::new(11) ))) .is_ok()); @@ -543,10 +541,10 @@ mod tests { assert!(roles .rm(Role::PoolRole(PoolRole::TrancheInvestor( into_tranche_id(0), - 0 + Seconds::new(0) ))) .is_err()); - Now::pass(1); + Now::pass(Seconds::new(1)); assert!(roles.exists(Role::PoolRole(PoolRole::TrancheInvestor( into_tranche_id(0), UNION @@ -561,7 +559,7 @@ mod tests { into_tranche_id(0), UNION )))); - Now::set(0); + Now::set(Seconds::new(0)); // Removing after MinDelay works (i.e. this is after min_delay the account will // be invalid) @@ -571,12 +569,12 @@ mod tests { MinDelay::get() ))) .is_ok()); - Now::pass(6); + Now::pass(Seconds::new(6)); assert!(!roles.exists(Role::PoolRole(PoolRole::TrancheInvestor( into_tranche_id(0), UNION )))); - Now::set(0); + Now::set(Seconds::new(0)); // Multiple tranches work assert!(roles @@ -624,12 +622,12 @@ mod tests { into_tranche_id(8), UNION )))); - Now::pass(1); + Now::pass(Seconds::new(1)); assert!(!roles.exists(Role::PoolRole(PoolRole::TrancheInvestor( into_tranche_id(8), UNION )))); - Now::set(0); + Now::set(Seconds::new(0)); // Role must be added for at least min_delay assert!(roles diff --git a/libs/types/src/pools.rs b/libs/types/src/pools.rs index 767c3cdff7..dd799e957f 100644 --- a/libs/types/src/pools.rs +++ b/libs/types/src/pools.rs @@ -10,10 +10,8 @@ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. -use cfg_traits::{ - fee::{FeeAmountProration, PoolFeeBucket}, - Seconds, -}; +use cfg_primitives::Seconds; +use cfg_traits::fee::{FeeAmountProration, PoolFeeBucket}; use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; use sp_arithmetic::FixedPointOperand; @@ -224,27 +222,15 @@ where } } -/// Converts an annual balance amount into its proratio based on the given -/// period duration. -pub fn saturated_balance_proration< - Balance: From + FixedPointOperand + sp_std::ops::Div, ->( - annual_amount: Balance, - period: Seconds, -) -> Balance { - let amount = annual_amount.saturating_mul(period.into()); - amount.div(cfg_primitives::SECONDS_PER_YEAR.into()) -} - /// Converts an annual rate into its proratio based on the given /// period duration. pub fn saturated_rate_proration( annual_rate: Rate, period: Seconds, ) -> Rate { - let rate = annual_rate.saturating_mul(Rate::saturating_from_integer::(period)); + let rate = annual_rate.saturating_mul(Rate::saturating_from_integer(period)); - rate.saturating_div_ceil(&Rate::saturating_from_integer::( + rate.saturating_div_ceil(&Rate::saturating_from_integer( cfg_primitives::SECONDS_PER_YEAR, )) } @@ -267,7 +253,7 @@ mod tests { use super::*; mod saturated_proration { - use cfg_primitives::{CFG, DAYS, SECONDS_PER_YEAR}; + use cfg_primitives::{CFG, SECONDS_PER_DAY, SECONDS_PER_YEAR}; use sp_arithmetic::{ traits::{One, Zero}, FixedPointNumber, @@ -276,51 +262,13 @@ mod tests { use super::*; use crate::fixed_point::{Quantity, Rate}; - type Balance = u128; - - #[test] - fn balance_zero() { - assert_eq!( - saturated_balance_proration::(SECONDS_PER_YEAR.into(), 0), - 0 - ); - assert_eq!( - saturated_balance_proration::(0u128, SECONDS_PER_YEAR), - 0 - ); - assert_eq!( - saturated_balance_proration::((SECONDS_PER_YEAR - 1).into(), 1), - 0 - ); - assert_eq!( - saturated_balance_proration::(1u128, SECONDS_PER_YEAR - 1), - 0 - ); - } - - #[test] - fn balance_one() { - assert_eq!( - saturated_balance_proration::(SECONDS_PER_YEAR.into(), 1), - 1u128 - ); - assert_eq!( - saturated_balance_proration::(1u128, SECONDS_PER_YEAR), - 1u128 - ); - } - #[test] - fn balance_overflow() { - assert_eq!( - saturated_balance_proration::(u128::MAX, u64::MAX), - u128::MAX / u128::from(SECONDS_PER_YEAR) - ); - } - #[test] fn rate_zero() { assert_eq!( - saturated_rate_proration::(Rate::from_integer(SECONDS_PER_YEAR.into()), 0), + saturated_rate_proration::( + Rate::from_integer(SECONDS_PER_YEAR.into()), + Seconds::new(0) + ), Rate::zero() ); assert_eq!( @@ -330,7 +278,7 @@ mod tests { assert!( saturated_rate_proration::( Rate::from_integer((SECONDS_PER_YEAR - 1).into()), - 1 + Seconds::new(1) ) > Rate::zero() ); assert!( @@ -341,7 +289,10 @@ mod tests { #[test] fn rate_one() { assert_eq!( - saturated_rate_proration::(Rate::from_integer(SECONDS_PER_YEAR.into()), 1), + saturated_rate_proration::( + Rate::from_integer(SECONDS_PER_YEAR.into()), + Seconds::new(1) + ), Rate::one() ); assert_eq!( @@ -356,23 +307,25 @@ mod tests { let rate = saturated_rate_proration::( Rate::from_integer(u128::from(u128::MAX / 10u128.pow(27))), - 1, + Seconds::new(1), ); assert!(left_bound < rate); assert!(rate < right_bound); - assert!(saturated_rate_proration::(Rate::one(), u64::MAX) > left_bound); - assert!(saturated_rate_proration::(Rate::one(), u64::MAX) < right_bound); + assert!(saturated_rate_proration::(Rate::one(), Seconds::MAX) > left_bound); + assert!(saturated_rate_proration::(Rate::one(), Seconds::MAX) < right_bound); - assert!(saturated_rate_proration::(Rate::from_integer(2), u64::MAX) > left_bound); assert!( - saturated_rate_proration::(Rate::from_integer(2), u64::MAX) < right_bound + saturated_rate_proration::(Rate::from_integer(2), Seconds::MAX) > left_bound + ); + assert!( + saturated_rate_proration::(Rate::from_integer(2), Seconds::MAX) < right_bound ); } #[test] fn precision_quantity_vs_rate() { - let period = (DAYS / 4) as Seconds; + let period = SECONDS_PER_DAY / 4; let nav_multiplier = 1_000_000; let nav = nav_multiplier * CFG; diff --git a/libs/types/src/portfolio.rs b/libs/types/src/portfolio.rs index f7a8f877c7..d3f383bf86 100644 --- a/libs/types/src/portfolio.rs +++ b/libs/types/src/portfolio.rs @@ -10,7 +10,8 @@ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. -use cfg_traits::{Seconds, TimeAsSecs}; +use cfg_primitives::Seconds; +use cfg_traits::time::UnixTimeSecs; use frame_support::{pallet_prelude::RuntimeDebug, traits::Get, BoundedVec}; use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; @@ -153,11 +154,11 @@ impl Get, - Timer: TimeAsSecs, + Timer: UnixTimeSecs, ElemId: Eq, { fn get() -> PortfolioValuation { - PortfolioValuation::new(::now()) + PortfolioValuation::new(::now()) } } @@ -179,7 +180,7 @@ mod tests { #[test] fn general_usage() { - let mut portfolio = PortfolioValuation::>::new(10); + let mut portfolio = PortfolioValuation::>::new(Seconds::new(10)); assert_ok!(portfolio.insert_elem(1, 100)); assert_ok!(portfolio.insert_elem(2, 200)); diff --git a/libs/utils/Cargo.toml b/libs/utils/Cargo.toml index 31c1f028b1..af2c08c983 100644 --- a/libs/utils/Cargo.toml +++ b/libs/utils/Cargo.toml @@ -24,6 +24,7 @@ sp-arithmetic = { workspace = true } sp-consensus-aura = { workspace = true } sp-runtime = { workspace = true } sp-std = { workspace = true } +serde = { workspace = true } [features] default = ["std"] @@ -39,6 +40,7 @@ std = [ "scale-info/std", "sp-consensus-aura/std", "hex/std", + "serde/std", ] runtime-benchmarks = [ "frame-support/runtime-benchmarks", diff --git a/libs/utils/src/lib.rs b/libs/utils/src/lib.rs index a1f8bc7412..aef4700364 100644 --- a/libs/utils/src/lib.rs +++ b/libs/utils/src/lib.rs @@ -16,6 +16,9 @@ use parity_scale_codec::Encode; use sp_std::cmp::min; +pub mod num_wrapper; +pub mod time; + /// Build a fixed-size array using as many elements from `src` as possible /// without overflowing and ensuring that the array is 0 padded in the case /// where `src.len()` is smaller than S. diff --git a/libs/utils/src/num_wrapper.rs b/libs/utils/src/num_wrapper.rs new file mode 100644 index 0000000000..e2bcf37271 --- /dev/null +++ b/libs/utils/src/num_wrapper.rs @@ -0,0 +1,657 @@ +use parity_scale_codec::{Compact, CompactAs, Decode, Encode, MaxEncodedLen}; +use scale_info::TypeInfo; +use serde::{Deserialize, Serialize}; +use sp_arithmetic::traits::{ + Bounded, CheckedAdd, CheckedDiv, CheckedMul, CheckedNeg, CheckedRem, CheckedShl, CheckedShr, + CheckedSub, IntegerSquareRoot, One, Saturating, Zero, +}; +use sp_runtime::traits::Scale; +use sp_std::{ + cmp::Ordering, + fmt::{self, Debug}, + marker::PhantomData, + ops::{ + Add, AddAssign, Div, DivAssign, Mul, MulAssign, Rem, RemAssign, Shl, Shr, Sub, SubAssign, + }, +}; + +/// Type that allows to create different typed numbers with the same inner +/// type: +/// +/// ``` +/// # use cfg_utils::num_wrapper::NumWrapper; +/// +/// struct Id1; +/// struct Id2; +/// +/// type FooU64 = NumWrapper; +/// type BarU64 = NumWrapper; +/// ``` +#[derive(TypeInfo, Serialize, Deserialize, Encode, Decode, MaxEncodedLen)] +#[scale_info(skip_type_params(T, I))] +pub struct NumWrapper { + pub inner: T, + _instance: PhantomData, +} + +impl NumWrapper { + pub const fn new(value: T) -> Self { + NumWrapper { + inner: value, + _instance: PhantomData, + } + } +} + +macro_rules! const_methods { + ($t:ty) => { + impl NumWrapper<$t, I> { + pub const BITS: u32 = <$t>::BITS; + pub const MAX: Self = Self::new(<$t>::MAX); + pub const MIN: Self = Self::new(<$t>::MIN); + + pub const fn add(self, other: Self) -> Self { + Self::new(self.inner + other.inner) + } + + pub const fn sub(self, other: Self) -> Self { + Self::new(self.inner - other.inner) + } + + pub const fn mul(self, other: Self) -> Self { + Self::new(self.inner * other.inner) + } + + pub const fn div(self, other: Self) -> Self { + Self::new(self.inner / other.inner) + } + + pub const fn saturating_add(self, other: Self) -> Self { + Self::new(self.inner.saturating_add(other.inner)) + } + + pub const fn saturating_sub(self, other: Self) -> Self { + Self::new(self.inner.saturating_sub(other.inner)) + } + + pub const fn saturating_mul(self, other: Self) -> Self { + Self::new(self.inner.saturating_mul(other.inner)) + } + + pub const fn saturating_div(self, other: Self) -> Self { + Self::new(self.inner.saturating_div(other.inner)) + } + + pub const fn add_int(self, other: $t) -> Self { + Self::new(self.inner + other) + } + + pub const fn sub_int(self, other: $t) -> Self { + Self::new(self.inner - other) + } + + pub const fn mul_int(self, other: $t) -> Self { + Self::new(self.inner * other) + } + + pub const fn div_int(self, other: $t) -> Self { + Self::new(self.inner / other) + } + + pub const fn leading_zeros(self) -> u32 { + self.inner.leading_zeros() + } + } + }; +} + +macro_rules! impl_from { + ($from:ty, $to:ty) => { + impl From<$from> for NumWrapper<$to, I> { + fn from(other: $from) -> Self { + Self::new(other as $to) + } + } + }; +} + +macro_rules! impl_try_from { + ($from:ty, $to:ty) => { + impl TryFrom<$from> for NumWrapper<$to, I> { + type Error = <$to as TryFrom<$from>>::Error; + + fn try_from(other: $from) -> Result { + Ok(Self::new(<$to>::try_from(other)?)) + } + } + }; +} + +macro_rules! impl_into { + ($from:ty, $to:ty) => { + // Implemented as an opposite From to have + // the inverse Into automatically implemented + impl From> for $to { + fn from(other: NumWrapper<$from, I>) -> Self { + other.inner.into() + } + } + }; +} + +macro_rules! impl_try_into { + ($from:ty, $to:ty) => { + // Implemented as an opposite TryFrom to have + // the inverse TryInto automatically implemented + impl TryFrom> for $to { + type Error = <$to as TryFrom<$from>>::Error; + + fn try_from(other: NumWrapper<$from, I>) -> Result<$to, Self::Error> { + other.inner.try_into() + } + } + }; +} + +const_methods!(u8); +const_methods!(u16); +const_methods!(u32); +const_methods!(u64); +const_methods!(u128); + +impl_from!(u8, u8); +impl_try_from!(u16, u8); +impl_try_from!(u32, u8); +impl_try_from!(u64, u8); +impl_try_from!(u128, u8); +impl_try_from!(usize, u8); + +impl_from!(u8, u16); +impl_from!(u16, u16); +impl_try_from!(u32, u16); +impl_try_from!(u64, u16); +impl_try_from!(u128, u16); +impl_try_from!(usize, u16); + +impl_from!(u8, u32); +impl_from!(u16, u32); +impl_from!(u32, u32); +impl_try_from!(u64, u32); +impl_try_from!(u128, u32); +impl_try_from!(usize, u32); + +impl_from!(u8, u64); +impl_from!(u16, u64); +impl_from!(u32, u64); +impl_from!(u64, u64); +impl_try_from!(u128, u64); +impl_from!(usize, u64); + +impl_from!(u8, u128); +impl_from!(u16, u128); +impl_from!(u32, u128); +impl_from!(u64, u128); +impl_from!(u128, u128); +impl_from!(usize, u128); + +impl_from!(u8, usize); +impl_from!(u16, usize); +impl_from!(u32, usize); +impl_from!(u64, usize); +impl_from!(u128, usize); +impl_from!(usize, usize); + +impl_into!(u8, u8); +impl_try_into!(u16, u8); +impl_try_into!(u32, u8); +impl_try_into!(u64, u8); +impl_try_into!(u128, u8); +impl_try_into!(usize, u8); + +impl_into!(u8, u16); +impl_into!(u16, u16); +impl_try_into!(u32, u16); +impl_try_into!(u64, u16); +impl_try_into!(u128, u16); +impl_try_into!(usize, u16); + +impl_into!(u8, u32); +impl_into!(u16, u32); +impl_into!(u32, u32); +impl_try_into!(u64, u32); +impl_try_into!(u128, u32); +impl_try_into!(usize, u32); + +impl_into!(u8, u64); +impl_into!(u16, u64); +impl_into!(u32, u64); +impl_into!(u64, u64); +impl_try_into!(u128, u64); +impl_try_into!(usize, u64); + +impl_into!(u8, u128); +impl_into!(u16, u128); +impl_into!(u32, u128); +impl_into!(u64, u128); +impl_into!(u128, u128); +impl_try_into!(usize, u128); + +impl_into!(u8, usize); +impl_into!(u16, usize); +impl_try_into!(u32, usize); +impl_try_into!(u64, usize); +impl_try_into!(u128, usize); +impl_into!(usize, usize); + +/// ----------------------------------------------------- + +impl Default for NumWrapper { + fn default() -> Self { + Self::new(T::default()) + } +} + +impl Clone for NumWrapper { + fn clone(&self) -> Self { + Self::new(self.inner.clone()) + } +} + +impl Copy for NumWrapper {} + +impl PartialEq for NumWrapper { + fn eq(&self, other: &Self) -> bool { + self.inner.eq(&other.inner) + } +} + +impl Eq for NumWrapper {} + +impl PartialOrd for NumWrapper { + fn partial_cmp(&self, other: &Self) -> Option { + self.inner.partial_cmp(&other.inner) + } +} + +impl Ord for NumWrapper { + fn cmp(&self, other: &Self) -> Ordering { + self.inner.cmp(&other.inner) + } +} + +impl Debug for NumWrapper { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + self.inner.fmt(f) + } +} + +impl, I> Add for NumWrapper { + type Output = Self; + + fn add(self, rhs: Self) -> Self { + Self::new(self.inner.add(rhs.inner)) + } +} + +impl, I> Sub for NumWrapper { + type Output = Self; + + fn sub(self, rhs: Self) -> Self { + Self::new(self.inner.sub(rhs.inner)) + } +} + +impl, I> Mul for NumWrapper { + type Output = Self; + + fn mul(self, rhs: Self) -> Self { + Self::new(self.inner.mul(rhs.inner)) + } +} + +impl, I> Div for NumWrapper { + type Output = Self; + + fn div(self, rhs: Self) -> Self { + Self::new(self.inner.div(rhs.inner)) + } +} + +impl, I> Rem for NumWrapper { + type Output = Self; + + fn rem(self, rhs: Self) -> Self { + Self::new(self.inner.rem(rhs.inner)) + } +} + +impl, I> Shl for NumWrapper { + type Output = Self; + + fn shl(self, rhs: u32) -> Self { + Self::new(self.inner.shl(rhs)) + } +} + +impl, I> Shr for NumWrapper { + type Output = Self; + + fn shr(self, rhs: u32) -> Self { + Self::new(self.inner.shr(rhs)) + } +} + +impl, I> Add for NumWrapper { + type Output = Self; + + fn add(self, rhs: T) -> Self { + Self::new(self.inner.add(rhs)) + } +} + +impl, I> Sub for NumWrapper { + type Output = Self; + + fn sub(self, rhs: T) -> Self { + Self::new(self.inner.sub(rhs)) + } +} + +impl, I> Mul for NumWrapper { + type Output = Self; + + fn mul(self, rhs: T) -> Self { + Self::new(self.inner.mul(rhs)) + } +} + +impl, I> Div for NumWrapper { + type Output = Self; + + fn div(self, rhs: T) -> Self { + Self::new(self.inner.div(rhs)) + } +} + +impl, I> Rem for NumWrapper { + type Output = Self; + + fn rem(self, rhs: T) -> Self { + Self::new(self.inner.rem(rhs)) + } +} + +impl AddAssign for NumWrapper { + fn add_assign(&mut self, rhs: Self) { + self.inner.add_assign(rhs.inner) + } +} + +impl SubAssign for NumWrapper { + fn sub_assign(&mut self, rhs: Self) { + self.inner.sub_assign(rhs.inner) + } +} + +impl MulAssign for NumWrapper { + fn mul_assign(&mut self, rhs: Self) { + self.inner.mul_assign(rhs.inner) + } +} + +impl DivAssign for NumWrapper { + fn div_assign(&mut self, rhs: Self) { + self.inner.div_assign(rhs.inner) + } +} + +impl RemAssign for NumWrapper { + fn rem_assign(&mut self, rhs: Self) { + self.inner.rem_assign(rhs.inner) + } +} + +impl AddAssign for NumWrapper { + fn add_assign(&mut self, rhs: T) { + self.inner.add_assign(rhs) + } +} + +impl SubAssign for NumWrapper { + fn sub_assign(&mut self, rhs: T) { + self.inner.sub_assign(rhs) + } +} + +impl MulAssign for NumWrapper { + fn mul_assign(&mut self, rhs: T) { + self.inner.mul_assign(rhs) + } +} + +impl DivAssign for NumWrapper { + fn div_assign(&mut self, rhs: T) { + self.inner.div_assign(rhs) + } +} + +impl RemAssign for NumWrapper { + fn rem_assign(&mut self, rhs: T) { + self.inner.rem_assign(rhs) + } +} + +impl CheckedAdd for NumWrapper { + fn checked_add(&self, rhs: &Self) -> Option { + Some(Self::new(self.inner.checked_add(&rhs.inner)?)) + } +} + +impl CheckedSub for NumWrapper { + fn checked_sub(&self, rhs: &Self) -> Option { + Some(Self::new(self.inner.checked_sub(&rhs.inner)?)) + } +} + +impl CheckedMul for NumWrapper { + fn checked_mul(&self, rhs: &Self) -> Option { + Some(Self::new(self.inner.checked_mul(&rhs.inner)?)) + } +} + +impl CheckedDiv for NumWrapper { + fn checked_div(&self, rhs: &Self) -> Option { + Some(Self::new(self.inner.checked_div(&rhs.inner)?)) + } +} + +impl CheckedRem for NumWrapper { + fn checked_rem(&self, rhs: &Self) -> Option { + Some(Self::new(self.inner.checked_rem(&rhs.inner)?)) + } +} + +impl CheckedShl for NumWrapper { + fn checked_shl(&self, rhs: u32) -> Option { + Some(Self::new(self.inner.checked_shl(rhs)?)) + } +} + +impl CheckedShr for NumWrapper { + fn checked_shr(&self, rhs: u32) -> Option { + Some(Self::new(self.inner.checked_shr(rhs)?)) + } +} + +impl CheckedNeg for NumWrapper { + fn checked_neg(&self) -> Option { + Some(Self::new(self.inner.checked_neg()?)) + } +} + +impl Saturating for NumWrapper { + fn saturating_add(self, rhs: Self) -> Self { + Self::new(self.inner.saturating_add(rhs.inner)) + } + + fn saturating_sub(self, rhs: Self) -> Self { + Self::new(self.inner.saturating_sub(rhs.inner)) + } + + fn saturating_mul(self, rhs: Self) -> Self { + Self::new(self.inner.saturating_mul(rhs.inner)) + } + + fn saturating_pow(self, exp: usize) -> Self { + Self::new(self.inner.saturating_pow(exp)) + } +} + +impl Zero for NumWrapper { + fn zero() -> Self { + Self::new(T::zero()) + } + + fn is_zero(&self) -> bool { + self.inner.is_zero() + } +} + +impl One for NumWrapper { + fn one() -> Self { + Self::new(T::one()) + } + + fn is_one(&self) -> bool { + self.inner.is_one() + } +} + +impl IntegerSquareRoot for NumWrapper { + fn integer_sqrt_checked(&self) -> Option { + Some(Self::new(self.inner.integer_sqrt_checked()?)) + } +} + +impl Bounded for NumWrapper { + fn min_value() -> Self { + Self::new(T::min_value()) + } + + fn max_value() -> Self { + Self::new(T::max_value()) + } +} + +impl From> for NumWrapper { + fn from(other: Compact) -> Self { + other.0 + } +} + +impl CompactAs for NumWrapper { + type As = T; + + fn encode_as(&self) -> &Self::As { + &self.inner + } + + fn decode_from(x: Self::As) -> Result { + Ok(Self::new(x)) + } +} + +impl, S, I> Scale for NumWrapper { + type Output = Self; + + fn mul(self, other: S) -> Self::Output { + Self::new(self.inner.mul(other)) + } + + fn div(self, other: S) -> Self::Output { + Self::new(self.inner.div(other)) + } + + fn rem(self, other: S) -> Self::Output { + Self::new(self.inner.rem(other)) + } +} + +#[cfg(test)] +mod tests { + use frame_support::Parameter; + use parity_scale_codec::{EncodeLike, HasCompact}; + use sp_arithmetic::traits::BaseArithmetic; + use sp_runtime::{traits::Member, FixedPointOperand}; + + use super::*; + + fn is_has_compact() {} + fn is_base_arithmetic() {} + fn is_encode() {} + fn is_member() {} + fn is_parameter() {} + fn is_type_info() {} + fn is_encode_like() {} + fn is_fixed_point_operand() {} + fn is_scale, S>() {} + + // Id does not require any implementation + struct Id; + + macro_rules! check_wrapper { + ($name:ident, $t:ty) => { + mod $name { + use super::*; + + #[test] + fn check_wrapper() { + type Num = NumWrapper<$t, Id>; + + is_has_compact::(); + is_base_arithmetic::(); + is_encode::(); + is_member::(); + is_parameter::(); + is_type_info::(); + is_encode_like::(); + is_fixed_point_operand::(); + } + } + }; + } + + check_wrapper!(u8_type, u8); + check_wrapper!(u16_type, u16); + check_wrapper!(u32_type, u32); + check_wrapper!(u64_type, u64); + check_wrapper!(u128_type, u128); + + #[test] + fn check_scale() { + type U8 = NumWrapper; + is_scale::(); + + type U16 = NumWrapper; + is_scale::(); + is_scale::(); + + type U32 = NumWrapper; + is_scale::(); + is_scale::(); + is_scale::(); + + type U64 = NumWrapper; + is_scale::(); + is_scale::(); + is_scale::(); + is_scale::(); + + type U128 = NumWrapper; + is_scale::(); + is_scale::(); + is_scale::(); + is_scale::(); + is_scale::(); + } +} diff --git a/libs/utils/src/time.rs b/libs/utils/src/time.rs new file mode 100644 index 0000000000..44ce0ff776 --- /dev/null +++ b/libs/utils/src/time.rs @@ -0,0 +1,101 @@ +use sp_arithmetic::traits::{Bounded, Saturating}; +use sp_std::ops::Div; + +use crate::num_wrapper::NumWrapper; + +/// Type to distinguish NumWrapper as millis +pub struct MillisId; + +/// Type to represent milliseconds +pub type Millis = NumWrapper; + +impl + From + Copy> Millis { + pub fn into_seconds + Bounded>(self) -> Seconds { + let inner = self.inner / M::from(1000); + Seconds::new(S::try_from(inner).unwrap_or(Bounded::max_value())) + } + + pub fn into_days + Bounded>(self) -> Days { + let inner = self.inner / M::from(1000 * 24 * 3600); + Days::new(D::try_from(inner).unwrap_or(Bounded::max_value())) + } +} + +/// Type to distinguish NumWrapper as seconds +pub struct SecondsId; + +/// Type to represent seconds +pub type Seconds = NumWrapper; + +impl Seconds { + pub fn into_millis + From + Saturating + Bounded>(self) -> Millis { + let inner = M::try_from(self.inner) + .unwrap_or(Bounded::max_value()) + .saturating_mul(M::from(1000)); + Millis::new(inner) + } +} + +impl + From + Copy> Seconds { + pub fn into_days + Bounded>(self) -> Days { + let inner = self.inner / S::from(24 * 3600); + Days::new(D::try_from(inner).unwrap_or(Bounded::max_value())) + } +} + +/// Type to distinguish NumWrapper as days +pub struct DaysId; + +/// Type to represent days +pub type Days = NumWrapper; + +impl Days { + pub fn into_millis + From + Saturating + Bounded>(self) -> Millis { + let inner = M::try_from(self.inner) + .unwrap_or(Bounded::max_value()) + .saturating_mul(M::from(1000 * 24 * 3600)); + Millis::new(inner) + } + + pub fn into_seconds + From + Saturating + Bounded>(self) -> Seconds { + let inner = S::try_from(self.inner) + .unwrap_or(Bounded::max_value()) + .saturating_mul(S::from(24 * 3600)); + Seconds::new(inner) + } +} + +// TODO: evaluate if we need a macro here. Some thoughts below. +// Could we get the above method as const with the macro? + +/* +time_coversion_down!(into_millis, Seconds, Millis, factor) +time_coversion_down!(into_millis, Days, Millis, factor) + +time_coversion_up!(into_seconds, Millis, Seconds, factor) +time_coversion_down!(into_seconds, Days, Seconds, factor) + +time_coversion_up!(into_days, Millis, Days, factor) +time_coversion_up!(into_days, Seconds, Days, factor) +*/ + +/* +macro_rules! into_unit { + ($from_name:ident < $from:ty >, $to_name:ident < $to:ty >, $method_name:ident, $n:expr, $d:expr) => { + impl $from_name<$from> { + pub const fn $method_name(self) -> $to_name<$to> { + let n: $to = $n as $to; + $to_name::from((self.get() as $to).saturating_mul(n) / $d) + } + } + }; + + ($from_name:ident < $from:ty >, $to_name:ident < $to:ty >, $method_name:ident, $n:expr, $d:expr, try) => { + impl $from_name<$from> { + pub fn $method_name(self) -> Result<$to_name<$to>, ArithmeticError> { + $to_name::ensure_from(self.get().saturating_mul($n) / $d) + } + } + }; +} +*/ diff --git a/pallets/block-rewards/src/lib.rs b/pallets/block-rewards/src/lib.rs index 8970b65500..9054c42718 100644 --- a/pallets/block-rewards/src/lib.rs +++ b/pallets/block-rewards/src/lib.rs @@ -39,10 +39,11 @@ pub mod weights; #[cfg(feature = "runtime-benchmarks")] mod benchmarking; +use cfg_primitives::Seconds; use cfg_traits::{ self, rewards::{AccountRewards, CurrencyGroupChange, GroupRewards}, - Seconds, TimeAsSecs, + time::UnixTimeSecs, }; use cfg_types::fixed_point::FixedPointNumberExtension; use frame_support::{ @@ -190,7 +191,7 @@ pub mod pallet { + MaxEncodedLen; /// The source of truth for the current time in seconds - type Time: TimeAsSecs; + type Time: UnixTimeSecs; /// Information of runtime weights type WeightInfo: WeightInfo; diff --git a/pallets/block-rewards/src/migrations.rs b/pallets/block-rewards/src/migrations.rs index f8d8e67d05..4c4e00af01 100644 --- a/pallets/block-rewards/src/migrations.rs +++ b/pallets/block-rewards/src/migrations.rs @@ -10,7 +10,7 @@ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. -use cfg_traits::TimeAsSecs; +use cfg_traits::time::UnixTimeSecs; use frame_support::{ pallet_prelude::Weight, traits::{Get, GetStorageVersion, OnRuntimeUpgrade}, diff --git a/pallets/interest-accrual/Cargo.toml b/pallets/interest-accrual/Cargo.toml index deca59e00f..0f2636e1ff 100644 --- a/pallets/interest-accrual/Cargo.toml +++ b/pallets/interest-accrual/Cargo.toml @@ -32,6 +32,7 @@ cfg-types = { workspace = true } [dev-dependencies] bitflags = { workspace = true } +cfg-mocks = { workspace = true } [features] default = ["std"] @@ -60,6 +61,7 @@ runtime-benchmarks = [ "frame-system/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", "sp-runtime/runtime-benchmarks", + "cfg-mocks/runtime-benchmarks", ] try-runtime = [ "cfg-primitives/try-runtime", @@ -69,4 +71,5 @@ try-runtime = [ "frame-system/try-runtime", "pallet-timestamp/try-runtime", "sp-runtime/try-runtime", + "cfg-mocks/try-runtime", ] diff --git a/pallets/interest-accrual/src/benchmarking.rs b/pallets/interest-accrual/src/benchmarking.rs index 65018e8668..76237d395f 100644 --- a/pallets/interest-accrual/src/benchmarking.rs +++ b/pallets/interest-accrual/src/benchmarking.rs @@ -31,9 +31,9 @@ benchmarks! { // and returns a reasonably-precise weight for the pow. calculate_accumulated_rate { let n in 1..25; - let now: Seconds = (1 << n) - 1; + let now = Seconds::from((1 << n) - 1); let rate = interest_rate_per_sec(T::Rate::saturating_from_rational(10, 100)).unwrap(); - }: { Pallet::::calculate_accumulated_rate(rate, One::one(), 0, now).unwrap() } + }: { Pallet::::calculate_accumulated_rate(rate, One::one(), Zero::zero(), now).unwrap() } verify { } } diff --git a/pallets/interest-accrual/src/lib.rs b/pallets/interest-accrual/src/lib.rs index 42147bed22..5f956c94f3 100644 --- a/pallets/interest-accrual/src/lib.rs +++ b/pallets/interest-accrual/src/lib.rs @@ -122,17 +122,17 @@ #![cfg_attr(not(feature = "std"), no_std)] -use cfg_primitives::SECONDS_PER_YEAR; +use cfg_primitives::{Seconds, SECONDS_PER_YEAR}; use cfg_traits::{ interest::{InterestAccrual, InterestRate, RateCollection}, - Seconds, TimeAsSecs, + time::UnixTimeSecs, }; use cfg_types::adjustments::Adjustment; use frame_support::{pallet_prelude::RuntimeDebug, BoundedVec}; use frame_system::pallet_prelude::BlockNumberFor; use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; -use sp_arithmetic::traits::{checked_pow, One, Zero}; +use sp_arithmetic::traits::{ensure_pow, One, Zero}; use sp_runtime::{ traits::{ AtLeast32BitUnsigned, CheckedAdd, CheckedSub, EnsureAdd, EnsureAddAssign, EnsureDiv, @@ -211,7 +211,7 @@ pub mod pallet { + FixedPointNumber + MaxEncodedLen; - type Time: TimeAsSecs; + type Time: UnixTimeSecs; type MaxRateCount: Get; @@ -315,8 +315,7 @@ pub mod pallet { Ordering::Less => { let delta = now.ensure_sub(when)?; let rate_adjustment = - checked_pow(rate.interest_rate_per_sec, delta.ensure_into()?) - .ok_or(ArithmeticError::Overflow)?; + ensure_pow(rate.interest_rate_per_sec, delta.ensure_into()?)?; rate.accumulated_rate.ensure_div(rate_adjustment)? } Ordering::Greater => { @@ -389,8 +388,7 @@ pub mod pallet { ) -> Result { // accumulated_rate * interest_rate_per_sec ^ (now - last_updated) let time_difference_secs = now.ensure_sub(last_updated)?; - checked_pow(interest_rate_per_sec, time_difference_secs as usize) - .ok_or(ArithmeticError::Overflow)? // TODO: This line can be remove once #1241 be merged + ensure_pow(interest_rate_per_sec, time_difference_secs.ensure_into()?)? .ensure_mul(accumulated_rate) } diff --git a/pallets/interest-accrual/src/mock.rs b/pallets/interest-accrual/src/mock.rs index e19324e0bf..f3c42e2b7d 100644 --- a/pallets/interest-accrual/src/mock.rs +++ b/pallets/interest-accrual/src/mock.rs @@ -1,17 +1,16 @@ -use cfg_traits::Millis; -use frame_support::{derive_impl, parameter_types, traits::Hooks}; -use sp_io::TestExternalities; -use sp_runtime::BuildStorage; +use frame_support::{derive_impl, parameter_types}; use crate::*; +pub const START_DATE: Seconds = Seconds::from(1640995200); + pub type Balance = u128; pub type Rate = sp_arithmetic::fixed_point::FixedU128; frame_support::construct_runtime!( pub enum Runtime { System: frame_system, - Timestamp: pallet_timestamp, + Timer: cfg_mocks::time::pallet, InterestAccrual: crate, } ); @@ -21,11 +20,8 @@ impl frame_system::Config for Runtime { type Block = frame_system::mocking::MockBlock; } -impl pallet_timestamp::Config for Runtime { - type MinimumPeriod = (); - type Moment = Millis; - type OnTimestampSet = (); - type WeightInfo = (); +impl cfg_mocks::time::pallet::Config for Runtime { + type Moment = Seconds; } parameter_types! { @@ -37,25 +33,15 @@ impl Config for Runtime { type MaxRateCount = MaxRateCount; type Rate = Rate; type RuntimeEvent = RuntimeEvent; - type Time = Timestamp; + type Time = Timer; type Weights = (); } #[allow(unused)] pub fn new_test_ext() -> sp_io::TestExternalities { - const SECONDS: u64 = 1000; - const START_DATE: u64 = 1640995200; - - let storage = frame_system::GenesisConfig::::default() - .build_storage() - .unwrap(); - - let mut externalities = TestExternalities::new(storage); - externalities.execute_with(|| { - System::set_block_number(1); - System::on_initialize(System::block_number()); - Timestamp::on_initialize(System::block_number()); - Timestamp::set(RuntimeOrigin::none(), START_DATE * SECONDS).unwrap(); + let mut ext = System::externalities(); + ext.execute_with(|| { + Timer::mock_now(|| START_DATE); }); - externalities + ext } diff --git a/pallets/liquidity-pools/src/inbound.rs b/pallets/liquidity-pools/src/inbound.rs index 2b7d5b442a..c5404d120f 100644 --- a/pallets/liquidity-pools/src/inbound.rs +++ b/pallets/liquidity-pools/src/inbound.rs @@ -12,8 +12,8 @@ // GNU General Public License for more details. use cfg_traits::{ - investments::ForeignInvestment, liquidity_pools::OutboundMessageHandler, Permissions, - TimeAsSecs, + investments::ForeignInvestment, liquidity_pools::OutboundMessageHandler, time::UnixTimeSecs, + Permissions, }; use cfg_types::{ domain_address::{Domain, DomainAddress}, diff --git a/pallets/liquidity-pools/src/lib.rs b/pallets/liquidity-pools/src/lib.rs index 8e55c470c3..c4fbe45255 100644 --- a/pallets/liquidity-pools/src/lib.rs +++ b/pallets/liquidity-pools/src/lib.rs @@ -106,9 +106,10 @@ pub type GeneralCurrencyIndexOf = #[frame_support::pallet] pub mod pallet { + use cfg_primitives::Seconds; use cfg_traits::{ - investments::ForeignInvestment, liquidity_pools::InboundMessageHandler, CurrencyInspect, - Permissions, PoolInspect, Seconds, TimeAsSecs, TrancheTokenPrice, + investments::ForeignInvestment, liquidity_pools::InboundMessageHandler, time::UnixTimeSecs, + CurrencyInspect, Permissions, PoolInspect, TrancheTokenPrice, }; use cfg_types::{ permissions::{PermissionScope, PoolRole, Role}, @@ -200,7 +201,7 @@ pub mod pallet { /// The UNIX timestamp provider type required for checking the validity /// of investments. - type Time: TimeAsSecs; + type Time: UnixTimeSecs; /// The type for handling transfers, burning and minting of /// multi-assets. diff --git a/pallets/liquidity-pools/src/message.rs b/pallets/liquidity-pools/src/message.rs index fc917eee29..731f996224 100644 --- a/pallets/liquidity-pools/src/message.rs +++ b/pallets/liquidity-pools/src/message.rs @@ -5,7 +5,8 @@ //! also have a custom GMPF implementation, aiming for a fixed-size encoded //! representation for each message variant. -use cfg_traits::{liquidity_pools::LPEncoding, Seconds}; +use cfg_primitives::Seconds; +use cfg_traits::liquidity_pools::LPEncoding; use cfg_types::domain_address::Domain; use frame_support::{pallet_prelude::RuntimeDebug, BoundedVec}; use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; diff --git a/pallets/loans/src/benchmarking.rs b/pallets/loans/src/benchmarking.rs index b893fef06c..92d1369362 100644 --- a/pallets/loans/src/benchmarking.rs +++ b/pallets/loans/src/benchmarking.rs @@ -16,7 +16,8 @@ use cfg_traits::{ benchmarking::FundedPoolBenchmarkHelper, changes::ChangeGuard, interest::{CompoundingSchedule, InterestAccrual, InterestRate}, - Permissions, PoolWriteOffPolicyMutate, TimeAsSecs, ValueProvider, + time::UnixTimeSecs, + Permissions, PoolWriteOffPolicyMutate, ValueProvider, }; use cfg_types::{ adjustments::Adjustment, @@ -74,7 +75,7 @@ fn config_mocks() { MockChangeGuard::mock_released(move |_, _| Ok(change.clone())); Ok(sp_core::H256::default()) }); - MockTimer::mock_now(|| 0); + MockTimer::mock_now(|| 0u32.into()); } struct Helper(sp_std::marker::PhantomData); @@ -90,7 +91,6 @@ where AccountId = T::AccountId, Balance = T::Balance, >, - T::Moment: Default, T::PriceRegistry: ValueProvider<(u32, T::PoolId), T::PriceId, Value = PriceOf>, { fn prepare_benchmark() -> T::PoolId { @@ -325,7 +325,6 @@ benchmarks! { T::ItemId: From, T::PriceId: From, T::Pool: FundedPoolBenchmarkHelper, - T::Moment: Default, T::PriceRegistry: ValueProvider<(u32, T::PoolId), T::PriceId, Value = PriceOf>, } diff --git a/pallets/loans/src/entities/changes.rs b/pallets/loans/src/entities/changes.rs index eff6cd3ee4..7edb9b3635 100644 --- a/pallets/loans/src/entities/changes.rs +++ b/pallets/loans/src/entities/changes.rs @@ -1,4 +1,5 @@ -use cfg_traits::{interest::InterestRate, Seconds}; +use cfg_primitives::Seconds; +use cfg_traits::interest::InterestRate; use frame_support::{pallet_prelude::RuntimeDebug, storage::bounded_vec::BoundedVec}; use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; diff --git a/pallets/loans/src/entities/interest.rs b/pallets/loans/src/entities/interest.rs index d826346c23..9e08076b2a 100644 --- a/pallets/loans/src/entities/interest.rs +++ b/pallets/loans/src/entities/interest.rs @@ -1,6 +1,6 @@ use cfg_traits::{ interest::{InterestAccrual, InterestRate, RateCollection}, - TimeAsSecs, + time::UnixTimeSecs, }; use cfg_types::adjustments::Adjustment; use frame_support::RuntimeDebugNoBound; diff --git a/pallets/loans/src/entities/loans.rs b/pallets/loans/src/entities/loans.rs index b9cb593740..af52a189ea 100644 --- a/pallets/loans/src/entities/loans.rs +++ b/pallets/loans/src/entities/loans.rs @@ -1,7 +1,8 @@ +use cfg_primitives::Seconds; use cfg_traits::{ self, interest::{InterestAccrual, InterestRate, RateCollection}, - Seconds, TimeAsSecs, + time::UnixTimeSecs, }; use cfg_types::adjustments::Adjustment; use frame_support::{ensure, pallet_prelude::DispatchResult, RuntimeDebugNoBound}; @@ -598,7 +599,8 @@ impl TryFrom<(T::PoolId, ActiveLoan)> for ActiveLoanInfo { /// Adds `with_linear_pricing` to ExternalPricing struct for migration to v4 pub mod v3 { - use cfg_traits::{interest::InterestRate, Seconds}; + use cfg_primitives::Seconds; + use cfg_traits::interest::InterestRate; use parity_scale_codec::{Decode, Encode}; use crate::{ diff --git a/pallets/loans/src/entities/pricing/external.rs b/pallets/loans/src/entities/pricing/external.rs index 00d15a7532..a08c67946c 100644 --- a/pallets/loans/src/entities/pricing/external.rs +++ b/pallets/loans/src/entities/pricing/external.rs @@ -1,6 +1,5 @@ -use cfg_traits::{ - self, data::DataRegistry, interest::InterestRate, IntoSeconds, Seconds, TimeAsSecs, -}; +use cfg_primitives::Seconds; +use cfg_traits::{self, data::DataRegistry, interest::InterestRate, time::UnixTimeSecs}; use cfg_types::adjustments::Adjustment; use frame_support::{self, ensure, pallet_prelude::RuntimeDebug, RuntimeDebugNoBound}; use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; @@ -329,7 +328,7 @@ impl ExternalActivePricing { /// Adds `with_linear_pricing` to ExternalPricing struct for migration to v4 pub mod v3 { - use cfg_traits::Seconds; + use cfg_primitives::Seconds; use parity_scale_codec::{Decode, Encode}; use crate::{ diff --git a/pallets/loans/src/entities/pricing/internal.rs b/pallets/loans/src/entities/pricing/internal.rs index 10ea0d779a..75c1aad09d 100644 --- a/pallets/loans/src/entities/pricing/internal.rs +++ b/pallets/loans/src/entities/pricing/internal.rs @@ -1,6 +1,7 @@ +use cfg_primitives::Seconds; use cfg_traits::{ interest::{InterestRate, RateCollection}, - Seconds, TimeAsSecs, + time::UnixTimeSecs, }; use cfg_types::adjustments::Adjustment; use frame_support::{ diff --git a/pallets/loans/src/lib.rs b/pallets/loans/src/lib.rs index f3c3bceae7..1329c22c34 100644 --- a/pallets/loans/src/lib.rs +++ b/pallets/loans/src/lib.rs @@ -70,13 +70,14 @@ pub use weights::WeightInfo; #[frame_support::pallet] pub mod pallet { + use cfg_primitives::{Millis, Seconds}; use cfg_traits::{ self, changes::ChangeGuard, data::{DataCollection, DataRegistry}, interest::InterestAccrual, - IntoSeconds, Permissions, PoolInspect, PoolNAV, PoolReserve, PoolWriteOffPolicyMutate, - Seconds, TimeAsSecs, + time::UnixTimeSecs, + Permissions, PoolInspect, PoolNAV, PoolReserve, PoolWriteOffPolicyMutate, }; use cfg_types::{ adjustments::Adjustment, @@ -117,7 +118,7 @@ pub mod pallet { pub type PortfolioInfoOf = Vec<(::LoanId, ActiveLoanInfo)>; pub type AssetOf = (::CollectionId, ::ItemId); - pub type PriceOf = (::Balance, ::Moment); + pub type PriceOf = (::Balance, Millis); const STORAGE_VERSION: StorageVersion = StorageVersion::new(4); @@ -167,10 +168,7 @@ pub mod pallet { type PerThing: Parameter + Member + PerThing + TypeInfo + MaxEncodedLen; /// Fetching method for the time of the current block - type Time: TimeAsSecs; - - /// Generic time type - type Moment: Parameter + Member + Copy + IntoSeconds; + type Time: UnixTimeSecs; /// Used to mint, transfer, and inspect assets. type NonFungible: Transfer @@ -1300,7 +1298,7 @@ pub mod pallet { vec![ WriteOffRule::new( - [WriteOffTrigger::PrincipalOverdue(0)], + [WriteOffTrigger::PrincipalOverdue(0u32.into())], T::Rate::zero(), T::Rate::zero(), ); diff --git a/pallets/loans/src/tests/borrow_loan.rs b/pallets/loans/src/tests/borrow_loan.rs index ad3cf70651..c283beda05 100644 --- a/pallets/loans/src/tests/borrow_loan.rs +++ b/pallets/loans/src/tests/borrow_loan.rs @@ -11,7 +11,7 @@ fn config_mocks(withdraw_amount: Balance) { MockPrices::mock_get(|id, pool_id| { assert_eq!(*pool_id, POOL_A); match *id { - REGISTER_PRICE_ID => Ok((PRICE_VALUE, BLOCK_TIME_MS)), + REGISTER_PRICE_ID => Ok((PRICE_VALUE, PRICE_TIMESTAMP)), _ => Err(PRICE_ID_NO_FOUND), } }); @@ -73,7 +73,7 @@ fn with_restriction_no_written_off() { PrincipalInput::Internal(COLLATERAL_VALUE / 2) )); - advance_time(YEAR + DAY); + advance_time(SECONDS_PER_YEAR + SECONDS_PER_DAY); util::write_off_loan(loan_id); assert_noop!( @@ -136,7 +136,7 @@ fn with_maturity_passed() { new_test_ext().execute_with(|| { let loan_id = util::create_loan(util::base_internal_loan()); - advance_time(YEAR); + advance_time(SECONDS_PER_YEAR); config_mocks(COLLATERAL_VALUE); assert_noop!( @@ -333,7 +333,7 @@ fn with_unregister_price_id_and_oracle_not_required() { ); // Suddenty, the oracle set a value - MockPrices::mock_get(|_, _| Ok((PRICE_VALUE * 8, BLOCK_TIME_MS))); + MockPrices::mock_get(|_, _| Ok((PRICE_VALUE * 8, PRICE_TIMESTAMP))); assert_eq!( (QUANTITY).saturating_mul_int(PRICE_VALUE * 8), @@ -552,11 +552,11 @@ fn twice_with_elapsed_time() { )); assert_eq!(COLLATERAL_VALUE / 2, util::current_loan_debt(loan_id)); - advance_time(YEAR / 2); + advance_time(SECONDS_PER_YEAR / 2); assert_eq!( util::current_debt_for( - util::interest_for(DEFAULT_INTEREST_RATE, YEAR / 2), + util::interest_for(DEFAULT_INTEREST_RATE, SECONDS_PER_YEAR / 2), COLLATERAL_VALUE / 2, ), util::current_loan_debt(loan_id) @@ -633,7 +633,7 @@ mod cashflow { let principal = COLLATERAL_VALUE / 2; let acc_interest_rate_per_year = checked_pow( util::default_interest_rate().per_sec().unwrap(), - SECONDS_PER_YEAR as usize, + SECONDS_PER_YEAR.inner as usize, ) .unwrap(); let interest = acc_interest_rate_per_year.saturating_mul_int(principal) - principal; @@ -669,7 +669,7 @@ mod cashflow { let principal = amount.balance().unwrap(); let acc_interest_rate_per_year = checked_pow( util::default_interest_rate().per_sec().unwrap(), - SECONDS_PER_YEAR as usize, + SECONDS_PER_YEAR.inner as usize, ) .unwrap(); diff --git a/pallets/loans/src/tests/close_loan.rs b/pallets/loans/src/tests/close_loan.rs index 307603e4a0..d9d3645696 100644 --- a/pallets/loans/src/tests/close_loan.rs +++ b/pallets/loans/src/tests/close_loan.rs @@ -72,7 +72,7 @@ fn with_time_after_fully_repaid_internal() { util::borrow_loan(loan_id, PrincipalInput::Internal(COLLATERAL_VALUE)); util::repay_loan(loan_id, PrincipalInput::Internal(COLLATERAL_VALUE)); - advance_time(YEAR); + advance_time(SECONDS_PER_YEAR); assert_ok!(Loans::close( RuntimeOrigin::signed(BORROWER), diff --git a/pallets/loans/src/tests/create_loan.rs b/pallets/loans/src/tests/create_loan.rs index 37b87acdf9..1035e704e8 100644 --- a/pallets/loans/src/tests/create_loan.rs +++ b/pallets/loans/src/tests/create_loan.rs @@ -17,7 +17,7 @@ fn config_mocks(pool_id: PoolId) { MockPrices::mock_get(|id, pool_id| { assert_eq!(*pool_id, POOL_A); match *id { - REGISTER_PRICE_ID => Ok((PRICE_VALUE, BLOCK_TIME_MS)), + REGISTER_PRICE_ID => Ok((PRICE_VALUE, PRICE_TIMESTAMP)), _ => Err("Should never be dispatched".into()), } }); @@ -91,7 +91,7 @@ fn with_wrong_schedule() { let loan = LoanInfo { schedule: RepaymentSchedule { - maturity: Maturity::fixed(now().as_secs()), + maturity: Maturity::fixed(now()), interest_payments: InterestPayments::OnceAtMaturity, pay_down_schedule: PayDownSchedule::None, }, diff --git a/pallets/loans/src/tests/mock.rs b/pallets/loans/src/tests/mock.rs index ee381f73a6..0ad1084a37 100644 --- a/pallets/loans/src/tests/mock.rs +++ b/pallets/loans/src/tests/mock.rs @@ -11,18 +11,17 @@ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. -use std::time::Duration; - use cfg_mocks::{ pallet_mock_change_guard, pallet_mock_data, pallet_mock_permissions, pallet_mock_pools, }; -use cfg_traits::Millis; +use cfg_primitives::{Millis, Seconds}; +use cfg_traits::time::UnixTimeSecs; use cfg_types::permissions::PermissionScope; use frame_support::{ derive_impl, traits::{ tokens::nonfungibles::{Create, Mutate}, - AsEnsureOriginWithArg, Hooks, UnixTime, + AsEnsureOriginWithArg, Hooks, }, }; use frame_system::{EnsureRoot, EnsureSigned}; @@ -34,11 +33,7 @@ use sp_runtime::{DispatchError, FixedU128}; use crate::{entities::changes::Change, pallet as pallet_loans}; -pub const BLOCK_TIME: Duration = Duration::from_secs(10); -pub const YEAR: Duration = Duration::from_secs(365 * 24 * 3600); -pub const DAY: Duration = Duration::from_secs(24 * 3600); - -pub const BLOCK_TIME_MS: u64 = BLOCK_TIME.as_millis() as u64; +pub const INITIAL_TIME: Seconds = Seconds::new(10); pub const ASSET_COLLECTION_OWNER: AccountId = 1; pub const BORROWER: AccountId = 1; @@ -75,6 +70,7 @@ pub const PRICE_VALUE: Balance = 980; pub const NOTIONAL: Balance = 1000; pub const QUANTITY: Quantity = Quantity::from_rational(12, 1); pub const CHANGE_ID: ChangeId = H256::repeat_byte(0x42); +pub const PRICE_TIMESTAMP: Millis = Millis::new(10000); pub const MAX_PRICE_VARIATION: Rate = Rate::from_rational(1, 100); pub const PRICE_ID_NO_FOUND: DispatchError = DispatchError::Other("Price ID not found"); @@ -121,7 +117,7 @@ impl frame_system::Config for Runtime { } impl cfg_mocks::time::pallet::Config for Runtime { - type Moment = Millis; + type Moment = Seconds; } #[derive_impl(pallet_balances::config_preludes::TestDefaultConfig as pallet_balances::DefaultConfig)] @@ -199,7 +195,6 @@ impl pallet_loans::Config for Runtime { type LoanId = LoanId; type MaxActiveLoansPerPool = MaxActiveLoansPerPool; type MaxWriteOffPolicySize = MaxWriteOffPolicySize; - type Moment = Millis; type NonFungible = Uniques; type PerThing = Perbill; type Permissions = MockPermissions; @@ -218,7 +213,7 @@ impl pallet_loans::Config for Runtime { pub fn new_test_ext() -> sp_io::TestExternalities { let mut ext = System::externalities(); ext.execute_with(|| { - MockTimer::mock_now(|| BLOCK_TIME.as_millis() as u64); + MockTimer::mock_now(|| INITIAL_TIME); Uniques::create_collection(&COLLECTION_A, &BORROWER, &ASSET_COLLECTION_OWNER).unwrap(); Uniques::mint_into(&COLLECTION_A, &ASSET_AA.1, &BORROWER).unwrap(); @@ -232,12 +227,12 @@ pub fn new_test_ext() -> sp_io::TestExternalities { ext } -pub fn now() -> Duration { - ::now() +pub fn now() -> Seconds { + MockTimer::now_secs() } -pub fn advance_time(elapsed: Duration) { +pub fn advance_time(elapsed: Seconds) { let before = now(); - MockTimer::mock_now(move || (before + elapsed).as_millis() as u64); + MockTimer::mock_now(move || before + elapsed); InterestAccrual::on_initialize(0); } diff --git a/pallets/loans/src/tests/mod.rs b/pallets/loans/src/tests/mod.rs index 01ebc3b8ae..bfa5839bc4 100644 --- a/pallets/loans/src/tests/mod.rs +++ b/pallets/loans/src/tests/mod.rs @@ -1,7 +1,5 @@ -use std::time::Duration; - use cfg_mocks::pallet_mock_data::util::MockDataCollection; -use cfg_primitives::{SECONDS_PER_DAY, SECONDS_PER_YEAR}; +use cfg_primitives::{Seconds, SECONDS_PER_DAY, SECONDS_PER_YEAR}; use cfg_traits::interest::{CompoundingSchedule, InterestRate}; use cfg_types::permissions::{PermissionScope, PoolRole, Role}; use frame_support::{assert_noop, assert_ok, storage::bounded_vec::BoundedVec}; diff --git a/pallets/loans/src/tests/mutate_loan.rs b/pallets/loans/src/tests/mutate_loan.rs index 7f4d5f2e83..beb6c833b6 100644 --- a/pallets/loans/src/tests/mutate_loan.rs +++ b/pallets/loans/src/tests/mutate_loan.rs @@ -147,7 +147,7 @@ mod wrong_mutation { let loan_id = util::create_loan(util::base_internal_loan()); util::borrow_loan(loan_id, PrincipalInput::Internal(0)); - let mutation = LoanMutation::MaturityExtension(YEAR.as_secs()); + let mutation = LoanMutation::MaturityExtension(SECONDS_PER_YEAR); config_mocks(loan_id, &mutation); assert_noop!( @@ -211,8 +211,8 @@ fn with_successful_mutation_application() { let loan = LoanInfo { schedule: RepaymentSchedule { maturity: Maturity::Fixed { - date: (now() + YEAR).as_secs(), - extension: YEAR.as_secs(), + date: now() + SECONDS_PER_YEAR, + extension: SECONDS_PER_YEAR, }, interest_payments: InterestPayments::OnceAtMaturity, pay_down_schedule: PayDownSchedule::None, @@ -242,10 +242,10 @@ fn with_successful_mutation_application() { // LoanMutation::InterestPayments(..), No changes, only one variant // LoanMutation::PayDownSchedule(..), No changes, only one variant LoanMutation::Maturity(Maturity::Fixed { - date: (now() + YEAR * 2).as_secs(), - extension: (YEAR * 2).as_secs(), + date: now() + SECONDS_PER_YEAR * 2, + extension: SECONDS_PER_YEAR * 2, }), - LoanMutation::MaturityExtension(YEAR.as_secs()), + LoanMutation::MaturityExtension(SECONDS_PER_YEAR), LoanMutation::InterestRate(InterestRate::Fixed { rate_per_year: Rate::from_float(0.5), compounding: CompoundingSchedule::Secondly, diff --git a/pallets/loans/src/tests/policy.rs b/pallets/loans/src/tests/policy.rs index ef2c0a4198..1cd9f05e80 100644 --- a/pallets/loans/src/tests/policy.rs +++ b/pallets/loans/src/tests/policy.rs @@ -10,7 +10,7 @@ fn config_mocks(pool_id: PoolId, policy: &BoundedVec, MaxWrit MockPrices::mock_get(|id, pool_id| { assert_eq!(*pool_id, POOL_A); assert_eq!(*id, REGISTER_PRICE_ID); - Ok((PRICE_VALUE, BLOCK_TIME_MS)) + Ok((PRICE_VALUE, PRICE_TIMESTAMP)) }); MockChangeGuard::mock_note({ let policy = policy.clone(); @@ -98,7 +98,7 @@ fn with_wrong_loan_mutation_change() { fn with_successful_overwriting() { new_test_ext().execute_with(|| { let policy: BoundedVec<_, _> = vec![WriteOffRule::new( - [WriteOffTrigger::PrincipalOverdue(1)], + [WriteOffTrigger::PrincipalOverdue(1u32.into())], Rate::from_float(POLICY_PERCENTAGE), Rate::from_float(POLICY_PENALTY), )] @@ -131,7 +131,7 @@ fn with_price_outdated() { util::borrow_loan(loan_id, PrincipalInput::External(amount)); let policy: BoundedVec<_, _> = vec![WriteOffRule::new( - [WriteOffTrigger::PriceOutdated(10)], + [WriteOffTrigger::PriceOutdated(10u32.into())], Rate::from_float(POLICY_PERCENTAGE), Rate::from_float(POLICY_PENALTY), )] @@ -150,13 +150,13 @@ fn with_price_outdated() { CHANGE_ID )); - advance_time(Duration::from_secs(9)); + advance_time(Seconds::new(9)); assert_noop!( Loans::write_off(RuntimeOrigin::signed(ANY), POOL_A, loan_id), Error::::NoValidWriteOffRule ); - advance_time(Duration::from_secs(1)); + advance_time(Seconds::new(9)); assert_ok!(Loans::write_off( RuntimeOrigin::signed(ANY), POOL_A, @@ -181,25 +181,25 @@ fn with_success() { let policy: BoundedVec<_, _> = vec![ WriteOffRule::new( - [WriteOffTrigger::PriceOutdated(10)], + [WriteOffTrigger::PriceOutdated(10u32.into())], Rate::from_float(0.8), Rate::from_float(0.8), ), WriteOffRule::new( [ - WriteOffTrigger::PrincipalOverdue(1), - WriteOffTrigger::PriceOutdated(0), + WriteOffTrigger::PrincipalOverdue(1u32.into()), + WriteOffTrigger::PriceOutdated(0u32.into()), ], Rate::from_float(0.2), Rate::from_float(0.2), ), WriteOffRule::new( - [WriteOffTrigger::PrincipalOverdue(4)], + [WriteOffTrigger::PrincipalOverdue(4u32.into())], Rate::from_float(0.5), Rate::from_float(0.5), ), WriteOffRule::new( - [WriteOffTrigger::PrincipalOverdue(9)], + [WriteOffTrigger::PrincipalOverdue(9u32.into())], Rate::from_float(0.3), Rate::from_float(0.9), ), @@ -216,7 +216,7 @@ fn with_success() { )); // Check if a loan is correctly writen off - advance_time(YEAR + DAY * 10); + advance_time(SECONDS_PER_YEAR + SECONDS_PER_DAY * 10); assert_ok!(Loans::write_off( RuntimeOrigin::signed(ANY), POOL_A, diff --git a/pallets/loans/src/tests/portfolio_valuation.rs b/pallets/loans/src/tests/portfolio_valuation.rs index a6887adde4..2f1de8bac3 100644 --- a/pallets/loans/src/tests/portfolio_valuation.rs +++ b/pallets/loans/src/tests/portfolio_valuation.rs @@ -5,14 +5,14 @@ fn config_mocks() { MockPrices::mock_get(move |id, pool_id| { assert_eq!(*pool_id, POOL_A); match *id { - REGISTER_PRICE_ID => Ok((PRICE_VALUE, BLOCK_TIME_MS)), + REGISTER_PRICE_ID => Ok((PRICE_VALUE, PRICE_TIMESTAMP)), _ => Err(PRICE_ID_NO_FOUND), } }); MockPrices::mock_collection(|pool_id| { assert_eq!(*pool_id, POOL_A); Ok(MockDataCollection::new(|id| match *id { - REGISTER_PRICE_ID => Ok((PRICE_VALUE, BLOCK_TIME_MS)), + REGISTER_PRICE_ID => Ok((PRICE_VALUE, PRICE_TIMESTAMP)), _ => Err(PRICE_ID_NO_FOUND), })) }); @@ -59,7 +59,7 @@ fn without_active_loans() { ..util::base_internal_loan() }); - advance_time(YEAR / 2); + advance_time(SECONDS_PER_YEAR / 2); config_mocks(); update_portfolio(); @@ -88,7 +88,7 @@ fn with_active_loans() { update_portfolio(); expected_portfolio(valuation); - advance_time(YEAR / 2); + advance_time(SECONDS_PER_YEAR / 2); update_portfolio(); expected_portfolio(util::current_loan_pv(loan_1) + util::current_loan_pv(loan_2)); @@ -109,7 +109,7 @@ fn with_active_written_off_loans() { util::borrow_loan(loan_2, PrincipalInput::Internal(COLLATERAL_VALUE)); util::repay_loan(loan_2, PrincipalInput::Internal(COLLATERAL_VALUE / 4)); - advance_time(YEAR + DAY); + advance_time(SECONDS_PER_YEAR + SECONDS_PER_DAY); util::write_off_loan(loan_1); util::write_off_loan(loan_2); @@ -134,16 +134,16 @@ fn filled_and_cleaned() { util::borrow_loan(loan_2, PrincipalInput::Internal(COLLATERAL_VALUE)); util::repay_loan(loan_2, PrincipalInput::Internal(COLLATERAL_VALUE / 2)); - advance_time(YEAR + DAY); + advance_time(SECONDS_PER_YEAR + SECONDS_PER_DAY); util::write_off_loan(loan_1); - advance_time(YEAR / 2); + advance_time(SECONDS_PER_YEAR / 2); util::repay_loan(loan_1, PrincipalInput::External(amount)); util::repay_loan(loan_2, PrincipalInput::Internal(COLLATERAL_VALUE / 2)); - advance_time(YEAR / 2); + advance_time(SECONDS_PER_YEAR / 2); config_mocks(); update_portfolio(); @@ -162,7 +162,7 @@ fn exact_and_inexact_matches() { let loan_1 = util::create_loan(util::base_internal_loan()); util::borrow_loan(loan_1, PrincipalInput::Internal(COLLATERAL_VALUE)); - advance_time(YEAR / 2); + advance_time(SECONDS_PER_YEAR / 2); config_mocks(); update_portfolio(); @@ -188,7 +188,7 @@ fn with_unregister_price_id_and_oracle_not_required() { let amount = ExternalAmount::new(QUANTITY, PRICE_VALUE); util::borrow_loan(loan_1, PrincipalInput::External(amount.clone())); - advance_time(YEAR / 2); + advance_time(SECONDS_PER_YEAR / 2); // This is affected by the linear_accrual_price() computation. let price_value_after_half_year = PRICE_VALUE + (NOTIONAL - PRICE_VALUE) / 2; @@ -201,7 +201,7 @@ fn with_unregister_price_id_and_oracle_not_required() { const MARKET_PRICE_VALUE: Balance = 999; MockPrices::mock_collection(|_| { Ok(MockDataCollection::new(|_| { - Ok((MARKET_PRICE_VALUE, BLOCK_TIME_MS)) + Ok((MARKET_PRICE_VALUE, PRICE_TIMESTAMP)) })) }); let price_value_after_half_year = MARKET_PRICE_VALUE + (NOTIONAL - MARKET_PRICE_VALUE) / 2; @@ -216,7 +216,7 @@ fn empty_portfolio_with_current_timestamp() { new_test_ext().execute_with(|| { assert_eq!( PortfolioValuation::::get(POOL_A).last_updated(), - now().as_secs() + now() ); }); } @@ -241,12 +241,12 @@ fn no_linear_pricing_either_settlement_or_oracle() { util::borrow_loan(loan_1, PrincipalInput::External(amount.clone())); - advance_time(YEAR / 2); + advance_time(SECONDS_PER_YEAR / 2); const MARKET_PRICE_VALUE: Balance = 999; MockPrices::mock_collection(|_| { Ok(MockDataCollection::new(|_| { - Ok((MARKET_PRICE_VALUE, BLOCK_TIME_MS)) + Ok((MARKET_PRICE_VALUE, PRICE_TIMESTAMP)) })) }); @@ -263,7 +263,7 @@ fn no_linear_pricing_either_settlement_or_oracle() { MockPrices::mock_collection(|_| { Ok(MockDataCollection::new(|_| { - Ok((MARKET_PRICE_VALUE, BLOCK_TIME_MS)) + Ok((MARKET_PRICE_VALUE, PRICE_TIMESTAMP)) })) }); update_portfolio(); @@ -313,11 +313,11 @@ fn internal_oustanding_debt_with_no_maturity() { update_portfolio(); expected_portfolio(pv); - advance_time(YEAR); + advance_time(SECONDS_PER_YEAR); update_portfolio(); expected_portfolio( - Rate::from_float(util::interest_for(DEFAULT_INTEREST_RATE, YEAR)) + Rate::from_float(util::interest_for(DEFAULT_INTEREST_RATE, SECONDS_PER_YEAR)) .checked_mul_int(COLLATERAL_VALUE) .unwrap(), ); diff --git a/pallets/loans/src/tests/repay_loan.rs b/pallets/loans/src/tests/repay_loan.rs index 5177a0dcdf..7aa50f1e41 100644 --- a/pallets/loans/src/tests/repay_loan.rs +++ b/pallets/loans/src/tests/repay_loan.rs @@ -16,7 +16,7 @@ pub fn config_mocks_with_price(deposit_amount: Balance, price: Balance) { MockPrices::mock_get(move |id, pool_id| { assert_eq!(*pool_id, POOL_A); match *id { - REGISTER_PRICE_ID => Ok((price, BLOCK_TIME_MS)), + REGISTER_PRICE_ID => Ok((price, PRICE_TIMESTAMP)), _ => Err(PRICE_ID_NO_FOUND), } }); @@ -94,7 +94,7 @@ fn has_been_written_off() { let loan_id = util::create_loan(util::base_internal_loan()); util::borrow_loan(loan_id, PrincipalInput::Internal(COLLATERAL_VALUE)); - advance_time(YEAR + DAY); + advance_time(SECONDS_PER_YEAR + SECONDS_PER_DAY); util::write_off_loan(loan_id); config_mocks(util::current_loan_debt(loan_id)); @@ -494,11 +494,11 @@ fn twice_internal_with_elapsed_time() { }, )); - advance_time(YEAR / 2); + advance_time(SECONDS_PER_YEAR / 2); assert_eq!( util::current_debt_for( - util::interest_for(DEFAULT_INTEREST_RATE, YEAR / 2), + util::interest_for(DEFAULT_INTEREST_RATE, SECONDS_PER_YEAR / 2), COLLATERAL_VALUE / 2, ), util::current_loan_debt(loan_id) @@ -555,11 +555,11 @@ fn twice_external_with_elapsed_time() { }, )); - advance_time(YEAR / 2); + advance_time(SECONDS_PER_YEAR / 2); assert_eq!( util::current_debt_for( - util::interest_for(DEFAULT_INTEREST_RATE, YEAR / 2), + util::interest_for(DEFAULT_INTEREST_RATE, SECONDS_PER_YEAR / 2), (QUANTITY / 2.into()).saturating_mul_int(NOTIONAL), ), util::current_loan_debt(loan_id) @@ -611,7 +611,7 @@ fn current_debt_rate_no_increase_if_fully_repaid() { }); util::borrow_loan(loan_id, PrincipalInput::Internal(COLLATERAL_VALUE)); - advance_time(YEAR / 2); + advance_time(SECONDS_PER_YEAR / 2); config_mocks(util::current_loan_debt(loan_id)); assert_ok!(Loans::repay( @@ -625,7 +625,7 @@ fn current_debt_rate_no_increase_if_fully_repaid() { }, )); - advance_time(YEAR); + advance_time(SECONDS_PER_YEAR); assert_eq!(0, util::current_loan_debt(loan_id)); }); @@ -942,11 +942,11 @@ fn with_external_pricing() { assert_eq!(current_price(), PRICE_VALUE); // In the middle of the line - advance_time(YEAR / 2); + advance_time(SECONDS_PER_YEAR / 2); assert_eq!(current_price(), PRICE_VALUE + (NOTIONAL - PRICE_VALUE) / 2); // BEFORE: the loan not yet overdue - advance_time(YEAR / 2 - DAY); + advance_time(SECONDS_PER_YEAR / 2 - SECONDS_PER_DAY); assert_ok!(Loans::repay( RuntimeOrigin::signed(BORROWER), POOL_A, @@ -956,7 +956,7 @@ fn with_external_pricing() { assert!(current_price() < NOTIONAL); // EXACT: the loan is just at matuyrity date - advance_time(DAY); + advance_time(SECONDS_PER_DAY); assert_ok!(Loans::repay( RuntimeOrigin::signed(BORROWER), @@ -967,7 +967,7 @@ fn with_external_pricing() { assert_eq!(current_price(), NOTIONAL); // AFTER: the loan overpassing maturity date - advance_time(DAY); + advance_time(SECONDS_PER_DAY); assert_ok!(Loans::repay( RuntimeOrigin::signed(BORROWER), diff --git a/pallets/loans/src/tests/transfer_debt.rs b/pallets/loans/src/tests/transfer_debt.rs index 59c2a13d88..4371060101 100644 --- a/pallets/loans/src/tests/transfer_debt.rs +++ b/pallets/loans/src/tests/transfer_debt.rs @@ -11,7 +11,7 @@ fn config_mocks( MockPrices::mock_get(|id, pool_id| { assert_eq!(*id, REGISTER_PRICE_ID); assert_eq!(*pool_id, POOL_A); - Ok((PRICE_VALUE, BLOCK_TIME_MS)) + Ok((PRICE_VALUE, PRICE_TIMESTAMP)) }); MockPrices::mock_register_id(|id, pool_id| { assert_eq!(*pool_id, POOL_A); @@ -247,7 +247,7 @@ fn with_mismatch_external_internal_amounts() { MockPrices::mock_get(|id, pool_id| { assert_eq!(*id, REGISTER_PRICE_ID); assert_eq!(*pool_id, POOL_A); - Ok((PRICE_VALUE, BLOCK_TIME_MS)) + Ok((PRICE_VALUE, PRICE_TIMESTAMP)) }); assert_noop!( Loans::propose_transfer_debt( @@ -316,7 +316,7 @@ fn with_mismatch_external_external_amounts() { MockPrices::mock_get(|id, pool_id| { assert_eq!(*id, REGISTER_PRICE_ID); assert_eq!(*pool_id, POOL_A); - Ok((PRICE_VALUE, BLOCK_TIME_MS)) + Ok((PRICE_VALUE, PRICE_TIMESTAMP)) }); assert_noop!( Loans::propose_transfer_debt( diff --git a/pallets/loans/src/tests/util.rs b/pallets/loans/src/tests/util.rs index 4bd83467c6..d947f673a1 100644 --- a/pallets/loans/src/tests/util.rs +++ b/pallets/loans/src/tests/util.rs @@ -50,8 +50,8 @@ pub fn current_loan_pv(loan_id: LoanId) -> Balance { get_loan(loan_id).present_value(POOL_A).unwrap() } -pub fn interest_for(rate: f64, elapsed: Duration) -> f64 { - (1.0 + rate / YEAR.as_secs() as f64).powi(elapsed.as_secs() as i32) +pub fn interest_for(rate: f64, elapsed: Seconds) -> f64 { + (1.0 + rate / SECONDS_PER_YEAR.inner as f64).powi(elapsed.inner as i32) } pub fn current_debt_for(interest: f64, balance: Balance) -> Balance { @@ -121,8 +121,8 @@ pub fn base_internal_loan() -> LoanInfo { LoanInfo { schedule: RepaymentSchedule { maturity: Maturity::Fixed { - date: (now() + YEAR).as_secs(), - extension: (YEAR / 2).as_secs(), + date: now() + SECONDS_PER_YEAR, + extension: SECONDS_PER_YEAR / 2, }, interest_payments: InterestPayments::OnceAtMaturity, pay_down_schedule: PayDownSchedule::None, @@ -150,7 +150,7 @@ pub fn base_external_pricing() -> ExternalPricing { pub fn base_external_loan() -> LoanInfo { LoanInfo { schedule: RepaymentSchedule { - maturity: Maturity::fixed((now() + YEAR).as_secs()), + maturity: Maturity::fixed(now() + SECONDS_PER_YEAR), interest_payments: InterestPayments::OnceAtMaturity, pay_down_schedule: PayDownSchedule::None, }, @@ -172,7 +172,7 @@ pub fn create_loan_by(loan: LoanInfo, borrower: AccountId) -> LoanId { MockPermissions::mock_has(|_, _, _| true); MockPools::mock_pool_exists(|_| true); MockPools::mock_account_for(|_| POOL_A_ACCOUNT); - MockPrices::mock_get(|_, _| Ok((PRICE_VALUE, BLOCK_TIME_MS))); + MockPrices::mock_get(|_, _| Ok((PRICE_VALUE, PRICE_TIMESTAMP))); Loans::create(RuntimeOrigin::signed(borrower), POOL_A, loan).expect("successful creation"); @@ -186,7 +186,7 @@ pub fn create_loan_by(loan: LoanInfo, borrower: AccountId) -> LoanId { pub fn borrow_loan(loan_id: LoanId, borrow_amount: PrincipalInput) { MockPools::mock_withdraw(|_, _, _| Ok(())); - MockPrices::mock_get(|_, _| Ok((PRICE_VALUE, BLOCK_TIME_MS))); + MockPrices::mock_get(|_, _| Ok((PRICE_VALUE, PRICE_TIMESTAMP))); MockPrices::mock_register_id(|_, _| Ok(())); Loans::borrow( @@ -204,7 +204,7 @@ pub fn borrow_loan(loan_id: LoanId, borrow_amount: PrincipalInput) { pub fn repay_loan(loan_id: LoanId, repay_amount: PrincipalInput) { MockPools::mock_deposit(|_, _, _| Ok(())); - MockPrices::mock_get(|_, _| Ok((PRICE_VALUE, BLOCK_TIME_MS))); + MockPrices::mock_get(|_, _| Ok((PRICE_VALUE, PRICE_TIMESTAMP))); Loans::repay( RuntimeOrigin::signed(borrower(loan_id)), @@ -224,7 +224,7 @@ pub fn repay_loan(loan_id: LoanId, repay_amount: PrincipalInput) { pub fn write_off_loan(loan_id: LoanId) { set_up_policy(POLICY_PERCENTAGE, POLICY_PENALTY); - MockPrices::mock_get(|_, _| Ok((PRICE_VALUE, BLOCK_TIME_MS))); + MockPrices::mock_get(|_, _| Ok((PRICE_VALUE, PRICE_TIMESTAMP))); Loans::write_off(RuntimeOrigin::signed(ANY), POOL_A, loan_id).expect("successful write off"); diff --git a/pallets/loans/src/tests/write_off_loan.rs b/pallets/loans/src/tests/write_off_loan.rs index 47d3b172ce..1af14e6f01 100644 --- a/pallets/loans/src/tests/write_off_loan.rs +++ b/pallets/loans/src/tests/write_off_loan.rs @@ -38,7 +38,7 @@ fn with_policy_but_not_overdue() { let loan_id = util::create_loan(util::base_internal_loan()); util::borrow_loan(loan_id, PrincipalInput::Internal(COLLATERAL_VALUE)); - advance_time(YEAR + BLOCK_TIME); + advance_time(SECONDS_PER_YEAR + INITIAL_TIME); // The loan maturity date has passed, but the policy can no be applied yet. assert_noop!( @@ -56,7 +56,7 @@ fn with_valid_maturity() { let loan_id = util::create_loan(util::base_internal_loan()); util::borrow_loan(loan_id, PrincipalInput::Internal(COLLATERAL_VALUE)); - advance_time(YEAR / 2); + advance_time(SECONDS_PER_YEAR / 2); // The loan maturity date has no passed. assert_noop!( @@ -123,7 +123,7 @@ fn with_wrong_permission() { let loan_id = util::create_loan(util::base_internal_loan()); util::borrow_loan(loan_id, PrincipalInput::Internal(COLLATERAL_VALUE)); - advance_time(YEAR + DAY); + advance_time(SECONDS_PER_YEAR + SECONDS_PER_DAY); config_mocks(); assert_noop!( @@ -147,7 +147,7 @@ fn with_success() { let loan_id = util::create_loan(util::base_internal_loan()); util::borrow_loan(loan_id, PrincipalInput::Internal(COLLATERAL_VALUE)); - advance_time(YEAR + DAY); + advance_time(SECONDS_PER_YEAR + SECONDS_PER_DAY); assert_ok!(Loans::write_off( RuntimeOrigin::signed(ANY), @@ -165,7 +165,7 @@ fn with_admin_success() { let loan_id = util::create_loan(util::base_internal_loan()); util::borrow_loan(loan_id, PrincipalInput::Internal(COLLATERAL_VALUE)); - advance_time(YEAR + DAY); + advance_time(SECONDS_PER_YEAR + SECONDS_PER_DAY); config_mocks(); @@ -215,7 +215,7 @@ fn with_admin_less_than_policy() { let loan_id = util::create_loan(util::base_internal_loan()); util::borrow_loan(loan_id, PrincipalInput::Internal(COLLATERAL_VALUE)); - advance_time(YEAR + DAY); + advance_time(SECONDS_PER_YEAR + SECONDS_PER_DAY); config_mocks(); @@ -253,7 +253,7 @@ fn with_policy_change_after() { let loan_id = util::create_loan(util::base_internal_loan()); util::borrow_loan(loan_id, PrincipalInput::Internal(COLLATERAL_VALUE)); - advance_time(YEAR + DAY); + advance_time(SECONDS_PER_YEAR + SECONDS_PER_DAY); assert_ok!(Loans::write_off( RuntimeOrigin::signed(ANY), @@ -296,7 +296,7 @@ fn with_policy_change_after_admin() { util::set_up_policy(POLICY_PERCENTAGE, POLICY_PENALTY); - advance_time(YEAR + DAY); + advance_time(SECONDS_PER_YEAR + SECONDS_PER_DAY); assert_ok!(Loans::write_off( RuntimeOrigin::signed(ANY), @@ -322,7 +322,7 @@ fn with_percentage_applied_internal() { let loan_id = util::create_loan(util::base_internal_loan()); util::borrow_loan(loan_id, PrincipalInput::Internal(COLLATERAL_VALUE)); - advance_time(YEAR + DAY); + advance_time(SECONDS_PER_YEAR + SECONDS_PER_DAY); let pv = util::current_loan_pv(loan_id); @@ -349,12 +349,12 @@ fn with_percentage_applied_external() { let amount = ExternalAmount::new(QUANTITY, PRICE_VALUE); util::borrow_loan(loan_id, PrincipalInput::External(amount)); - advance_time(YEAR + DAY); + advance_time(SECONDS_PER_YEAR + SECONDS_PER_DAY); MockPrices::mock_get(|id, pool_id| { assert_eq!(*pool_id, POOL_A); assert_eq!(*id, REGISTER_PRICE_ID); - Ok((PRICE_VALUE, BLOCK_TIME_MS)) + Ok((PRICE_VALUE, PRICE_TIMESTAMP)) }); let pv = util::current_loan_pv(loan_id); @@ -380,7 +380,7 @@ fn with_penalty_applied() { let loan_id = util::create_loan(util::base_internal_loan()); util::borrow_loan(loan_id, PrincipalInput::Internal(COLLATERAL_VALUE)); - advance_time(YEAR + DAY); + advance_time(SECONDS_PER_YEAR + SECONDS_PER_DAY); assert_ok!(Loans::write_off( RuntimeOrigin::signed(ANY), @@ -391,13 +391,13 @@ fn with_penalty_applied() { // Modify an interest rate doesn't have effect in the same instant assert_eq!( util::current_debt_for( - util::interest_for(DEFAULT_INTEREST_RATE, YEAR + DAY), + util::interest_for(DEFAULT_INTEREST_RATE, SECONDS_PER_YEAR + SECONDS_PER_DAY), COLLATERAL_VALUE, ), util::current_loan_debt(loan_id) ); - advance_time(YEAR); + advance_time(SECONDS_PER_YEAR); // Because of math arithmetic preccission, // we get a difference that makes the test fail @@ -405,8 +405,8 @@ fn with_penalty_applied() { assert_eq!( util::current_debt_for( - util::interest_for(DEFAULT_INTEREST_RATE, YEAR + DAY) - * util::interest_for(DEFAULT_INTEREST_RATE + POLICY_PENALTY, YEAR), + util::interest_for(DEFAULT_INTEREST_RATE, SECONDS_PER_YEAR + SECONDS_PER_DAY) + * util::interest_for(DEFAULT_INTEREST_RATE + POLICY_PENALTY, SECONDS_PER_YEAR), COLLATERAL_VALUE, ) - precission_error, util::current_loan_debt(loan_id) @@ -420,7 +420,7 @@ fn fully() { let loan_id = util::create_loan(util::base_internal_loan()); util::borrow_loan(loan_id, PrincipalInput::Internal(COLLATERAL_VALUE)); - advance_time(YEAR + DAY); + advance_time(SECONDS_PER_YEAR + SECONDS_PER_DAY); config_mocks(); assert_ok!(Loans::admin_write_off( @@ -433,7 +433,7 @@ fn fully() { assert_eq!(0, util::current_loan_pv(loan_id)); - advance_time(YEAR); + advance_time(SECONDS_PER_YEAR); assert_eq!(0, util::current_loan_pv(loan_id)); }); diff --git a/pallets/loans/src/types/cashflow.rs b/pallets/loans/src/types/cashflow.rs index 4b5021a5c3..e2875422d0 100644 --- a/pallets/loans/src/types/cashflow.rs +++ b/pallets/loans/src/types/cashflow.rs @@ -11,7 +11,8 @@ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. -use cfg_traits::{interest::InterestRate, Seconds}; +use cfg_primitives::Seconds; +use cfg_traits::interest::InterestRate; use frame_support::pallet_prelude::RuntimeDebug; use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; @@ -40,7 +41,10 @@ pub enum Maturity { impl Maturity { pub fn fixed(date: Seconds) -> Self { - Self::Fixed { date, extension: 0 } + Self::Fixed { + date, + extension: Seconds::new(0), + } } pub fn date(&self) -> Option { @@ -207,11 +211,13 @@ pub mod tests { min: u32, sec: u32, ) -> Seconds { - from_ymd(year, month, day) - .and_hms_opt(hour, min, sec) - .unwrap() - .and_utc() - .timestamp() as Seconds + Seconds::from( + from_ymd(year, month, day) + .and_hms_opt(hour, min, sec) + .unwrap() + .and_utc() + .timestamp() as u64, + ) } pub fn last_secs_from_ymd(year: i32, month: u32, day: u32) -> Seconds { diff --git a/pallets/loans/src/types/policy.rs b/pallets/loans/src/types/policy.rs index 3cd5501b84..a6786b54f7 100644 --- a/pallets/loans/src/types/policy.rs +++ b/pallets/loans/src/types/policy.rs @@ -11,7 +11,7 @@ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. -use cfg_traits::Seconds; +use cfg_primitives::Seconds; use frame_support::{pallet_prelude::RuntimeDebug, storage::bounded_btree_set::BoundedBTreeSet}; use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; @@ -200,8 +200,8 @@ mod tests { #[test] fn same_trigger_kinds() { let triggers: BoundedBTreeSet = BTreeSet::from_iter([ - UniqueWriteOffTrigger(WriteOffTrigger::PrincipalOverdue(1)), - UniqueWriteOffTrigger(WriteOffTrigger::PrincipalOverdue(2)), + UniqueWriteOffTrigger(WriteOffTrigger::PrincipalOverdue(1u32.into())), + UniqueWriteOffTrigger(WriteOffTrigger::PrincipalOverdue(2u32.into())), ]) .try_into() .unwrap(); @@ -212,8 +212,8 @@ mod tests { #[test] fn different_trigger_kinds() { let triggers: BoundedBTreeSet = BTreeSet::from_iter([ - UniqueWriteOffTrigger(WriteOffTrigger::PrincipalOverdue(1)), - UniqueWriteOffTrigger(WriteOffTrigger::PriceOutdated(1)), + UniqueWriteOffTrigger(WriteOffTrigger::PrincipalOverdue(1u32.into())), + UniqueWriteOffTrigger(WriteOffTrigger::PriceOutdated(1u32.into())), ]) .try_into() .unwrap(); @@ -224,18 +224,18 @@ mod tests { #[test] fn find_correct_rule() { let rules = [ - WriteOffRule::new([WriteOffTrigger::PriceOutdated(0)], 5, 1), - WriteOffRule::new([WriteOffTrigger::PriceOutdated(1)], 7, 1), - WriteOffRule::new([WriteOffTrigger::PriceOutdated(2)], 7, 2), // <= - WriteOffRule::new([WriteOffTrigger::PriceOutdated(3)], 3, 4), - WriteOffRule::new([WriteOffTrigger::PriceOutdated(4)], 9, 1), + WriteOffRule::new([WriteOffTrigger::PriceOutdated(0u32.into())], 5, 1), + WriteOffRule::new([WriteOffTrigger::PriceOutdated(1u32.into())], 7, 1), + WriteOffRule::new([WriteOffTrigger::PriceOutdated(2u32.into())], 7, 2), // <= + WriteOffRule::new([WriteOffTrigger::PriceOutdated(3u32.into())], 3, 4), + WriteOffRule::new([WriteOffTrigger::PriceOutdated(4u32.into())], 9, 1), ]; let expected = rules[2].clone(); assert_ok!( find_rule(rules.into_iter(), |trigger| match trigger { - WriteOffTrigger::PriceOutdated(secs) => Ok(*secs <= 3), + WriteOffTrigger::PriceOutdated(secs) => Ok(*secs <= 3u32.into()), _ => unreachable!(), }), Some(expected) @@ -244,7 +244,11 @@ mod tests { #[test] fn find_err_rule() { - let rules = [WriteOffRule::new([WriteOffTrigger::PriceOutdated(0)], 5, 1)]; + let rules = [WriteOffRule::new( + [WriteOffTrigger::PriceOutdated(0u32.into())], + 5, + 1, + )]; assert_err!( find_rule(rules.into_iter(), |trigger| match trigger { @@ -256,7 +260,11 @@ mod tests { #[test] fn find_none_rule() { - let rules = [WriteOffRule::new([WriteOffTrigger::PriceOutdated(0)], 5, 1)]; + let rules = [WriteOffRule::new( + [WriteOffTrigger::PriceOutdated(0u32.into())], + 5, + 1, + )]; assert_ok!( find_rule(rules.into_iter(), |trigger| match trigger { diff --git a/pallets/loans/src/types/valuation.rs b/pallets/loans/src/types/valuation.rs index 0bd9b7330f..6269ca5b77 100644 --- a/pallets/loans/src/types/valuation.rs +++ b/pallets/loans/src/types/valuation.rs @@ -11,8 +11,8 @@ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. -use cfg_primitives::SECONDS_PER_YEAR; -use cfg_traits::{interest::InterestRate, Seconds}; +use cfg_primitives::{Seconds, SECONDS_PER_YEAR}; +use cfg_traits::interest::InterestRate; use frame_support::{ pallet_prelude::RuntimeDebug, traits::tokens::{self}, diff --git a/pallets/pool-fees/src/lib.rs b/pallets/pool-fees/src/lib.rs index 4cf182a03f..0429433190 100644 --- a/pallets/pool-fees/src/lib.rs +++ b/pallets/pool-fees/src/lib.rs @@ -30,12 +30,14 @@ pub use weights::WeightInfo; #[frame_support::pallet] pub mod pallet { + use cfg_primitives::Seconds; #[cfg(feature = "runtime-benchmarks")] use cfg_traits::benchmarking::PoolFeesBenchmarkHelper; use cfg_traits::{ changes::ChangeGuard, fee::{FeeAmountProration, PoolFeeBucket, PoolFeesInspect, PoolFeesMutate}, - EpochTransitionHook, PoolInspect, PoolNAV, PoolReserve, PreConditions, Seconds, TimeAsSecs, + time::UnixTimeSecs, + EpochTransitionHook, PoolInspect, PoolNAV, PoolReserve, PreConditions, }; use cfg_types::{ pools::{ @@ -157,7 +159,7 @@ pub mod pallet { type PalletId: Get; /// Fetching method for the time of the current block - type Time: TimeAsSecs; + type Time: UnixTimeSecs; type WeightInfo: WeightInfo; } diff --git a/pallets/pool-fees/src/mock.rs b/pallets/pool-fees/src/mock.rs index 97a7b50fe8..552a8b1bb3 100644 --- a/pallets/pool-fees/src/mock.rs +++ b/pallets/pool-fees/src/mock.rs @@ -15,7 +15,7 @@ use cfg_mocks::{ pallet_mock_change_guard, pallet_mock_permissions, pallet_mock_pools, pre_conditions::pallet as pallet_mock_pre_conditions, }; -use cfg_primitives::{Balance, CollectionId, PoolFeeId, PoolId, TrancheId}; +use cfg_primitives::{Balance, CollectionId, PoolFeeId, PoolId, Seconds, TrancheId}; use cfg_traits::{fee::PoolFeeBucket, PoolNAV}; use cfg_types::{ fixed_point::{Rate, Ratio}, @@ -37,8 +37,6 @@ use crate::{ Event::Accrued, FeeIds, FeeIdsToPoolBucket, LastFeeId, PoolFeeInfoOf, PoolFeeOf, }; -pub const SECONDS: u64 = 1000; - pub const ADMIN: AccountId = 1; pub const EDITOR: AccountId = 2; pub const DESTINATION: AccountId = 3; @@ -137,7 +135,7 @@ impl orml_tokens::Config for Runtime { } impl cfg_mocks::pallet_mock_time::Config for Runtime { - type Moment = u64; + type Moment = Seconds; } impl cfg_test_utils::mocks::nav::Config for Runtime { @@ -373,7 +371,7 @@ pub(crate) fn init_mocks() { }); MockPools::mock_deposit(|_, _, _| Ok(())); MockChangeGuard::mock_note(|_, _| Ok(H256::default())); - MockTime::mock_now(|| 0); + MockTime::mock_now(|| Seconds::new(0)); } pub(crate) fn config_change_mocks(fee: &PoolFeeInfoOf) { diff --git a/pallets/pool-fees/src/tests.rs b/pallets/pool-fees/src/tests.rs index 7a4ea0fbd6..64b2460f13 100644 --- a/pallets/pool-fees/src/tests.rs +++ b/pallets/pool-fees/src/tests.rs @@ -1,4 +1,4 @@ -use cfg_primitives::Balance; +use cfg_primitives::{Balance, Seconds}; use frame_support::{assert_noop, assert_ok}; use rand::Rng; use sp_arithmetic::FixedPointNumber; @@ -520,7 +520,7 @@ mod extrinsics { mod disbursements { use cfg_primitives::SECONDS_PER_YEAR; - use cfg_traits::{EpochTransitionHook, PoolNAV, TimeAsSecs}; + use cfg_traits::{time::UnixTimeSecs, EpochTransitionHook, PoolNAV}; use cfg_types::{ fixed_point::Rate, pools::{PoolFeeAmount, PoolFeeType}, @@ -528,9 +528,7 @@ mod disbursements { use frame_support::traits::fungibles::Inspect; use super::*; - use crate::mock::{ - get_disbursements, pay_single_fee_and_assert, MockTime, NAV, POOL_CURRENCY, SECONDS, - }; + use crate::mock::{get_disbursements, pay_single_fee_and_assert, MockTime, NAV, POOL_CURRENCY}; mod single_fee { use super::*; @@ -544,7 +542,7 @@ mod disbursements { #[test] fn sufficient_reserve_sfs() { ExtBuilder::default().set_aum(NAV).build().execute_with(|| { - MockTime::mock_now(|| SECONDS_PER_YEAR * SECONDS); + MockTime::mock_now(|| SECONDS_PER_YEAR); let fee_id = 1; let res_pre_fees = NAV; @@ -585,7 +583,7 @@ mod disbursements { #[test] fn insufficient_reserve_sfs() { ExtBuilder::default().set_aum(NAV).build().execute_with(|| { - MockTime::mock_now(|| SECONDS_PER_YEAR * SECONDS); + MockTime::mock_now(|| SECONDS_PER_YEAR); let fee_id = 1; let res_pre_fees = NAV / 100; @@ -633,10 +631,10 @@ mod disbursements { #[test] fn sufficient_reserve_sfa() { ExtBuilder::default().set_aum(NAV).build().execute_with(|| { - MockTime::mock_now(|| SECONDS_PER_YEAR * SECONDS); + MockTime::mock_now(|| SECONDS_PER_YEAR); let fee_id = 1; - let res_pre_fees: Balance = (2 * SECONDS_PER_YEAR).into(); + let res_pre_fees: Balance = (SECONDS_PER_YEAR * 2).into(); let res_post_fees = &mut res_pre_fees.clone(); let amount_per_second = 1; let fee_amount = SECONDS_PER_YEAR.into(); @@ -674,7 +672,7 @@ mod disbursements { #[test] fn insufficient_reserve_sfa() { ExtBuilder::default().set_aum(NAV).build().execute_with(|| { - MockTime::mock_now(|| SECONDS_PER_YEAR * SECONDS); + MockTime::mock_now(|| SECONDS_PER_YEAR); let fee_id = 1; let res_pre_fees: Balance = (SECONDS_PER_YEAR / 2).into(); @@ -739,7 +737,7 @@ mod disbursements { #[test] fn empty_charge_scfs() { ExtBuilder::default().set_aum(NAV).build().execute_with(|| { - MockTime::mock_now(|| SECONDS_PER_YEAR * SECONDS); + MockTime::mock_now(|| SECONDS_PER_YEAR); let fee_id = 1; let res_pre_fees = NAV; @@ -770,7 +768,7 @@ mod disbursements { #[test] fn below_max_charge_sufficient_reserve_scfs() { ExtBuilder::default().set_aum(NAV).build().execute_with(|| { - MockTime::mock_now(|| SECONDS_PER_YEAR * SECONDS); + MockTime::mock_now(|| SECONDS_PER_YEAR); let fee_id = 1; let res_pre_fees = NAV; @@ -808,7 +806,7 @@ mod disbursements { #[test] fn max_charge_sufficient_reserve_scfs() { ExtBuilder::default().set_aum(NAV).build().execute_with(|| { - MockTime::mock_now(|| SECONDS_PER_YEAR * SECONDS); + MockTime::mock_now(|| SECONDS_PER_YEAR); let fee_id = 1; let res_pre_fees = NAV; @@ -845,7 +843,7 @@ mod disbursements { #[test] fn excess_charge_sufficient_reserve_scfs() { ExtBuilder::default().set_aum(NAV).build().execute_with(|| { - MockTime::mock_now(|| SECONDS_PER_YEAR * SECONDS); + MockTime::mock_now(|| SECONDS_PER_YEAR); let fee_id = 1; let res_pre_fees = NAV; @@ -884,7 +882,7 @@ mod disbursements { #[test] fn insufficient_reserve_scfs() { ExtBuilder::default().set_aum(NAV).build().execute_with(|| { - MockTime::mock_now(|| SECONDS_PER_YEAR * SECONDS); + MockTime::mock_now(|| SECONDS_PER_YEAR); let fee_id = 1; let res_pre_fees = NAV / 100; @@ -939,7 +937,7 @@ mod disbursements { #[test] fn empty_charge_scfa() { ExtBuilder::default().set_aum(NAV).build().execute_with(|| { - MockTime::mock_now(|| SECONDS_PER_YEAR * SECONDS); + MockTime::mock_now(|| SECONDS_PER_YEAR); let fee_id = 1; let res_pre_fees = NAV; @@ -970,7 +968,7 @@ mod disbursements { #[test] fn below_max_charge_sufficient_reserve_scfa() { ExtBuilder::default().set_aum(NAV).build().execute_with(|| { - MockTime::mock_now(|| SECONDS_PER_YEAR * SECONDS); + MockTime::mock_now(|| SECONDS_PER_YEAR); let fee_id = 1; let res_pre_fees = NAV; @@ -1008,7 +1006,7 @@ mod disbursements { #[test] fn max_charge_sufficient_reserve_scfa() { ExtBuilder::default().set_aum(NAV).build().execute_with(|| { - MockTime::mock_now(|| SECONDS_PER_YEAR * SECONDS); + MockTime::mock_now(|| SECONDS_PER_YEAR); let fee_id = 1; let res_pre_fees = NAV; @@ -1045,7 +1043,7 @@ mod disbursements { #[test] fn excess_charge_sufficient_reserve_scfa() { ExtBuilder::default().set_aum(NAV).build().execute_with(|| { - MockTime::mock_now(|| SECONDS_PER_YEAR * SECONDS); + MockTime::mock_now(|| SECONDS_PER_YEAR); let fee_id = 1; let res_pre_fees = NAV; @@ -1084,7 +1082,7 @@ mod disbursements { #[test] fn insufficient_reserve_scfa() { ExtBuilder::default().set_aum(NAV).build().execute_with(|| { - MockTime::mock_now(|| SECONDS_PER_YEAR * SECONDS); + MockTime::mock_now(|| SECONDS_PER_YEAR); let fee_id = 1; let amount_per_second = 1; @@ -1191,10 +1189,10 @@ mod disbursements { ExtBuilder::default().set_aum(NAV).build().execute_with(|| { add_fees(vec![default_fixed_fee()]); - assert_eq!(PoolFees::nav(POOL), Some((0, 0))); - MockTime::mock_now(|| SECONDS_PER_YEAR * SECONDS); + assert_eq!(PoolFees::nav(POOL), Some((0, Seconds::new(0)))); + MockTime::mock_now(|| SECONDS_PER_YEAR); - assert_eq!(PoolFees::nav(POOL), Some((0, 0))); + assert_eq!(PoolFees::nav(POOL), Some((0, Seconds::new(0)))); assert_ok!(PoolFees::update_portfolio_valuation( RuntimeOrigin::signed(ANY), POOL @@ -1215,9 +1213,9 @@ mod disbursements { fn update_single_charged() { ExtBuilder::default().set_aum(NAV).build().execute_with(|| { add_fees(vec![default_chargeable_fee()]); - MockTime::mock_now(|| SECONDS_PER_YEAR * SECONDS); + MockTime::mock_now(|| SECONDS_PER_YEAR); - assert_eq!(PoolFees::nav(POOL), Some((0, 0))); + assert_eq!(PoolFees::nav(POOL), Some((0, Seconds::new(0)))); assert_ok!(PoolFees::update_portfolio_valuation( RuntimeOrigin::signed(ANY), POOL @@ -1242,7 +1240,7 @@ mod disbursements { #[test] fn fixed_charged_charged() { ExtBuilder::default().set_aum(NAV).build().execute_with(|| { - MockTime::mock_now(|| SECONDS_PER_YEAR * SECONDS); + MockTime::mock_now(|| SECONDS_PER_YEAR); let charged_fee_ids = vec![2, 3]; let res_pre_fees = NAV; @@ -1250,7 +1248,7 @@ mod disbursements { let annual_rate = Rate::saturating_from_rational(1, 100); let fixed_fee_amount = NAV / 100; let amount_per_seconds = vec![2, 1]; - let payable = vec![(2 * SECONDS_PER_YEAR).into(), SECONDS_PER_YEAR.into()]; + let payable = vec![(SECONDS_PER_YEAR * 2).into(), SECONDS_PER_YEAR.into()]; let charged_y1 = vec![1, 2 * payable[1]]; let charged_y2 = vec![payable[0], payable[1]]; @@ -1347,7 +1345,7 @@ mod disbursements { // Year 2: Make reserve insufficient to handle all fees (last fee // falls short - MockTime::mock_now(|| 2 * SECONDS_PER_YEAR * SECONDS); + MockTime::mock_now(|| SECONDS_PER_YEAR * 2); let res_pre_fees = fixed_fee_amount + charged_y2[0] + 1; let res_post_fees = &mut res_pre_fees.clone(); assert_ok!(PoolFees::charge_fee( diff --git a/pallets/pool-system/src/impls.rs b/pallets/pool-system/src/impls.rs index cf80f26ec8..cfe200007c 100644 --- a/pallets/pool-system/src/impls.rs +++ b/pallets/pool-system/src/impls.rs @@ -268,7 +268,7 @@ impl PoolMutate for Pallet { }; let num_tranches = pool.tranches.num_tranches().try_into().unwrap(); - if T::MinUpdateDelay::get() == 0 && T::UpdateGuard::released(&pool, &update, now) { + if T::MinUpdateDelay::get().is_zero() && T::UpdateGuard::released(&pool, &update, now) { Self::do_update_pool(&pool_id, &changes)?; Ok(UpdateState::Executed(num_tranches)) @@ -431,7 +431,7 @@ impl ChangeGuard for Pallet { allowed &= match requirement { Requirement::NextEpoch => submitted_time < pool.epoch.last_closed, Requirement::DelayTime(secs) => { - T::Time::now().saturating_sub(submitted_time) >= secs as u64 + T::Time::now().saturating_sub(submitted_time) >= Seconds::new(secs.into()) } Requirement::BlockedByLockedRedemptions => true, // TODO: #1407 } diff --git a/pallets/pool-system/src/lib.rs b/pallets/pool-system/src/lib.rs index 097c3ac4d5..1eff0d70c1 100644 --- a/pallets/pool-system/src/lib.rs +++ b/pallets/pool-system/src/lib.rs @@ -13,7 +13,8 @@ #![cfg_attr(not(feature = "std"), no_std)] #![allow(clippy::or_fun_call)] -use cfg_traits::{Permissions, PoolInspect, PoolMutate, PoolNAV, PoolReserve, Seconds, TimeAsSecs}; +use cfg_primitives::Seconds; +use cfg_traits::{time::UnixTimeSecs, Permissions, PoolInspect, PoolMutate, PoolNAV, PoolReserve}; use cfg_types::{ orders::SummarizedOrders, permissions::{PermissionScope, PoolRole, Role}, @@ -320,7 +321,7 @@ pub mod pallet { Fulfillment = FulfillmentWithPrice, >; - type Time: TimeAsSecs; + type Time: UnixTimeSecs; /// Add pool fees type PoolFees: PoolFeesMutate< diff --git a/pallets/pool-system/src/pool_types.rs b/pallets/pool-system/src/pool_types.rs index 0594d15ab2..e615f3eee9 100644 --- a/pallets/pool-system/src/pool_types.rs +++ b/pallets/pool-system/src/pool_types.rs @@ -10,7 +10,7 @@ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. -use cfg_traits::Seconds; +use cfg_primitives::Seconds; use cfg_types::{epoch::EpochState, pools::TrancheMetadata}; pub use changes::PoolChangeProposal; use frame_support::{ diff --git a/pallets/pool-system/src/tranches.rs b/pallets/pool-system/src/tranches.rs index 946977e587..797ba4f7cf 100644 --- a/pallets/pool-system/src/tranches.rs +++ b/pallets/pool-system/src/tranches.rs @@ -10,7 +10,8 @@ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. -use cfg_traits::{investments::TrancheCurrency as TrancheCurrencyT, Seconds}; +use cfg_primitives::Seconds; +use cfg_traits::investments::TrancheCurrency as TrancheCurrencyT; use cfg_types::{ pools::TrancheMetadata, tokens::{CrossChainTransferability, CustomMetadata}, diff --git a/runtime/altair/src/lib.rs b/runtime/altair/src/lib.rs index 69f2607ed3..db0f0771f1 100644 --- a/runtime/altair/src/lib.rs +++ b/runtime/altair/src/lib.rs @@ -27,11 +27,10 @@ use cfg_primitives::{ IBalance, InvestmentId, ItemId, LoanId, Nonce, OrderId, PalletIndex, PoolEpochId, PoolFeeId, PoolId, Signature, TrancheId, TrancheWeight, }, - LPGatewayQueueMessageNonce, + LPGatewayQueueMessageNonce, Millis, Seconds, }; use cfg_traits::{ - investments::OrderManager, Millis, Permissions as PermissionsT, PoolUpdateGuard, PreConditions, - Seconds, + investments::OrderManager, Permissions as PermissionsT, PoolUpdateGuard, PreConditions, }; use cfg_types::{ fee_keys::{Fee, FeeKey}, diff --git a/runtime/centrifuge/src/lib.rs b/runtime/centrifuge/src/lib.rs index 1deb9a249a..367f83b8d5 100644 --- a/runtime/centrifuge/src/lib.rs +++ b/runtime/centrifuge/src/lib.rs @@ -27,11 +27,10 @@ use cfg_primitives::{ IBalance, InvestmentId, ItemId, LoanId, Nonce, OrderId, PalletIndex, PoolEpochId, PoolFeeId, PoolId, Signature, TrancheId, TrancheWeight, }, - LPGatewayQueueMessageNonce, + LPGatewayQueueMessageNonce, Millis, Seconds, }; use cfg_traits::{ - investments::OrderManager, Millis, Permissions as PermissionsT, PoolUpdateGuard, PreConditions, - Seconds, + investments::OrderManager, Permissions as PermissionsT, PoolUpdateGuard, PreConditions, }; use cfg_types::{ fee_keys::{Fee, FeeKey}, diff --git a/runtime/common/src/changes.rs b/runtime/common/src/changes.rs index c777e81247..4526789c2a 100644 --- a/runtime/common/src/changes.rs +++ b/runtime/common/src/changes.rs @@ -40,7 +40,7 @@ impl RuntimeChange { use sp_std::vec; let epoch = Requirement::NextEpoch; - let week = Requirement::DelayTime(SECONDS_PER_WEEK as u32); + let week = Requirement::DelayTime(SECONDS_PER_WEEK.inner as u32); let blocked = Requirement::BlockedByLockedRedemptions; match self { diff --git a/runtime/common/src/oracle.rs b/runtime/common/src/oracle.rs index a300306aae..8d8fc8d033 100644 --- a/runtime/common/src/oracle.rs +++ b/runtime/common/src/oracle.rs @@ -1,8 +1,9 @@ use cfg_primitives::{ conversion::fixed_point_to_balance, types::{AccountId, Balance, PoolId}, + Millis, }; -use cfg_traits::{HasLocalAssetRepresentation, Millis, PoolInspect, ValueProvider}; +use cfg_traits::{HasLocalAssetRepresentation, PoolInspect, ValueProvider}; use cfg_types::{ fixed_point::{Quantity, Ratio}, oracles::OracleKey, diff --git a/runtime/development/src/lib.rs b/runtime/development/src/lib.rs index 025c648d2d..854959a801 100644 --- a/runtime/development/src/lib.rs +++ b/runtime/development/src/lib.rs @@ -27,11 +27,11 @@ use cfg_primitives::{ IBalance, InvestmentId, ItemId, LoanId, Nonce, OrderId, PalletIndex, PoolEpochId, PoolFeeId, PoolId, Signature, TrancheId, TrancheWeight, }, - LPGatewayQueueMessageNonce, + LPGatewayQueueMessageNonce, Millis, Seconds, }; use cfg_traits::{ - investments::OrderManager, Millis, Permissions as PermissionsT, PoolUpdateGuard, PreConditions, - Seconds, + investments::OrderManager, time::UnixTimeSecs, Permissions as PermissionsT, PoolUpdateGuard, + PreConditions, }; use cfg_types::{ fee_keys::{Fee, FeeKey}, @@ -63,7 +63,7 @@ use frame_support::{ tokens::{PayFromAccount, UnityAssetBalanceConversion}, AsEnsureOriginWithArg, ConstBool, ConstU32, ConstU64, Contains, EitherOf, EitherOfDiverse, EqualPrivilegeOnly, Get, InstanceFilter, LinearStoragePrice, LockIdentifier, OnFinalize, - PalletInfoAccess, TransformOrigin, UnixTime, WithdrawReasons, + PalletInfoAccess, TransformOrigin, WithdrawReasons, }, weights::{ constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}, @@ -368,7 +368,7 @@ impl cumulus_pallet_dmp_queue::Config for Runtime { } parameter_types! { - pub const MinimumPeriod: Millis = SLOT_DURATION / 2; + pub MinimumPeriod: Millis = SLOT_DURATION / 2; } impl pallet_timestamp::Config for Runtime { @@ -1123,12 +1123,12 @@ parameter_types! { // Defaults for pool parameters pub const DefaultMinEpochTime: u64 = 0; // No minimum epoch time - pub const DefaultMaxNAVAge: u64 = 1 * SECONDS_PER_MINUTE; // 1 minute + pub const DefaultMaxNAVAge: Seconds = SECONDS_PER_MINUTE; // 1 minute // Runtime-defined constraints for pool parameters pub const MinEpochTimeLowerBound: u64 = 0; // Allow closing an epoch in the same block as the creation of a pool and also multiple per block if wanted - pub const MinEpochTimeUpperBound: u64 = 30 * SECONDS_PER_DAY; // 1 month - pub const MaxNAVAgeUpperBound: u64 = SECONDS_PER_HOUR; // 1 hour + pub const MinEpochTimeUpperBound: Seconds = SECONDS_PER_MONTH; // 1 month + pub const MaxNAVAgeUpperBound: Seconds = SECONDS_PER_HOUR; // 1 hour // Pool metadata limit #[derive(scale_info::TypeInfo, Eq, PartialEq, Debug, Clone, Copy )] @@ -1418,7 +1418,6 @@ impl pallet_loans::Config for Runtime { type LoanId = LoanId; type MaxActiveLoansPerPool = MaxActiveLoansPerPool; type MaxWriteOffPolicySize = MaxWriteOffPolicySize; - type Moment = Millis; type NonFungible = Uniques; type PerThing = Perquintill; type Permissions = Permissions; @@ -1441,7 +1440,7 @@ parameter_types! { // How much time should lapse before a tranche investor can be removed #[derive(Debug, Eq, PartialEq, scale_info::TypeInfo, Clone)] - pub const MinDelay: Seconds = 7 * SECONDS_PER_DAY; + pub const MinDelay: Seconds = SECONDS_PER_WEEK; #[derive(Debug, Eq, PartialEq, scale_info::TypeInfo, Clone)] pub const MaxRolesPerPool: u32 = 1_000; @@ -1700,7 +1699,7 @@ impl pallet_investments::Config for Runtime { pub struct IsTrancheInvestor(PhantomData<(P, T)>); impl< P: PermissionsT, Role = Role>, - T: UnixTime, + T: UnixTimeSecs, > PreConditions> for IsTrancheInvestor { type Result = DispatchResult; @@ -1714,7 +1713,7 @@ impl< } => P::has( PermissionScope::Pool(pool_id), who, - Role::PoolRole(PoolRole::TrancheInvestor(tranche_id, T::now().as_secs())), + Role::PoolRole(PoolRole::TrancheInvestor(tranche_id, T::now_secs())), ), OrderType::Redemption { who, @@ -1723,7 +1722,7 @@ impl< } => P::has( PermissionScope::Pool(pool_id), who, - Role::PoolRole(PoolRole::TrancheInvestor(tranche_id, T::now().as_secs())), + Role::PoolRole(PoolRole::TrancheInvestor(tranche_id, T::now_secs())), ), }; @@ -1749,7 +1748,7 @@ parameter_types! { pub const MaxGroups: u32 = 20; #[derive(scale_info::TypeInfo, Debug, PartialEq, Eq, Clone)] pub const MaxChangesPerEpoch: u32 = 50; - pub const InitialEpochDuration: Millis = SECONDS_PER_MINUTE * 1000; // 1 min in milliseconds + pub InitialEpochDuration: Millis = SECONDS_PER_MINUTE.into_millis(); } impl pallet_rewards::mechanism::gap::Config for Runtime { @@ -2441,7 +2440,7 @@ impl_runtime_apis! { impl sp_consensus_aura::AuraApi for Runtime { fn slot_duration() -> sp_consensus_aura::SlotDuration { - sp_consensus_aura::SlotDuration::from_millis(Aura::slot_duration()) + sp_consensus_aura::SlotDuration::from_millis(Aura::slot_duration().inner) } fn authorities() -> Vec { @@ -2510,7 +2509,7 @@ impl_runtime_apis! { } fn tranche_token_prices(pool_id: PoolId) -> Option>{ - let now = ::now().as_secs(); + let now = Timestamp::now_secs(); let mut pool = PoolSystem::pool(pool_id)?; pool .tranches diff --git a/runtime/integration-tests/submodules/liquidity-pools b/runtime/integration-tests/submodules/liquidity-pools index 1f92ebdd72..4301885b9a 160000 --- a/runtime/integration-tests/submodules/liquidity-pools +++ b/runtime/integration-tests/submodules/liquidity-pools @@ -1 +1 @@ -Subproject commit 1f92ebdd72e91248b8133973c3bdfdea9f1f7e0d +Subproject commit 4301885b9a3b8ec36f3bda4b789daa5b115c006a