Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

time stretch bugfix #727

Merged
merged 1 commit into from
Jan 18, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading