diff --git a/test/units/hyperdrive/CloseLongTest.t.sol b/test/units/hyperdrive/CloseLongTest.t.sol index 70ee25f5e..67a59853d 100644 --- a/test/units/hyperdrive/CloseLongTest.t.sol +++ b/test/units/hyperdrive/CloseLongTest.t.sol @@ -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), diff --git a/test/units/hyperdrive/CloseShortTest.t.sol b/test/units/hyperdrive/CloseShortTest.t.sol index af162fa36..89a9a26f6 100644 --- a/test/units/hyperdrive/CloseShortTest.t.sol +++ b/test/units/hyperdrive/CloseShortTest.t.sol @@ -74,7 +74,6 @@ 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(); @@ -82,52 +81,17 @@ contract CloseShortTest is HyperdriveTest { // 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 { @@ -140,7 +104,6 @@ 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(); @@ -148,55 +111,19 @@ contract CloseShortTest is HyperdriveTest { // 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; @@ -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(); @@ -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( @@ -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( diff --git a/test/units/hyperdrive/HyperdriveTest.sol b/test/units/hyperdrive/HyperdriveTest.sol index 7531d9d8d..a5aa318ad 100644 --- a/test/units/hyperdrive/HyperdriveTest.sol +++ b/test/units/hyperdrive/HyperdriveTest.sol @@ -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( diff --git a/test/units/hyperdrive/OpenLongTest.t.sol b/test/units/hyperdrive/OpenLongTest.t.sol index 1b9de3259..644bf7fcd 100644 --- a/test/units/hyperdrive/OpenLongTest.t.sol +++ b/test/units/hyperdrive/OpenLongTest.t.sol @@ -112,8 +112,7 @@ contract OpenLongTest is HyperdriveTest { uint256 realizedApr = calculateAPRFromRealizedPrice( baseAmount, bondAmount, - FixedPointMath.ONE_18, - POSITION_DURATION + FixedPointMath.ONE_18 ); assertGt(apr, realizedApr); diff --git a/test/units/hyperdrive/OpenShortTest.t.sol b/test/units/hyperdrive/OpenShortTest.t.sol index 9f84a3e5f..9c0c0b4eb 100644 --- a/test/units/hyperdrive/OpenShortTest.t.sol +++ b/test/units/hyperdrive/OpenShortTest.t.sol @@ -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 ); } @@ -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 @@ -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(