From e24eb2ffe953da4f9353d2908b47424554430183 Mon Sep 17 00:00:00 2001 From: jonny rhea Date: Sun, 17 Dec 2023 21:05:55 -0600 Subject: [PATCH 1/9] add zombie interest fee and rename governanceFee to governanceLPFee --- contracts/src/external/HyperdriveTarget0.sol | 7 +- contracts/src/factory/HyperdriveFactory.sol | 16 +- contracts/src/interfaces/IHyperdrive.sol | 4 +- contracts/src/internal/HyperdriveBase.sol | 14 +- .../src/internal/HyperdriveCheckpoint.sol | 2 +- contracts/src/internal/HyperdriveStorage.sol | 10 +- contracts/test/MockMultiToken.sol | 14 +- script/DevnetMigration.s.sol | 33 ++-- test/integrations/ERC4626Hyperdrive.t.sol | 10 +- test/integrations/ERC4626Validation.t.sol | 4 +- test/integrations/HyperdriveFactory.t.sol | 50 +++--- test/integrations/UsdcERC4626.t.sol | 4 +- .../IntraCheckpointNettingTest.t.sol | 14 +- .../NegativeInterestLongFeeTest.t.sol | 30 +++- .../NegativeInterestShortFeeTest.t.sol | 30 +++- .../hyperdrive/RoundTripTest.t.sol | 4 +- .../hyperdrive/SandwichTest.t.sol | 8 +- .../hyperdrive/VariableInterestLongTest.t.sol | 6 +- .../VariableInterestShortTest.t.sol | 6 +- .../hyperdrive/ZombieInterestTest.t.sol | 10 +- test/units/ForceRevertDelegatecall.t.sol | 7 +- test/units/hyperdrive/CloseLongTest.t.sol | 6 +- test/units/hyperdrive/CloseShortTest.t.sol | 32 +++- test/units/hyperdrive/ExtremeInputs.t.sol | 2 +- test/units/hyperdrive/FeeTest.t.sol | 153 +++++++++++++++--- test/units/hyperdrive/HyperdriveDeploy.t.sol | 18 ++- test/units/hyperdrive/OpenLongTest.t.sol | 2 +- test/units/hyperdrive/OpenShortTest.t.sol | 16 +- test/units/libraries/HyperdriveMath.t.sol | 10 +- test/units/libraries/YieldSpaceMath.t.sol | 2 +- test/utils/HyperdriveTest.sol | 15 +- test/utils/HyperdriveUtils.sol | 30 ++-- 32 files changed, 417 insertions(+), 152 deletions(-) diff --git a/contracts/src/external/HyperdriveTarget0.sol b/contracts/src/external/HyperdriveTarget0.sol index cd43f5ce4..7e8f156d9 100644 --- a/contracts/src/external/HyperdriveTarget0.sol +++ b/contracts/src/external/HyperdriveTarget0.sol @@ -252,7 +252,12 @@ abstract contract HyperdriveTarget0 is timeStretch: _timeStretch, governance: _governance, feeCollector: _feeCollector, - fees: IHyperdrive.Fees(_curveFee, _flatFee, _governanceFee) + fees: IHyperdrive.Fees( + _curveFee, + _flatFee, + _governanceLPFee, + _governanceZombieFee + ) }) ) ); diff --git a/contracts/src/factory/HyperdriveFactory.sol b/contracts/src/factory/HyperdriveFactory.sol index a5355f4e2..685bf94e7 100644 --- a/contracts/src/factory/HyperdriveFactory.sol +++ b/contracts/src/factory/HyperdriveFactory.sol @@ -73,7 +73,10 @@ contract HyperdriveFactory { uint256 internal immutable maxFlatFee; /// @dev The maximum governance fee that can be used as a factory default. - uint256 internal immutable maxGovernanceFee; + uint256 internal immutable maxGovernanceLPFee; + + /// @dev The maximum governance zombie fee that can be used as a factory default. + uint256 internal immutable maxGovernanceZombieFee; /// @dev The defaultPausers used when new instances are deployed. address[] internal _defaultPausers; @@ -118,14 +121,16 @@ contract HyperdriveFactory { // constraint. maxCurveFee = _factoryConfig.maxFees.curve; maxFlatFee = _factoryConfig.maxFees.flat; - maxGovernanceFee = _factoryConfig.maxFees.governance; - if (maxCurveFee > ONE || maxFlatFee > ONE || maxGovernanceFee > ONE) { + maxGovernanceLPFee = _factoryConfig.maxFees.governanceLP; + maxGovernanceZombieFee = _factoryConfig.maxFees.governanceZombie; + if (maxCurveFee > ONE || maxFlatFee > ONE || maxGovernanceLPFee > ONE || maxGovernanceZombieFee > ONE) { revert IHyperdrive.MaxFeeTooHigh(); } if ( _factoryConfig.fees.curve > maxCurveFee || _factoryConfig.fees.flat > maxFlatFee || - _factoryConfig.fees.governance > maxGovernanceFee + _factoryConfig.fees.governanceLP > maxGovernanceLPFee || + _factoryConfig.fees.governanceZombie > maxGovernanceZombieFee ) { revert IHyperdrive.FeeTooHigh(); } @@ -198,7 +203,8 @@ contract HyperdriveFactory { if ( _fees.curve > maxCurveFee || _fees.flat > maxFlatFee || - _fees.governance > maxGovernanceFee + _fees.governanceLP > maxGovernanceLPFee || + _fees.governanceZombie > maxGovernanceZombieFee ) { revert IHyperdrive.FeeTooHigh(); } diff --git a/contracts/src/interfaces/IHyperdrive.sol b/contracts/src/interfaces/IHyperdrive.sol index f29a21d1c..ebc13bc25 100644 --- a/contracts/src/interfaces/IHyperdrive.sol +++ b/contracts/src/interfaces/IHyperdrive.sol @@ -141,7 +141,9 @@ interface IHyperdrive is IHyperdriveRead, IHyperdriveCore, IMultiToken { /// @dev The LP fee applied to the flat portion of a trade. uint256 flat; /// @dev The portion of the LP fee that goes to governance. - uint256 governance; + uint256 governanceLP; + /// @dev The portion of the zombie interest that goes to governance. + uint256 governanceZombie; } struct PoolConfig { diff --git a/contracts/src/internal/HyperdriveBase.sol b/contracts/src/internal/HyperdriveBase.sol index 1931abac8..66f25d193 100644 --- a/contracts/src/internal/HyperdriveBase.sol +++ b/contracts/src/internal/HyperdriveBase.sol @@ -355,7 +355,15 @@ abstract contract HyperdriveBase is HyperdriveStorage { _newSharePrice - _oldSharePrice, _newSharePrice ); + uint256 governanceZombieFeeCollected = zombieInterest.mulDown( + _governanceZombieFee + ); + _governanceFeesAccrued += governanceZombieFeeCollected; _marketState.zombieShareReserves -= zombieInterest.toUint128(); + + // Ensure that any zombie interest collected by governance + // doesn't go into the share reserves. + zombieInterest -= governanceZombieFeeCollected; _marketState.shareReserves += zombieInterest.toUint128(); _marketState.shareAdjustment += int128(zombieInterest.toUint128()); } @@ -420,7 +428,7 @@ abstract contract HyperdriveBase is HyperdriveStorage { // We leave the governance fee in terms of bonds: // governanceCurveFee = curve_fee * p * phi_gov // = bonds * phi_gov - governanceCurveFee = curveFee.mulDown(_governanceFee); + governanceCurveFee = curveFee.mulDown(_governanceLPFee); } /// @dev Calculates the fees that go to the LPs and governance. @@ -469,7 +477,7 @@ abstract contract HyperdriveBase is HyperdriveStorage { // // governanceCurveFee = curve_fee * phi_gov // = shares * phi_gov - governanceCurveFee = curveFee.mulDown(_governanceFee); + governanceCurveFee = curveFee.mulDown(_governanceLPFee); // The flat portion of the fee is taken from the matured bonds. // Since a matured bond is worth 1 base, it is appropriate to consider @@ -493,7 +501,7 @@ abstract contract HyperdriveBase is HyperdriveStorage { // The totalGovernanceFee is the sum of the curve and flat governance fees. totalGovernanceFee = governanceCurveFee + - flatFee.mulDown(_governanceFee); + flatFee.mulDown(_governanceLPFee); } /// @dev Converts input to base if necessary according to what is specified in options. diff --git a/contracts/src/internal/HyperdriveCheckpoint.sol b/contracts/src/internal/HyperdriveCheckpoint.sol index 2b0fa0033..f6288ef79 100644 --- a/contracts/src/internal/HyperdriveCheckpoint.sol +++ b/contracts/src/internal/HyperdriveCheckpoint.sol @@ -229,7 +229,7 @@ abstract contract HyperdriveCheckpoint is // bond amount divided by the share price. shareProceeds = _bondAmount.divDown(_sharePrice); uint256 flatFee = shareProceeds.mulDown(_flatFee); - governanceFee = flatFee.mulDown(_governanceFee); + governanceFee = flatFee.mulDown(_governanceLPFee); // If the position is a long, the share proceeds are removed from the // share reserves. The proceeds are decreased by the flat fee because diff --git a/contracts/src/internal/HyperdriveStorage.sol b/contracts/src/internal/HyperdriveStorage.sol index c777910d2..c3be18a2d 100644 --- a/contracts/src/internal/HyperdriveStorage.sol +++ b/contracts/src/internal/HyperdriveStorage.sol @@ -41,7 +41,10 @@ abstract contract HyperdriveStorage is ReentrancyGuard { uint256 internal immutable _flatFee; /// @dev The portion of the LP fee that goes to governance. - uint256 internal immutable _governanceFee; + uint256 internal immutable _governanceLPFee; + + /// @dev The portion of the zombie interest that goes to governance. + uint256 internal immutable _governanceZombieFee; /// Market State /// @@ -164,13 +167,14 @@ abstract contract HyperdriveStorage is ReentrancyGuard { if ( _config.fees.curve > 1e18 || _config.fees.flat > 1e18 || - _config.fees.governance > 1e18 + _config.fees.governanceLP > 1e18 ) { revert IHyperdrive.InvalidFeeAmounts(); } _curveFee = _config.fees.curve; _flatFee = _config.fees.flat; - _governanceFee = _config.fees.governance; + _governanceLPFee = _config.fees.governanceLP; + _governanceZombieFee = _config.fees.governanceZombie; // Initialize the MultiToken immutables. _linkerFactory = _config.linkerFactory; diff --git a/contracts/test/MockMultiToken.sol b/contracts/test/MockMultiToken.sol index 5480e7607..66884ff32 100644 --- a/contracts/test/MockMultiToken.sol +++ b/contracts/test/MockMultiToken.sol @@ -60,7 +60,12 @@ contract MockMultiToken is HyperdriveMultiToken, MockHyperdriveBase { timeStretch: HyperdriveUtils.calculateTimeStretch(0.05e18), governance: address(0), feeCollector: address(0), - fees: IHyperdrive.Fees({ curve: 0, flat: 0, governance: 0 }) + fees: IHyperdrive.Fees({ + curve: 0, + flat: 0, + governanceLP: 0, + governanceZombie: 0 + }) }) ) { @@ -79,7 +84,12 @@ contract MockMultiToken is HyperdriveMultiToken, MockHyperdriveBase { timeStretch: HyperdriveUtils.calculateTimeStretch(0.05e18), governance: address(0), feeCollector: address(0), - fees: IHyperdrive.Fees({ curve: 0, flat: 0, governance: 0 }) + fees: IHyperdrive.Fees({ + curve: 0, + flat: 0, + governanceLP: 0, + governanceZombie: 0 + }) }) ) ); diff --git a/script/DevnetMigration.s.sol b/script/DevnetMigration.s.sol index 6977b3444..2145b6dcc 100644 --- a/script/DevnetMigration.s.sol +++ b/script/DevnetMigration.s.sol @@ -47,10 +47,12 @@ contract DevnetMigration is Script { // factory configuration uint256 factoryCurveFee; uint256 factoryFlatFee; - uint256 factoryGovernanceFee; + uint256 factoryGovernanceLPFee; + uint256 factoryGovernanceZombieFee; uint256 factoryMaxCurveFee; uint256 factoryMaxFlatFee; - uint256 factoryMaxGovernanceFee; + uint256 factoryMaxGovernanceLPFee; + uint256 factoryMaxGovernanceZombieFee; // hyperdrive configuration uint256 hyperdriveContribution; uint256 hyperdriveFixedRate; @@ -89,9 +91,13 @@ contract DevnetMigration is Script { ), factoryCurveFee: vm.envOr("FACTORY_CURVE_FEE", uint256(0.1e18)), factoryFlatFee: vm.envOr("FACTORY_FLAT_FEE", uint256(0.0005e18)), - factoryGovernanceFee: vm.envOr( - "FACTORY_GOVERNANCE_FEE", - uint256(0.15e18) + factoryGovernanceLPFee: vm.envOr( + "FACTORY_GOVERNANCE_LP_FEE", + uint256(0.01e18) + ), + factoryGovernanceZombieFee: vm.envOr( + "FACTORY_GOVERNANCE_ZOMBIE_FEE", + uint256(0.1e18) ), factoryMaxCurveFee: vm.envOr( "FACTORY_MAX_CURVE_FEE", @@ -101,8 +107,12 @@ contract DevnetMigration is Script { "FACTORY_MAX_FLAT_FEE", uint256(0.0015e18) ), - factoryMaxGovernanceFee: vm.envOr( - "FACTORY_MAX_GOVERNANCE_FEE", + factoryMaxGovernanceLPFee: vm.envOr( + "FACTORY_MAX_GOVERNANCE_LP_FEE", + uint256(0.3e18) + ), + factoryMaxGovernanceZombieFee: vm.envOr( + "FACTORY_MAX_GOVERNANCE_ZOMBIE_FEE", uint256(0.3e18) ), hyperdriveContribution: vm.envOr( @@ -191,12 +201,14 @@ contract DevnetMigration is Script { fees: IHyperdrive.Fees({ curve: config.factoryCurveFee, flat: config.factoryFlatFee, - governance: config.factoryGovernanceFee + governanceLP: config.factoryGovernanceLPFee, + governanceZombie: config.factoryGovernanceZombieFee }), maxFees: IHyperdrive.Fees({ curve: config.factoryMaxCurveFee, flat: config.factoryMaxFlatFee, - governance: config.factoryMaxGovernanceFee + governanceLP: config.factoryMaxGovernanceLPFee, + governanceZombie: config.factoryMaxGovernanceZombieFee }), defaultPausers: defaultPausers, linkerFactory: address(forwarderFactory), @@ -230,7 +242,8 @@ contract DevnetMigration is Script { fees: IHyperdrive.Fees({ curve: config.factoryCurveFee, flat: config.factoryFlatFee, - governance: config.factoryGovernanceFee + governanceLP: config.factoryGovernanceLPFee, + governanceZombie: config.factoryGovernanceZombieFee }) }); address hyperdriveDeployer = address( diff --git a/test/integrations/ERC4626Hyperdrive.t.sol b/test/integrations/ERC4626Hyperdrive.t.sol index e3ae97a1f..705bdbdb9 100644 --- a/test/integrations/ERC4626Hyperdrive.t.sol +++ b/test/integrations/ERC4626Hyperdrive.t.sol @@ -75,8 +75,8 @@ contract ERC4626HyperdriveTest is HyperdriveTest { hyperdriveGovernance: bob, feeCollector: bob, defaultPausers: defaults, - fees: IHyperdrive.Fees(0, 0, 0), - maxFees: IHyperdrive.Fees(0, 0, 0), + fees: IHyperdrive.Fees(0, 0, 0, 0), + maxFees: IHyperdrive.Fees(0, 0, 0, 0), linkerFactory: address(forwarderFactory), linkerCodeHash: forwarderFactory.ERC20LINK_HASH() }) @@ -99,7 +99,7 @@ contract ERC4626HyperdriveTest is HyperdriveTest { timeStretch: ONE.divDown(22.186877016851916266e18), governance: alice, feeCollector: bob, - fees: IHyperdrive.Fees(0, 0, 0) + fees: IHyperdrive.Fees(0, 0, 0, 0) }); address target0 = address(new ERC4626Target0(config, pool)); address target1 = address(new ERC4626Target1(config, pool)); @@ -249,7 +249,7 @@ contract ERC4626HyperdriveTest is HyperdriveTest { timeStretch: HyperdriveUtils.calculateTimeStretch(apr), governance: alice, feeCollector: bob, - fees: IHyperdrive.Fees(0, 0, 0) + fees: IHyperdrive.Fees(0, 0, 0, 0) }); dai.approve(address(factory), type(uint256).max); hyperdrive = factory.deployAndInitialize( @@ -300,7 +300,7 @@ contract ERC4626HyperdriveTest is HyperdriveTest { timeStretch: HyperdriveUtils.calculateTimeStretch(apr), governance: alice, feeCollector: bob, - fees: IHyperdrive.Fees(0, 0, 0) + fees: IHyperdrive.Fees(0, 0, 0, 0) }); dai.approve(address(factory), type(uint256).max); hyperdrive = factory.deployAndInitialize( diff --git a/test/integrations/ERC4626Validation.t.sol b/test/integrations/ERC4626Validation.t.sol index 316fa79e1..d6d20e584 100644 --- a/test/integrations/ERC4626Validation.t.sol +++ b/test/integrations/ERC4626Validation.t.sol @@ -65,8 +65,8 @@ abstract contract ERC4626ValidationTest is HyperdriveTest { hyperdriveGovernance: bob, feeCollector: bob, defaultPausers: defaults, - fees: IHyperdrive.Fees(0, 0, 0), - maxFees: IHyperdrive.Fees(1e18, 1e18, 1e18), + fees: IHyperdrive.Fees(0, 0, 0, 0), + maxFees: IHyperdrive.Fees(1e18, 1e18, 1e18, 1e18), linkerFactory: address(forwarderFactory), linkerCodeHash: forwarderFactory.ERC20LINK_HASH() }) diff --git a/test/integrations/HyperdriveFactory.t.sol b/test/integrations/HyperdriveFactory.t.sol index 4ac8737fb..5b8fc2ece 100644 --- a/test/integrations/HyperdriveFactory.t.sol +++ b/test/integrations/HyperdriveFactory.t.sol @@ -29,8 +29,8 @@ contract HyperdriveFactoryTest is HyperdriveTest { hyperdriveGovernance: bob, feeCollector: bob, defaultPausers: defaults, - fees: IHyperdrive.Fees(0, 0, 0), - maxFees: IHyperdrive.Fees(0, 0, 0), + fees: IHyperdrive.Fees(0, 0, 0, 0), + maxFees: IHyperdrive.Fees(0, 0, 0, 0), linkerFactory: address(0), linkerCodeHash: bytes32(0) }) @@ -41,15 +41,19 @@ contract HyperdriveFactoryTest is HyperdriveTest { // Curve fee can not exceed maximum curve fee. vm.expectRevert(IHyperdrive.FeeTooHigh.selector); - factory.updateFees(IHyperdrive.Fees(2e18, 0, 0)); + factory.updateFees(IHyperdrive.Fees(2e18, 0, 0, 0)); // Flat fee can not exceed maximum flat fee. vm.expectRevert(IHyperdrive.FeeTooHigh.selector); - factory.updateFees(IHyperdrive.Fees(0, 2e18, 0)); + factory.updateFees(IHyperdrive.Fees(0, 2e18, 0, 0)); - // Governance fee can not exceed maximum governance fee. + // Governance LP fee can not exceed maximum governance LP fee. vm.expectRevert(IHyperdrive.FeeTooHigh.selector); - factory.updateFees(IHyperdrive.Fees(0, 0, 2e18)); + factory.updateFees(IHyperdrive.Fees(0, 0, 2e18, 0)); + + // Governance Zombie fee can not exceed maximum governance zombie fee. + vm.expectRevert(IHyperdrive.FeeTooHigh.selector); + factory.updateFees(IHyperdrive.Fees(0, 0, 0, 2e18)); } // Ensure that the maximum curve fee can not exceed 100%. @@ -62,8 +66,8 @@ contract HyperdriveFactoryTest is HyperdriveTest { hyperdriveGovernance: bob, feeCollector: bob, defaultPausers: defaults, - fees: IHyperdrive.Fees(0, 0, 0), - maxFees: IHyperdrive.Fees(0, 0, 0), + fees: IHyperdrive.Fees(0, 0, 0, 0), + maxFees: IHyperdrive.Fees(0, 0, 0, 0), linkerFactory: address(0), linkerCodeHash: bytes32(0) }); @@ -80,11 +84,17 @@ contract HyperdriveFactoryTest is HyperdriveTest { new HyperdriveFactory(config); config.maxFees.flat = 0; - // Ensure that the maximum governance fee can not exceed 100%. + // Ensure that the maximum governance LP fee can not exceed 100%. + vm.expectRevert(IHyperdrive.MaxFeeTooHigh.selector); + config.maxFees.governanceLP = 2e18; + new HyperdriveFactory(config); + config.maxFees.governanceLP = 0; + + // Ensure that the maximum governance zombie fee can not exceed 100%. vm.expectRevert(IHyperdrive.MaxFeeTooHigh.selector); - config.maxFees.governance = 2e18; + config.maxFees.governanceZombie = 2e18; new HyperdriveFactory(config); - config.maxFees.governance = 0; + config.maxFees.governanceZombie = 0; } } @@ -136,8 +146,8 @@ contract HyperdriveFactoryBaseTest is HyperdriveTest { governance: alice, hyperdriveGovernance: bob, feeCollector: bob, - fees: IHyperdrive.Fees(0, 0, 0), - maxFees: IHyperdrive.Fees(0, 0, 0), + fees: IHyperdrive.Fees(0, 0, 0, 0), + maxFees: IHyperdrive.Fees(0, 0, 0, 0), defaultPausers: defaults, linkerFactory: address(forwarderFactory), linkerCodeHash: forwarderFactory.ERC20LINK_HASH() @@ -155,7 +165,7 @@ contract HyperdriveFactoryBaseTest is HyperdriveTest { timeStretch: HyperdriveUtils.calculateTimeStretch(APR), governance: alice, feeCollector: bob, - fees: IHyperdrive.Fees(0, 0, 0), + fees: IHyperdrive.Fees(0, 0, 0, 0), linkerFactory: address(forwarderFactory), linkerCodeHash: forwarderFactory.ERC20LINK_HASH() }); @@ -479,8 +489,8 @@ contract HyperdriveDeployerGetterTest is HyperdriveTest { hyperdriveGovernance: bob, feeCollector: bob, defaultPausers: defaults, - fees: IHyperdrive.Fees(0, 0, 0), - maxFees: IHyperdrive.Fees(0, 0, 0), + fees: IHyperdrive.Fees(0, 0, 0, 0), + maxFees: IHyperdrive.Fees(0, 0, 0, 0), linkerFactory: address(0), linkerCodeHash: bytes32(0) }) @@ -623,8 +633,8 @@ contract HyperdriveFactoryAddHyperdriveFactoryTest is HyperdriveTest { hyperdriveGovernance: bob, feeCollector: bob, defaultPausers: defaults, - fees: IHyperdrive.Fees(0, 0, 0), - maxFees: IHyperdrive.Fees(0, 0, 0), + fees: IHyperdrive.Fees(0, 0, 0, 0), + maxFees: IHyperdrive.Fees(0, 0, 0, 0), linkerFactory: address(0), linkerCodeHash: bytes32(0) }) @@ -697,8 +707,8 @@ contract HyperdriveFactoryRemoveInstanceTest is HyperdriveTest { hyperdriveGovernance: bob, feeCollector: bob, defaultPausers: defaults, - fees: IHyperdrive.Fees(0, 0, 0), - maxFees: IHyperdrive.Fees(0, 0, 0), + fees: IHyperdrive.Fees(0, 0, 0, 0), + maxFees: IHyperdrive.Fees(0, 0, 0, 0), linkerFactory: address(0), linkerCodeHash: bytes32(0) }) diff --git a/test/integrations/UsdcERC4626.t.sol b/test/integrations/UsdcERC4626.t.sol index c3ab8db25..c71619cf1 100644 --- a/test/integrations/UsdcERC4626.t.sol +++ b/test/integrations/UsdcERC4626.t.sol @@ -76,8 +76,8 @@ contract UsdcERC4626 is ERC4626ValidationTest { hyperdriveGovernance: bob, defaultPausers: defaults, feeCollector: bob, - fees: IHyperdrive.Fees(0, 0, 0), - maxFees: IHyperdrive.Fees(1e18, 1e18, 1e18), + fees: IHyperdrive.Fees(0, 0, 0, 0), + maxFees: IHyperdrive.Fees(1e18, 1e18, 1e18, 1e18), linkerFactory: address(forwarderFactory), linkerCodeHash: forwarderFactory.ERC20LINK_HASH() }) diff --git a/test/integrations/hyperdrive/IntraCheckpointNettingTest.t.sol b/test/integrations/hyperdrive/IntraCheckpointNettingTest.t.sol index 251833af9..e809f88b5 100644 --- a/test/integrations/hyperdrive/IntraCheckpointNettingTest.t.sol +++ b/test/integrations/hyperdrive/IntraCheckpointNettingTest.t.sol @@ -20,7 +20,7 @@ contract IntraCheckpointNettingTest is HyperdriveTest { // Initialize the market uint256 apr = 0.05e18; - deploy(alice, apr, initialSharePrice, 0, 0, 0); + deploy(alice, apr, initialSharePrice, 0, 0, 0, 0); uint256 contribution = 100e18; uint256 aliceLpShares = initialize(alice, apr, contribution); @@ -80,7 +80,7 @@ contract IntraCheckpointNettingTest is HyperdriveTest { uint256 aliceLpShares = 0; { uint256 apr = 0.05e18; - deploy(alice, apr, initialSharePrice, 0, 0, 0); + deploy(alice, apr, initialSharePrice, 0, 0, 0, 0); uint256 contribution = 500_000_000e18; aliceLpShares = initialize(alice, apr, contribution); @@ -157,7 +157,7 @@ contract IntraCheckpointNettingTest is HyperdriveTest { uint256 aliceLpShares = 0; { uint256 apr = 0.05e18; - deploy(alice, apr, initialSharePrice, 0, 0, 0); + deploy(alice, apr, initialSharePrice, 0, 0, 0, 0); uint256 contribution = 500_000_000e18; aliceLpShares = initialize(alice, apr, contribution); @@ -434,7 +434,7 @@ contract IntraCheckpointNettingTest is HyperdriveTest { ) internal { // Initialize the market uint256 apr = 0.05e18; - deploy(alice, apr, initialSharePrice, 0, 0, 0); + deploy(alice, apr, initialSharePrice, 0, 0, 0, 0); uint256 contribution = 500_000_000e18; uint256 aliceLpShares = initialize(alice, apr, contribution); @@ -570,7 +570,7 @@ contract IntraCheckpointNettingTest is HyperdriveTest { ) internal { // Initialize the market uint256 apr = 0.05e18; - deploy(alice, apr, initialSharePrice, 0, 0, 0); + deploy(alice, apr, initialSharePrice, 0, 0, 0, 0); uint256 contribution = 500_000_000e18; initialize(alice, apr, contribution); @@ -727,7 +727,7 @@ contract IntraCheckpointNettingTest is HyperdriveTest { ) internal { // initialize the market uint256 apr = 0.05e18; - deploy(alice, apr, initialSharePrice, 0, 0, 0); + deploy(alice, apr, initialSharePrice, 0, 0, 0, 0); uint256 contribution = 500_000_000e18; uint256 aliceLpShares = initialize(alice, apr, contribution); @@ -805,7 +805,7 @@ contract IntraCheckpointNettingTest is HyperdriveTest { uint256 aliceLpShares = 0; { uint256 apr = 0.05e18; - deploy(alice, apr, initialSharePrice, 0, 0, 0); + deploy(alice, apr, initialSharePrice, 0, 0, 0, 0); // JR TODO: we should add this as a parameter to fuzz to ensure that we are solvent with withdrawal shares uint256 contribution = 500_000_000e18; aliceLpShares = initialize(alice, apr, contribution); diff --git a/test/integrations/hyperdrive/NegativeInterestLongFeeTest.t.sol b/test/integrations/hyperdrive/NegativeInterestLongFeeTest.t.sol index 645ad4412..3ad61cd03 100644 --- a/test/integrations/hyperdrive/NegativeInterestLongFeeTest.t.sol +++ b/test/integrations/hyperdrive/NegativeInterestLongFeeTest.t.sol @@ -108,7 +108,15 @@ contract NegativeInterestLongFeeTest is HyperdriveTest { ) internal { // Initialize the market uint256 apr = 0.05e18; - deploy(alice, apr, initialSharePrice, curveFee, flatFee, governanceFee); + deploy( + alice, + apr, + initialSharePrice, + curveFee, + flatFee, + governanceFee, + 0 + ); uint256 contribution = 500_000_000e18; initialize(alice, apr, contribution); @@ -299,7 +307,15 @@ contract NegativeInterestLongFeeTest is HyperdriveTest { ) internal { // Initialize the market uint256 apr = 0.05e18; - deploy(alice, apr, initialSharePrice, curveFee, flatFee, governanceFee); + deploy( + alice, + apr, + initialSharePrice, + curveFee, + flatFee, + governanceFee, + 0 + ); uint256 contribution = 500_000_000e18; initialize(alice, apr, contribution); @@ -491,7 +507,15 @@ contract NegativeInterestLongFeeTest is HyperdriveTest { ) internal { // Initialize the market uint256 apr = 0.05e18; - deploy(alice, apr, initialSharePrice, curveFee, flatFee, governanceFee); + deploy( + alice, + apr, + initialSharePrice, + curveFee, + flatFee, + governanceFee, + 0 + ); uint256 contribution = 500_000_000e18; initialize(alice, apr, contribution); diff --git a/test/integrations/hyperdrive/NegativeInterestShortFeeTest.t.sol b/test/integrations/hyperdrive/NegativeInterestShortFeeTest.t.sol index f27a2fa87..012c9665b 100644 --- a/test/integrations/hyperdrive/NegativeInterestShortFeeTest.t.sol +++ b/test/integrations/hyperdrive/NegativeInterestShortFeeTest.t.sol @@ -105,7 +105,15 @@ contract NegativeInterestShortFeeTest is HyperdriveTest { ) internal { // Initialize the market uint256 apr = 0.05e18; - deploy(alice, apr, initialSharePrice, curveFee, flatFee, governanceFee); + deploy( + alice, + apr, + initialSharePrice, + curveFee, + flatFee, + governanceFee, + 0 + ); uint256 contribution = 500_000_000e18; initialize(alice, apr, contribution); @@ -293,7 +301,15 @@ contract NegativeInterestShortFeeTest is HyperdriveTest { ) internal { // Initialize the market uint256 apr = 0.05e18; - deploy(alice, apr, initialSharePrice, curveFee, flatFee, governanceFee); + deploy( + alice, + apr, + initialSharePrice, + curveFee, + flatFee, + governanceFee, + 0 + ); uint256 contribution = 500_000_000e18; initialize(alice, apr, contribution); @@ -493,7 +509,15 @@ contract NegativeInterestShortFeeTest is HyperdriveTest { ) internal { // Initialize the market uint256 apr = 0.05e18; - deploy(alice, apr, initialSharePrice, curveFee, flatFee, governanceFee); + deploy( + alice, + apr, + initialSharePrice, + curveFee, + flatFee, + governanceFee, + 0 + ); uint256 contribution = 500_000_000e18; initialize(alice, apr, contribution); diff --git a/test/integrations/hyperdrive/RoundTripTest.t.sol b/test/integrations/hyperdrive/RoundTripTest.t.sol index b47ae4976..df9fea37c 100644 --- a/test/integrations/hyperdrive/RoundTripTest.t.sol +++ b/test/integrations/hyperdrive/RoundTripTest.t.sol @@ -135,7 +135,7 @@ contract RoundTripTest is HyperdriveTest { // Deploy the pool and initialize the market { uint256 timeStretchApr = 0.05e18; - deploy(alice, timeStretchApr, 0, 0, 0); + deploy(alice, timeStretchApr, 0, 0, 0, 0); } uint256 contribution = 500_000_000e18; initialize(alice, apr, contribution); @@ -228,7 +228,7 @@ contract RoundTripTest is HyperdriveTest { // Deploy the pool and initialize the market uint256 curveFee = 0.05e18; // 5% of APR uint256 flatFee = 0.0005e18; // 5 bps - deploy(alice, timeStretchApr, curveFee, flatFee, .015e18); + deploy(alice, timeStretchApr, curveFee, flatFee, 0.015e18, 0.015e18); uint256 contribution = 500_000_000e18; initialize(alice, apr, contribution); diff --git a/test/integrations/hyperdrive/SandwichTest.t.sol b/test/integrations/hyperdrive/SandwichTest.t.sol index 283b28e2d..79c7c2af9 100644 --- a/test/integrations/hyperdrive/SandwichTest.t.sol +++ b/test/integrations/hyperdrive/SandwichTest.t.sol @@ -20,7 +20,7 @@ contract SandwichTest is HyperdriveTest { // Deploy the pool and initialize the market { uint256 timeStretchApr = 0.02e18; - deploy(alice, timeStretchApr, 0, 0, 0); + deploy(alice, timeStretchApr, 0, 0, 0, 0); } uint256 contribution = 500_000_000e18; uint256 lpShares = initialize(alice, apr, contribution); @@ -69,7 +69,7 @@ contract SandwichTest is HyperdriveTest { // Deploy the pool and initialize the market { uint256 timeStretchApr = 0.05e18; - deploy(alice, timeStretchApr, 0, 0, 0); + deploy(alice, timeStretchApr, 0, 0, 0, 0); } uint256 contribution = 500_000_000e18; initialize(alice, apr, contribution); @@ -106,7 +106,7 @@ contract SandwichTest is HyperdriveTest { // Deploy the pool and initialize the market { uint256 timeStretchApr = 0.05e18; - deploy(alice, timeStretchApr, 0, 0, 0); + deploy(alice, timeStretchApr, 0, 0, 0, 0); } initialize(alice, apr, contribution); @@ -190,7 +190,7 @@ contract SandwichTest is HyperdriveTest { { uint256 timeStretchApr = 0.02e18; uint256 curveFee = 0.001e18; - deploy(alice, timeStretchApr, curveFee, 0, 0); + deploy(alice, timeStretchApr, curveFee, 0, 0, 0); } // Initialize the market. diff --git a/test/integrations/hyperdrive/VariableInterestLongTest.t.sol b/test/integrations/hyperdrive/VariableInterestLongTest.t.sol index 8dc4436b2..b99d20c13 100644 --- a/test/integrations/hyperdrive/VariableInterestLongTest.t.sol +++ b/test/integrations/hyperdrive/VariableInterestLongTest.t.sol @@ -94,7 +94,7 @@ contract VariableInterestLongTest is HyperdriveTest { ) internal { // Initialize the market uint256 apr = 0.05e18; - deploy(alice, apr, initialSharePrice, 0, 0, 0); + deploy(alice, apr, initialSharePrice, 0, 0, 0, 0); uint256 contribution = 500_000_000e18; initialize(alice, apr, contribution); @@ -248,7 +248,7 @@ contract VariableInterestLongTest is HyperdriveTest { ) internal { // Initialize the market uint256 apr = 0.05e18; - deploy(alice, apr, initialSharePrice, 0, 0, 0); + deploy(alice, apr, initialSharePrice, 0, 0, 0, 0); uint256 contribution = 500_000_000e18; initialize(alice, apr, contribution); @@ -414,7 +414,7 @@ contract VariableInterestLongTest is HyperdriveTest { ) internal { // Initialize the market uint256 apr = 0.05e18; - deploy(alice, apr, initialSharePrice, 0, 0, 0); + deploy(alice, apr, initialSharePrice, 0, 0, 0, 0); uint256 contribution = 500_000_000e18; initialize(alice, apr, contribution); diff --git a/test/integrations/hyperdrive/VariableInterestShortTest.t.sol b/test/integrations/hyperdrive/VariableInterestShortTest.t.sol index 2bffd77b8..d241a24fa 100644 --- a/test/integrations/hyperdrive/VariableInterestShortTest.t.sol +++ b/test/integrations/hyperdrive/VariableInterestShortTest.t.sol @@ -170,7 +170,7 @@ contract VariableInterestShortTest is HyperdriveTest { ) internal { // Initialize the market uint256 apr = 0.05e18; - deploy(alice, apr, initialSharePrice, 0, 0, 0); + deploy(alice, apr, initialSharePrice, 0, 0, 0, 0); uint256 contribution = 500_000_000e18; initialize(alice, apr, contribution); @@ -330,7 +330,7 @@ contract VariableInterestShortTest is HyperdriveTest { ) internal returns (uint256) { // Initialize the market uint256 apr = 0.05e18; - deploy(alice, apr, initialSharePrice, 0, 0, 0); + deploy(alice, apr, initialSharePrice, 0, 0, 0, 0); uint256 contribution = 500_000_000e18; initialize(alice, apr, contribution); @@ -493,7 +493,7 @@ contract VariableInterestShortTest is HyperdriveTest { ) internal { // Initialize the market uint256 apr = 0.05e18; - deploy(alice, apr, initialSharePrice, 0, 0, 0); + deploy(alice, apr, initialSharePrice, 0, 0, 0, 0); uint256 contribution = 500_000_000e18; initialize(alice, apr, contribution); diff --git a/test/integrations/hyperdrive/ZombieInterestTest.t.sol b/test/integrations/hyperdrive/ZombieInterestTest.t.sol index 72d912b15..d01ac7637 100644 --- a/test/integrations/hyperdrive/ZombieInterestTest.t.sol +++ b/test/integrations/hyperdrive/ZombieInterestTest.t.sol @@ -83,7 +83,7 @@ contract ZombieInterestTest is HyperdriveTest { ) internal { // Initialize the pool with capital. uint256 fixedRate = 0.035e18; - deploy(bob, fixedRate, 1e18, 0, 0, 0); + deploy(bob, fixedRate, 1e18, 0, 0, 0, 0); initialize(bob, fixedRate, 2 * MINIMUM_SHARE_RESERVES); // Alice adds liquidity. @@ -272,7 +272,7 @@ contract ZombieInterestTest is HyperdriveTest { ) internal { // Initialize the pool with capital. uint256 fixedRate = 0.035e18; - deploy(bob, fixedRate, 1e18, 0, 0, 0); + deploy(bob, fixedRate, 1e18, 0, 0, 0, 0); initialize(bob, fixedRate, 2 * MINIMUM_SHARE_RESERVES); // Alice adds liquidity. @@ -391,7 +391,7 @@ contract ZombieInterestTest is HyperdriveTest { // This test just demonstrates that shorts redeemed late do not receive zombie interest. function test_zombie_short() external { // Initialize the pool with capital. - deploy(bob, 0.035e18, 1e18, 0, 0, 0); + deploy(bob, 0.035e18, 1e18, 0, 0, 0, 0); initialize(bob, 0.035e18, 2 * MINIMUM_SHARE_RESERVES); // Alice adds liquidity. @@ -464,7 +464,7 @@ contract ZombieInterestTest is HyperdriveTest { uint256 shareReserves1; { // Initialize the pool with capital. - deploy(bob, fixedRate, 1e18, 0, 0, 0); + deploy(bob, fixedRate, 1e18, 0, 0, 0, 0); initialize(bob, fixedRate, 2 * MINIMUM_SHARE_RESERVES); // Alice adds liquidity. @@ -509,7 +509,7 @@ contract ZombieInterestTest is HyperdriveTest { uint256 shareReserves2; { // Initialize the pool with capital. - deploy(bob, fixedRate, 1e18, 0, 0, 0); + deploy(bob, fixedRate, 1e18, 0, 0, 0, 0); assertEq(baseToken.balanceOf(address(hyperdrive)), 0); initialize(bob, fixedRate, 2 * MINIMUM_SHARE_RESERVES); diff --git a/test/units/ForceRevertDelegatecall.t.sol b/test/units/ForceRevertDelegatecall.t.sol index 5a33e1360..4f4895907 100644 --- a/test/units/ForceRevertDelegatecall.t.sol +++ b/test/units/ForceRevertDelegatecall.t.sol @@ -39,7 +39,12 @@ contract DummyHyperdrive is Hyperdrive, MockHyperdriveBase { timeStretch: HyperdriveUtils.calculateTimeStretch(0.05e18), governance: address(0), feeCollector: address(0), - fees: IHyperdrive.Fees({ curve: 0, flat: 0, governance: 0 }) + fees: IHyperdrive.Fees({ + curve: 0, + flat: 0, + governanceLP: 0, + governanceZombie: 0 + }) }), address(new DummyProvider()), address(0) diff --git a/test/units/hyperdrive/CloseLongTest.t.sol b/test/units/hyperdrive/CloseLongTest.t.sol index 236ad3557..e65b2175c 100644 --- a/test/units/hyperdrive/CloseLongTest.t.sol +++ b/test/units/hyperdrive/CloseLongTest.t.sol @@ -705,7 +705,8 @@ contract CloseLongTest is HyperdriveTest { config.fees = IHyperdrive.Fees({ curve: 0, flat: 0.01e18, - governance: 1e18 + governanceLP: 1e18, + governanceZombie: 1e18 }); deploy(address(deployer), config); initialize(alice, fixedRate, contribution); @@ -728,7 +729,8 @@ contract CloseLongTest is HyperdriveTest { config.fees = IHyperdrive.Fees({ curve: 0, flat: 0.01e18, - governance: 0 + governanceLP: 0, + governanceZombie: 0 }); // Deploy and initialize the new pool deploy(address(deployer), config); diff --git a/test/units/hyperdrive/CloseShortTest.t.sol b/test/units/hyperdrive/CloseShortTest.t.sol index 4cedc6c74..67d4171d7 100644 --- a/test/units/hyperdrive/CloseShortTest.t.sol +++ b/test/units/hyperdrive/CloseShortTest.t.sol @@ -503,7 +503,8 @@ contract CloseShortTest is HyperdriveTest { config.fees = IHyperdrive.Fees({ curve: 0, flat: 1e18, - governance: 1e18 + governanceLP: 1e18, + governanceZombie: 1e18 }); deploy(address(deployer), config); initialize(alice, fixedRate, contribution); @@ -534,7 +535,12 @@ contract CloseShortTest is HyperdriveTest { // 7. deploy a pool with 100% curve fees and 0% gov fees config = testConfig(fixedRate); - config.fees = IHyperdrive.Fees({ curve: 0, flat: 1e18, governance: 0 }); + config.fees = IHyperdrive.Fees({ + curve: 0, + flat: 1e18, + governanceLP: 0, + governanceZombie: 0 + }); // Deploy and initialize the new pool deploy(address(deployer), config); initialize(alice, fixedRate, contribution); @@ -574,7 +580,12 @@ contract CloseShortTest is HyperdriveTest { // Initialize a pool with no flat fee as a baseline IHyperdrive.PoolConfig memory config = testConfig(fixedRate); - config.fees = IHyperdrive.Fees({ curve: 0, flat: 0, governance: 0 }); + config.fees = IHyperdrive.Fees({ + curve: 0, + flat: 0, + governanceLP: 0, + governanceZombie: 0 + }); deploy(address(deployer), config); initialize(alice, fixedRate, contribution); @@ -603,7 +614,12 @@ contract CloseShortTest is HyperdriveTest { // Configure a pool with a 100% flatFee config = testConfig(fixedRate); - config.fees = IHyperdrive.Fees({ curve: 0, flat: 1e18, governance: 0 }); + config.fees = IHyperdrive.Fees({ + curve: 0, + flat: 1e18, + governanceLP: 0, + governanceZombie: 0 + }); // Deploy and initialize the new pool deploy(address(deployer), config); initialize(alice, fixedRate, contribution); @@ -643,7 +659,7 @@ contract CloseShortTest is HyperdriveTest { uint256 shortProceeds1; { // Initialize the pool with capital. - deploy(bob, 0.035e18, 1e18, 0, 0, 0); + deploy(bob, 0.035e18, 1e18, 0, 0, 0, 0); initialize(bob, 0.035e18, 2 * MINIMUM_SHARE_RESERVES); // Alice adds liquidity. @@ -663,7 +679,7 @@ contract CloseShortTest is HyperdriveTest { uint256 shortProceeds2; { // Initialize the pool with capital. - deploy(bob, 0.035e18, 1e18, 0, 0, 0); + deploy(bob, 0.035e18, 1e18, 0, 0, 0, 0); initialize(bob, 0.035e18, 2 * MINIMUM_SHARE_RESERVES); // Alice adds liquidity. @@ -686,7 +702,7 @@ contract CloseShortTest is HyperdriveTest { uint256 shortProceeds3; { // Initialize the pool with capital. - deploy(bob, 0.035e18, 1e18, 0, 0, 0); + deploy(bob, 0.035e18, 1e18, 0, 0, 0, 0); initialize(bob, 0.035e18, 2 * MINIMUM_SHARE_RESERVES); // Alice adds liquidity. @@ -709,7 +725,7 @@ contract CloseShortTest is HyperdriveTest { uint256 shortProceeds4; { // Initialize the pool with capital. - deploy(bob, 0.035e18, 1e18, 0, 0, 0); + deploy(bob, 0.035e18, 1e18, 0, 0, 0, 0); initialize(bob, 0.035e18, 2 * MINIMUM_SHARE_RESERVES); // Alice adds liquidity. diff --git a/test/units/hyperdrive/ExtremeInputs.t.sol b/test/units/hyperdrive/ExtremeInputs.t.sol index d44a06036..2ea41b5f4 100644 --- a/test/units/hyperdrive/ExtremeInputs.t.sol +++ b/test/units/hyperdrive/ExtremeInputs.t.sol @@ -204,7 +204,7 @@ contract ExtremeInputs is HyperdriveTest { minimumShareReserves: targetReserves, curveFee: poolConfig.fees.curve, flatFee: poolConfig.fees.flat, - governanceFee: poolConfig.fees.governance + governanceLPFee: poolConfig.fees.governanceLP }), hyperdrive.getCheckpointExposure(hyperdrive.latestCheckpoint()), 7 diff --git a/test/units/hyperdrive/FeeTest.t.sol b/test/units/hyperdrive/FeeTest.t.sol index 1cedc809c..fe38a61d1 100644 --- a/test/units/hyperdrive/FeeTest.t.sol +++ b/test/units/hyperdrive/FeeTest.t.sol @@ -15,13 +15,21 @@ contract FeeTest is HyperdriveTest { using Lib for *; uint256 deployCurveFee = 0.1e18; // 10% uint256 deployFlatFee = 0.01e18; // 0.1% - uint256 deployGovernanceFee = 0.5e18; // 50% + uint256 deployGovernanceLPFee = 0.5e18; // 50% + uint256 deployGovernanceZombieFee = 0.5e18; // 50% function test_governanceFeeAccrual_invalidFeeDestination_failure() public { // Deploy and initialize a new pool with fees. uint256 apr = 0.05e18; uint256 contribution = 500_000_000e18; - deploy(alice, apr, deployCurveFee, deployFlatFee, deployGovernanceFee); + deploy( + alice, + apr, + deployCurveFee, + deployFlatFee, + deployGovernanceLPFee, + deployGovernanceZombieFee + ); initialize(alice, apr, contribution); // Open a long and ensure that the governance fees accrued are non-zero. @@ -48,7 +56,14 @@ contract FeeTest is HyperdriveTest { uint256 contribution = 500_000_000e18; // Deploy and initialize a new pool with fees. - deploy(alice, apr, deployCurveFee, deployFlatFee, deployGovernanceFee); + deploy( + alice, + apr, + deployCurveFee, + deployFlatFee, + deployGovernanceLPFee, + deployGovernanceZombieFee + ); initialize(alice, apr, contribution); // Open a long, record the accrued fees x share price @@ -77,13 +92,74 @@ contract FeeTest is HyperdriveTest { assertGt(governanceBalanceAfter, governanceFeesAfterOpenLong); } + function test_zombie_interest_governance_fee() external { + uint256 initialSharePrice = 1e18; + + // Initialize the market + uint256 apr = 0.05e18; + // Set zombie fee to 100% to verify it works. + uint256 governanceZombieFee = 1e18; + deploy(alice, apr, initialSharePrice, 0, 0, 0, governanceZombieFee); + uint256 contribution = 100e18; + uint256 aliceLpShares = initialize(alice, apr, contribution); + + // Open a long. + uint256 basePaidLong = 10e18; + (uint256 maturityTimeLong, uint256 bondAmountLong) = openLong( + alice, + basePaidLong + ); + + // Wait two term lengths. + advanceTimeWithCheckpoints(POSITION_DURATION * 2, int256(apr)); + + // Close the long 1 term too late. + closeLong(alice, maturityTimeLong, bondAmountLong); + + // Verify that the value represented in the share reserves is <= the actual amount in the contract. + uint256 sharePrice = hyperdrive.getPoolInfo().sharePrice; + uint256 governanceFeesAccrued = IMockHyperdrive(address(hyperdrive)) + .getGovernanceFeesAccrued(); + uint256 baseReserves = hyperdrive.getPoolInfo().shareReserves.mulDown( + sharePrice + ); + uint256 zombieShareReserves = hyperdrive + .getPoolInfo() + .zombieShareReserves; + uint256 expectedBalance = baseReserves + + governanceFeesAccrued.mulDown(sharePrice) + + zombieShareReserves.mulDown(sharePrice); + assertApproxEqAbs( + baseToken.balanceOf(address(hyperdrive)), + expectedBalance, + 2 wei + ); + + // Verify that the governance fees accrued as expected. + (, int256 expectedGovernanceFeesAccrued) = HyperdriveUtils + .calculateCompoundInterest( + bondAmountLong, + int256(apr), + POSITION_DURATION + ); + assertApproxEqAbs( + governanceFeesAccrued.mulDown(sharePrice), + uint256(expectedGovernanceFeesAccrued), + 1e4 + ); + + // Governance zombie fees should be greater than 0. + assertGt(governanceFeesAccrued, 0); + } + // This test demonstrates that the governance fees from flat fee are NOT included in the shareReserves. function test_flat_gov_fee_close_long() public { uint256 initialSharePrice = 1e18; int256 variableInterest = 0.0e18; uint256 curveFee = 0e18; // 0% uint256 flatFee = 0.001e18; // 0.1% - uint256 governanceFee = 1e18; // 100% + uint256 governanceLPFee = 1e18; // 100% + uint256 governanceZombieFee = 0; // 0% uint256 timeElapsed = 73 days; uint256 governanceFees = 0; @@ -98,7 +174,8 @@ contract FeeTest is HyperdriveTest { initialSharePrice, curveFee, flatFee, - governanceFee + governanceLPFee, + governanceZombieFee ); uint256 contribution = 500_000_000e18; initialize(alice, apr, contribution); @@ -124,7 +201,8 @@ contract FeeTest is HyperdriveTest { ).getGovernanceFeesAccrued(); // 1/2 term matures and accrues interest - advanceTime(timeElapsed, variableInterest); + int256 _variableInterest = variableInterest; // Stack too deep error + advanceTime(timeElapsed, _variableInterest); // Close the long. closeLong(bob, maturityTime, bondAmount); @@ -141,7 +219,7 @@ contract FeeTest is HyperdriveTest { uint256 shareReservesFlatFee = 0; { uint256 apr = 0.01e18; - deploy(alice, apr, initialSharePrice, curveFee, flatFee, 0); + deploy(alice, apr, initialSharePrice, curveFee, flatFee, 0, 0); uint256 contribution = 500_000_000e18; initialize(alice, apr, contribution); @@ -161,7 +239,8 @@ contract FeeTest is HyperdriveTest { ); // 1/2 term matures and accrues interest - advanceTime(timeElapsed, variableInterest); + int256 _variableInterest = variableInterest; // Stack too deep error + advanceTime(timeElapsed, _variableInterest); // Close the long. closeLong(bob, maturityTime, bondAmount); @@ -187,8 +266,9 @@ contract FeeTest is HyperdriveTest { function test_curve_gov_fee_close_long() public { uint256 initialSharePrice = 1e18; uint256 curveFee = 0.1e18; // 10% - uint256 flatFee = 0e18; // 0% - uint256 governanceFee = 1e18; // 100% + uint256 flatFee = 0; // 0% + uint256 governanceLPFee = 1e18; // 100% + uint256 governanceZombieFee = 0; // 0% uint256 timeElapsed = 73 days; uint256 governanceFeesFromCloseLong = 0; @@ -205,7 +285,8 @@ contract FeeTest is HyperdriveTest { initialSharePrice, curveFee, flatFee, - governanceFee + governanceLPFee, + governanceZombieFee ); uint256 contribution = 500_000_000e18; initialize(alice, apr, contribution); @@ -252,8 +333,9 @@ contract FeeTest is HyperdriveTest { // Calculate curve fee uint256 expectedFeeSubtractedFromShareReserves = ONE - spotPrice; + uint256 _curveFee = curveFee; expectedFeeSubtractedFromShareReserves = expectedFeeSubtractedFromShareReserves - .mulDown(curveFee) + .mulDown(_curveFee) .mulDown(bondsPurchased) .mulDown(normalizedTimeRemaining); @@ -268,7 +350,7 @@ contract FeeTest is HyperdriveTest { uint256 shareReservesCurveFee = 0; { uint256 apr = 0.01e18; - deploy(alice, apr, initialSharePrice, curveFee, flatFee, 0); + deploy(alice, apr, initialSharePrice, curveFee, flatFee, 0, 0); uint256 contribution = 500_000_000e18; initialize(alice, apr, contribution); @@ -332,7 +414,14 @@ contract FeeTest is HyperdriveTest { uint256 contribution = 500_000_000e18; // Deploy and initialize a new pool with fees. - deploy(alice, apr, deployCurveFee, deployFlatFee, deployGovernanceFee); + deploy( + alice, + apr, + deployCurveFee, + deployFlatFee, + deployGovernanceLPFee, + deployGovernanceZombieFee + ); initialize(alice, apr, contribution); // Ensure that the governance initially has zero balance @@ -395,7 +484,14 @@ contract FeeTest is HyperdriveTest { uint256 contribution = 500_000_000e18; // Deploy and initialize a new pool with fees. - deploy(alice, apr, deployCurveFee, deployFlatFee, deployGovernanceFee); + deploy( + alice, + apr, + deployCurveFee, + deployFlatFee, + deployGovernanceLPFee, + deployGovernanceZombieFee + ); initialize(alice, apr, contribution); // Ensure that the governance initially has zero balance @@ -465,7 +561,14 @@ contract FeeTest is HyperdriveTest { // Initialize the pool with a large amount of capital. uint256 contribution = 500_000_000e18; // Deploy and initialize a new pool with fees. - deploy(alice, apr, deployCurveFee, deployFlatFee, deployGovernanceFee); + deploy( + alice, + apr, + deployCurveFee, + deployFlatFee, + deployGovernanceLPFee, + deployGovernanceZombieFee + ); initialize(alice, apr, contribution); (uint256 curveFee, uint256 governanceCurveFee) = MockHyperdrive( @@ -488,7 +591,14 @@ contract FeeTest is HyperdriveTest { // Initialize the pool with a large amount of capital. uint256 contribution = 500_000_000e18; // Deploy and initialize a new pool with fees. - deploy(alice, apr, deployCurveFee, 0.1e18, deployGovernanceFee); + deploy( + alice, + apr, + deployCurveFee, + 0.1e18, + deployGovernanceLPFee, + deployGovernanceZombieFee + ); initialize(alice, apr, contribution); ( uint256 curveFee, @@ -527,7 +637,14 @@ contract FeeTest is HyperdriveTest { // Initialize the pool with a large amount of capital. uint256 contribution = 500_000_000e18; // Deploy and initialize a new pool with fees. - deploy(alice, apr, deployCurveFee, 0.1e18, deployGovernanceFee); + deploy( + alice, + apr, + deployCurveFee, + 0.1e18, + deployGovernanceLPFee, + deployGovernanceZombieFee + ); initialize(alice, apr, contribution); ( uint256 curveFee, diff --git a/test/units/hyperdrive/HyperdriveDeploy.t.sol b/test/units/hyperdrive/HyperdriveDeploy.t.sol index 9d7e0fecf..475b4a4bc 100644 --- a/test/units/hyperdrive/HyperdriveDeploy.t.sol +++ b/test/units/hyperdrive/HyperdriveDeploy.t.sol @@ -38,8 +38,8 @@ contract HyperdriveFactoryTest is HyperdriveTest { hyperdriveGovernance: bob, defaultPausers: defaults, feeCollector: bob, - fees: IHyperdrive.Fees(0, 0, 0), - maxFees: IHyperdrive.Fees(1e18, 1e18, 1e18), + fees: IHyperdrive.Fees(0, 0, 0, 0), + maxFees: IHyperdrive.Fees(1e18, 1e18, 1e18, 1e18), linkerFactory: address(forwarderFactory), linkerCodeHash: forwarderFactory.ERC20LINK_HASH() }) @@ -63,7 +63,7 @@ contract HyperdriveFactoryTest is HyperdriveTest { vm.expectRevert(IHyperdrive.Unauthorized.selector); factory.updateFeeCollector(bob); vm.expectRevert(IHyperdrive.Unauthorized.selector); - factory.updateFees(IHyperdrive.Fees(1, 2, 4)); + factory.updateFees(IHyperdrive.Fees(1, 2, 4, 5)); vm.expectRevert(IHyperdrive.Unauthorized.selector); factory.updateDefaultPausers(defaults); vm.expectRevert(IHyperdrive.Unauthorized.selector); @@ -86,11 +86,17 @@ contract HyperdriveFactoryTest is HyperdriveTest { assertEq(factory.linkerFactory(), address(uint160(0xdeadbeef))); factory.updateLinkerCodeHash(bytes32(uint256(0xdeadbeef))); assertEq(factory.linkerCodeHash(), bytes32(uint256(0xdeadbeef))); - factory.updateFees(IHyperdrive.Fees(1, 2, 3)); - (uint256 curve, uint256 flat, uint256 govFee) = factory.fees(); + factory.updateFees(IHyperdrive.Fees(1, 2, 3, 4)); + ( + uint256 curve, + uint256 flat, + uint256 govLPFee, + uint256 govZombieFee + ) = factory.fees(); assertEq(curve, 1); assertEq(flat, 2); - assertEq(govFee, 3); + assertEq(govLPFee, 3); + assertEq(govZombieFee, 4); defaults[0] = alice; factory.updateDefaultPausers(defaults); address[] memory updateDefaultPausers = factory.getDefaultPausers(); diff --git a/test/units/hyperdrive/OpenLongTest.t.sol b/test/units/hyperdrive/OpenLongTest.t.sol index 3cd94e30e..7c62f20c6 100644 --- a/test/units/hyperdrive/OpenLongTest.t.sol +++ b/test/units/hyperdrive/OpenLongTest.t.sol @@ -369,7 +369,7 @@ contract OpenLongTest is HyperdriveTest { // Deploy and initialize a new pool with fees. // Curve fee is set to 10%. // Flat fee is set to 1%. - deploy(alice, apr, 0.1e18, 0.01e18, 0); + deploy(alice, apr, 0.1e18, 0.01e18, 0, 0); initialize(alice, apr, contribution); // Open a long with fees. diff --git a/test/units/hyperdrive/OpenShortTest.t.sol b/test/units/hyperdrive/OpenShortTest.t.sol index c742e77e6..ce2fd1655 100644 --- a/test/units/hyperdrive/OpenShortTest.t.sol +++ b/test/units/hyperdrive/OpenShortTest.t.sol @@ -224,7 +224,8 @@ contract OpenShortTest is HyperdriveTest { config.fees = IHyperdrive.Fees({ curve: 1e18, flat: 1e18, - governance: 1e18 + governanceLP: 1e18, + governanceZombie: 1e18 }); deploy(address(deployer), config); initialize(alice, apr, contribution); @@ -255,7 +256,12 @@ contract OpenShortTest is HyperdriveTest { // 7. deploy a pool with 100% curve fees and 0% gov fees config = testConfig(apr); - config.fees = IHyperdrive.Fees({ curve: 1e18, flat: 0, governance: 0 }); + config.fees = IHyperdrive.Fees({ + curve: 1e18, + flat: 0, + governanceLP: 0, + governanceZombie: 0 + }); // Deploy and initialize the new pool deploy(address(deployer), config); initialize(alice, apr, contribution); @@ -283,7 +289,8 @@ contract OpenShortTest is HyperdriveTest { // governance fees of 0%. IHyperdrive.PoolConfig memory config = testConfig(fixedRate); config.fees.curve = 1e18; - config.fees.governance = 0; + config.fees.governanceLP = 0; + config.fees.governanceZombie = 0; deploy(address(deployer), config); initialize(alice, fixedRate, contribution); @@ -295,7 +302,8 @@ contract OpenShortTest is HyperdriveTest { // governance fees of 100%. config = testConfig(fixedRate); config.fees.curve = 1e18; - config.fees.governance = 1e18; + config.fees.governanceLP = 1e18; + config.fees.governanceZombie = 1e18; deploy(address(deployer), config); initialize(alice, fixedRate, contribution); diff --git a/test/units/libraries/HyperdriveMath.t.sol b/test/units/libraries/HyperdriveMath.t.sol index 9cd31fb32..604f43b71 100644 --- a/test/units/libraries/HyperdriveMath.t.sol +++ b/test/units/libraries/HyperdriveMath.t.sol @@ -829,7 +829,7 @@ contract HyperdriveMathTest is HyperdriveTest { ) external { // Deploy Hyperdrive. fixedRate = fixedRate.normalizeToRange(0.001e18, 0.5e18); - deploy(alice, fixedRate, 0, 0, 0); + deploy(alice, fixedRate, 0, 0, 0, 0); // Initialize the Hyperdrive pool. contribution = contribution.normalizeToRange(1_000e18, 500_000_000e18); @@ -863,7 +863,7 @@ contract HyperdriveMathTest is HyperdriveTest { ) external { // Deploy Hyperdrive. fixedRate = fixedRate.normalizeToRange(0.001e18, 0.5e18); - deploy(alice, fixedRate, 0, 0, 0); + deploy(alice, fixedRate, 0, 0, 0, 0); // Initialize the Hyperdrive pool. contribution = contribution.normalizeToRange(1_000e18, 500_000_000e18); @@ -925,7 +925,7 @@ contract HyperdriveMathTest is HyperdriveTest { ) internal { // Deploy Hyperdrive. fixedRate = fixedRate.normalizeToRange(0.001e18, 0.5e18); - deploy(alice, fixedRate, 0, 0, 0); + deploy(alice, fixedRate, 0, 0, 0, 0); // Initialize the Hyperdrive pool. contribution = contribution.normalizeToRange(1_000e18, 500_000_000e18); @@ -985,7 +985,7 @@ contract HyperdriveMathTest is HyperdriveTest { minimumShareReserves: config.minimumShareReserves, curveFee: config.fees.curve, flatFee: config.fees.flat, - governanceFee: config.fees.governance + governanceLPFee: config.fees.governanceLP }), hyperdrive.getCheckpointExposure(hyperdrive.latestCheckpoint()), maxIterations @@ -1142,7 +1142,7 @@ contract HyperdriveMathTest is HyperdriveTest { minimumShareReserves: config.minimumShareReserves, curveFee: config.fees.curve, flatFee: config.fees.flat, - governanceFee: config.fees.governance + governanceLPFee: config.fees.governanceLP }), hyperdrive.getCheckpointExposure(hyperdrive.latestCheckpoint()), 7 diff --git a/test/units/libraries/YieldSpaceMath.t.sol b/test/units/libraries/YieldSpaceMath.t.sol index 90a7b74ec..a15552af5 100644 --- a/test/units/libraries/YieldSpaceMath.t.sol +++ b/test/units/libraries/YieldSpaceMath.t.sol @@ -149,7 +149,7 @@ contract YieldSpaceMathTest is Test { minimumShareReserves: minimumShareReserves, curveFee: 0, flatFee: 0, - governanceFee: 0 + governanceLPFee: 0 }), 0, 15 diff --git a/test/utils/HyperdriveTest.sol b/test/utils/HyperdriveTest.sol index f9e1877a9..95258299a 100644 --- a/test/utils/HyperdriveTest.sol +++ b/test/utils/HyperdriveTest.sol @@ -68,7 +68,8 @@ contract HyperdriveTest is BaseTest { uint256 apr, uint256 curveFee, uint256 flatFee, - uint256 governanceFee + uint256 governanceLPFee, + uint256 governanceZombieFee ) internal { deploy( deployer, @@ -76,7 +77,8 @@ contract HyperdriveTest is BaseTest { INITIAL_SHARE_PRICE, curveFee, flatFee, - governanceFee + governanceLPFee, + governanceZombieFee ); } @@ -86,13 +88,15 @@ contract HyperdriveTest is BaseTest { uint256 initialSharePrice, uint256 curveFee, uint256 flatFee, - uint256 governanceFee + uint256 governanceLPFee, + uint256 governanceZombieFee ) internal { IHyperdrive.PoolConfig memory config = testConfig(apr); config.initialSharePrice = initialSharePrice; config.fees.curve = curveFee; config.fees.flat = flatFee; - config.fees.governance = governanceFee; + config.fees.governanceLP = governanceLPFee; + config.fees.governanceZombie = governanceZombieFee; deploy(deployer, config); } @@ -102,7 +106,8 @@ contract HyperdriveTest is BaseTest { IHyperdrive.Fees memory fees = IHyperdrive.Fees({ curve: 0, flat: 0, - governance: 0 + governanceLP: 0, + governanceZombie: 0 }); return IHyperdrive.PoolConfig({ diff --git a/test/utils/HyperdriveUtils.sol b/test/utils/HyperdriveUtils.sol index 32994c888..d643cb4a7 100644 --- a/test/utils/HyperdriveUtils.sol +++ b/test/utils/HyperdriveUtils.sol @@ -190,7 +190,7 @@ library HyperdriveUtils { minimumShareReserves: poolConfig.minimumShareReserves, curveFee: poolConfig.fees.curve, flatFee: poolConfig.fees.flat, - governanceFee: poolConfig.fees.governance + governanceLPFee: poolConfig.fees.governanceLP }), _hyperdrive.getCheckpointExposure(_hyperdrive.latestCheckpoint()), _maxIterations @@ -231,7 +231,7 @@ library HyperdriveUtils { minimumShareReserves: poolConfig.minimumShareReserves, curveFee: poolConfig.fees.curve, flatFee: poolConfig.fees.flat, - governanceFee: poolConfig.fees.governance + governanceLPFee: poolConfig.fees.governanceLP }), _hyperdrive.getCheckpointExposure( _hyperdrive.latestCheckpoint() @@ -261,7 +261,7 @@ library HyperdriveUtils { uint256 minimumShareReserves; uint256 curveFee; uint256 flatFee; - uint256 governanceFee; + uint256 governanceLPFee; } /// @dev Gets the max long that can be opened given a budget. @@ -613,7 +613,7 @@ library HyperdriveUtils { _params.minimumShareReserves).mulDivDown(_params.sharePrice, 2e18); estimate = estimate.divDown( ONE.divDown(_estimatePrice) + - _params.governanceFee.mulDown(_params.curveFee).mulDown( + _params.governanceLPFee.mulDown(_params.curveFee).mulDown( ONE - _spotPrice ) - ONE - @@ -680,7 +680,7 @@ library HyperdriveUtils { _baseAmount, _spotPrice, _params.curveFee, - _params.governanceFee + _params.governanceLPFee ); uint256 shareReserves = _params.shareReserves + _baseAmount.divDown(_params.sharePrice) - @@ -750,7 +750,7 @@ library HyperdriveUtils { } // Finish computing the derivative. - derivative += _params.governanceFee.mulDown(_params.curveFee).mulDown( + derivative += _params.governanceLPFee.mulDown(_params.curveFee).mulDown( ONE - _spotPrice ); derivative -= ONE; @@ -910,15 +910,15 @@ library HyperdriveUtils { /// @param _baseAmount The base amount, $x$. /// @param _spotPrice The spot price, $p$. /// @param _curveFee The curve fee, $\phi_{c}$. - /// @param _governanceFee The governance fee, $\phi_{g}$. + /// @param _governanceLPFee The governance fee, $\phi_{g}$. function calculateLongGovernanceFee( uint256 _baseAmount, uint256 _spotPrice, uint256 _curveFee, - uint256 _governanceFee + uint256 _governanceLPFee ) internal pure returns (uint256) { return - _governanceFee.mulDown(_spotPrice).mulDown( + _governanceLPFee.mulDown(_spotPrice).mulDown( calculateLongCurveFee(_baseAmount, _spotPrice, _curveFee) ); } @@ -1151,7 +1151,7 @@ library HyperdriveUtils { guess.divDown( estimatePrice - _params.curveFee.mulDown(ONE - _spotPrice) + - _params.governanceFee.mulDown(_params.curveFee).mulDown( + _params.governanceLPFee.mulDown(_params.curveFee).mulDown( ONE - _spotPrice ) ); @@ -1237,7 +1237,7 @@ library HyperdriveUtils { _shortAmount, _spotPrice, _params.curveFee, - _params.governanceFee + _params.governanceLPFee )).divDown(_params.sharePrice)); uint256 exposure = (_params.longExposure - uint256(_checkpointExposure.max(0))).divDown(_params.sharePrice); @@ -1284,7 +1284,7 @@ library HyperdriveUtils { uint256 rhs = _params .curveFee .mulDown(ONE - _spotPrice) - .mulDown(ONE - _params.governanceFee) + .mulDown(ONE - _params.governanceLPFee) .divDown(_params.sharePrice); if (lhs >= rhs) { return (lhs - rhs, true); @@ -1354,16 +1354,16 @@ library HyperdriveUtils { /// @param _bondAmount The bond amount. /// @param _spotPrice The spot price. /// @param _curveFee The curve fee parameter. - /// @param _governanceFee The governance fee parameter. + /// @param _governanceLPFee The governance fee parameter. /// @return The governance fee. function calculateShortGovernanceFee( uint256 _bondAmount, uint256 _spotPrice, uint256 _curveFee, - uint256 _governanceFee + uint256 _governanceLPFee ) internal pure returns (uint256) { return - _governanceFee.mulDown( + _governanceLPFee.mulDown( calculateShortCurveFee(_bondAmount, _spotPrice, _curveFee) ); } From 1dd503068719227b562afe0b86ffaaa8a5b08985 Mon Sep 17 00:00:00 2001 From: jonny rhea Date: Sun, 17 Dec 2023 21:17:28 -0600 Subject: [PATCH 2/9] fix rust --- contracts/src/factory/HyperdriveFactory.sol | 3 ++- crates/hyperdrive-math/src/lib.rs | 7 ++++--- crates/hyperdrive-math/src/long/fees.rs | 2 +- crates/hyperdrive-math/src/long/max.rs | 8 ++++---- crates/hyperdrive-math/src/short/fees.rs | 2 +- crates/hyperdrive-math/src/short/max.rs | 6 +++--- crates/hyperdrive-math/src/short/open.rs | 2 +- crates/test-utils/src/chain/test_chain.rs | 6 ++++-- 8 files changed, 20 insertions(+), 16 deletions(-) diff --git a/contracts/src/factory/HyperdriveFactory.sol b/contracts/src/factory/HyperdriveFactory.sol index 685bf94e7..06ea2980f 100644 --- a/contracts/src/factory/HyperdriveFactory.sol +++ b/contracts/src/factory/HyperdriveFactory.sol @@ -123,7 +123,8 @@ contract HyperdriveFactory { maxFlatFee = _factoryConfig.maxFees.flat; maxGovernanceLPFee = _factoryConfig.maxFees.governanceLP; maxGovernanceZombieFee = _factoryConfig.maxFees.governanceZombie; - if (maxCurveFee > ONE || maxFlatFee > ONE || maxGovernanceLPFee > ONE || maxGovernanceZombieFee > ONE) { + if (maxCurveFee > ONE || maxFlatFee > ONE || maxGovernanceLPFee > ONE + || maxGovernanceZombieFee > ONE) { revert IHyperdrive.MaxFeeTooHigh(); } if ( diff --git a/crates/hyperdrive-math/src/lib.rs b/crates/hyperdrive-math/src/lib.rs index 682d24ade..7a3333967 100644 --- a/crates/hyperdrive-math/src/lib.rs +++ b/crates/hyperdrive-math/src/lib.rs @@ -36,7 +36,8 @@ impl Distribution for Standard { fees: Fees { curve: rng.gen_range(fixed!(0.0001e18)..=fixed!(0.2e18)).into(), flat: rng.gen_range(fixed!(0.0001e18)..=fixed!(0.2e18)).into(), - governance: rng.gen_range(fixed!(0.0001e18)..=fixed!(0.2e18)).into(), + governance_lp: rng.gen_range(fixed!(0.0001e18)..=fixed!(0.2e18)).into(), + governance_zombie: rng.gen_range(fixed!(0.0001e18)..=fixed!(0.2e18)).into(), }, initial_share_price: rng.gen_range(fixed!(0.5e18)..=fixed!(2.5e18)).into(), minimum_share_reserves: rng.gen_range(fixed!(0.1e18)..=fixed!(1e18)).into(), @@ -167,8 +168,8 @@ impl State { self.config.fees.flat.into() } - fn governance_fee(&self) -> FixedPoint { - self.config.fees.governance.into() + fn governance_lp_fee(&self) -> FixedPoint { + self.config.fees.governance_lp.into() } /// Info /// diff --git a/crates/hyperdrive-math/src/long/fees.rs b/crates/hyperdrive-math/src/long/fees.rs index 3e531fe82..c5b66b870 100644 --- a/crates/hyperdrive-math/src/long/fees.rs +++ b/crates/hyperdrive-math/src/long/fees.rs @@ -26,7 +26,7 @@ impl State { /// g(x) = \phi_{g} \cdot p \cdot c(x) /// $$ pub fn open_long_governance_fee(&self, base_amount: FixedPoint) -> FixedPoint { - self.governance_fee() * self.get_spot_price() * self.open_long_curve_fees(base_amount) + self.governance_lp_fee() * self.get_spot_price() * self.open_long_curve_fees(base_amount) } /// Gets the curve fee paid by longs for a given bond amount. diff --git a/crates/hyperdrive-math/src/long/max.rs b/crates/hyperdrive-math/src/long/max.rs index 94638415a..91e430f33 100644 --- a/crates/hyperdrive-math/src/long/max.rs +++ b/crates/hyperdrive-math/src/long/max.rs @@ -290,7 +290,7 @@ impl State { let mut estimate = self.get_solvency() + checkpoint_exposure / self.share_price(); estimate = estimate.mul_div_down(self.share_price(), fixed!(2e18)); estimate /= fixed!(1e18) / estimate_price - + self.governance_fee() * self.curve_fee() * (fixed!(1e18) - spot_price) + + self.governance_lp_fee() * self.curve_fee() * (fixed!(1e18) - spot_price) - fixed!(1e18) - self.curve_fee() * (fixed!(1e18) / spot_price - fixed!(1e18)); estimate @@ -375,7 +375,7 @@ impl State { let maybe_derivative = self.long_amount_derivative(base_amount); maybe_derivative.map(|derivative| { (derivative - + self.governance_fee() * self.curve_fee() * (fixed!(1e18) - self.get_spot_price()) + + self.governance_lp_fee() * self.curve_fee() * (fixed!(1e18) - self.get_spot_price()) - fixed!(1e18)) .mul_div_down(fixed!(1e18), self.share_price()) }) @@ -480,7 +480,7 @@ mod tests { minimum_share_reserves: state.config.minimum_share_reserves, curve_fee: state.config.fees.curve, flat_fee: state.config.fees.flat, - governance_fee: state.config.fees.governance, + governance_lp_fee: state.config.fees.governance_lp, }, get_effective_share_reserves( state.info.share_reserves.into(), @@ -544,7 +544,7 @@ mod tests { minimum_share_reserves: state.config.minimum_share_reserves, curve_fee: state.config.fees.curve, flat_fee: state.config.fees.flat, - governance_fee: state.config.fees.governance, + governance_lp_fee: state.config.fees.governance_lp, }, checkpoint_exposure, uint256!(7), diff --git a/crates/hyperdrive-math/src/short/fees.rs b/crates/hyperdrive-math/src/short/fees.rs index 8df570a3f..05e18364c 100644 --- a/crates/hyperdrive-math/src/short/fees.rs +++ b/crates/hyperdrive-math/src/short/fees.rs @@ -19,7 +19,7 @@ impl State { short_amount: FixedPoint, spot_price: FixedPoint, ) -> FixedPoint { - self.governance_fee() * self.open_short_curve_fee(short_amount, spot_price) + self.governance_lp_fee() * self.open_short_curve_fee(short_amount, spot_price) } /// Gets the curve fee paid by shorts for a given bond amount. diff --git a/crates/hyperdrive-math/src/short/max.rs b/crates/hyperdrive-math/src/short/max.rs index cf597d53c..c0c51eb8c 100644 --- a/crates/hyperdrive-math/src/short/max.rs +++ b/crates/hyperdrive-math/src/short/max.rs @@ -337,7 +337,7 @@ impl State { FixedPoint::from(checkpoint_exposure.max(I256::zero())) / self.share_price(); (self.share_price() * (self.get_solvency() + checkpoint_exposure)) / (estimate_price - self.curve_fee() * (fixed!(1e18) - spot_price) - + self.governance_fee() * self.curve_fee() * (fixed!(1e18) - spot_price)) + + self.governance_lp_fee() * self.curve_fee() * (fixed!(1e18) - spot_price)) } /// Gets the derivative of the short deposit function with respect to the @@ -451,7 +451,7 @@ impl State { ) -> Option { let lhs = self.short_principal_derivative(short_amount); let rhs = - self.curve_fee() * (fixed!(1e18) - spot_price) * (fixed!(1e18) - self.governance_fee()) + self.curve_fee() * (fixed!(1e18) - spot_price) * (fixed!(1e18) - self.governance_lp_fee()) / self.share_price(); if lhs >= rhs { Some(lhs - rhs) @@ -567,7 +567,7 @@ mod tests { minimum_share_reserves: state.config.minimum_share_reserves, curve_fee: state.config.fees.curve, flat_fee: state.config.fees.flat, - governance_fee: state.config.fees.governance, + governance_lp_fee: state.config.fees.governance_lp, }, checkpoint_exposure, max_iterations.into(), diff --git a/crates/hyperdrive-math/src/short/open.rs b/crates/hyperdrive-math/src/short/open.rs index c4db78dda..af9efe463 100644 --- a/crates/hyperdrive-math/src/short/open.rs +++ b/crates/hyperdrive-math/src/short/open.rs @@ -142,7 +142,7 @@ mod tests { minimum_share_reserves: state.config.minimum_share_reserves, curve_fee: state.config.fees.curve, flat_fee: state.config.fees.flat, - governance_fee: state.config.fees.governance, + governance_lp_fee: state.config.fees.governance_lp, }, checkpoint_exposure, max_iterations.into(), diff --git a/crates/test-utils/src/chain/test_chain.rs b/crates/test-utils/src/chain/test_chain.rs index d8b2f2bbd..01f586ab2 100644 --- a/crates/test-utils/src/chain/test_chain.rs +++ b/crates/test-utils/src/chain/test_chain.rs @@ -274,7 +274,8 @@ impl TestChain { fees: Fees { curve: uint256!(0.05e18), flat: uint256!(0.0005e18), - governance: uint256!(0.15e18), + governance_lp: uint256!(0.15e18), + governance_zombie: uint256!(0.15e18), }, }; let target0 = ERC4626Target0::deploy(client.clone(), (config.clone(), pool.address()))? @@ -613,7 +614,8 @@ mod tests { Fees { curve: uint256!(0.05e18), flat: uint256!(0.0005e18), - governance: uint256!(0.15e18), + governance_lp: uint256!(0.15e18), + governance_zombie: uint256!(0.15e18), } ); From cf213193c7105b61a47649b96820f039a586f524 Mon Sep 17 00:00:00 2001 From: jonny rhea Date: Sun, 17 Dec 2023 21:21:48 -0600 Subject: [PATCH 3/9] fix style --- contracts/src/factory/HyperdriveFactory.sol | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/contracts/src/factory/HyperdriveFactory.sol b/contracts/src/factory/HyperdriveFactory.sol index 06ea2980f..c6ecca402 100644 --- a/contracts/src/factory/HyperdriveFactory.sol +++ b/contracts/src/factory/HyperdriveFactory.sol @@ -123,8 +123,10 @@ contract HyperdriveFactory { maxFlatFee = _factoryConfig.maxFees.flat; maxGovernanceLPFee = _factoryConfig.maxFees.governanceLP; maxGovernanceZombieFee = _factoryConfig.maxFees.governanceZombie; - if (maxCurveFee > ONE || maxFlatFee > ONE || maxGovernanceLPFee > ONE - || maxGovernanceZombieFee > ONE) { + if (maxCurveFee > ONE || + maxFlatFee > ONE || + maxGovernanceLPFee > ONE || + maxGovernanceZombieFee > ONE) { revert IHyperdrive.MaxFeeTooHigh(); } if ( From b02adb4c933774d60199f7af442d81f8968012cb Mon Sep 17 00:00:00 2001 From: jonny rhea Date: Sun, 17 Dec 2023 21:26:59 -0600 Subject: [PATCH 4/9] fix style --- contracts/src/factory/HyperdriveFactory.sol | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/contracts/src/factory/HyperdriveFactory.sol b/contracts/src/factory/HyperdriveFactory.sol index c6ecca402..d36a1b09b 100644 --- a/contracts/src/factory/HyperdriveFactory.sol +++ b/contracts/src/factory/HyperdriveFactory.sol @@ -123,10 +123,12 @@ contract HyperdriveFactory { maxFlatFee = _factoryConfig.maxFees.flat; maxGovernanceLPFee = _factoryConfig.maxFees.governanceLP; maxGovernanceZombieFee = _factoryConfig.maxFees.governanceZombie; - if (maxCurveFee > ONE || - maxFlatFee > ONE || - maxGovernanceLPFee > ONE || - maxGovernanceZombieFee > ONE) { + if ( + maxCurveFee > ONE || + maxFlatFee > ONE || + maxGovernanceLPFee > ONE || + maxGovernanceZombieFee > ONE + ) { revert IHyperdrive.MaxFeeTooHigh(); } if ( From 7e20b8838d9ace4b5a261e55218fd56d279401c1 Mon Sep 17 00:00:00 2001 From: jonny rhea Date: Sun, 17 Dec 2023 21:31:49 -0600 Subject: [PATCH 5/9] fix lint --- test/units/hyperdrive/FeeTest.t.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/units/hyperdrive/FeeTest.t.sol b/test/units/hyperdrive/FeeTest.t.sol index fe38a61d1..fc1b70566 100644 --- a/test/units/hyperdrive/FeeTest.t.sol +++ b/test/units/hyperdrive/FeeTest.t.sol @@ -101,7 +101,7 @@ contract FeeTest is HyperdriveTest { uint256 governanceZombieFee = 1e18; deploy(alice, apr, initialSharePrice, 0, 0, 0, governanceZombieFee); uint256 contribution = 100e18; - uint256 aliceLpShares = initialize(alice, apr, contribution); + initialize(alice, apr, contribution); // Open a long. uint256 basePaidLong = 10e18; From 77b9dbe36a7e8a222836b7bbd3469d2d84a61dac Mon Sep 17 00:00:00 2001 From: jonny rhea Date: Sun, 17 Dec 2023 21:54:29 -0600 Subject: [PATCH 6/9] fucking bullshit --- crates/hyperdrive-math/src/long/max.rs | 4 +++- crates/hyperdrive-math/src/short/max.rs | 7 ++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/crates/hyperdrive-math/src/long/max.rs b/crates/hyperdrive-math/src/long/max.rs index 91e430f33..156adfd02 100644 --- a/crates/hyperdrive-math/src/long/max.rs +++ b/crates/hyperdrive-math/src/long/max.rs @@ -375,7 +375,9 @@ impl State { let maybe_derivative = self.long_amount_derivative(base_amount); maybe_derivative.map(|derivative| { (derivative - + self.governance_lp_fee() * self.curve_fee() * (fixed!(1e18) - self.get_spot_price()) + + self.governance_lp_fee() + * self.curve_fee() + * (fixed!(1e18) - self.get_spot_price()) - fixed!(1e18)) .mul_div_down(fixed!(1e18), self.share_price()) }) diff --git a/crates/hyperdrive-math/src/short/max.rs b/crates/hyperdrive-math/src/short/max.rs index c0c51eb8c..26afb1fae 100644 --- a/crates/hyperdrive-math/src/short/max.rs +++ b/crates/hyperdrive-math/src/short/max.rs @@ -450,9 +450,10 @@ impl State { spot_price: FixedPoint, ) -> Option { let lhs = self.short_principal_derivative(short_amount); - let rhs = - self.curve_fee() * (fixed!(1e18) - spot_price) * (fixed!(1e18) - self.governance_lp_fee()) - / self.share_price(); + let rhs = self.curve_fee() + * (fixed!(1e18) - spot_price) + * (fixed!(1e18) - self.governance_lp_fee()) + / self.share_price(); if lhs >= rhs { Some(lhs - rhs) } else { From 59b1c35aaf73e94d6eab96b45ec3c9c0cb8ea522 Mon Sep 17 00:00:00 2001 From: Jonny Rhea <5555162+jrhea@users.noreply.github.com> Date: Mon, 18 Dec 2023 00:38:36 -0600 Subject: [PATCH 7/9] Update contracts/src/factory/HyperdriveFactory.sol Co-authored-by: Alex Towle --- contracts/src/factory/HyperdriveFactory.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/src/factory/HyperdriveFactory.sol b/contracts/src/factory/HyperdriveFactory.sol index d36a1b09b..a63f25169 100644 --- a/contracts/src/factory/HyperdriveFactory.sol +++ b/contracts/src/factory/HyperdriveFactory.sol @@ -72,7 +72,7 @@ contract HyperdriveFactory { /// @dev The maximum flat fee that can be used as a factory default. uint256 internal immutable maxFlatFee; - /// @dev The maximum governance fee that can be used as a factory default. + /// @dev The maximum governance LP fee that can be used as a factory default. uint256 internal immutable maxGovernanceLPFee; /// @dev The maximum governance zombie fee that can be used as a factory default. From 0a17bf8a8e9b9e494f8c8f7ad0ba43ef7f5d2f3f Mon Sep 17 00:00:00 2001 From: Jonny Rhea <5555162+jrhea@users.noreply.github.com> Date: Mon, 18 Dec 2023 00:40:25 -0600 Subject: [PATCH 8/9] Update contracts/src/internal/HyperdriveBase.sol Co-authored-by: Alex Towle --- contracts/src/internal/HyperdriveBase.sol | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/contracts/src/internal/HyperdriveBase.sol b/contracts/src/internal/HyperdriveBase.sol index 66f25d193..eb90125b6 100644 --- a/contracts/src/internal/HyperdriveBase.sol +++ b/contracts/src/internal/HyperdriveBase.sol @@ -361,8 +361,10 @@ abstract contract HyperdriveBase is HyperdriveStorage { _governanceFeesAccrued += governanceZombieFeeCollected; _marketState.zombieShareReserves -= zombieInterest.toUint128(); - // Ensure that any zombie interest collected by governance - // doesn't go into the share reserves. + // The zombie interest that was collected (minus the fees paid to + // governance), are reinvested in the share reserves. The share + // adjustment is updated in lock-step to avoid changing the curve's + // k invariant. zombieInterest -= governanceZombieFeeCollected; _marketState.shareReserves += zombieInterest.toUint128(); _marketState.shareAdjustment += int128(zombieInterest.toUint128()); From 51f0d18e529badbdf654b562a6d83a6397bb6898 Mon Sep 17 00:00:00 2001 From: jonny rhea Date: Mon, 18 Dec 2023 00:49:15 -0600 Subject: [PATCH 9/9] address review comments from alex --- contracts/src/internal/HyperdriveBase.sol | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/contracts/src/internal/HyperdriveBase.sol b/contracts/src/internal/HyperdriveBase.sol index eb90125b6..9a4a620e6 100644 --- a/contracts/src/internal/HyperdriveBase.sol +++ b/contracts/src/internal/HyperdriveBase.sol @@ -350,21 +350,26 @@ abstract contract HyperdriveBase is HyperdriveStorage { uint256 _newSharePrice ) internal { if (_newSharePrice > _oldSharePrice && _oldSharePrice > 0) { + // Calculate the zombie interest to be collected in shares. // dz * (c1 - c0)/c1 uint256 zombieInterest = _amount.mulDivDown( _newSharePrice - _oldSharePrice, _newSharePrice ); + _marketState.zombieShareReserves -= zombieInterest.toUint128(); + + // Calculate and collect the governance fee. + // The fee is calculated in terms of shares and paid to + // governance. uint256 governanceZombieFeeCollected = zombieInterest.mulDown( _governanceZombieFee ); _governanceFeesAccrued += governanceZombieFeeCollected; - _marketState.zombieShareReserves -= zombieInterest.toUint128(); - // The zombie interest that was collected (minus the fees paid to - // governance), are reinvested in the share reserves. The share + // The zombie interest that was collected (minus the fees paid to + // governance), are reinvested in the share reserves. The share // adjustment is updated in lock-step to avoid changing the curve's - // k invariant. + // k invariant. zombieInterest -= governanceZombieFeeCollected; _marketState.shareReserves += zombieInterest.toUint128(); _marketState.shareAdjustment += int128(zombieInterest.toUint128());