Skip to content

Commit

Permalink
correct get_time_stretch formula
Browse files Browse the repository at this point in the history
the rust get_time_stretch formulat was an incorrect port from solidity,
and the test was not sufficiently complicated to catch it.

update CI to not run solidity when rust updates

bugfix time stretch

seconds in a day

put these back

rate no longer needs to be mutable
  • Loading branch information
dpaiton committed Jan 18, 2024
1 parent 2a8c81f commit 053d34a
Showing 1 changed file with 18 additions and 17 deletions.
35 changes: 18 additions & 17 deletions crates/hyperdrive-math/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ use ethers::types::{I256, U256};
use fixed_point::FixedPoint;
use fixed_point_macros::{fixed, uint256};

pub fn get_time_stretch(mut rate: FixedPoint, position_duration: FixedPoint) -> FixedPoint {
pub fn get_time_stretch(rate: FixedPoint, position_duration: FixedPoint) -> FixedPoint {
let seconds_in_a_year = FixedPoint::from(U256::from(60 * 60 * 24 * 365));
let annualized_time = position_duration / seconds_in_a_year;
rate = (U256::from(rate) * uint256!(100)).into();
// Calculate the benchmark time stretch. This time stretch is tuned for
// a position duration of 1 year.
let time_stretch = fixed!(1e18) / (fixed!(5.24592e18) / (fixed!(0.04665e18) * rate));
let time_stretch = fixed!(5.24592e18)
/ (fixed!(0.04665e18) * FixedPoint::from(U256::from(rate) * uint256!(100)));
let time_stretch = fixed!(1e18) / time_stretch;
// if the position duration is 1 year, we can return the benchmark
if annualized_time == fixed!(1e18) {
if position_duration == seconds_in_a_year {
return time_stretch;
}

Expand All @@ -33,6 +33,8 @@ pub fn get_time_stretch(mut rate: FixedPoint, position_duration: FixedPoint) ->
// p = ratio ** timeStretch
// =>
// timeStretch = ln(p) / ln(ratio)
let target_spot_price =
fixed!(1e18) / (fixed!(1e18) + rate.mul_div_down(position_duration, seconds_in_a_year));
let benchmark_reserve_ratio = fixed!(1e18)
/ calculate_initial_bond_reserves(
fixed!(1e18),
Expand All @@ -41,16 +43,12 @@ pub fn get_time_stretch(mut rate: FixedPoint, position_duration: FixedPoint) ->
seconds_in_a_year,
time_stretch,
);
let target_spot_price = fixed!(1e18) / (fixed!(1e18) * annualized_time);
// target spot price and benchmark reserve ratio will have negative ln,
// but since we are dividing them we can cast to positive before converting types
// TODO: implement FixedPoint `neg` pub fn to support "-"
let new_time_stretch = U256::from(FixedPoint::from(-FixedPoint::ln(I256::from(
target_spot_price,
)))) / U256::from(FixedPoint::from(-FixedPoint::ln(I256::from(
benchmark_reserve_ratio,
))));
new_time_stretch.into()
let new_time_stretch = FixedPoint::from(-FixedPoint::ln(I256::from(target_spot_price)))
/ FixedPoint::from(-FixedPoint::ln(I256::from(benchmark_reserve_ratio)));
new_time_stretch
}

pub fn get_effective_share_reserves(
Expand Down Expand Up @@ -117,15 +115,18 @@ mod tests {
let chain = TestChainWithMocks::new(1).await?;
let mock = chain.mock_hyperdrive_math();
// Fuzz the rust and solidity implementations against each other.
let apr = fixed!(0.05e18); // 5%
let seconds_in_a_year = U256::from(60 * 60 * 24 * 365);
let seconds_in_ten_years = U256::from(10 * 60 * 60 * 24 * 365);
let seconds_in_a_day = U256::from(60 * 60 * 24);
let mut rng = thread_rng();
for _ in 0..*FAST_FUZZ_RUNS {
// Get the current state of the mock contract
let state = rng.gen::<State>();
let actual_t = get_time_stretch(apr, seconds_in_a_year.into());
let position_duration = rng.gen_range(
FixedPoint::from(seconds_in_a_day)..=FixedPoint::from(seconds_in_ten_years),
);
let apr = rng.gen_range(fixed!(0.001e18)..=fixed!(10.0e18));
let actual_t = get_time_stretch(apr, position_duration);
match mock
.calculate_time_stretch(apr.into(), seconds_in_a_year)
.calculate_time_stretch(apr.into(), position_duration.into())
.call()
.await
{
Expand Down

0 comments on commit 053d34a

Please sign in to comment.