Bank: fix collection of loan fee
Instead of changing indexed_borrows, change borrow_index!
This commit is contained in:
parent
4c65204c19
commit
e8bbfbef83
|
@ -91,14 +91,16 @@ pub fn token_update_index_and_rate(ctx: Context<TokenUpdateIndexAndRate>) -> Res
|
|||
|
||||
// compute and set latest index and average utilization on each bank
|
||||
{
|
||||
let some_bank = ctx.remaining_accounts[0].load::<Bank>()?;
|
||||
let mut some_bank = ctx.remaining_accounts[0].load_mut::<Bank>()?;
|
||||
|
||||
let now_ts_i80f48 = I80F48::from_num(now_ts);
|
||||
let diff_ts = I80F48::from_num(now_ts - some_bank.index_last_updated);
|
||||
|
||||
let (deposit_index, borrow_index) =
|
||||
let (deposit_index, borrow_index, borrow_fees) =
|
||||
some_bank.compute_index(indexed_total_deposits, indexed_total_borrows, diff_ts)?;
|
||||
|
||||
some_bank.collected_fees_native = cm!(some_bank.collected_fees_native + borrow_fees);
|
||||
|
||||
let new_avg_utilization = some_bank.compute_new_avg_utilization(
|
||||
indexed_total_deposits,
|
||||
indexed_total_borrows,
|
||||
|
@ -135,7 +137,6 @@ pub fn token_update_index_and_rate(ctx: Context<TokenUpdateIndexAndRate>) -> Res
|
|||
bank.cached_indexed_total_borrows = indexed_total_borrows;
|
||||
|
||||
bank.index_last_updated = now_ts;
|
||||
bank.charge_loan_fee(diff_ts);
|
||||
|
||||
bank.deposit_index = deposit_index;
|
||||
bank.borrow_index = borrow_index;
|
||||
|
|
|
@ -392,22 +392,12 @@ impl Bank {
|
|||
}
|
||||
}
|
||||
|
||||
// Borrows continously expose insurance fund to risk, collect fees from borrowers
|
||||
pub fn charge_loan_fee(&mut self, diff_ts: I80F48) {
|
||||
let native_borrows_old = self.native_borrows();
|
||||
self.indexed_borrows =
|
||||
cm!((self.indexed_borrows
|
||||
* (I80F48::ONE + self.loan_fee_rate * (diff_ts / YEAR_I80F48))));
|
||||
self.collected_fees_native =
|
||||
cm!(self.collected_fees_native + self.native_borrows() - native_borrows_old);
|
||||
}
|
||||
|
||||
pub fn compute_index(
|
||||
&self,
|
||||
indexed_total_deposits: I80F48,
|
||||
indexed_total_borrows: I80F48,
|
||||
diff_ts: I80F48,
|
||||
) -> Result<(I80F48, I80F48)> {
|
||||
) -> Result<(I80F48, I80F48, I80F48)> {
|
||||
// compute index based on utilization
|
||||
let native_total_deposits = cm!(self.deposit_index * indexed_total_deposits);
|
||||
let native_total_borrows = cm!(self.borrow_index * indexed_total_borrows);
|
||||
|
@ -419,21 +409,31 @@ impl Bank {
|
|||
cm!(native_total_borrows / native_total_deposits)
|
||||
};
|
||||
|
||||
let borrow_interest_rate = self.compute_interest_rate(instantaneous_utilization);
|
||||
let borrow_rate = self.compute_interest_rate(instantaneous_utilization);
|
||||
|
||||
let borrow_interest = cm!(borrow_interest_rate * diff_ts);
|
||||
let deposit_interest = cm!(borrow_interest * instantaneous_utilization);
|
||||
// We want to grant depositors a rate that exactly matches the amount that is
|
||||
// taken from borrowers. That means:
|
||||
// (new_deposit_index - old_deposit_index) * indexed_deposits
|
||||
// = (new_borrow_index - old_borrow_index) * indexed_borrows
|
||||
// with
|
||||
// new_deposit_index = old_deposit_index * (1 + deposit_rate) and
|
||||
// new_borrow_index = old_borrow_index * (1 * borrow_rate)
|
||||
// we have
|
||||
// deposit_rate = borrow_rate * (old_borrow_index * indexed_borrows) / (old_deposit_index * indexed_deposits)
|
||||
// and the latter factor is exactly instantaneous_utilization.
|
||||
let deposit_rate = cm!(borrow_rate * instantaneous_utilization);
|
||||
|
||||
if borrow_interest <= I80F48::ZERO || deposit_interest <= I80F48::ZERO {
|
||||
return Ok((self.deposit_index, self.borrow_index));
|
||||
}
|
||||
// The loan fee rate is not distributed to depositors.
|
||||
let borrow_rate_with_fees = cm!(borrow_rate + self.loan_fee_rate);
|
||||
let borrow_fees = native_total_borrows * self.loan_fee_rate * diff_ts / YEAR_I80F48;
|
||||
|
||||
let borrow_index =
|
||||
cm!((self.borrow_index * borrow_interest) / YEAR_I80F48 + self.borrow_index);
|
||||
let borrow_index = cm!(
|
||||
(self.borrow_index * borrow_rate_with_fees * diff_ts) / YEAR_I80F48 + self.borrow_index
|
||||
);
|
||||
let deposit_index =
|
||||
cm!((self.deposit_index * deposit_interest) / YEAR_I80F48 + self.deposit_index);
|
||||
cm!((self.deposit_index * deposit_rate * diff_ts) / YEAR_I80F48 + self.deposit_index);
|
||||
|
||||
Ok((deposit_index, borrow_index))
|
||||
Ok((deposit_index, borrow_index, borrow_fees))
|
||||
}
|
||||
|
||||
/// returns the current interest rate in APR
|
||||
|
|
|
@ -107,9 +107,11 @@ async fn test_token_update_index_and_rate() -> Result<(), TransportError> {
|
|||
.await
|
||||
.unwrap();
|
||||
|
||||
let bank_before_update_index_and_rate = solana.get_account::<Bank>(tokens[0].bank).await;
|
||||
let bank_before = solana.get_account::<Bank>(tokens[0].bank).await;
|
||||
|
||||
let time_before = solana.get_clock().await.unix_timestamp;
|
||||
solana.advance_clock().await;
|
||||
let time_after = solana.get_clock().await.unix_timestamp;
|
||||
|
||||
send_tx(
|
||||
solana,
|
||||
|
@ -120,16 +122,38 @@ async fn test_token_update_index_and_rate() -> Result<(), TransportError> {
|
|||
.await
|
||||
.unwrap();
|
||||
|
||||
let bank_after_update_index_and_rate = solana.get_account::<Bank>(tokens[0].bank).await;
|
||||
dbg!(bank_after_update_index_and_rate);
|
||||
dbg!(bank_after_update_index_and_rate);
|
||||
let bank_after = solana.get_account::<Bank>(tokens[0].bank).await;
|
||||
dbg!(bank_after);
|
||||
dbg!(bank_after);
|
||||
|
||||
let utilization = 0.5; // 10000 deposits / 5000 borrows
|
||||
let diff_ts = (time_after - time_before) as f64;
|
||||
let year = 31536000.0;
|
||||
let loan_fee_rate = 0.0005;
|
||||
let dynamic_rate = 0.07 + 0.9 * (utilization - 0.4) / (0.8 - 0.4);
|
||||
let interest_change = 5000.0 * (dynamic_rate + loan_fee_rate) * diff_ts / year;
|
||||
let fee_change = 5000.0 * loan_fee_rate * diff_ts / year;
|
||||
|
||||
assert!(
|
||||
bank_before_update_index_and_rate.deposit_index
|
||||
< bank_after_update_index_and_rate.deposit_index
|
||||
(bank_after.native_borrows().to_num::<f64>()
|
||||
- bank_before.native_borrows().to_num::<f64>()
|
||||
- interest_change)
|
||||
.abs()
|
||||
< 0.1
|
||||
);
|
||||
assert!(
|
||||
bank_before_update_index_and_rate.borrow_index
|
||||
< bank_after_update_index_and_rate.borrow_index
|
||||
(bank_after.native_deposits().to_num::<f64>()
|
||||
- bank_before.native_deposits().to_num::<f64>()
|
||||
- interest_change)
|
||||
.abs()
|
||||
< 0.1
|
||||
);
|
||||
assert!(
|
||||
(bank_after.collected_fees_native.to_num::<f64>()
|
||||
- bank_before.collected_fees_native.to_num::<f64>()
|
||||
- fee_change)
|
||||
.abs()
|
||||
< 0.1
|
||||
);
|
||||
|
||||
Ok(())
|
||||
|
|
Loading…
Reference in New Issue