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
|
// 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 now_ts_i80f48 = I80F48::from_num(now_ts);
|
||||||
let diff_ts = I80F48::from_num(now_ts - some_bank.index_last_updated);
|
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.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(
|
let new_avg_utilization = some_bank.compute_new_avg_utilization(
|
||||||
indexed_total_deposits,
|
indexed_total_deposits,
|
||||||
indexed_total_borrows,
|
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.cached_indexed_total_borrows = indexed_total_borrows;
|
||||||
|
|
||||||
bank.index_last_updated = now_ts;
|
bank.index_last_updated = now_ts;
|
||||||
bank.charge_loan_fee(diff_ts);
|
|
||||||
|
|
||||||
bank.deposit_index = deposit_index;
|
bank.deposit_index = deposit_index;
|
||||||
bank.borrow_index = borrow_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(
|
pub fn compute_index(
|
||||||
&self,
|
&self,
|
||||||
indexed_total_deposits: I80F48,
|
indexed_total_deposits: I80F48,
|
||||||
indexed_total_borrows: I80F48,
|
indexed_total_borrows: I80F48,
|
||||||
diff_ts: I80F48,
|
diff_ts: I80F48,
|
||||||
) -> Result<(I80F48, I80F48)> {
|
) -> Result<(I80F48, I80F48, I80F48)> {
|
||||||
// compute index based on utilization
|
// compute index based on utilization
|
||||||
let native_total_deposits = cm!(self.deposit_index * indexed_total_deposits);
|
let native_total_deposits = cm!(self.deposit_index * indexed_total_deposits);
|
||||||
let native_total_borrows = cm!(self.borrow_index * indexed_total_borrows);
|
let native_total_borrows = cm!(self.borrow_index * indexed_total_borrows);
|
||||||
|
@ -419,21 +409,31 @@ impl Bank {
|
||||||
cm!(native_total_borrows / native_total_deposits)
|
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);
|
// We want to grant depositors a rate that exactly matches the amount that is
|
||||||
let deposit_interest = cm!(borrow_interest * instantaneous_utilization);
|
// 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 {
|
// The loan fee rate is not distributed to depositors.
|
||||||
return Ok((self.deposit_index, self.borrow_index));
|
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 =
|
let borrow_index = cm!(
|
||||||
cm!((self.borrow_index * borrow_interest) / YEAR_I80F48 + self.borrow_index);
|
(self.borrow_index * borrow_rate_with_fees * diff_ts) / YEAR_I80F48 + self.borrow_index
|
||||||
|
);
|
||||||
let deposit_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
|
/// returns the current interest rate in APR
|
||||||
|
|
|
@ -107,9 +107,11 @@ async fn test_token_update_index_and_rate() -> Result<(), TransportError> {
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.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;
|
solana.advance_clock().await;
|
||||||
|
let time_after = solana.get_clock().await.unix_timestamp;
|
||||||
|
|
||||||
send_tx(
|
send_tx(
|
||||||
solana,
|
solana,
|
||||||
|
@ -120,16 +122,38 @@ async fn test_token_update_index_and_rate() -> Result<(), TransportError> {
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let bank_after_update_index_and_rate = solana.get_account::<Bank>(tokens[0].bank).await;
|
let bank_after = solana.get_account::<Bank>(tokens[0].bank).await;
|
||||||
dbg!(bank_after_update_index_and_rate);
|
dbg!(bank_after);
|
||||||
dbg!(bank_after_update_index_and_rate);
|
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!(
|
assert!(
|
||||||
bank_before_update_index_and_rate.deposit_index
|
(bank_after.native_borrows().to_num::<f64>()
|
||||||
< bank_after_update_index_and_rate.deposit_index
|
- bank_before.native_borrows().to_num::<f64>()
|
||||||
|
- interest_change)
|
||||||
|
.abs()
|
||||||
|
< 0.1
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
bank_before_update_index_and_rate.borrow_index
|
(bank_after.native_deposits().to_num::<f64>()
|
||||||
< bank_after_update_index_and_rate.borrow_index
|
- 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(())
|
Ok(())
|
||||||
|
|
Loading…
Reference in New Issue