Skip to content
Merged
Show file tree
Hide file tree
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
3 changes: 1 addition & 2 deletions test/units/hyperdrive/CloseLongTest.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,7 @@ contract CloseLongTest is HyperdriveTest {
) internal {
uint256 checkpointTime = maturityTime - POSITION_DURATION;

// Verify that all of Bob's bonds were burned and that the base proceeds
// are as expected.
// Verify that all of Bob's bonds were burned.
assertEq(
hyperdrive.balanceOf(
AssetId.encodeAssetId(AssetId.AssetIdPrefix.Long, maturityTime),
Expand Down
155 changes: 54 additions & 101 deletions test/units/hyperdrive/CloseShortTest.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -74,60 +74,24 @@ contract CloseShortTest is HyperdriveTest {
// Purchase some bonds.
uint256 bondAmount = 10e18;
(uint256 maturityTime, uint256 basePaid) = openShort(bob, bondAmount);
uint256 checkpointTime = maturityTime - POSITION_DURATION;

// Get the reserves before closing the long.
PoolInfo memory poolInfoBefore = getPoolInfo();

// Immediately close the bonds.
uint256 baseProceeds = closeShort(bob, maturityTime, bondAmount);

// Verify that all of Bob's bonds were burned and that he doesn't end
// up with more base than he started with.
assertEq(
hyperdrive.balanceOf(
AssetId.encodeAssetId(
AssetId.AssetIdPrefix.Short,
maturityTime
),
bob
),
0
);
// Verify that Bob doesn't end up with more base than he started with.
assertGe(basePaid, baseProceeds);

// Verify that the reserves were updated correctly. Since this trade
// happens at the beginning of the term, the bond reserves should be
// increased by the full amount.
PoolInfo memory poolInfoAfter = getPoolInfo();
assertApproxEqAbs(
poolInfoAfter.shareReserves,
poolInfoBefore.shareReserves +
(bondAmount + baseProceeds - basePaid).divDown(
poolInfoBefore.sharePrice
),
1e18
);
assertEq(
poolInfoAfter.bondReserves,
poolInfoBefore.bondReserves - bondAmount
);
assertEq(poolInfoAfter.lpTotalSupply, poolInfoBefore.lpTotalSupply);
assertEq(poolInfoAfter.sharePrice, poolInfoBefore.sharePrice);
assertEq(
poolInfoAfter.longsOutstanding,
poolInfoBefore.longsOutstanding
);
assertEq(poolInfoAfter.longAverageMaturityTime, 0);
assertEq(poolInfoAfter.longBaseVolume, 0);
assertEq(hyperdrive.longBaseVolumeCheckpoints(checkpointTime), 0);
assertEq(
poolInfoAfter.shortsOutstanding,
poolInfoBefore.shortsOutstanding - bondAmount
// Verify that the close long updates were correct.
verifyCloseShort(
poolInfoBefore,
basePaid,
baseProceeds,
bondAmount,
maturityTime
);
assertEq(poolInfoAfter.shortAverageMaturityTime, 0);
assertEq(poolInfoAfter.shortBaseVolume, 0);
assertEq(hyperdrive.shortBaseVolumeCheckpoints(checkpointTime), 0);
}

function test_close_short_immediately_with_small_amount() external {
Expand All @@ -140,63 +104,26 @@ contract CloseShortTest is HyperdriveTest {
// Short some bonds.
uint256 bondAmount = .1e18;
(uint256 maturityTime, uint256 basePaid) = openShort(bob, bondAmount);
uint256 checkpointTime = maturityTime - POSITION_DURATION;

// Get the reserves before closing the long.
PoolInfo memory poolInfoBefore = getPoolInfo();

// Immediately close the bonds.
uint256 baseProceeds = closeShort(bob, maturityTime, bondAmount);

// Verify that all of Bob's bonds were burned and that bob doesn't end
// up with more base than he started with.
assertEq(
hyperdrive.balanceOf(
AssetId.encodeAssetId(
AssetId.AssetIdPrefix.Short,
maturityTime
),
bob
),
0
);
// Verify that Bob doesn't end up with more base than he started with.
assertGe(basePaid, baseProceeds);

// Verify that the reserves were updated correctly. Since this trade
// happens at the beginning of the term, the bond reserves should be
// increased by the full amount.
PoolInfo memory poolInfoAfter = getPoolInfo();
assertApproxEqAbs(
poolInfoAfter.shareReserves,
poolInfoBefore.shareReserves +
(bondAmount + baseProceeds - basePaid).divDown(
poolInfoBefore.sharePrice
),
1e18
);
assertEq(
poolInfoAfter.bondReserves,
poolInfoBefore.bondReserves - bondAmount
);
assertEq(poolInfoAfter.lpTotalSupply, poolInfoBefore.lpTotalSupply);
assertEq(poolInfoAfter.sharePrice, poolInfoBefore.sharePrice);
assertEq(
poolInfoAfter.longsOutstanding,
poolInfoBefore.longsOutstanding
);
assertEq(poolInfoAfter.longAverageMaturityTime, 0);
assertEq(poolInfoAfter.longBaseVolume, 0);
assertEq(hyperdrive.longBaseVolumeCheckpoints(checkpointTime), 0);
assertEq(
poolInfoAfter.shortsOutstanding,
poolInfoBefore.shortsOutstanding - bondAmount
// Verify that the close long updates were correct.
verifyCloseShort(
poolInfoBefore,
basePaid,
baseProceeds,
bondAmount,
maturityTime
);
assertEq(poolInfoAfter.shortAverageMaturityTime, 0);
assertEq(poolInfoAfter.shortBaseVolume, 0);
assertEq(hyperdrive.shortBaseVolumeCheckpoints(checkpointTime), 0);
}

// TODO: Clean up these tests.
function test_close_short_redeem() external {
uint256 apr = 0.05e18;

Expand All @@ -206,8 +133,7 @@ contract CloseShortTest is HyperdriveTest {

// Short some bonds.
uint256 bondAmount = 10e18;
(uint256 maturityTime, ) = openShort(bob, bondAmount);
uint256 checkpointTime = maturityTime - POSITION_DURATION;
(uint256 maturityTime, uint256 basePaid) = openShort(bob, bondAmount);

// Get the reserves before closing the long.
PoolInfo memory poolInfoBefore = getPoolInfo();
Expand All @@ -218,11 +144,29 @@ contract CloseShortTest is HyperdriveTest {
// Redeem the bonds.
uint256 baseProceeds = closeShort(bob, maturityTime, bondAmount);

// TODO: Investigate this more to see if there are any irregularities
// like there are with the long redemption test.
//
// Verify that all of Bob's bonds were burned and that he received no
// base for posting the shorts.
// Verify that Bob doesn't receive any base from closing the short.
assertEq(baseProceeds, 0);

// Verify that the close long updates were correct.
verifyCloseShort(
poolInfoBefore,
basePaid,
baseProceeds,
bondAmount,
maturityTime
);
}

function verifyCloseShort(
PoolInfo memory poolInfoBefore,
uint256 basePaid,
uint256 baseProceeds,
uint256 bondAmount,
uint256 maturityTime
) internal {
uint256 checkpointTime = maturityTime - POSITION_DURATION;

// Verify that all of Bob's shorts were burned.
assertEq(
hyperdrive.balanceOf(
AssetId.encodeAssetId(
Expand All @@ -233,17 +177,26 @@ contract CloseShortTest is HyperdriveTest {
),
0
);
assertEq(baseProceeds, 0);

// Verify that the reserves were updated correctly. Since this trade
// is a redemption, there should be no changes to the bond reserves.
// Verify that the reserves were updated according to flat+curve.
// The bond adjustment should be equal to timeRemaining * bondAmount
// because the bond update decays as the term progresses.
PoolInfo memory poolInfoAfter = getPoolInfo();
uint256 timeRemaining = calculateTimeRemaining(maturityTime);
assertEq(
poolInfoAfter.bondReserves,
poolInfoBefore.bondReserves - timeRemaining.mulDown(bondAmount)
);

// Verify that the other state was updated correctly.
assertApproxEqAbs(
poolInfoAfter.shareReserves,
poolInfoBefore.shareReserves +
bondAmount.divDown(poolInfoBefore.sharePrice)
(bondAmount + baseProceeds - basePaid).divDown(
poolInfoBefore.sharePrice
),
1e18 // TODO: This error bar is too big. Analyze this.
);
assertEq(poolInfoAfter.bondReserves, poolInfoBefore.bondReserves);
assertEq(poolInfoAfter.lpTotalSupply, poolInfoBefore.lpTotalSupply);
assertEq(poolInfoAfter.sharePrice, poolInfoBefore.sharePrice);
assertEq(
Expand Down
9 changes: 5 additions & 4 deletions test/units/hyperdrive/HyperdriveTest.sol
Original file line number Diff line number Diff line change
Expand Up @@ -200,12 +200,13 @@ contract HyperdriveTest is Test {
function calculateAPRFromRealizedPrice(
uint256 baseAmount,
uint256 bondAmount,
uint256 timeRemaining,
uint256 positionDuration
uint256 timeRemaining
) internal pure returns (uint256) {
// apr = (dy - dx) / (dx * t)
uint256 t = timeRemaining.divDown(positionDuration);
return (bondAmount.sub(baseAmount)).divDown(baseAmount.mulDown(t));
return
(bondAmount.sub(baseAmount)).divDown(
baseAmount.mulDown(timeRemaining)
);
}

function calculateTimeRemaining(
Expand Down
3 changes: 1 addition & 2 deletions test/units/hyperdrive/OpenLongTest.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,7 @@ contract OpenLongTest is HyperdriveTest {
uint256 realizedApr = calculateAPRFromRealizedPrice(
baseAmount,
bondAmount,
FixedPointMath.ONE_18,
POSITION_DURATION
FixedPointMath.ONE_18
);
assertGt(apr, realizedApr);

Expand Down
93 changes: 31 additions & 62 deletions test/units/hyperdrive/OpenShortTest.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -53,68 +53,15 @@ contract OpenShortTest is HyperdriveTest {
// Short a small amount of bonds.
uint256 bondAmount = 10e18;
(uint256 maturityTime, uint256 baseAmount) = openShort(bob, bondAmount);
uint256 checkpointTime = maturityTime - POSITION_DURATION;

// Verify that Hyperdrive received the max loss and that Bob received
// the short tokens.
assertEq(
baseToken.balanceOf(address(hyperdrive)),
contribution + baseAmount
);
assertEq(
hyperdrive.balanceOf(
AssetId.encodeAssetId(
AssetId.AssetIdPrefix.Short,
maturityTime
),
bob
),
bondAmount
);

// Verify that opening a short doesn't cause the pool's APR to go down.
uint256 baseProceeds = bondAmount - baseAmount;
uint256 realizedApr = calculateAPRFromRealizedPrice(
baseProceeds,
// Verify the open short updates occurred correctly.
verifyOpenShort(
poolInfoBefore,
contribution,
baseAmount,
bondAmount,
maturityTime - block.timestamp,
POSITION_DURATION
);
assertLt(apr, realizedApr);

// Verify that the reserves were updated correctly.
PoolInfo memory poolInfoAfter = getPoolInfo();
assertEq(
poolInfoAfter.shareReserves,
poolInfoBefore.shareReserves -
baseProceeds.divDown(poolInfoBefore.sharePrice)
);
assertEq(
poolInfoAfter.bondReserves,
poolInfoBefore.bondReserves + bondAmount
);
assertEq(poolInfoAfter.lpTotalSupply, poolInfoBefore.lpTotalSupply);
assertEq(poolInfoAfter.sharePrice, poolInfoBefore.sharePrice);
assertEq(
poolInfoAfter.longsOutstanding,
poolInfoBefore.longsOutstanding
);
assertEq(poolInfoAfter.longAverageMaturityTime, 0);
assertEq(poolInfoAfter.longBaseVolume, 0);
assertEq(hyperdrive.longBaseVolumeCheckpoints(checkpointTime), 0);
assertEq(
poolInfoAfter.shortsOutstanding,
poolInfoBefore.shortsOutstanding + bondAmount
);
assertApproxEqAbs(
poolInfoAfter.shortAverageMaturityTime,
maturityTime,
1
);
assertEq(poolInfoAfter.shortBaseVolume, baseProceeds);
assertEq(
hyperdrive.shortBaseVolumeCheckpoints(checkpointTime),
baseProceeds
apr
);
}

Expand All @@ -131,6 +78,26 @@ contract OpenShortTest is HyperdriveTest {
// Short a small amount of bonds.
uint256 bondAmount = .1e18;
(uint256 maturityTime, uint256 baseAmount) = openShort(bob, bondAmount);

// Verify the open short updates occurred correctly.
verifyOpenShort(
poolInfoBefore,
contribution,
baseAmount,
bondAmount,
maturityTime,
apr
);
}

function verifyOpenShort(
PoolInfo memory poolInfoBefore,
uint256 contribution,
uint256 baseAmount,
uint256 bondAmount,
uint256 maturityTime,
uint256 apr
) internal {
uint256 checkpointTime = maturityTime - POSITION_DURATION;

// Verify that Hyperdrive received the max loss and that Bob received
Expand All @@ -150,16 +117,18 @@ contract OpenShortTest is HyperdriveTest {
bondAmount
);

// Verify that opening a short doesn't cause the pool's APR to go down.
// Verify that the short didn't receive an APR higher than the pool's
// APR.
uint256 baseProceeds = bondAmount - baseAmount;
uint256 realizedApr = calculateAPRFromRealizedPrice(
baseProceeds,
bondAmount,
maturityTime - block.timestamp,
POSITION_DURATION
FixedPointMath.ONE_18
);
assertLt(apr, realizedApr);

// Verify that the pool's APR didn't go down.

// Verify that the reserves were updated correctly.
PoolInfo memory poolInfoAfter = getPoolInfo();
assertEq(
Expand Down