hyperdrive_math/
base.rs

1use ethers::types::U256;
2use eyre::{eyre, Result};
3use fixedpointmath::{fixed, FixedPoint};
4
5use crate::State;
6
7impl State {
8    /// Calculates the number of share reserves that are not reserved by open
9    /// positions.
10    pub fn calculate_idle_share_reserves(&self) -> FixedPoint<U256> {
11        let long_exposure = self.long_exposure().div_up(self.vault_share_price());
12        match self.share_reserves() > long_exposure + self.minimum_share_reserves() {
13            true => self.share_reserves() - long_exposure - self.minimum_share_reserves(),
14            false => fixed!(0),
15        }
16    }
17
18    /// Calculates the pool's solvency.
19    ///
20    /// ```math
21    /// s = z - \tfrac{\text{exposure}}{c} - z_{\text{min}}
22    /// ```
23    pub fn calculate_solvency(&self) -> Result<FixedPoint<U256>> {
24        let share_reserves = self.share_reserves();
25        let long_exposure_shares = self.long_exposure() / self.vault_share_price();
26        let min_share_reserves = self.minimum_share_reserves();
27        if share_reserves > long_exposure_shares + min_share_reserves {
28            Ok((share_reserves - long_exposure_shares) - min_share_reserves)
29        } else {
30            return Err(eyre!(
31                "State is insolvent. Expected share_reserves={} > long_exposure_shares={} + min_share_reserves={}",
32                share_reserves, long_exposure_shares, min_share_reserves
33            ));
34        }
35    }
36
37    /// Calculates the number of base reserves that are not reserved by open
38    /// positions.
39    pub fn calculate_idle_share_reserves_in_base(&self) -> FixedPoint<U256> {
40        // NOTE: Round up to underestimate the pool's idle.
41        let long_exposure = self.long_exposure().div_up(self.vault_share_price());
42
43        // Calculate the idle base reserves.
44        let mut idle_shares_in_base = fixed!(0);
45        if self.share_reserves() > (long_exposure + self.minimum_share_reserves()) {
46            idle_shares_in_base =
47                (self.share_reserves() - long_exposure - self.minimum_share_reserves())
48                    * self.vault_share_price();
49        }
50
51        idle_shares_in_base
52    }
53
54    /// Given a scaled FixedPoint<U256> maturity time, calculate the normalized time
55    /// remaining with high precision.
56    pub fn calculate_scaled_normalized_time_remaining(
57        &self,
58        scaled_maturity_time: FixedPoint<U256>,
59        current_time: U256,
60    ) -> FixedPoint<U256> {
61        let scaled_latest_checkpoint =
62            FixedPoint::from(self.to_checkpoint(current_time)) * fixed!(1e36);
63        let scaled_position_duration = self.position_duration() * fixed!(1e36);
64        if scaled_maturity_time > scaled_latest_checkpoint {
65            // NOTE: Round down to underestimate the time remaining.
66            (scaled_maturity_time - scaled_latest_checkpoint).div_down(scaled_position_duration)
67        } else {
68            fixed!(0)
69        }
70    }
71}