From a78e12cd50065726c79ab811a31b9faebac5282b Mon Sep 17 00:00:00 2001 From: Alex Towle Date: Thu, 24 Oct 2024 16:05:03 -0500 Subject: [PATCH] Fixed the issue with open short (#1199) * Added a fix for the open short insolvency * Added some testing and modified the fix slightly * Fixed the remaining tests --- contracts/src/internal/HyperdriveShort.sol | 11 +++++++++++ .../MorphoBlue_USDe_DAI_Hyperdrive.t.sol | 2 +- .../MorphoBlue_sUSDe_DAI_Hyperdrive.t.sol | 2 +- .../MorphoBlue_wstETH_USDA_Hyperdrive.t.sol | 2 +- .../hyperdrive/NonstandardDecimals.sol | 2 +- test/units/hyperdrive/AddLiquidityTest.t.sol | 10 ++++++---- test/units/hyperdrive/OpenShortTest.t.sol | 7 ++++--- test/utils/InstanceTest.sol | 14 ++++++++++++++ 8 files changed, 39 insertions(+), 11 deletions(-) diff --git a/contracts/src/internal/HyperdriveShort.sol b/contracts/src/internal/HyperdriveShort.sol index b2a6e92d3..3fa726fc0 100644 --- a/contracts/src/internal/HyperdriveShort.sol +++ b/contracts/src/internal/HyperdriveShort.sol @@ -99,6 +99,17 @@ abstract contract HyperdriveShort is IHyperdriveEvents, HyperdriveLP { block.timestamp, spotPrice ); + + // Adjust the base deposit to ensure that we are overestimating the + // amount of base that needs to be deposited to provide the required + // amount of vault shares for the pool to remain solvent. + // + // NOTE: We round up and add 1 wei to the base deposit to ensure + // that the deposit in shares is greater than or equal to the amount + // required to maintain solvency. + baseDeposit = + _convertToBase(baseDeposit.divUp(vaultSharePrice)) + + 1; } // Take custody of the trader's deposit and ensure that the trader diff --git a/test/instances/morpho-blue/MorphoBlue_USDe_DAI_Hyperdrive.t.sol b/test/instances/morpho-blue/MorphoBlue_USDe_DAI_Hyperdrive.t.sol index 525b7cdd9..8ff3e63d9 100644 --- a/test/instances/morpho-blue/MorphoBlue_USDe_DAI_Hyperdrive.t.sol +++ b/test/instances/morpho-blue/MorphoBlue_USDe_DAI_Hyperdrive.t.sol @@ -69,7 +69,7 @@ contract MorphoBlue_USDe_DAI_HyperdriveTest is roundTripLongMaturityWithBaseUpperBoundTolerance: 1e3, roundTripLongMaturityWithBaseTolerance: 1e5, roundTripShortInstantaneousWithBaseUpperBoundTolerance: 1e3, - roundTripShortInstantaneousWithBaseTolerance: 1e5, + roundTripShortInstantaneousWithBaseTolerance: 1e7, roundTripShortMaturityWithBaseTolerance: 1e10, // NOTE: Share deposits and withdrawals are disabled, so these are // 0. diff --git a/test/instances/morpho-blue/MorphoBlue_sUSDe_DAI_Hyperdrive.t.sol b/test/instances/morpho-blue/MorphoBlue_sUSDe_DAI_Hyperdrive.t.sol index d83598c92..3cb032ef5 100644 --- a/test/instances/morpho-blue/MorphoBlue_sUSDe_DAI_Hyperdrive.t.sol +++ b/test/instances/morpho-blue/MorphoBlue_sUSDe_DAI_Hyperdrive.t.sol @@ -69,7 +69,7 @@ contract MorphoBlue_sUSDe_DAI_HyperdriveTest is roundTripLongMaturityWithBaseUpperBoundTolerance: 1e3, roundTripLongMaturityWithBaseTolerance: 1e5, roundTripShortInstantaneousWithBaseUpperBoundTolerance: 1e3, - roundTripShortInstantaneousWithBaseTolerance: 1e5, + roundTripShortInstantaneousWithBaseTolerance: 1e8, roundTripShortMaturityWithBaseTolerance: 1e10, // NOTE: Share deposits and withdrawals are disabled, so these are // 0. diff --git a/test/instances/morpho-blue/MorphoBlue_wstETH_USDA_Hyperdrive.t.sol b/test/instances/morpho-blue/MorphoBlue_wstETH_USDA_Hyperdrive.t.sol index 907401e1d..0c29d5f5f 100644 --- a/test/instances/morpho-blue/MorphoBlue_wstETH_USDA_Hyperdrive.t.sol +++ b/test/instances/morpho-blue/MorphoBlue_wstETH_USDA_Hyperdrive.t.sol @@ -69,7 +69,7 @@ contract MorphoBlue_wstETH_USDA_HyperdriveTest is roundTripLongMaturityWithBaseUpperBoundTolerance: 1e3, roundTripLongMaturityWithBaseTolerance: 1e5, roundTripShortInstantaneousWithBaseUpperBoundTolerance: 1e3, - roundTripShortInstantaneousWithBaseTolerance: 1e5, + roundTripShortInstantaneousWithBaseTolerance: 1e8, roundTripShortMaturityWithBaseTolerance: 1e10, // NOTE: Share deposits and withdrawals are disabled, so these are // 0. diff --git a/test/integrations/hyperdrive/NonstandardDecimals.sol b/test/integrations/hyperdrive/NonstandardDecimals.sol index 3dd8c48c6..36464fa47 100644 --- a/test/integrations/hyperdrive/NonstandardDecimals.sol +++ b/test/integrations/hyperdrive/NonstandardDecimals.sol @@ -54,7 +54,7 @@ contract NonstandardDecimalsTest is HyperdriveTest { (, uint256 shortBasePaid) = openShort(celine, bondAmount); // Ensure that the long and short fixed interest are equal. - assertApproxEqAbs(bondAmount - longBasePaid, shortBasePaid, 3); + assertApproxEqAbs(bondAmount - longBasePaid, shortBasePaid, 4); } function test_nonstandard_decimals_longs_outstanding() external { diff --git a/test/units/hyperdrive/AddLiquidityTest.t.sol b/test/units/hyperdrive/AddLiquidityTest.t.sol index 09e302fa4..767620d47 100644 --- a/test/units/hyperdrive/AddLiquidityTest.t.sol +++ b/test/units/hyperdrive/AddLiquidityTest.t.sol @@ -392,11 +392,12 @@ contract AddLiquidityTest is HyperdriveTest { // Ensure that all of the capital except for the minimum share reserves // and the zero address's LP present value was removed from the system. - assertEq( + assertApproxEqAbs( baseToken.balanceOf(address(hyperdrive)), (ONE + hyperdrive.lpSharePrice()).mulDown( hyperdrive.getPoolConfig().minimumShareReserves - ) + ), + 1 ); } @@ -502,11 +503,12 @@ contract AddLiquidityTest is HyperdriveTest { // Ensure that all of the capital except for the minimum share reserves // and the zero address's LP present value was removed from the system. - assertEq( + assertApproxEqAbs( baseToken.balanceOf(address(hyperdrive)), (ONE + hyperdrive.lpSharePrice()).mulDown( hyperdrive.getPoolConfig().minimumShareReserves - ) + ), + 1 ); } diff --git a/test/units/hyperdrive/OpenShortTest.t.sol b/test/units/hyperdrive/OpenShortTest.t.sol index 753832dbf..7feccfff2 100644 --- a/test/units/hyperdrive/OpenShortTest.t.sol +++ b/test/units/hyperdrive/OpenShortTest.t.sol @@ -488,10 +488,11 @@ contract OpenShortTest is HyperdriveTest { IHyperdrive.PoolInfo memory poolInfoAfter = hyperdrive.getPoolInfo(); { - assertEq( + assertApproxEqAbs( poolInfoAfter.shareReserves, poolInfoBefore.shareReserves - - baseProceeds.divDown(poolInfoBefore.vaultSharePrice) + baseProceeds.divDown(poolInfoBefore.vaultSharePrice), + 1 ); assertEq( poolInfoAfter.vaultSharePrice, @@ -573,7 +574,7 @@ contract OpenShortTest is HyperdriveTest { hyperdrive.getPoolInfo().vaultSharePrice ); assertEq(eventAsBase, true); - assertEq(eventBaseProceeds, shortAmount - basePaid); + assertApproxEqAbs(eventBaseProceeds, shortAmount - basePaid, 1); assertEq(eventBondAmount, shortAmount); } } diff --git a/test/utils/InstanceTest.sol b/test/utils/InstanceTest.sol index 4735aa1cd..1db3b991e 100644 --- a/test/utils/InstanceTest.sol +++ b/test/utils/InstanceTest.sol @@ -2673,6 +2673,20 @@ abstract contract InstanceTest is HyperdriveTest { ); (uint256 maturityTime, uint256 basePaid) = openShort(bob, _shortAmount); + // Ensure that the pool has more shares than expected. This verifies + // that the pool is solvent. + assertGe( + hyperdrive.totalShares(), + hyperdrive.getPoolInfo().shareReserves + + (hyperdrive.getPoolInfo().shortsOutstanding + + hyperdrive.getPoolInfo().shortsOutstanding.mulDown( + hyperdrive.getPoolConfig().fees.flat + )).divDown(hyperdrive.getPoolInfo().vaultSharePrice) + + hyperdrive.getUncollectedGovernanceFees() + + hyperdrive.getPoolInfo().withdrawalSharesProceeds + + hyperdrive.getPoolInfo().zombieShareReserves + ); + // Get some balance information before the withdrawal. ( uint256 totalSupplyAssetsBefore,