Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Loans: Add Runtime API #1457

Merged
merged 12 commits into from
Jul 18, 2023
49 changes: 19 additions & 30 deletions pallets/loans/docs/types.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,14 @@ set namespaceSeparator ::
hide methods

enum Maturity {
Fixed: Moment
Fixed::date: Moment
Fixed::extension: Moment
}

enum CalendarEvent {
End
}

enum ReferenceDate{
CalendarDate: CalendarEvent,
OriginationDate
}

ReferenceDate *--> CalendarEvent

enum InterestPayments {
None
Monthly: ReferenceDate
SemiAnnually: ReferenceDate
}

InterestPayments *-----> ReferenceDate

enum PayDownSchedule {
None
}
Expand All @@ -38,8 +24,8 @@ class RepaymentSchedule {
}

RepaymentSchedule *--> Maturity
RepaymentSchedule *--> PayDownSchedule
RepaymentSchedule *--> InterestPayments
RepaymentSchedule *---> PayDownSchedule
RepaymentSchedule *----> InterestPayments

enum BorrowRestrictions {
NoWrittenOff
Expand All @@ -59,24 +45,27 @@ class LoanRestrictions {
LoanRestrictions *--> BorrowRestrictions
LoanRestrictions *--> RepayRestrictions

enum CompoundingCadence {
Secondly: ReferenceDate
}

CompoundingCadence *-r-> ReferenceDate

enum InterestRate {
Fixed: Rate, CompoundingCadence
}

InterestRate *--> CompoundingCadence

class RepaidAmount {
principal: Balance
interest: Balance
unscheduled: Balance
}

node traits {
package interest {
enum CompoundingSchedule {
Secondly
}

enum InterestRate {
Fixed::rate_per_year: Rate
Fixed::compounding: CompoundingSchedule
}

InterestRate *--> CompoundingSchedule
}
}

package portfolio {
class PortfolioValuation {
value: Balance
Expand Down
42 changes: 42 additions & 0 deletions pallets/loans/src/entities/loans.rs
Original file line number Diff line number Diff line change
Expand Up @@ -489,3 +489,45 @@ impl<T: Config> ActiveLoan<T> {
self.schedule.maturity = crate::types::Maturity::fixed(duration);
}
}

/// Data containing an active loan with extra computed.
#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebugNoBound, TypeInfo, MaxEncodedLen)]
#[scale_info(skip_type_params(T))]
pub struct ActiveLoanInfo<T: Config> {
/// Related active loan
active_loan: ActiveLoan<T>,

/// Interest accrued for this loan
interest_accrued: T::Balance,

/// Present value of the loan
present_value: T::Balance,
}

impl<T: Config> TryFrom<ActiveLoan<T>> for ActiveLoanInfo<T> {
type Error = DispatchError;

fn try_from(active_loan: ActiveLoan<T>) -> Result<Self, Self::Error> {
let (interest_accrued, present_value) = match &active_loan.pricing {
ActivePricing::Internal(inner) => {
let principal = active_loan
.total_borrowed
.ensure_sub(active_loan.total_repaid.principal)?;

let maturity_date = active_loan.schedule.maturity.date();

(
inner.current_interest(principal)?,
inner.present_value(active_loan.origination_date, maturity_date)?,
)
}
ActivePricing::External(inner) => (inner.current_interest()?, inner.present_value()?),
};

Ok(Self {
active_loan,
interest_accrued,
present_value,
})
}
}
55 changes: 26 additions & 29 deletions pallets/loans/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ pub mod pallet {
};
use codec::HasCompact;
use entities::{
loans::{self, ActiveLoan, LoanInfo},
loans::{self, ActiveLoan, ActiveLoanInfo, LoanInfo},
pricing::{PricingAmount, RepaidPricingAmount},
};
use frame_support::{
Expand Down Expand Up @@ -120,7 +120,7 @@ pub mod pallet {
<T as Config>::PriceId,
<T as Config>::PoolId,
>>::Collection;

pub type PortfolioInfoOf<T> = Vec<(<T as Config>::LoanId, ActiveLoanInfo<T>)>;
pub type AssetOf<T> = (<T as Config>::CollectionId, <T as Config>::ItemId);
pub type PriceOf<T> = (<T as Config>::Balance, Moment);
pub type PriceResultOf<T> = Result<PriceOf<T>, DispatchError>;
Expand All @@ -145,27 +145,14 @@ pub mod pallet {
type CurrencyId: Parameter + Copy + MaxEncodedLen;

/// Identify a non fungible collection
type CollectionId: Parameter
+ Member
+ MaybeSerializeDeserialize
+ Default
+ TypeInfo
+ Copy
+ MaxEncodedLen;
type CollectionId: Parameter + Member + Default + TypeInfo + Copy + MaxEncodedLen;

/// Identify a non fungible item
type ItemId: Parameter
+ Member
+ MaybeSerializeDeserialize
+ Default
+ TypeInfo
+ Copy
+ MaxEncodedLen;
type ItemId: Parameter + Member + Default + TypeInfo + Copy + MaxEncodedLen;

/// Identify a loan in the pallet
type LoanId: Parameter
+ Member
+ MaybeSerializeDeserialize
+ Default
+ TypeInfo
+ MaxEncodedLen
Expand All @@ -174,20 +161,10 @@ pub mod pallet {
+ One;

/// Identify a loan in the pallet
type PriceId: Parameter
+ Member
+ MaybeSerializeDeserialize
+ TypeInfo
+ Copy
+ MaxEncodedLen;
type PriceId: Parameter + Member + TypeInfo + Copy + MaxEncodedLen;

/// Defines the rate type used for math computations
type Rate: Parameter
+ Member
+ MaybeSerializeDeserialize
+ FixedPointNumber
+ TypeInfo
+ MaxEncodedLen;
type Rate: Parameter + Member + FixedPointNumber + TypeInfo + MaxEncodedLen;

/// Defines the balance type used for math computations
type Balance: tokens::Balance + FixedPointOperand;
Expand Down Expand Up @@ -992,6 +969,26 @@ pub mod pallet {
Ok((loan, count))
}

pub fn get_active_loans_info(
pool_id: T::PoolId,
) -> Result<PortfolioInfoOf<T>, DispatchError> {
ActiveLoans::<T>::get(pool_id)
.into_iter()
.map(|(loan_id, loan)| Ok((loan_id, loan.try_into()?)))
.collect()
}

pub fn get_active_loan_info(
pool_id: T::PoolId,
loan_id: T::LoanId,
) -> Result<Option<ActiveLoanInfo<T>>, DispatchError> {
ActiveLoans::<T>::get(pool_id)
.into_iter()
.find(|(id, _)| *id == loan_id)
.map(|(_, loan)| loan.try_into())
.transpose()
}

/// Set the maturity date of the loan to this instant.
#[cfg(feature = "runtime-benchmarks")]
pub fn expire(pool_id: T::PoolId, loan_id: T::LoanId) -> DispatchResult {
Expand Down
23 changes: 23 additions & 0 deletions runtime/altair/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1823,6 +1823,14 @@ impl fp_rpc::ConvertTransaction<sp_runtime::OpaqueExtrinsic> for TransactionConv
}
}

#[cfg(not(feature = "disable-runtime-api"))]
mod __runtime_api_use {
pub use pallet_loans::entities::loans::ActiveLoanInfo;
}

#[cfg(not(feature = "disable-runtime-api"))]
use __runtime_api_use::*;

#[cfg(not(feature = "disable-runtime-api"))]
impl_runtime_apis! {
impl sp_api::Core<Block> for Runtime {
Expand Down Expand Up @@ -2010,6 +2018,21 @@ impl_runtime_apis! {
}
}

impl runtime_common::apis::LoansApi<Block, PoolId, LoanId, ActiveLoanInfo<Runtime>> for Runtime {
fn portfolio(
pool_id: PoolId
) -> Vec<(LoanId, ActiveLoanInfo<Runtime>)> {
Loans::get_active_loans_info(pool_id).unwrap_or_default()
}

fn portfolio_loan(
pool_id: PoolId,
loan_id: LoanId
) -> Option<ActiveLoanInfo<Runtime>> {
Loans::get_active_loan_info(pool_id, loan_id).ok().flatten()
}
}

// Frontier APIs
impl fp_rpc::EthereumRuntimeRPCApi<Block> for Runtime {
fn chain_id() -> u64 {
Expand Down
23 changes: 23 additions & 0 deletions runtime/centrifuge/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1912,6 +1912,14 @@ impl fp_rpc::ConvertTransaction<sp_runtime::OpaqueExtrinsic> for TransactionConv
}
}

#[cfg(not(feature = "disable-runtime-api"))]
mod __runtime_api_use {
pub use pallet_loans::entities::loans::ActiveLoanInfo;
}

#[cfg(not(feature = "disable-runtime-api"))]
use __runtime_api_use::*;

#[cfg(not(feature = "disable-runtime-api"))]
impl_runtime_apis! {
impl sp_api::Core<Block> for Runtime {
Expand Down Expand Up @@ -2100,6 +2108,21 @@ impl_runtime_apis! {
}
}

impl runtime_common::apis::LoansApi<Block, PoolId, LoanId, ActiveLoanInfo<Runtime>> for Runtime {
fn portfolio(
pool_id: PoolId
) -> Vec<(LoanId, ActiveLoanInfo<Runtime>)> {
Loans::get_active_loans_info(pool_id).unwrap_or_default()
}

fn portfolio_loan(
pool_id: PoolId,
loan_id: LoanId
) -> Option<ActiveLoanInfo<Runtime>> {
Loans::get_active_loan_info(pool_id, loan_id).ok().flatten()
}
}

// Frontier APIs
impl fp_rpc::EthereumRuntimeRPCApi<Block> for Runtime {
fn chain_id() -> u64 {
Expand Down
29 changes: 29 additions & 0 deletions runtime/common/src/apis/loans.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright 2023 Centrifuge Foundation (centrifuge.io).
// This file is part of 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 codec::Codec;
use sp_api::decl_runtime_apis;
use sp_std::vec::Vec;

decl_runtime_apis! {
/// Runtime API for the rewards pallet.
pub trait LoansApi<PoolId, LoanId, Loan>
where
PoolId: Codec,
LoanId: Codec,
Loan: Codec,
{
fn portfolio(pool_id: PoolId) -> Vec<(LoanId, Loan)>;
fn portfolio_loan(pool_id: PoolId, loan_id: LoanId) -> Option<Loan>;
}
}
2 changes: 2 additions & 0 deletions runtime/common/src/apis/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@

//! Runtime apis useful in the Centrifuge ecosystem
pub use anchors::*;
pub use loans::*;
pub use pools::*;
pub use rewards::*;

mod anchors;
mod loans;
mod pools;
mod rewards;
23 changes: 23 additions & 0 deletions runtime/development/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2090,6 +2090,14 @@ impl fp_rpc::ConvertTransaction<sp_runtime::OpaqueExtrinsic> for TransactionConv
}
}

#[cfg(not(feature = "disable-runtime-api"))]
mod __runtime_api_use {
pub use pallet_loans::entities::loans::ActiveLoanInfo;
}

#[cfg(not(feature = "disable-runtime-api"))]
use __runtime_api_use::*;

#[cfg(not(feature = "disable-runtime-api"))]
impl_runtime_apis! {
impl sp_api::Core<Block> for Runtime {
Expand Down Expand Up @@ -2281,6 +2289,21 @@ impl_runtime_apis! {
}
}

impl runtime_common::apis::LoansApi<Block, PoolId, LoanId, ActiveLoanInfo<Runtime>> for Runtime {
fn portfolio(
pool_id: PoolId
) -> Vec<(LoanId, ActiveLoanInfo<Runtime>)> {
Loans::get_active_loans_info(pool_id).unwrap_or_default()
}

fn portfolio_loan(
pool_id: PoolId,
loan_id: LoanId
) -> Option<ActiveLoanInfo<Runtime>> {
Loans::get_active_loan_info(pool_id, loan_id).ok().flatten()
}
}

// Frontier APIs
impl fp_rpc::EthereumRuntimeRPCApi<Block> for Runtime {
fn chain_id() -> u64 {
Expand Down