Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Spearbit issue where governance fees are included in Share reserves #520

Merged
merged 14 commits into from
Jul 24, 2023
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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()
ControlCplusControlV marked this conversation as resolved.
Show resolved Hide resolved
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
);

ControlCplusControlV marked this conversation as resolved.
Show resolved Hide resolved
// 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
68 changes: 67 additions & 1 deletion test/units/hyperdrive/OpenShortTest.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ 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";
import { console } from "forge-std/console.sol";
ControlCplusControlV marked this conversation as resolved.
Show resolved Hide resolved

contract OpenShortTest is HyperdriveTest {
using FixedPointMath for uint256;
Expand Down Expand Up @@ -179,6 +180,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);
ControlCplusControlV marked this conversation as resolved.
Show resolved Hide resolved

// 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
ControlCplusControlV marked this conversation as resolved.
Show resolved Hide resolved
config = testConfig(apr);
config.fees = IHyperdrive.Fees({
curve: 1e18,
flat: 1e18,
governance: 1e18
});

// Deploy and initialize the new pool
deploy(address(deployer), config);
initialize(alice, apr, contribution);
ControlCplusControlV marked this conversation as resolved.
Show resolved Hide resolved

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

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

assertEq(zeroFeeState.shareReserves, maxFeeState.shareReserves);
ControlCplusControlV marked this conversation as resolved.
Show resolved Hide resolved

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);
ControlCplusControlV marked this conversation as resolved.
Show resolved Hide resolved

// 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
Loading