Skip to content

Commit

Permalink
make it works without chronoutils
Browse files Browse the repository at this point in the history
  • Loading branch information
lemunozm committed Apr 25, 2024
1 parent 6fdf348 commit 85eb05f
Show file tree
Hide file tree
Showing 8 changed files with 78 additions and 63 deletions.
10 changes: 0 additions & 10 deletions Cargo.lock

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

1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@ impl-trait-for-tuples = "0.2.1"
num-traits = { version = "0.2", default-features = false }
num_enum = { version = "0.5.3", default-features = false }
chrono = { version = "0.4", default-features = false }
chronoutil = "0.2"

# Cumulus
cumulus-pallet-aura-ext = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, branch = "release-polkadot-v1.1.0" }
Expand Down
1 change: 0 additions & 1 deletion pallets/loans/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ targets = ["x86_64-unknown-linux-gnu"]
parity-scale-codec = { workspace = true }
scale-info = { workspace = true }
chrono = { workspace = true }
chronoutil = { workspace = true }

frame-support = { workspace = true }
frame-system = { workspace = true }
Expand Down
2 changes: 1 addition & 1 deletion pallets/loans/src/entities/loans.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use sp_runtime::{
},
DispatchError,
};
use sp_std::collections::btree_map::BTreeMap;
use sp_std::{collections::btree_map::BTreeMap, vec::Vec};

use crate::{
entities::{
Expand Down
109 changes: 62 additions & 47 deletions pallets/loans/src/types/cashflow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@
// GNU General Public License for more details.

use cfg_traits::{interest::InterestRate, Seconds};
use chrono::{DateTime, NaiveDate};
use chronoutil::DateRule;
use chrono::{DateTime, Datelike, NaiveDate};
use frame_support::pallet_prelude::RuntimeDebug;
use parity_scale_codec::{Decode, Encode, MaxEncodedLen};
use scale_info::TypeInfo;
Expand All @@ -23,6 +22,13 @@ use sp_runtime::{
},
ArithmeticError, DispatchError, FixedPointNumber, FixedPointOperand,
};
use sp_std::{cmp::min, vec, vec::Vec};

// By now only "day 1" of the month is supported for monthly cashflows.
// Modifying this value would make `monthly_dates()` and
// `monthly_dates_intervals()` to no longer work as expected.
// Supporting more reference dates will imply more logic related to dates.
const REFERENCE_DAY_1: u32 = 1;

/// Specify the expected repayments date
#[derive(Encode, Decode, Clone, PartialEq, Eq, TypeInfo, RuntimeDebug, MaxEncodedLen)]
Expand Down Expand Up @@ -78,6 +84,15 @@ fn date_to_seconds(date: NaiveDate) -> Result<Seconds, DispatchError> {
.ensure_into()?)
}

fn next_month_with_day(date: NaiveDate, day: u32) -> Option<NaiveDate> {
let (month, year) = match date.month() {
12 => (1, date.year() + 1),
n => (n + 1, date.year()),
};

NaiveDate::from_ymd_opt(year, month, day)
}

/// Interest payment periods
#[derive(Encode, Decode, Clone, PartialEq, Eq, TypeInfo, RuntimeDebug, MaxEncodedLen)]
pub enum InterestPayments {
Expand Down Expand Up @@ -161,30 +176,37 @@ fn monthly_dates(
end_date: NaiveDate,
reference_day: u32,
) -> Result<Vec<NaiveDate>, DispatchError> {
if start_date > end_date {
if start_date >= end_date {
return Err(DispatchError::Other("Cashflow must start before it ends"));
}

let mut dates = DateRule::monthly(start_date)
.with_end(end_date)
.with_rolling_day(reference_day)
.map_err(|_| DispatchError::Other("Paydown day must be less than 31 days"))?
.into_iter()
.collect::<Vec<_>>();

// We want to skip the first month if the date is before the starting date
if let Some(first) = dates.first() {
if *first <= start_date {
dates.remove(0);
}
if reference_day != REFERENCE_DAY_1 {
return Err(DispatchError::Other(
"Only day 1 as reference is supported by now",
));
}

// We want to add the end date as last date if the previous last date is before
// end date
if let Some(last) = dates.last() {
if *last < end_date {
dates.push(end_date);
let first_date =
next_month_with_day(start_date, REFERENCE_DAY_1).ok_or("must be a correct date, qed")?;

let mut dates = vec![min(first_date, end_date)];

loop {
let last = dates
.last()
.ok_or(DispatchError::Other("must be a last date, qed"))?;

let next =
next_month_with_day(*last, REFERENCE_DAY_1).ok_or("must be a correct date, qed")?;

if next >= end_date {
if *last < end_date {
dates.push(end_date);
}
break;
}

dates.push(next);
}

Ok(dates)
Expand All @@ -204,13 +226,16 @@ fn monthly_dates_intervals<Rate: FixedPointNumber>(
.enumerate()
.map(|(i, date)| {
let days = match i {
0 => (date - start_date).num_days(),
0 if last_index == 0 => end_date.day() - REFERENCE_DAY_1,
0 if start_date.day() == REFERENCE_DAY_1 => 30,
0 => (date - start_date).num_days().ensure_into()?,
n if n == last_index && end_date.day() == REFERENCE_DAY_1 => 30,
n if n == last_index => {
let prev_date = monthly_dates
.get(n.ensure_sub(1)?)
.ok_or(DispatchError::Other("n > 1. qed"))?;

(date - *prev_date).num_days()
(date - *prev_date).num_days().ensure_into()?
}
_ => 30,
};
Expand Down Expand Up @@ -276,50 +301,40 @@ mod tests {
(from_ymd(2022, 4, 20), Rate::from((19, 30))),
]
);

assert_ok!(
monthly_dates_intervals(from_ymd(2022, 1, 20), from_ymd(2022, 4, 20), 31),
vec![
(from_ymd(2022, 1, 31), Rate::from((11, 30))),
(from_ymd(2022, 2, 28), Rate::one()),
(from_ymd(2022, 3, 31), Rate::one()),
(from_ymd(2022, 4, 20), Rate::from((20, 30))),
]
);
}

#[test]
fn one_day() {
assert_err!(
monthly_dates(from_ymd(2022, 1, 20), from_ymd(2022, 1, 20), 31),
DispatchError::Arithmetic(ArithmeticError::Underflow),
monthly_dates(from_ymd(2022, 1, 20), from_ymd(2022, 1, 20), 1),
DispatchError::Other("Cashflow must start before it ends")
);
}

#[test]
fn same_month() {
assert_ok!(
monthly_dates_intervals(from_ymd(2022, 1, 10), from_ymd(2022, 1, 15), 20),
vec![(from_ymd(2022, 1, 15), Rate::from((5, 30)))]
fn unsupported_reference_day() {
assert_err!(
monthly_dates(from_ymd(2022, 1, 20), from_ymd(2022, 4, 20), 2),
DispatchError::Other("Only day 1 as reference is supported by now")
);
}

#[test]
fn start_and_end_same_day_as_reference_day() {
assert_ok!(
monthly_dates_intervals(from_ymd(2022, 1, 10), from_ymd(2022, 1, 20), 15),
monthly_dates_intervals(from_ymd(2022, 1, 1), from_ymd(2022, 3, 1), 1),
vec![
(from_ymd(2022, 1, 15), Rate::from((5, 30))),
(from_ymd(2022, 1, 20), Rate::from((5, 30))),
(from_ymd(2022, 2, 1), Rate::one()),
(from_ymd(2022, 3, 1), Rate::one()),
]
);
}

#[test]
fn same_day_as_paydown_day() {
fn same_month() {
assert_ok!(
monthly_dates_intervals(from_ymd(2022, 1, 15), from_ymd(2022, 3, 15), 15),
vec![
(from_ymd(2022, 2, 15), Rate::one()),
(from_ymd(2022, 3, 15), Rate::one()),
]
monthly_dates_intervals(from_ymd(2022, 1, 1), from_ymd(2022, 1, 15), 1),
vec![(from_ymd(2022, 1, 15), Rate::from((14, 30)))]
);
}
}
Expand Down
6 changes: 5 additions & 1 deletion runtime/altair/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ use sp_runtime::{
ApplyExtrinsicResult, DispatchError, DispatchResult, FixedI128, Perbill, Permill, Perquintill,
};
use sp_staking::currency_to_vote::U128CurrencyToVote;
use sp_std::{marker::PhantomData, prelude::*};
use sp_std::{marker::PhantomData, prelude::*, vec::Vec};
use sp_version::RuntimeVersion;
use staging_xcm_executor::XcmExecutor;
use static_assertions::const_assert;
Expand Down Expand Up @@ -2350,6 +2350,10 @@ impl_runtime_apis! {
) -> Result<Balance, DispatchError> {
Ok(runtime_common::update_nav_with_input(pool_id, input_prices)?.nav_aum)
}

fn cashflow(pool_id: PoolId, loan_id: LoanId) -> Result<Vec<(Seconds, Balance)>, DispatchError> {
Loans::cashflow(pool_id, loan_id)
}
}


Expand Down
6 changes: 5 additions & 1 deletion runtime/centrifuge/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ use sp_runtime::{
ApplyExtrinsicResult, FixedI128, Perbill, Permill, Perquintill,
};
use sp_staking::currency_to_vote::U128CurrencyToVote;
use sp_std::{marker::PhantomData, prelude::*};
use sp_std::{marker::PhantomData, prelude::*, vec::Vec};
use sp_version::RuntimeVersion;
use staging_xcm_executor::XcmExecutor;
use static_assertions::const_assert;
Expand Down Expand Up @@ -2398,6 +2398,10 @@ impl_runtime_apis! {
) -> Result<Balance, DispatchError> {
Ok(runtime_common::update_nav_with_input(pool_id, input_prices)?.nav_aum)
}

fn cashflow(pool_id: PoolId, loan_id: LoanId) -> Result<Vec<(Seconds, Balance)>, DispatchError> {
Loans::cashflow(pool_id, loan_id)
}
}

// Investment Runtime APIs
Expand Down
6 changes: 5 additions & 1 deletion runtime/development/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ use sp_runtime::{
ApplyExtrinsicResult, FixedI128, Perbill, Permill, Perquintill,
};
use sp_staking::currency_to_vote::U128CurrencyToVote;
use sp_std::{marker::PhantomData, prelude::*};
use sp_std::{marker::PhantomData, prelude::*, vec::Vec};
use sp_version::RuntimeVersion;
use staging_xcm_executor::XcmExecutor;
use static_assertions::const_assert;
Expand Down Expand Up @@ -2437,6 +2437,10 @@ impl_runtime_apis! {
) -> Result<Balance, DispatchError> {
Ok(runtime_common::update_nav_with_input(pool_id, input_prices)?.nav_aum)
}

fn cashflow(pool_id: PoolId, loan_id: LoanId) -> Result<Vec<(Seconds, Balance)>, DispatchError> {
Loans::cashflow(pool_id, loan_id)
}
}

// Investment Runtime APIs
Expand Down

0 comments on commit 85eb05f

Please sign in to comment.