diff --git a/pallets/loans/src/entities/loans.rs b/pallets/loans/src/entities/loans.rs index 23a2ddf052..f6899f8a79 100644 --- a/pallets/loans/src/entities/loans.rs +++ b/pallets/loans/src/entities/loans.rs @@ -574,3 +574,62 @@ impl TryFrom<(T::PoolId, ActiveLoan)> for ActiveLoanInfo { }) } } + +/// Migration module that contains old loans types. +/// Can be removed once chains contains pallet-loans version v3 +pub(crate) mod v2 { + use cfg_traits::Seconds; + use parity_scale_codec::Decode; + + use crate::{ + entities::pricing::{external::v2::ExternalActivePricing, internal::InternalActivePricing}, + types::{LoanRestrictions, RepaidAmount, RepaymentSchedule}, + AssetOf, Config, + }; + + #[derive(Decode)] + pub enum ActivePricing { + Internal(InternalActivePricing), + External(ExternalActivePricing), + } + + #[derive(Decode)] + pub struct ActiveLoan { + pub schedule: RepaymentSchedule, + pub collateral: AssetOf, + pub restrictions: LoanRestrictions, + pub borrower: T::AccountId, + pub write_off_percentage: T::Rate, + pub origination_date: Seconds, + pub pricing: ActivePricing, + pub total_borrowed: T::Balance, + pub total_repaid: RepaidAmount, + pub repayments_on_schedule_until: Seconds, + } + + impl ActiveLoan { + pub fn migrate(self) -> crate::entities::loans::ActiveLoan { + crate::entities::loans::ActiveLoan { + schedule: self.schedule, + collateral: self.collateral, + restrictions: self.restrictions, + borrower: self.borrower, + write_off_percentage: self.write_off_percentage, + origination_date: self.origination_date, + pricing: match self.pricing { + ActivePricing::Internal(inner) => { + crate::entities::pricing::ActivePricing::Internal(inner) + } + ActivePricing::External(inner) => { + crate::entities::pricing::ActivePricing::External( + inner.migrate(self.origination_date), + ) + } + }, + total_borrowed: self.total_borrowed, + total_repaid: self.total_repaid, + repayments_on_schedule_until: self.repayments_on_schedule_until, + } + } + } +} diff --git a/pallets/loans/src/entities/pricing/external.rs b/pallets/loans/src/entities/pricing/external.rs index e153cbd888..47c7433ba1 100644 --- a/pallets/loans/src/entities/pricing/external.rs +++ b/pallets/loans/src/entities/pricing/external.rs @@ -289,3 +289,38 @@ impl ExternalActivePricing { Ok(()) } } + +/// Migration module that contains old loans types. +/// Can be removed once chains contains pallet-loans version v3 +pub(crate) mod v2 { + use cfg_traits::Seconds; + use parity_scale_codec::Decode; + + use crate::{ + entities::{interest::ActiveInterestRate, pricing::external::ExternalPricing}, + Config, + }; + + #[derive(Decode)] + pub struct ExternalActivePricing { + pub info: ExternalPricing, + pub outstanding_quantity: T::Quantity, + pub interest: ActiveInterestRate, + pub latest_settlement_price: T::Balance, + } + + impl ExternalActivePricing { + pub fn migrate( + self, + settlement_price_updated: Seconds, + ) -> crate::entities::pricing::external::ExternalActivePricing { + crate::entities::pricing::external::ExternalActivePricing { + info: self.info, + outstanding_quantity: self.outstanding_quantity, + interest: self.interest, + latest_settlement_price: self.latest_settlement_price, + settlement_price_updated, + } + } + } +} diff --git a/pallets/loans/src/lib.rs b/pallets/loans/src/lib.rs index 9ad4afddb9..a6058174c2 100644 --- a/pallets/loans/src/lib.rs +++ b/pallets/loans/src/lib.rs @@ -65,6 +65,8 @@ mod tests; #[cfg(feature = "runtime-benchmarks")] mod benchmarking; +mod migrations; + pub use pallet::*; pub use weights::WeightInfo; @@ -115,7 +117,7 @@ pub mod pallet { pub type AssetOf = (::CollectionId, ::ItemId); pub type PriceOf = (::Balance, ::Moment); - const STORAGE_VERSION: StorageVersion = StorageVersion::new(2); + const STORAGE_VERSION: StorageVersion = StorageVersion::new(3); #[pallet::pallet] #[pallet::storage_version(STORAGE_VERSION)] @@ -433,6 +435,13 @@ pub mod pallet { } } + #[pallet::hooks] + impl Hooks> for Pallet { + fn on_runtime_upgrade() -> frame_support::weights::Weight { + migrations::migrate_from_v2_to_v3::() + } + } + #[pallet::call] impl Pallet { /// Creates a new loan against the collateral provided diff --git a/pallets/loans/src/migrations.rs b/pallets/loans/src/migrations.rs new file mode 100644 index 0000000000..2cbeea8e8d --- /dev/null +++ b/pallets/loans/src/migrations.rs @@ -0,0 +1,65 @@ +// Copyright 2024 Centrifuge Foundation (centrifuge.io). +// +// This file is part of the Centrifuge chain project. +// Centrifuge is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version (see http://www.gnu.org/licenses). +// Centrifuge is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +use frame_support::{ + dispatch::GetStorageVersion, inherent::Vec, log, pallet_prelude::StorageVersion, traits::Get, + weights::Weight, +}; + +use crate::{ActiveLoans, Config, Pallet}; + +mod v2 { + use frame_support::{pallet_prelude::*, storage_alias}; + + use crate::{entities::loans::v2, Config, Pallet}; + + pub type ActiveLoansVec = BoundedVec< + (::LoanId, v2::ActiveLoan), + ::MaxActiveLoansPerPool, + >; + + #[storage_alias] + pub type ActiveLoans = StorageMap< + Pallet, + Blake2_128Concat, + ::PoolId, + ActiveLoansVec, + ValueQuery, + >; +} + +pub fn migrate_from_v2_to_v3() -> Weight { + if Pallet::::on_chain_storage_version() == StorageVersion::new(2) { + log::info!("Loans: Starting migration v2 -> v3"); + + ActiveLoans::::translate::, _>(|_, active_loans| { + Some( + active_loans + .into_iter() + .map(|(loan_id, active_loan)| (loan_id, active_loan.migrate())) + .collect::>() + .try_into() + .expect("size doest not change, qed"), + ) + }); + + Pallet::::current_storage_version().put::>(); + + let count = ActiveLoans::::iter().count() as u64; + log::info!("Loans: Migrated {} pools", count); + T::DbWeight::get().reads_writes(count + 1, count + 1) + } else { + // wrong storage version + log::info!("Loans: Migration did not execute. This probably should be removed"); + T::DbWeight::get().reads_writes(1, 0) + } +}