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

Oracle valuation implementation #1311

Merged
merged 62 commits into from
May 25, 2023
Merged
Show file tree
Hide file tree
Changes from 57 commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
3539091
first oracle valuation schema
lemunozm May 3, 2023
4c326e2
remove time dependency for calculate debt
lemunozm May 3, 2023
6740234
add trigger
lemunozm May 3, 2023
60db297
configure mock, minor renames
lemunozm May 3, 2023
52ffd9f
fix tests
lemunozm May 3, 2023
e8d5613
simplify price bound. Fix benchmarks
lemunozm May 3, 2023
8967657
minor benchmark fix
lemunozm May 3, 2023
5a86ac3
updating development runtime
lemunozm May 4, 2023
e04b02f
add pallet-collection-data-feed to development runtime
lemunozm May 4, 2023
cf65d35
add to altair & centrifuge runtimes
lemunozm May 4, 2023
c4df7cd
avoid fixing features out of this scope
lemunozm May 5, 2023
2983301
refactor test utility create_loan
lemunozm May 5, 2023
4481e14
add utility to configure pallet loans without price registry
lemunozm May 5, 2023
80e19ee
revert runtime oracle & collection-data-feed additions
lemunozm May 5, 2023
32d1e4d
revert runtime oracle & collection-data-feed additions
lemunozm May 5, 2023
48e4206
fix std issue
lemunozm May 5, 2023
41324d8
price as balance instead of rate
lemunozm May 9, 2023
c068920
fix broken test
lemunozm May 9, 2023
f43753c
correct oracle repayment
lemunozm May 9, 2023
65b8493
add write off to oracle valuation
lemunozm May 9, 2023
a46511b
minor rename
lemunozm May 10, 2023
7d312c8
fix integration tests
lemunozm May 16, 2023
59a881f
fix Cargo.lock
lemunozm May 16, 2023
1fe4e58
add missing license files
lemunozm May 16, 2023
7f09165
write_off penalty refactor
lemunozm May 16, 2023
5a336e0
reestructure types
lemunozm May 17, 2023
99fac9e
use Config for pricing
lemunozm May 17, 2023
a104233
reorganization in files
lemunozm May 17, 2023
1d832a2
collections handled in pricing
lemunozm May 17, 2023
f064311
minor write off comments
lemunozm May 17, 2023
32cfda6
minor rename
lemunozm May 18, 2023
5cc8a47
add required restrictions
lemunozm May 18, 2023
690fa2d
minor comment fix
lemunozm May 18, 2023
3f41067
remove write off wrappers, avoid pricing field access from loans
lemunozm May 19, 2023
6ca7032
rename write_off to policy
lemunozm May 19, 2023
68f686d
split pricing modules in files
lemunozm May 19, 2023
373bd7b
legacy tests passing
lemunozm May 19, 2023
71c3b3d
fix legacy benchmarks
lemunozm May 19, 2023
774dbb8
add to codeowners
lemunozm May 19, 2023
3b2c44a
add create & borrow test cases for external pricing
lemunozm May 19, 2023
3c625d0
apply thea suggestion for collection data
lemunozm May 19, 2023
5e4072c
add repay tests
lemunozm May 22, 2023
6ff3a7e
fix clippy
lemunozm May 22, 2023
b564da6
reorganize policy & better testing
lemunozm May 22, 2023
ba4f822
add tests for write_off and policy
lemunozm May 22, 2023
c596528
add tests for close
lemunozm May 22, 2023
29b23dd
fix policy compilation issue in no-std
lemunozm May 22, 2023
1558d3f
fix present value for repayment loans & portfolio tests
lemunozm May 22, 2023
cd99864
fix integration tests
lemunozm May 22, 2023
037ba2c
add outstanding quantity to external pricing
lemunozm May 23, 2023
3560136
add wrong quantity tests
lemunozm May 23, 2023
57b2f1b
fix portfolio valuation
lemunozm May 23, 2023
0302a1f
minor doc move
lemunozm May 23, 2023
a79f4a7
portfolio in a new mod
lemunozm May 23, 2023
1f85c97
Initialize portfolio with current time
lemunozm May 23, 2023
4f1b43a
add test checking the initial portfolio timestamp
lemunozm May 23, 2023
55c379e
fix lints
lemunozm May 24, 2023
fa0b176
unify Loan errors
lemunozm May 24, 2023
e4f1eee
renaming quantity
lemunozm May 24, 2023
e7e2fa4
rename NotWrittenOff restriction
lemunozm May 24, 2023
e0afeff
rename config to util, minor doc change
lemunozm May 24, 2023
c106a49
Merge remote-tracking branch 'origin/main' into feature/oracle-valuation
lemunozm May 25, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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
lemunozm marked this conversation as resolved.
Show resolved Hide resolved
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
lemunozm marked this conversation as resolved.
Show resolved Hide resolved
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::NoWrittenOff,
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
54 changes: 54 additions & 0 deletions pallets/loans-ref/src/config.rs
lemunozm marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name of the file is somhow really misleading for me...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted to express that it was a utility for configuration. In this case, it's used for configuring a pallet-loans without external pricing support. Maybe util mod is better?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to util.

Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// 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_traits::data::{DataCollection, DataRegistry};
use sp_runtime::{DispatchError, DispatchResult};
use sp_std::marker::PhantomData;

use crate::pallet::{Config, PoolIdOf, PriceResultOf};

const DEFAULT_ERR: DispatchError =
DispatchError::Other("No configured price registry for pallet-loans");

pub struct NoPriceRegistry<T>(PhantomData<T>);

impl<T: Config> DataRegistry<T::PriceId, PoolIdOf<T>> for NoPriceRegistry<T> {
type Collection = NoPriceCollection<T>;
type Data = PriceResultOf<T>;

fn get(_: &T::PriceId) -> Self::Data {
Err(DEFAULT_ERR)
}

fn collection(_: &PoolIdOf<T>) -> Self::Collection {
NoPriceCollection(PhantomData::default())
}

fn register_id(_: &T::PriceId, _: &PoolIdOf<T>) -> DispatchResult {
Err(DEFAULT_ERR)
}

fn unregister_id(_: &T::PriceId, _: &PoolIdOf<T>) -> DispatchResult {
Err(DEFAULT_ERR)
}
}

pub struct NoPriceCollection<T>(PhantomData<T>);

impl<T: Config> DataCollection<T::PriceId> for NoPriceCollection<T> {
type Data = PriceResultOf<T>;

fn get(&self, _: &T::PriceId) -> Self::Data {
Err(DEFAULT_ERR)
}
}