1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
use cfg_traits::{
	interest::{InterestRate, RateCollection},
	Seconds, TimeAsSecs,
};
use cfg_types::adjustments::Adjustment;
use frame_support::{
	ensure,
	pallet_prelude::{DispatchResult, RuntimeDebug},
	RuntimeDebugNoBound,
};
use parity_scale_codec::{Decode, Encode, MaxEncodedLen};
use scale_info::TypeInfo;
use sp_arithmetic::traits::Saturating;
use sp_runtime::{
	traits::{EnsureFixedPointNumber, EnsureSub},
	DispatchError,
};

use crate::{
	entities::{changes::InternalMutation, interest::ActiveInterestRate},
	pallet::{Config, Error},
	types::{
		valuation::{DiscountedCashFlow, ValuationMethod},
		CreateLoanError, MutationError,
	},
};

/// Diferents methods of how to compute the amount can be borrowed
#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)]
pub enum MaxBorrowAmount<Rate> {
	/// Max borrow amount computation using the total borrowed
	UpToTotalBorrowed { advance_rate: Rate },

	/// Max borrow amount computation using the outstanding debt
	UpToOutstandingDebt { advance_rate: Rate },
}

/// Internal pricing method
#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebugNoBound, TypeInfo, MaxEncodedLen)]
#[scale_info(skip_type_params(T))]
pub struct InternalPricing<T: Config> {
	/// Value of the collateral used for this loan
	pub collateral_value: T::Balance,

	/// Valuation method of this loan
	pub valuation_method: ValuationMethod<T::Rate>,

	/// How much can be borrowed
	pub max_borrow_amount: MaxBorrowAmount<T::Rate>,
}

impl<T: Config> InternalPricing<T> {
	pub fn validate(&self) -> DispatchResult {
		ensure!(
			self.valuation_method.is_valid(),
			Error::<T>::from(CreateLoanError::InvalidValuationMethod)
		);

		Ok(())
	}
}

/// Internal pricing method with extra attributes for active loans
#[derive(Encode, Decode, Clone, PartialEq, Eq, TypeInfo, RuntimeDebugNoBound, MaxEncodedLen)]
#[scale_info(skip_type_params(T))]
pub struct InternalActivePricing<T: Config> {
	/// Basic internal pricing info
	info: InternalPricing<T>,

	/// Current interest rate
	pub interest: ActiveInterestRate<T>,
}

impl<T: Config> InternalActivePricing<T> {
	pub fn activate(
		info: InternalPricing<T>,
		interest_rate: InterestRate<T::Rate>,
	) -> Result<Self, DispatchError> {
		Ok(Self {
			info,
			interest: ActiveInterestRate::activate(interest_rate)?,
		})
	}

	pub fn deactivate(self) -> Result<(InternalPricing<T>, InterestRate<T::Rate>), DispatchError> {
		Ok((self.info, self.interest.deactivate()?))
	}

	fn compute_present_value(
		&self,
		debt: T::Balance,
		origination_date: Seconds,
		maturity_date: Option<Seconds>,
	) -> Result<T::Balance, DispatchError> {
		match &self.info.valuation_method {
			ValuationMethod::DiscountedCashFlow(dcf) => {
				let maturity_date =
					maturity_date.ok_or(Error::<T>::MaturityDateNeededForValuationMethod)?;

				let now = T::Time::now();
				Ok(dcf.compute_present_value(
					debt,
					now,
					self.interest.rate(),
					maturity_date,
					origination_date,
				)?)
			}
			ValuationMethod::OutstandingDebt | ValuationMethod::Cash => Ok(debt),
		}
	}

	pub fn present_value(
		&self,
		origination_date: Seconds,
		maturity_date: Option<Seconds>,
	) -> Result<T::Balance, DispatchError> {
		let debt = self.interest.current_debt()?;
		self.compute_present_value(debt, origination_date, maturity_date)
	}

	pub fn present_value_cached<Rates>(
		&self,
		cache: &Rates,
		origination_date: Seconds,
		maturity_date: Option<Seconds>,
	) -> Result<T::Balance, DispatchError>
	where
		Rates: RateCollection<T::Rate, T::Balance, T::Balance>,
	{
		let debt = self.interest.current_debt_cached(cache)?;
		self.compute_present_value(debt, origination_date, maturity_date)
	}

	pub fn outstanding_interest(
		&self,
		outstanding_principal: T::Balance,
	) -> Result<T::Balance, DispatchError> {
		let debt = self.interest.current_debt()?;
		Ok(debt.ensure_sub(outstanding_principal)?)
	}

	pub fn max_borrow_amount(
		&self,
		total_borrowed: T::Balance,
	) -> Result<T::Balance, DispatchError> {
		Ok(match self.info.max_borrow_amount {
			MaxBorrowAmount::UpToTotalBorrowed { advance_rate } => advance_rate
				.ensure_mul_int(self.info.collateral_value)?
				.saturating_sub(total_borrowed),
			MaxBorrowAmount::UpToOutstandingDebt { advance_rate } => advance_rate
				.ensure_mul_int(self.info.collateral_value)?
				.saturating_sub(self.interest.current_debt()?),
		})
	}

	pub fn adjust(&mut self, adjustment: Adjustment<T::Balance>) -> DispatchResult {
		self.interest.adjust_debt(adjustment)
	}

	fn mut_dcf(&mut self) -> Result<&mut DiscountedCashFlow<T::Rate>, DispatchError> {
		match &mut self.info.valuation_method {
			ValuationMethod::DiscountedCashFlow(dcf) => Ok(dcf),
			_ => Err(Error::<T>::from(MutationError::DiscountedCashFlowExpected).into()),
		}
	}

	pub fn mutate_with(&mut self, mutation: InternalMutation<T::Rate>) -> DispatchResult {
		match mutation {
			InternalMutation::ValuationMethod(method) => self.info.valuation_method = method,
			InternalMutation::ProbabilityOfDefault(rate) => {
				self.mut_dcf()?.probability_of_default = rate;
			}
			InternalMutation::LossGivenDefault(rate) => self.mut_dcf()?.loss_given_default = rate,
			InternalMutation::DiscountRate(rate) => self.mut_dcf()?.discount_rate = rate,
		}

		self.info.validate()
	}
}