Skip to content

Commit

Permalink
Fix Spearbit issue where governance fees are included in Share reserv…
Browse files Browse the repository at this point in the history
…es (#520)

* .

* added test for Spearbit fix

* initial pass at review comments

* update comment

* fixed tests according to @jhrea 's reccomendation

* add comment

* Update test/units/hyperdrive/OpenShortTest.t.sol

Co-authored-by: Alex Towle <[email protected]>

* Update test/units/hyperdrive/OpenShortTest.t.sol

Co-authored-by: Alex Towle <[email protected]>

* Update test/units/hyperdrive/OpenShortTest.t.sol

Co-authored-by: Alex Towle <[email protected]>

* nits

---------

Co-authored-by: Alex Towle <[email protected]>
  • Loading branch information
ControlCplusControlV and jalextowle authored Jul 24, 2023
1 parent a3116be commit 4730953
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 8 deletions.
14 changes: 14 additions & 0 deletions contracts/src/HyperdriveDataProvider.sol
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,20 @@ abstract contract HyperdriveDataProvider is
_revert(abi.encode(poolInfo));
}

/// @notice Gets info about the fees presently accrued by the pool
/// @return Governance fees denominated in shares yet to be collected
function getUncollectedGovernanceFees() external view returns (uint256) {
_revert(abi.encode(_governanceFeesAccrued));
}

function getMarketState()
external
view
returns (IHyperdrive.MarketState memory)
{
_revert(abi.encode(_marketState));
}

/// @notice Allows plugin data libs to provide getters or other complex
/// logic instead of the main.
/// @param _slots The storage slots the caller wants the data from
Expand Down
14 changes: 7 additions & 7 deletions contracts/src/HyperdriveShort.sol
Original file line number Diff line number Diff line change
Expand Up @@ -449,13 +449,13 @@ abstract contract HyperdriveShort is HyperdriveLP {
_sharePrice
);

// Remove the curve fee from the amount of shares to remove from the shareReserves.
// We do this bc the shareReservesDelta represents how many shares to remove
// from the shareReserves. Making the shareReservesDelta smaller pays out the
// totalCurveFee to the LPs.
// The shareReservesDelta and the totalCurveFee are both in terms of shares
// shares -= shares
shareReservesDelta -= totalCurveFee;
// ShareReservesDelta is the number of shares to remove from the shareReserves and
// since the totalCurveFee includes the totalGovernanceFee it needs to be added back
// to so that it is removed from the shareReserves. The shareReservesDelta,
// totalCurveFee and totalGovernanceFee are all in terms of shares:

// shares -= shares - shares
shareReservesDelta -= totalCurveFee - totalGovernanceFee;
return (shareReservesDelta, totalGovernanceFee);
}

Expand Down
7 changes: 7 additions & 0 deletions contracts/src/interfaces/IHyperdriveRead.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@ interface IHyperdriveRead is IMultiTokenRead {
view
returns (IHyperdrive.PoolConfig memory);

function getMarketState()
external
view
returns (IHyperdrive.MarketState memory);

function getUncollectedGovernanceFees() external view returns (uint256);

function getPoolInfo() external view returns (IHyperdrive.PoolInfo memory);

function load(
Expand Down
67 changes: 66 additions & 1 deletion test/units/hyperdrive/OpenShortTest.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { IHyperdrive } from "contracts/src/interfaces/IHyperdrive.sol";
import { AssetId } from "contracts/src/libraries/AssetId.sol";
import { FixedPointMath } from "contracts/src/libraries/FixedPointMath.sol";
import { HyperdriveMath } from "contracts/src/libraries/HyperdriveMath.sol";
import { HyperdriveTest, HyperdriveUtils } from "../../utils/HyperdriveTest.sol";
import { HyperdriveTest, HyperdriveUtils, IERC20, MockHyperdrive, MockHyperdriveDataProvider } from "../../utils/HyperdriveTest.sol";
import { Lib } from "../../utils/Lib.sol";

contract OpenShortTest is HyperdriveTest {
Expand Down Expand Up @@ -179,6 +179,71 @@ contract OpenShortTest is HyperdriveTest {
//I think we could trigger this with big short, open long, and short?
}

function test_governance_fees_excluded_share_reserves() public {
uint256 apr = 0.05e18;
uint256 contribution = 500_000_000e18;

// 1. Deploy a pool with zero fees
IHyperdrive.PoolConfig memory config = testConfig(apr);
deploy(address(deployer), config);
// Initialize the pool with a large amount of capital.
initialize(alice, apr, contribution);

// 2. Open a short
uint256 bondAmount = (hyperdrive.calculateMaxShort() * 90) / 100;
openShort(bob, bondAmount);

// 3. Record Share Reserves
IHyperdrive.MarketState memory zeroFeeState = hyperdrive
.getMarketState();

// 4. deploy a pool with 100% curve fees and 100% gov fees (this is nice bc
// it ensures that all the fees are credited to governance and thus subtracted
// from the shareReserves
config = testConfig(apr);
config.fees = IHyperdrive.Fees({
curve: 1e18,
flat: 1e18,
governance: 1e18
});
deploy(address(deployer), config);
initialize(alice, apr, contribution);

// 5. Open a Short
bondAmount = (hyperdrive.calculateMaxShort() * 90) / 100;
openShort(bob, bondAmount);

// 6. Record Share Reserves
IHyperdrive.MarketState memory maxFeeState = hyperdrive
.getMarketState();

// Since the fees are subtracted from reserves and accounted for
// seperately, so this will be true
assertEq(zeroFeeState.shareReserves, maxFeeState.shareReserves);

uint256 govFees = hyperdrive.getUncollectedGovernanceFees();
// Governance fees collected are non-zero
assert(govFees > 1e5);

// 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 });
// Deploy and initialize the new pool
deploy(address(deployer), config);
initialize(alice, apr, contribution);

// 8. Open a Short
bondAmount = (hyperdrive.calculateMaxShort() * 90) / 100;
openShort(bob, bondAmount);

// 9. Record Share Reserves
IHyperdrive.MarketState memory maxCurveFeeState = hyperdrive
.getMarketState();

// shareReserves should be greater here because there is no Gov being deducted
assertGe(maxCurveFeeState.shareReserves, maxFeeState.shareReserves);
}

function verifyOpenShort(
IHyperdrive.PoolInfo memory poolInfoBefore,
uint256 contribution,
Expand Down

0 comments on commit 4730953

Please sign in to comment.