diff --git a/libs/mocks/src/data.rs b/libs/mocks/src/data.rs index 98e887f3c4..8fbcb7d65e 100644 --- a/libs/mocks/src/data.rs +++ b/libs/mocks/src/data.rs @@ -97,14 +97,6 @@ pub mod pallet { } } - impl Default for MockDataCollection { - fn default() -> Self { - Self(Box::new(|_| { - Err(DispatchError::Other("MockDataCollection: Data not found")) - })) - } - } - impl DataCollection for MockDataCollection { type Data = Data; diff --git a/libs/traits/src/data.rs b/libs/traits/src/data.rs index 922efac3a5..bd5400315b 100644 --- a/libs/traits/src/data.rs +++ b/libs/traits/src/data.rs @@ -37,7 +37,7 @@ pub trait DataRegistry { } /// Abstration to represent a collection of data in memory -pub trait DataCollection: Default { +pub trait DataCollection { /// Represents a data type Data; diff --git a/pallets/loans/src/entities/input.rs b/pallets/loans/src/entities/input.rs index c8725b8dfa..a858250de4 100644 --- a/pallets/loans/src/entities/input.rs +++ b/pallets/loans/src/entities/input.rs @@ -1,4 +1,4 @@ -use frame_support::RuntimeDebugNoBound; +use frame_support::{storage::bounded_btree_map::BoundedBTreeMap, RuntimeDebugNoBound}; use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; use sp_runtime::{ArithmeticError, DispatchError}; @@ -56,3 +56,11 @@ impl RepaidInput { }) } } + +#[derive(Encode, Decode, Clone, PartialEq, Eq, TypeInfo, RuntimeDebugNoBound, MaxEncodedLen)] +#[scale_info(skip_type_params(T))] +pub enum PriceCollectionInput { + Empty, + Custom(BoundedBTreeMap), + FromRegistry, +} diff --git a/pallets/loans/src/entities/loans.rs b/pallets/loans/src/entities/loans.rs index a261310461..74209b01a7 100644 --- a/pallets/loans/src/entities/loans.rs +++ b/pallets/loans/src/entities/loans.rs @@ -1,6 +1,5 @@ use cfg_traits::{ self, - data::DataCollection, interest::{InterestAccrual, InterestRate, RateCollection}, Seconds, TimeAsSecs, }; @@ -14,6 +13,7 @@ use sp_runtime::{ }, DispatchError, }; +use sp_std::collections::btree_map::BTreeMap; use crate::{ entities::{ @@ -24,7 +24,7 @@ use crate::{ Pricing, }, }, - pallet::{AssetOf, Config, Error, PriceOf}, + pallet::{AssetOf, Config, Error}, types::{ policy::{WriteOffStatus, WriteOffTrigger}, BorrowLoanError, BorrowRestrictions, CloseLoanError, CreateLoanError, LoanRestrictions, @@ -229,6 +229,13 @@ impl ActiveLoan { &self.pricing } + pub fn price_id(&self) -> Option { + match &self.pricing { + ActivePricing::Internal(_) => None, + ActivePricing::External(inner) => Some(inner.price_id()), + } + } + pub fn write_off_status(&self) -> WriteOffStatus { WriteOffStatus { percentage: self.write_off_percentage, @@ -278,15 +285,14 @@ impl ActiveLoan { /// An optimized version of `ActiveLoan::present_value()` when some input /// data can be used from cached collections. Instead of fetch the current /// debt and prices from the pallets, - /// it get the values from caches previously fetched. - pub fn present_value_by( + /// it the values from caches previously fetched. + pub fn present_value_by( &self, rates: &Rates, - prices: &Prices, + prices: &BTreeMap, ) -> Result where Rates: RateCollection, - Prices: DataCollection>, { let maturity_date = self.schedule.maturity.date(); let value = match &self.pricing { diff --git a/pallets/loans/src/entities/pricing/external.rs b/pallets/loans/src/entities/pricing/external.rs index c79e71cc8e..0bc614cf31 100644 --- a/pallets/loans/src/entities/pricing/external.rs +++ b/pallets/loans/src/entities/pricing/external.rs @@ -1,8 +1,5 @@ use cfg_traits::{ - self, - data::{DataCollection, DataRegistry}, - interest::InterestRate, - IntoSeconds, Seconds, TimeAsSecs, + self, data::DataRegistry, interest::InterestRate, IntoSeconds, Seconds, TimeAsSecs, }; use cfg_types::adjustments::Adjustment; use frame_support::{self, ensure, RuntimeDebug, RuntimeDebugNoBound}; @@ -12,10 +9,11 @@ use sp_runtime::{ traits::{EnsureAdd, EnsureFixedPointNumber, EnsureSub, Zero}, ArithmeticError, DispatchError, DispatchResult, FixedPointNumber, }; +use sp_std::collections::btree_map::BTreeMap; use crate::{ entities::interest::ActiveInterestRate, - pallet::{Config, Error, PriceOf}, + pallet::{Config, Error}, }; #[derive(Encode, Decode, Clone, PartialEq, Eq, TypeInfo, RuntimeDebugNoBound, MaxEncodedLen)] @@ -137,6 +135,10 @@ impl ExternalActivePricing { Ok((self.info, self.interest.deactivate()?)) } + pub fn price_id(&self) -> T::PriceId { + self.info.price_id + } + pub fn has_registered_price(&self, pool_id: T::PoolId) -> bool { T::PriceRegistry::get(&self.info.price_id, &pool_id).is_ok() } @@ -193,17 +195,14 @@ impl ExternalActivePricing { self.outstanding_principal(pool_id, maturity) } - pub fn present_value_cached( + pub fn present_value_cached( &self, - cache: &Prices, + cache: &BTreeMap, maturity: Seconds, - ) -> Result - where - Prices: DataCollection>, - { + ) -> Result { let price = match cache.get(&self.info.price_id) { - Ok(data) => data.0, - Err(_) => self.linear_accrual_price(maturity)?, + Some(data) => *data, + None => self.linear_accrual_price(maturity)?, }; Ok(self.outstanding_quantity.ensure_mul_int(price)?) } diff --git a/pallets/loans/src/lib.rs b/pallets/loans/src/lib.rs index b5d3910ab9..44bc9e25e8 100644 --- a/pallets/loans/src/lib.rs +++ b/pallets/loans/src/lib.rs @@ -73,9 +73,12 @@ pub use weights::WeightInfo; #[frame_support::pallet] pub mod pallet { use cfg_traits::{ - self, changes::ChangeGuard, data::DataRegistry, interest::InterestAccrual, IntoSeconds, - Permissions, PoolInspect, PoolNAV, PoolReserve, PoolWriteOffPolicyMutate, Seconds, - TimeAsSecs, + self, + changes::ChangeGuard, + data::{DataCollection, DataRegistry}, + interest::InterestAccrual, + IntoSeconds, Permissions, PoolInspect, PoolNAV, PoolReserve, PoolWriteOffPolicyMutate, + Seconds, TimeAsSecs, }; use cfg_types::{ adjustments::Adjustment, @@ -84,7 +87,7 @@ pub mod pallet { }; use entities::{ changes::{Change, LoanMutation}, - input::{PrincipalInput, RepaidInput}, + input::{PriceCollectionInput, PrincipalInput, RepaidInput}, loans::{self, ActiveLoan, ActiveLoanInfo, LoanInfo}, }; use frame_support::{ @@ -103,7 +106,7 @@ pub mod pallet { traits::{BadOrigin, EnsureAdd, EnsureAddAssign, EnsureInto, One, Zero}, ArithmeticError, FixedPointOperand, TransactionOutcome, }; - use sp_std::{vec, vec::Vec}; + use sp_std::{collections::btree_map::BTreeMap, vec, vec::Vec}; use types::{ self, policy::{self, WriteOffRule, WriteOffStatus}, @@ -150,7 +153,7 @@ pub mod pallet { + One; /// Identify a loan in the pallet - type PriceId: Parameter + Member + TypeInfo + Copy + MaxEncodedLen; + type PriceId: Parameter + Member + TypeInfo + Copy + MaxEncodedLen + Ord; /// Defines the rate type used for math computations type Rate: Parameter + Member + FixedPointNumber + TypeInfo + MaxEncodedLen; @@ -775,7 +778,10 @@ pub mod pallet { ensure_signed(origin)?; Self::ensure_pool_exists(pool_id)?; - let (_, count) = Self::update_portfolio_valuation_for_pool(pool_id)?; + let (_, count) = Self::update_portfolio_valuation_for_pool( + pool_id, + PriceCollectionInput::FromRegistry, + )?; Ok(Some(T::WeightInfo::update_portfolio_valuation(count)).into()) } @@ -1051,11 +1057,33 @@ pub mod pallet { .map_err(|_| Error::::NoLoanChangeId.into()) } - fn update_portfolio_valuation_for_pool( + fn registered_prices( + pool_id: T::PoolId, + ) -> Result, DispatchError> { + let collection = T::PriceRegistry::collection(&pool_id)?; + Ok(ActiveLoans::::get(pool_id) + .iter() + .filter_map(|(_, loan)| loan.price_id()) + .filter_map(|price_id| { + collection + .get(&price_id) + .map(|price| (price_id, price.0)) + .ok() + }) + .collect::>()) + } + + pub fn update_portfolio_valuation_for_pool( pool_id: T::PoolId, + input_prices: PriceCollectionInput, ) -> Result<(T::Balance, u32), DispatchError> { let rates = T::InterestAccrual::rates(); - let prices = T::PriceRegistry::collection(&pool_id)?; + let prices = match input_prices { + PriceCollectionInput::Empty => BTreeMap::default(), + PriceCollectionInput::Custom(prices) => prices.into(), + PriceCollectionInput::FromRegistry => Self::registered_prices(pool_id)?, + }; + let loans = ActiveLoans::::get(pool_id); let values = loans .iter() @@ -1208,7 +1236,8 @@ pub mod pallet { } fn update_nav(pool_id: T::PoolId) -> Result { - Ok(Self::update_portfolio_valuation_for_pool(pool_id)?.0) + Self::update_portfolio_valuation_for_pool(pool_id, PriceCollectionInput::FromRegistry) + .map(|portfolio| portfolio.0) } fn initialise(_: OriginFor, _: T::PoolId, _: T::ItemId) -> DispatchResult { diff --git a/runtime/altair/src/lib.rs b/runtime/altair/src/lib.rs index d0343753a4..2ba3b06361 100644 --- a/runtime/altair/src/lib.rs +++ b/runtime/altair/src/lib.rs @@ -2144,7 +2144,7 @@ impl fp_rpc::ConvertTransaction for TransactionConv #[cfg(not(feature = "disable-runtime-api"))] mod __runtime_api_use { - pub use pallet_loans::entities::loans::ActiveLoanInfo; + pub use pallet_loans::entities::{input::PriceCollectionInput, loans::ActiveLoanInfo}; } #[cfg(not(feature = "disable-runtime-api"))] @@ -2356,7 +2356,14 @@ impl_runtime_apis! { } } - impl runtime_common::apis::LoansApi> for Runtime { + impl runtime_common::apis::LoansApi< + Block, + PoolId, + LoanId, + ActiveLoanInfo, + Balance, + PriceCollectionInput + > for Runtime { fn portfolio( pool_id: PoolId ) -> Vec<(LoanId, ActiveLoanInfo)> { @@ -2369,6 +2376,13 @@ impl_runtime_apis! { ) -> Option> { Loans::get_active_loan_info(pool_id, loan_id).ok().flatten() } + + fn portfolio_valuation( + pool_id: PoolId, + input_prices: PriceCollectionInput + ) -> Result { + Ok(Loans::update_portfolio_valuation_for_pool(pool_id, input_prices)?.0) + } } diff --git a/runtime/centrifuge/src/lib.rs b/runtime/centrifuge/src/lib.rs index 33f1a39d56..7d62b0e67a 100644 --- a/runtime/centrifuge/src/lib.rs +++ b/runtime/centrifuge/src/lib.rs @@ -2193,7 +2193,7 @@ impl fp_rpc::ConvertTransaction for TransactionConv #[cfg(not(feature = "disable-runtime-api"))] mod __runtime_api_use { - pub use pallet_loans::entities::loans::ActiveLoanInfo; + pub use pallet_loans::entities::{input::PriceCollectionInput, loans::ActiveLoanInfo}; } #[cfg(not(feature = "disable-runtime-api"))] @@ -2407,7 +2407,14 @@ impl_runtime_apis! { } // LoansApi - impl runtime_common::apis::LoansApi> for Runtime { + impl runtime_common::apis::LoansApi< + Block, + PoolId, + LoanId, + ActiveLoanInfo, + Balance, + PriceCollectionInput + > for Runtime { fn portfolio( pool_id: PoolId ) -> Vec<(LoanId, ActiveLoanInfo)> { @@ -2420,6 +2427,13 @@ impl_runtime_apis! { ) -> Option> { Loans::get_active_loan_info(pool_id, loan_id).ok().flatten() } + + fn portfolio_valuation( + pool_id: PoolId, + input_prices: PriceCollectionInput + ) -> Result { + Ok(Loans::update_portfolio_valuation_for_pool(pool_id, input_prices)?.0) + } } // Investment Runtime APIs diff --git a/runtime/common/src/apis/loans.rs b/runtime/common/src/apis/loans.rs index f03304de09..def6a6759a 100644 --- a/runtime/common/src/apis/loans.rs +++ b/runtime/common/src/apis/loans.rs @@ -13,17 +13,22 @@ use parity_scale_codec::Codec; use sp_api::decl_runtime_apis; +use sp_runtime::DispatchError; use sp_std::vec::Vec; decl_runtime_apis! { /// Runtime API for the rewards pallet. - pub trait LoansApi + #[api_version(2)] + pub trait LoansApi where PoolId: Codec, LoanId: Codec, Loan: Codec, + Balance: Codec, + PriceCollectionInput: Codec { fn portfolio(pool_id: PoolId) -> Vec<(LoanId, Loan)>; fn portfolio_loan(pool_id: PoolId, loan_id: LoanId) -> Option; + fn portfolio_valuation(pool_id: PoolId, input_prices: PriceCollectionInput) -> Result; } } diff --git a/runtime/development/src/lib.rs b/runtime/development/src/lib.rs index 797fa8b8af..1a84b03a47 100644 --- a/runtime/development/src/lib.rs +++ b/runtime/development/src/lib.rs @@ -2233,7 +2233,7 @@ impl fp_rpc::ConvertTransaction for TransactionConv #[cfg(not(feature = "disable-runtime-api"))] mod __runtime_api_use { - pub use pallet_loans::entities::loans::ActiveLoanInfo; + pub use pallet_loans::entities::{input::PriceCollectionInput, loans::ActiveLoanInfo}; } #[cfg(not(feature = "disable-runtime-api"))] @@ -2449,7 +2449,14 @@ impl_runtime_apis! { } // LoansApi - impl runtime_common::apis::LoansApi> for Runtime { + impl runtime_common::apis::LoansApi< + Block, + PoolId, + LoanId, + ActiveLoanInfo, + Balance, + PriceCollectionInput + > for Runtime { fn portfolio( pool_id: PoolId ) -> Vec<(LoanId, ActiveLoanInfo)> { @@ -2462,6 +2469,13 @@ impl_runtime_apis! { ) -> Option> { Loans::get_active_loan_info(pool_id, loan_id).ok().flatten() } + + fn portfolio_valuation( + pool_id: PoolId, + input_prices: PriceCollectionInput + ) -> Result { + Ok(Loans::update_portfolio_valuation_for_pool(pool_id, input_prices)?.0) + } } // Investment Runtime APIs diff --git a/runtime/integration-tests/src/generic/cases/loans.rs b/runtime/integration-tests/src/generic/cases/loans.rs index cc888f86bc..800dc1ac08 100644 --- a/runtime/integration-tests/src/generic/cases/loans.rs +++ b/runtime/integration-tests/src/generic/cases/loans.rs @@ -1,18 +1,18 @@ use cfg_primitives::{Balance, CollectionId, ItemId, LoanId, PoolId, SECONDS_PER_MINUTE}; use cfg_traits::{ interest::{CompoundingSchedule, InterestRate}, - Seconds, TimeAsSecs, + Seconds, }; use cfg_types::{ fixed_point::{Quantity, Rate}, oracles::OracleKey, permissions::PoolRole, }; -use frame_support::{assert_err, traits::Get}; +use frame_support::{assert_err, assert_ok, traits::Get}; use pallet_loans::{ entities::{ changes::LoanMutation, - input::{PrincipalInput, RepaidInput}, + input::{PriceCollectionInput, PrincipalInput, RepaidInput}, loans::LoanInfo, pricing::{ external::{ExternalAmount, ExternalPricing, MaxBorrowAmount as ExtMaxBorrowAmount}, @@ -26,9 +26,11 @@ use pallet_loans::{ }, }; use runtime_common::{ - apis::{runtime_decl_for_loans_api::LoansApiV1, runtime_decl_for_pools_api::PoolsApiV1}, + apis::{runtime_decl_for_loans_api::LoansApiV2, runtime_decl_for_pools_api::PoolsApiV1}, oracle::Feeder, }; +use sp_runtime::FixedPointNumber; +use sp_std::collections::btree_map::BTreeMap; use crate::{ generic::{ @@ -55,7 +57,8 @@ const POOL_A: PoolId = 23; const NFT_A: (CollectionId, ItemId) = (1, ItemId(10)); const PRICE_A: OracleKey = OracleKey::Isin(*b"INE123456AB1"); const PRICE_VALUE_A: Quantity = Quantity::from_integer(1_000); -const PRICE_VALUE_B: Quantity = Quantity::from_integer(500); +const PRICE_VALUE_B: Quantity = Quantity::from_integer(800); +const PRICE_VALUE_C: Quantity = Quantity::from_integer(2_000); const FOR_FEES: Balance = cfg(1); const EXPECTED_POOL_BALANCE: Balance = usd6(1_000_000); @@ -159,9 +162,13 @@ mod common { price_id: PRICE_A, max_borrow_amount: ExtMaxBorrowAmount::Quantity(QUANTITY), notional: currency::price_to_currency(PRICE_VALUE_A, Usd6::ID), - max_price_variation: rate_from_percent(0), + max_price_variation: rate_from_percent(50), }) } + + pub fn price_to_usd6(price: Quantity) -> Balance { + currency::price_to_currency(price, Usd6::ID) + } } /// Predefined loan calls for use cases @@ -183,13 +190,13 @@ mod call { } } - pub fn borrow_external(loan_id: LoanId) -> pallet_loans::Call { + pub fn borrow_external(loan_id: LoanId, price: Quantity) -> pallet_loans::Call { pallet_loans::Call::borrow { pool_id: POOL_A, loan_id, amount: PrincipalInput::External(ExternalAmount { - quantity: Quantity::from_integer(50), - settlement_price: currency::price_to_currency(PRICE_VALUE_A, Usd6::ID), + quantity: QUANTITY / 2.into(), + settlement_price: common::price_to_usd6(price), }), } } @@ -216,8 +223,8 @@ mod call { loan_id, amount: RepaidInput { principal: PrincipalInput::External(ExternalAmount { - quantity: Quantity::from_integer(50), - settlement_price: currency::price_to_currency(settlement_price, Usd6::ID), + quantity: QUANTITY / 2.into(), + settlement_price: common::price_to_usd6(settlement_price), }), interest, unscheduled: 0, @@ -264,8 +271,7 @@ fn internal_priced() { let mut env = common::initialize_state_for_loans::, T>(); let info = env.parachain_state(|| { - let now = as TimeAsSecs>::now(); - common::default_loan_info::(now, common::default_internal_pricing()) + common::default_loan_info::(utils::now_secs::(), common::default_internal_pricing()) }); env.submit_now(BORROWER, call::create(&info)).unwrap(); @@ -303,14 +309,13 @@ fn oracle_priced() { }); let info = env.parachain_state(|| { - let now = as TimeAsSecs>::now(); - common::default_loan_info::(now, common::default_external_pricing()) + common::default_loan_info::(utils::now_secs::(), common::default_external_pricing()) }); env.submit_now(BORROWER, call::create(&info)).unwrap(); let loan_id = common::last_loan_id(&env); - env.submit_now(BORROWER, call::borrow_external(loan_id)) + env.submit_now(BORROWER, call::borrow_external(loan_id, PRICE_VALUE_A)) .unwrap(); env.pass(Blocks::BySeconds(SECONDS_PER_MINUTE / 2)); @@ -354,14 +359,13 @@ fn portfolio_valuated_by_oracle() { }); let info = env.parachain_state(|| { - let now = as TimeAsSecs>::now(); - common::default_loan_info::(now, common::default_external_pricing()) + common::default_loan_info::(utils::now_secs::(), common::default_external_pricing()) }); env.submit_now(BORROWER, call::create(&info)).unwrap(); let loan_id = common::last_loan_id(&env); - env.submit_now(BORROWER, call::borrow_external(loan_id)) + env.submit_now(BORROWER, call::borrow_external(loan_id, PRICE_VALUE_A)) .unwrap(); // There is no price fed, so the price comes from the one used as settement @@ -407,8 +411,7 @@ fn update_maturity_extension() { let mut env = common::initialize_state_for_loans::, T>(); let info = env.parachain_state(|| { - let now = as TimeAsSecs>::now(); - common::default_loan_info::(now, common::default_internal_pricing()) + common::default_loan_info::(utils::now_secs::(), common::default_internal_pricing()) }); env.submit_now(BORROWER, call::create(&info)).unwrap(); @@ -440,7 +443,63 @@ fn update_maturity_extension() { .unwrap(); } +fn fake_oracle_portfolio_api() { + let mut env = common::initialize_state_for_loans::, T>(); + + env.parachain_state_mut(|| { + utils::oracle::update_feeders::(POOL_ADMIN.id(), POOL_A, [Feeder::root()]); + utils::oracle::feed_from_root::(PRICE_A, PRICE_VALUE_B); + }); + + let info = env.parachain_state(|| { + common::default_loan_info::(utils::now_secs::(), common::default_external_pricing()) + }); + env.submit_now(BORROWER, call::create(&info)).unwrap(); + + env.submit_now( + BORROWER, + call::borrow_external(common::last_loan_id(&env), PRICE_VALUE_A), + ) + .unwrap(); + + env.parachain_state_mut(|| { + utils::oracle::update_collection::(ANY.id(), POOL_A); + }); + + env.parachain_state(|| { + let expected_portfolio_for = |used_price| { + (QUANTITY / 2.into()).saturating_mul_int(common::price_to_usd6(used_price)) + }; + + // Updating the portfolio with no prices will force to use linear accrual + // prices. Because no time has passed, it correspond to settlement price + assert_ok!( + T::Api::portfolio_valuation(POOL_A, PriceCollectionInput::Empty), + expected_portfolio_for(PRICE_VALUE_A) + ); + + // Updating the portfolio using the register will use the oracle values + assert_ok!( + T::Api::portfolio_valuation(POOL_A, PriceCollectionInput::FromRegistry), + expected_portfolio_for(PRICE_VALUE_B) + ); + + // Updating the portfolio with custom prices will use the overriden prices + let collection = [(PRICE_A, common::price_to_usd6(PRICE_VALUE_C))] + .into_iter() + .collect::>() + .try_into() + .unwrap(); + + assert_ok!( + T::Api::portfolio_valuation(POOL_A, PriceCollectionInput::Custom(collection)), + expected_portfolio_for(PRICE_VALUE_C) + ); + }); +} + crate::test_for_runtimes!(all, internal_priced); crate::test_for_runtimes!(all, oracle_priced); crate::test_for_runtimes!(all, portfolio_valuated_by_oracle); crate::test_for_runtimes!(all, update_maturity_extension); +crate::test_for_runtimes!(all, fake_oracle_portfolio_api); diff --git a/runtime/integration-tests/src/generic/config.rs b/runtime/integration-tests/src/generic/config.rs index 07e977baf3..5627c4561f 100644 --- a/runtime/integration-tests/src/generic/config.rs +++ b/runtime/integration-tests/src/generic/config.rs @@ -247,11 +247,13 @@ pub trait Runtime: /// You can extend this bounds to give extra API support type Api: sp_api::runtime_decl_for_core::CoreV4 + sp_block_builder::runtime_decl_for_block_builder::BlockBuilderV6 - + apis::runtime_decl_for_loans_api::LoansApiV1< + + apis::runtime_decl_for_loans_api::LoansApiV2< Self::Block, PoolId, LoanId, pallet_loans::entities::loans::ActiveLoanInfo, + Balance, + pallet_loans::entities::input::PriceCollectionInput, > + apis::runtime_decl_for_pools_api::PoolsApiV1< Self::Block, PoolId, diff --git a/runtime/integration-tests/src/generic/envs/runtime_env.rs b/runtime/integration-tests/src/generic/envs/runtime_env.rs index 036d8c2230..097981ffe9 100644 --- a/runtime/integration-tests/src/generic/envs/runtime_env.rs +++ b/runtime/integration-tests/src/generic/envs/runtime_env.rs @@ -8,6 +8,7 @@ use cumulus_test_relay_sproof_builder::RelayStateSproofBuilder; use frame_support::{ dispatch::GetDispatchInfo, inherent::{InherentData, ProvideInherent}, + storage::{transactional, TransactionOutcome}, traits::GenesisBuild, }; use parity_scale_codec::Encode; @@ -153,13 +154,14 @@ impl Env for RuntimeEnv { fn parachain_state(&self, f: impl FnOnce() -> R) -> R { self.parachain_ext.borrow_mut().execute_with(|| { - let version = frame_support::StateVersion::V1; - let hash = frame_support::storage_root(version); + transactional::with_transaction(|| { + let result = f(); - let result = f(); - - assert_eq!(hash, frame_support::storage_root(version)); - result + // We do not want to apply any changes, because this is inmutable + // only check if there is no error in applying it. + TransactionOutcome::Rollback(Ok::<_, DispatchError>(result)) + }) + .expect("Rollback result is always Ok") }) } @@ -169,13 +171,14 @@ impl Env for RuntimeEnv { fn sibling_state(&self, f: impl FnOnce() -> R) -> R { self.sibling_ext.borrow_mut().execute_with(|| { - let version = frame_support::StateVersion::V1; - let hash = frame_support::storage_root(version); + transactional::with_transaction(|| { + let result = f(); - let result = f(); - - assert_eq!(hash, frame_support::storage_root(version)); - result + // We do not want to apply any changes, because this is inmutable + // only check if there is no error in applying it. + TransactionOutcome::Rollback(Ok::<_, DispatchError>(result)) + }) + .expect("Rollback result is always Ok") }) } diff --git a/runtime/integration-tests/src/generic/utils/mod.rs b/runtime/integration-tests/src/generic/utils/mod.rs index a17268a194..2d5a4d2e00 100644 --- a/runtime/integration-tests/src/generic/utils/mod.rs +++ b/runtime/integration-tests/src/generic/utils/mod.rs @@ -13,7 +13,7 @@ pub mod democracy; pub mod genesis; use cfg_primitives::{AccountId, Balance, CollectionId, ItemId, PoolId, TrancheId}; -use cfg_traits::{investments::TrancheCurrency as _, Seconds}; +use cfg_traits::{investments::TrancheCurrency as _, Seconds, TimeAsSecs}; use cfg_types::{ fixed_point::Ratio, oracles::OracleKey, @@ -35,7 +35,11 @@ use crate::generic::config::{Runtime, RuntimeKind}; pub const POOL_MIN_EPOCH_TIME: Seconds = 24; -fn find_event(f: impl Fn(E) -> Option) -> Option +pub fn now_secs() -> Seconds { + as TimeAsSecs>::now() +} + +pub fn find_event(f: impl Fn(E) -> Option) -> Option where T::RuntimeEventExt: TryInto, {