-
Notifications
You must be signed in to change notification settings - Fork 91
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: borrow & repay method using pricing-based principal #1455
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,14 +10,41 @@ use frame_support::{self, ensure, RuntimeDebug, RuntimeDebugNoBound}; | |
use scale_info::TypeInfo; | ||
use sp_runtime::{ | ||
traits::{EnsureAdd, EnsureFixedPointNumber, EnsureSub, Zero}, | ||
DispatchError, DispatchResult, FixedPointNumber, | ||
ArithmeticError, DispatchError, DispatchResult, FixedPointNumber, | ||
}; | ||
|
||
use crate::{ | ||
entities::interest::ActiveInterestRate, | ||
pallet::{Config, Error, PriceOf}, | ||
}; | ||
|
||
#[derive(Encode, Decode, Clone, PartialEq, Eq, TypeInfo, RuntimeDebugNoBound, MaxEncodedLen)] | ||
#[scale_info(skip_type_params(T))] | ||
pub struct ExternalAmount<T: Config> { | ||
pub quantity: T::Rate, | ||
pub settlement_price: T::Balance, | ||
} | ||
|
||
impl<T: Config> ExternalAmount<T> { | ||
pub fn new(quantity: T::Rate, price: T::Balance) -> Self { | ||
Self { | ||
quantity, | ||
settlement_price: price, | ||
} | ||
} | ||
|
||
pub fn empty() -> Self { | ||
Self { | ||
quantity: T::Rate::zero(), | ||
settlement_price: T::Balance::zero(), | ||
} | ||
} | ||
|
||
pub fn balance(&self) -> Result<T::Balance, ArithmeticError> { | ||
self.quantity.ensure_mul_int(self.settlement_price) | ||
} | ||
} | ||
|
||
/// Define the max borrow amount of a loan | ||
#[derive(Encode, Decode, Clone, PartialEq, Eq, TypeInfo, RuntimeDebug, MaxEncodedLen)] | ||
pub enum MaxBorrowAmount<Quantity> { | ||
|
@@ -46,7 +73,7 @@ impl<T: Config> ExternalPricing<T> { | |
pub fn validate(&self) -> DispatchResult { | ||
if let MaxBorrowAmount::Quantity(quantity) = self.max_borrow_amount { | ||
ensure!( | ||
quantity.frac().is_zero() && quantity > T::Rate::zero(), | ||
quantity.frac().is_zero() && quantity >= T::Rate::zero(), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. WHy the change here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can create a loan that can not be borrowed only to repay later unchecked amounts. |
||
Error::<T>::AmountNotNaturalNumber | ||
) | ||
} | ||
|
@@ -99,11 +126,6 @@ impl<T: Config> ExternalActivePricing<T> { | |
Ok(T::PriceRegistry::get(&self.info.price_id)?.1) | ||
} | ||
|
||
pub fn outstanding_amount(&self) -> Result<T::Balance, DispatchError> { | ||
let price = self.current_price()?; | ||
Ok(self.outstanding_quantity.ensure_mul_int(price)?) | ||
} | ||
|
||
pub fn current_interest(&self) -> Result<T::Balance, DispatchError> { | ||
let outstanding_notional = self | ||
.outstanding_quantity | ||
|
@@ -114,7 +136,8 @@ impl<T: Config> ExternalActivePricing<T> { | |
} | ||
|
||
pub fn present_value(&self) -> Result<T::Balance, DispatchError> { | ||
self.outstanding_amount() | ||
let price = self.current_price()?; | ||
Ok(self.outstanding_quantity.ensure_mul_int(price)?) | ||
} | ||
|
||
pub fn present_value_cached<Prices>(&self, cache: &Prices) -> Result<T::Balance, DispatchError> | ||
|
@@ -127,42 +150,42 @@ impl<T: Config> ExternalActivePricing<T> { | |
|
||
pub fn max_borrow_amount( | ||
&self, | ||
desired_amount: T::Balance, | ||
amount: ExternalAmount<T>, | ||
) -> Result<T::Balance, DispatchError> { | ||
match self.info.max_borrow_amount { | ||
MaxBorrowAmount::Quantity(quantity) => { | ||
let price = self.current_price()?; | ||
let available = quantity.ensure_sub(self.outstanding_quantity)?; | ||
Ok(available.ensure_mul_int(price)?) | ||
Ok(available.ensure_mul_int(amount.settlement_price)?) | ||
} | ||
MaxBorrowAmount::NoLimit => Ok(desired_amount), | ||
MaxBorrowAmount::NoLimit => Ok(amount.balance()?), | ||
} | ||
} | ||
|
||
pub fn max_repay_principal( | ||
&self, | ||
amount: ExternalAmount<T>, | ||
) -> Result<T::Balance, DispatchError> { | ||
Ok(self | ||
.outstanding_quantity | ||
.ensure_mul_int(amount.settlement_price)?) | ||
} | ||
|
||
pub fn adjust( | ||
&mut self, | ||
principal_adj: Adjustment<T::Balance>, | ||
quantity_adj: Adjustment<T::Rate>, | ||
interest: T::Balance, | ||
) -> DispatchResult { | ||
let quantity_adj = principal_adj.try_map(|principal| -> Result<_, DispatchError> { | ||
let price = self.current_price()?; | ||
|
||
let quantity = T::Rate::ensure_from_rational(principal, price)?; | ||
self.outstanding_quantity = quantity_adj.ensure_add(self.outstanding_quantity)?; | ||
|
||
let interest_adj = quantity_adj.try_map(|quantity| -> Result<_, DispatchError> { | ||
ensure!( | ||
quantity.frac().is_zero(), | ||
Error::<T>::AmountNotMultipleOfPrice | ||
quantity.frac().is_zero() && quantity >= T::Rate::zero(), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Again, why the change to allow zero There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's totally fine to repay with 0 of quantity if you want, for example, to pay only interest. |
||
Error::<T>::AmountNotNaturalNumber | ||
); | ||
|
||
Ok(quantity) | ||
})?; | ||
|
||
self.outstanding_quantity = quantity_adj.ensure_add(self.outstanding_quantity)?; | ||
|
||
let interest_adj = quantity_adj.try_map(|quantity| { | ||
quantity | ||
Ok(quantity | ||
.ensure_mul_int(self.info.notional)? | ||
.ensure_add(interest) | ||
.ensure_add(interest)?) | ||
})?; | ||
|
||
self.interest.adjust_debt(interest_adj)?; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit pick. Not sure if
settlement_price
is the right tearm for when repaying? cc @denniswellThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As a note:
ExternalAmount
is used for both: borrow & repay. So if changed there, it's changed for both.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From my understanding of what this means, I think the name fits with its purpose.