Skip to content

Commit

Permalink
Loans: Add Runtime API (#1457)
Browse files Browse the repository at this point in the history
* update types diagram

* add ActiveLoanInfo

* add decl runtime api for loans

* add API to development runtime

* add serialization capability to loans structs

* add rpc & service

* minor rename

* compiling for development runtime

* taplo fmt

* add support for altair & centrifuge

* fix clippy

* Remove RPC support
  • Loading branch information
lemunozm committed Jul 18, 2023
1 parent a3bd197 commit 222581d
Show file tree
Hide file tree
Showing 8 changed files with 187 additions and 59 deletions.
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

0 comments on commit 222581d

Please sign in to comment.