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: multiple cashflows PoC #1408

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 20 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions pallets/loans/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ cfg-traits = { path = "../../libs/traits", default-features = false }
cfg-types = { path = "../../libs/types", default-features = false }
orml-traits = { git = "https://github.com/open-web3-stack/open-runtime-module-library", default-features = false, branch = "polkadot-v0.9.38" }

chrono = { version = "0.4", default-features = false }
chronoutil = "0.2"
strum = { version = "0.24", default-features = false, features = ["derive"] }

# Optionals for benchmarking
Expand Down Expand Up @@ -57,6 +59,7 @@ std = [
"frame-benchmarking/std",
"strum/std",
"orml-traits/std",
"chrono/std",
]
runtime-benchmarks = [
"frame-benchmarking/runtime-benchmarks",
Expand Down
5 changes: 3 additions & 2 deletions pallets/loans/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,10 @@ use crate::{
},
pallet::*,
types::{
cashflow::{InterestPayments, Maturity, PayDownSchedule, RepaymentSchedule},
policy::{WriteOffRule, WriteOffTrigger},
valuation::{DiscountedCashFlow, ValuationMethod},
BorrowRestrictions, InterestPayments, LoanMutation, LoanRestrictions, Maturity,
PayDownSchedule, RepayRestrictions, RepaymentSchedule,
BorrowRestrictions, LoanMutation, LoanRestrictions, RepayRestrictions,
},
};

Expand Down
57 changes: 51 additions & 6 deletions pallets/loans/src/entities/loans.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ use super::pricing::{
use crate::{
pallet::{AssetOf, Config, Error, PriceOf},
types::{
cashflow::RepaymentSchedule,
policy::{WriteOffStatus, WriteOffTrigger},
BorrowLoanError, BorrowRestrictions, CloseLoanError, CreateLoanError, LoanMutation,
LoanRestrictions, MutationError, RepaidAmount, RepayLoanError, RepayRestrictions,
RepaymentSchedule,
},
};

Expand Down Expand Up @@ -211,6 +211,12 @@ impl<T: Config> ActiveLoan<T> {
&self.pricing
}

pub fn principal(&self) -> Result<T::Balance, DispatchError> {
Ok(self
.total_borrowed
.ensure_sub(self.total_repaid.principal)?)
}

pub fn write_off_status(&self) -> WriteOffStatus<T::Rate> {
WriteOffStatus {
percentage: self.write_off_percentage,
Expand Down Expand Up @@ -242,6 +248,35 @@ impl<T: Config> ActiveLoan<T> {
}
ActivePricing::Internal(_) => Ok(false),
},
WriteOffTrigger::InterestOverdue(_overdue_seconds) => match &self.pricing {
// TODO: should be implemented once interest_rate is moved to ActiveLoan
ActivePricing::External(_) => Ok(false),
ActivePricing::Internal(pricing) => {
let cashflows = self.schedule.generate_expected_cashflows(
self.origination_date,
self.principal()?,
pricing.interest.rate(),
)?;

// TODO(Luis): If from this point, any field of ActiveLoan is needed, I would
// move this code into cashflow.rs

/*
let now_date = NaiveDateTime::from_timestamp_opt(now as i64, 0)
.ok_or(DispatchError::Other("Invalid now date"))?;
let cashflows_in_past = cashflows
.iter()
.filter(|(d, _)| d.and_hms_opt(0, 0, 0).unwrap() > now_date);
*/

// TODO: should find the first cashflow that was not paid,
// based on total_repaid_interest and by reducing the cashflows_in_past
// from this, and should the ncompare the first cash flow that was not paid date
// with now - overdue_secs.

Ok(false)
}
},
}
}

Expand Down Expand Up @@ -314,6 +349,19 @@ impl<T: Config> ActiveLoan<T> {
Error::<T>::from(BorrowLoanError::MaturityDatePassed)
);

// If the loan has an interest or pay down schedule other than None,
// then we should only allow borrowing more if no interest or principal
// payments are overdue.
//
// This is required because after borrowing more, it is not possible
// to validate anymore whether previous cashflows matched the repayment
// schedule, as we don't store historic data of the principal.
//
// Therefore, in `borrow()` we set repayments_on_schedule_until to now.
//
// TODO: check total_repaid_interest >= total_expected_interest
// and total_repaid_principal >= total_expected_principal

Ok(())
}

Expand Down Expand Up @@ -349,10 +397,7 @@ impl<T: Config> ActiveLoan<T> {
let (max_repay_principal, outstanding_interest) = match &self.pricing {
ActivePricing::Internal(inner) => {
amount.principal.internal()?;

let principal = self
.total_borrowed
.ensure_sub(self.total_repaid.principal)?;
let principal = self.principal()?;

(principal, inner.outstanding_interest(principal)?)
}
Expand Down Expand Up @@ -489,7 +534,7 @@ impl<T: Config> ActiveLoan<T> {

#[cfg(feature = "runtime-benchmarks")]
pub fn set_maturity(&mut self, duration: Moment) {
self.schedule.maturity = crate::types::Maturity::fixed(duration);
self.schedule.maturity = crate::types::cashflow::Maturity::fixed(duration);
}
}

Expand Down
6 changes: 3 additions & 3 deletions pallets/loans/src/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ use super::{
},
pallet::{ActiveLoans, Error, LastLoanId, PortfolioValuation},
types::{
cashflow::{InterestPayments, Maturity, PayDownSchedule, RepaymentSchedule},
policy::{WriteOffRule, WriteOffStatus, WriteOffTrigger},
valuation::{DiscountedCashFlow, ValuationMethod},
BorrowLoanError, BorrowRestrictions, Change, CloseLoanError, CreateLoanError,
InterestPayments, InternalMutation, LoanMutation, LoanRestrictions, Maturity,
MutationError, PayDownSchedule, RepayLoanError, RepayRestrictions, RepaymentSchedule,
WrittenOffError,
InternalMutation, LoanMutation, LoanRestrictions, MutationError, RepayLoanError,
RepayRestrictions, WrittenOffError,
},
};

Expand Down
Loading