Skip to main content

defituna_client/implementation/
vault.rs

1use crate::accounts::Vault;
2use crate::TunaError as ErrorCode;
3use defituna_core::borrow_curve::sample;
4use defituna_core::fixed::{mul_div_64, Rounding};
5use defituna_core::Fixed128;
6use std::fmt;
7
8pub const INTEREST_ACCRUE_MIN_INTERVAL: u64 = 60;
9
10impl Vault {
11    pub fn get_utilization(&self) -> f64 {
12        if self.deposited_funds > 0 {
13            self.borrowed_funds as f64 / self.deposited_funds as f64
14        } else {
15            1.0
16        }
17    }
18
19    /// Returns the sum of the first three terms of a Taylor expansion of e^r - 1, to approximate a
20    /// continuous compound interest rate.
21    pub fn compounded_interest_rate(r: f64) -> f64 {
22        let t1 = r;
23        let t2 = r * r / 2.0;
24        let t3 = t2 * r / 3.0;
25        t1 + t2 + t3
26    }
27
28    pub fn accrue_interest(&mut self, timestamp: u64) -> Result<(), ErrorCode> {
29        let elapsed_time_seconds = timestamp.checked_sub(self.last_update_timestamp).ok_or(ErrorCode::MathUnderflow)?;
30
31        // Nothing to accrue.
32        if self.borrowed_funds == 0 {
33            self.last_update_timestamp = timestamp;
34            return Ok(());
35        }
36
37        // Don't accrue interest too often to avoid losing precision.
38        if elapsed_time_seconds < INTEREST_ACCRUE_MIN_INTERVAL {
39            return Ok(());
40        }
41        // Compute interest based on utilization.
42        let utilization = self.get_utilization();
43        let interest_rate_multiplier = sample(utilization);
44        let interest_rate = Fixed128::from_bits(self.interest_rate as u128).to_num::<f64>() * interest_rate_multiplier;
45
46        let interest = Self::compounded_interest_rate(interest_rate * elapsed_time_seconds as f64);
47        let interest_amount = (interest * self.borrowed_funds as f64) as u64;
48
49        self.borrowed_funds = self.borrowed_funds.checked_add(interest_amount).ok_or(ErrorCode::MathOverflow)?;
50        self.deposited_funds = self.deposited_funds.checked_add(interest_amount).ok_or(ErrorCode::MathOverflow)?;
51
52        self.last_update_timestamp = timestamp;
53
54        Ok(())
55    }
56
57    pub fn funds_to_shares(funds: u64, total_funds: u64, total_shares: u64, rounding: Rounding) -> Result<u64, ErrorCode> {
58        if total_funds > 0 {
59            Ok(mul_div_64(funds, total_shares, total_funds, rounding).map_err(|_| ErrorCode::MathOverflow)?)
60        } else {
61            Ok(funds)
62        }
63    }
64
65    pub fn shares_to_funds(shares: u64, total_funds: u64, total_shares: u64, rounding: Rounding) -> Result<u64, ErrorCode> {
66        if total_shares > 0 {
67            Ok(mul_div_64(shares, total_funds, total_shares, rounding).map_err(|_| ErrorCode::MathOverflow)?)
68        } else {
69            Ok(shares)
70        }
71    }
72
73    pub fn calculate_deposited_shares(&self, funds: u64, rounding: Rounding) -> Result<u64, ErrorCode> {
74        Self::funds_to_shares(funds, self.deposited_funds, self.deposited_shares, rounding)
75    }
76
77    pub fn calculate_deposited_funds(&self, shares: u64, rounding: Rounding) -> Result<u64, ErrorCode> {
78        Self::shares_to_funds(shares, self.deposited_funds, self.deposited_shares, rounding)
79    }
80
81    pub fn calculate_borrowed_shares(&self, funds: u64, rounding: Rounding) -> Result<u64, ErrorCode> {
82        Self::funds_to_shares(funds, self.borrowed_funds, self.borrowed_shares, rounding)
83    }
84
85    pub fn calculate_borrowed_funds(&self, shares: u64, rounding: Rounding) -> Result<u64, ErrorCode> {
86        Self::shares_to_funds(shares, self.borrowed_funds, self.borrowed_shares, rounding)
87    }
88}
89
90impl fmt::Display for Vault {
91    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92        write!(
93            f,
94            "mint={}; borrowed/deposited funds = {} / {}; bad_debt = {}",
95            self.mint.to_string(),
96            self.borrowed_funds,
97            self.deposited_funds,
98            self.unpaid_debt_shares
99        )
100    }
101}