Skip to content

Commit

Permalink
Oracle valuation implementation (#1311)
Browse files Browse the repository at this point in the history
* first oracle valuation schema

* remove time dependency for calculate debt

* add trigger

* configure mock, minor renames

* fix tests

* simplify price bound. Fix benchmarks

* minor benchmark fix

* updating development runtime

* add pallet-collection-data-feed to development runtime

* add to altair & centrifuge runtimes

* avoid fixing features out of this scope

* refactor test utility create_loan

* add utility to configure pallet loans without price registry

* revert runtime oracle & collection-data-feed additions

* revert runtime oracle & collection-data-feed additions

* fix std issue

* price as balance instead of rate

* fix broken test

* correct oracle repayment

* add write off to oracle valuation

* minor rename

* fix integration tests

* fix Cargo.lock

* add missing license files

* write_off penalty refactor

* reestructure types

* use Config for pricing

* reorganization in files

* collections handled in pricing

* minor write off comments

* minor rename

* add required restrictions

* minor comment fix

* remove write off wrappers, avoid pricing field access from loans

* rename write_off to policy

* split pricing modules in files

* legacy tests passing

* fix legacy benchmarks

* add to codeowners

* add create & borrow test cases for external pricing

* apply thea suggestion for collection data

* add repay tests

* fix clippy

* reorganize policy & better testing

* add tests for write_off and policy

* add tests for close

* fix policy compilation issue in no-std

* fix present value for repayment loans & portfolio tests

* fix integration tests

* add outstanding quantity to external pricing

* add wrong quantity tests

* fix portfolio valuation

* minor doc move

* portfolio in a new mod

* Initialize portfolio with current time

* add test checking the initial portfolio timestamp

* fix lints

* unify Loan errors

* renaming quantity

* rename NotWrittenOff restriction

* rename config to util, minor doc change
  • Loading branch information
lemunozm committed May 25, 2023
1 parent b829c36 commit 5781da4
Show file tree
Hide file tree
Showing 24 changed files with 2,176 additions and 1,027 deletions.
10 changes: 8 additions & 2 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@ pallets/pool-registry/* @mustermeiszer
pallets/pool-system/* @mustermeiszer @branan @offerijns

## Pallet Pool-System
pallets/loans/* @mustermeiszer @branan @offerijns
pallets/loans-ref/* @mustermeiszer @branan @offerijns @lemunozm

## Pallet Interest-Accrual
pallets/interest-accrual/* @branan
pallets/interest-accrual/* @branan @lemunozm

## Pallet Investments
pallets/investments/* @mustermeiszer
Expand All @@ -48,6 +48,9 @@ pallets/permissions/* @mustermeiszer
## Pallet Restricted-Tokens
pallets/restricted-tokens/* @mustermeiszer

## Pallet Collection-Data-Feed
pallets/collection-data-feed/* @lemunozm

# Changes to libs
## Changes to specific libraries

Expand All @@ -58,6 +61,9 @@ libs/mock-builder/* @lemunozm
#### rewards module
libs/traits/src/rewards.rs @lemunozm

#### rewards module
libs/traits/src/data.rs @lemunozm

#### ensure module
libs/traits/src/ops.rs @lemunozm

Expand Down
3 changes: 3 additions & 0 deletions libs/primitives/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,9 @@ pub mod types {

/// A representation of a loan identifier
pub type LoanId = u64;

/// A representation of a price identifier
pub type PriceId = u64;
}

/// Common constants for all runtimes
Expand Down
10 changes: 10 additions & 0 deletions libs/types/src/adjustments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,17 @@
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

#[derive(Clone, Copy)]
pub enum Adjustment<Amount> {
Increase(Amount),
Decrease(Amount),
}

impl<Amount> Adjustment<Amount> {
pub fn abs(self) -> Amount {
match self {
Adjustment::Increase(amount) => amount,
Adjustment::Decrease(amount) => amount,
}
}
}
8 changes: 4 additions & 4 deletions pallets/collection-data-feed/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,10 +159,10 @@ pub mod pallet {
// for Data values.
for collection_id in Listening::<T>::get(data_id).keys() {
Collection::<T>::mutate(collection_id, |collection| {
if let Some(value) = collection.get_mut(data_id) {
if let Ok(new_value) = Self::get(data_id) {
*value = new_value;
}
if let (Some(value), Ok(new_value)) =
(collection.get_mut(data_id), Self::get(data_id))
{
*value = new_value;
}
});
}
Expand Down
82 changes: 61 additions & 21 deletions pallets/loans-ref/src/benchmarking.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
// 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 cfg_primitives::CFG;
use cfg_traits::{InterestAccrual, Permissions, PoolBenchmarkHelper};
use cfg_traits::{data::DataCollection, InterestAccrual, Permissions, PoolBenchmarkHelper};
use cfg_types::{
adjustments::Adjustment,
permissions::{PermissionScope, PoolRole, Role},
Expand All @@ -18,10 +31,18 @@ use sp_runtime::traits::{Get, One, Zero};
use sp_std::{time::Duration, vec};

use super::{
loan::LoanInfo,
pallet::*,
types::{LoanInfo, MaxBorrowAmount},
valuation::{DiscountedCashFlow, ValuationMethod},
write_off::{WriteOffRule, WriteOffTrigger},
pricing::{
internal::{InternalPricing, MaxBorrowAmount},
Pricing,
},
types::{
policy::{WriteOffRule, WriteOffTrigger},
valuation::{DiscountedCashFlow, ValuationMethod},
BorrowRestrictions, InterestPayments, LoanRestrictions, Maturity, PayDownSchedule,
RepayRestrictions, RepaymentSchedule,
},
};

const OFFSET: Duration = Duration::from_secs(120);
Expand All @@ -44,10 +65,13 @@ where
T::ItemId: From<u16>,
T::Pool:
PoolBenchmarkHelper<PoolId = PoolIdOf<T>, AccountId = T::AccountId, Balance = T::Balance>,
PriceCollectionOf<T>: DataCollection<T::PriceId, Data = PriceResultOf<T>>,
{
#[cfg(test)]
fn config_mocks() {
use crate::mock::{MockPermissions, MockPools};
use cfg_mocks::pallet_mock_data::util::MockDataCollection;

use crate::mock::{MockPermissions, MockPools, MockPrices};

MockPermissions::mock_add(|_, _, _| Ok(()));
MockPermissions::mock_has(|_, _, _| true);
Expand All @@ -57,6 +81,7 @@ where
MockPools::mock_deposit(|_, _, _| Ok(()));
MockPools::mock_benchmark_create_pool(|_, _| {});
MockPools::mock_benchmark_give_ausd(|_, _| {});
MockPrices::mock_collection(|_| MockDataCollection::new(|_| Ok((0, 0))));
}

fn prepare_benchmark() -> PoolIdOf<T> {
Expand Down Expand Up @@ -89,27 +114,42 @@ where
pool_id
}

fn base_loan(item_id: T::ItemId) -> LoanInfo<T> {
LoanInfo {
schedule: RepaymentSchedule {
maturity: Maturity::Fixed((T::Time::now() + OFFSET).as_secs()),
interest_payments: InterestPayments::None,
pay_down_schedule: PayDownSchedule::None,
},
collateral: (COLLECION_ID.into(), item_id),
pricing: Pricing::Internal(InternalPricing {
collateral_value: COLLATERAL_VALUE.into(),
interest_rate: T::Rate::saturating_from_rational(1, 5000),
max_borrow_amount: MaxBorrowAmount::UpToOutstandingDebt {
advance_rate: T::Rate::one(),
},
valuation_method: ValuationMethod::DiscountedCashFlow(DiscountedCashFlow {
probability_of_default: T::Rate::zero(),
loss_given_default: T::Rate::zero(),
discount_rate: T::Rate::one(),
}),
}),
restrictions: LoanRestrictions {
borrows: BorrowRestrictions::NotWrittenOff,
repayments: RepayRestrictions::None,
},
}
}

fn create_loan(pool_id: PoolIdOf<T>, item_id: T::ItemId) -> T::LoanId {
let borrower = account("borrower", 0, 0);

let collection_id = COLLECION_ID.into();
T::NonFungible::mint_into(&collection_id, &item_id, &borrower).unwrap();
T::NonFungible::mint_into(&COLLECION_ID.into(), &item_id, &borrower).unwrap();

Pallet::<T>::create(
RawOrigin::Signed(borrower).into(),
pool_id,
LoanInfo::new((collection_id, item_id))
.maturity(T::Time::now() + OFFSET)
.interest_rate(T::Rate::saturating_from_rational(1, 5000))
.collateral_value((COLLATERAL_VALUE).into())
.max_borrow_amount(MaxBorrowAmount::UpToOutstandingDebt {
advance_rate: T::Rate::one(),
})
.valuation_method(ValuationMethod::DiscountedCashFlow(DiscountedCashFlow {
probability_of_default: T::Rate::zero(),
loss_given_default: T::Rate::zero(),
discount_rate: T::Rate::one(),
})),
Self::base_loan(item_id),
)
.unwrap();

Expand Down Expand Up @@ -194,6 +234,7 @@ benchmarks! {
T::CollectionId: From<u16>,
T::ItemId: From<u16>,
T::Pool: PoolBenchmarkHelper<PoolId = PoolIdOf<T>, AccountId = T::AccountId, Balance = T::Balance>,
PriceCollectionOf<T>: DataCollection<T::PriceId, Data = PriceResultOf<T>>,
}

create {
Expand All @@ -202,8 +243,7 @@ benchmarks! {

let (collection_id, item_id) = (COLLECION_ID.into(), 1.into());
T::NonFungible::mint_into(&collection_id, &item_id, &borrower).unwrap();

let loan_info = LoanInfo::new((collection_id, item_id)).maturity(T::Time::now() + OFFSET);
let loan_info = Helper::<T>::base_loan(item_id);

}: _(RawOrigin::Signed(borrower), pool_id, loan_info)

Expand Down
Loading

0 comments on commit 5781da4

Please sign in to comment.