Skip to content

Commit

Permalink
fix so gov fees are properly accounted for in closeLong (#570)
Browse files Browse the repository at this point in the history
fix and test that gov fees are properly accounted for in closeLong()
  • Loading branch information
jrhea committed Aug 26, 2023
1 parent 7dc98da commit 662cc79
Show file tree
Hide file tree
Showing 2 changed files with 245 additions and 1 deletion.
2 changes: 1 addition & 1 deletion contracts/src/HyperdriveLong.sol
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ abstract contract HyperdriveLong is HyperdriveLP {
_applyCloseLong(
_bondAmount,
bondReservesDelta,
shareProceeds,
shareProceeds + totalGovernanceFee,
shareReservesDelta,
_maturityTime,
sharePrice
Expand Down
244 changes: 244 additions & 0 deletions test/units/hyperdrive/FeeTest.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ import { FixedPointMath } from "contracts/src/libraries/FixedPointMath.sol";
import { HyperdriveMath } from "contracts/src/libraries/HyperdriveMath.sol";
import { MockHyperdrive, IMockHyperdrive } from "../../mocks/MockHyperdrive.sol";
import { HyperdriveTest, HyperdriveUtils } from "../../utils/HyperdriveTest.sol";
import { Lib } from "../../utils/Lib.sol";

contract FeeTest is HyperdriveTest {
using FixedPointMath for uint256;
using Lib for *;

function test_governanceFeeAccrual() public {
uint256 apr = 0.05e18;
Expand Down Expand Up @@ -41,6 +43,248 @@ contract FeeTest is HyperdriveTest {
assertGt(governanceBalanceAfter, governanceFeesAfterOpenLong);
}

// 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;
uint256 flatFee = .1e18;
uint256 governanceFee = 1e18;
uint256 timeElapsed = 73 days;

uint256 governanceFees = 0;
uint256 shareReservesNoFees = 0;
uint256 bondsPurchased = 0;
// Initialize the market with 10% flat fee and 100% governance fee
{
uint256 apr = 0.01e18;
deploy(
alice,
apr,
initialSharePrice,
curveFee,
flatFee,
governanceFee
);
uint256 contribution = 500_000_000e18;
initialize(alice, apr, contribution);

// Open a long position.
uint256 basePaid = 100_000e18;
(uint256 maturityTime, uint256 bondAmount) = openLong(
bob,
basePaid,
DepositOverrides({
asUnderlying: true,
depositAmount: basePaid,
minSlippage: 0,
maxSlippage: type(uint256).max
})
);
bondsPurchased = bondAmount;
// Get the fees accrued from opening the long.
uint256 governanceFeesAfterOpenLong = IMockHyperdrive(
address(hyperdrive)
).getGovernanceFeesAccrued();

// 1/2 term matures and accrues interest
advanceTime(timeElapsed, variableInterest);

// Close the long.
closeLong(bob, maturityTime, bondAmount);

// Get the fees after closing the long.
governanceFees =
IMockHyperdrive(address(hyperdrive))
.getGovernanceFeesAccrued() -
governanceFeesAfterOpenLong;
shareReservesNoFees = hyperdrive.getPoolInfo().shareReserves;
}

// Initialize the market with 10% flat fee and 0% governance fee
uint256 shareReservesFlatFee = 0;
{
uint256 apr = 0.01e18;
deploy(alice, apr, initialSharePrice, curveFee, flatFee, 0);
uint256 contribution = 500_000_000e18;
initialize(alice, apr, contribution);

// Open a long position.
uint256 basePaid = 100_000e18;
(uint256 maturityTime, uint256 bondAmount) = openLong(
bob,
basePaid,
DepositOverrides({
asUnderlying: true,
depositAmount: basePaid,
minSlippage: 0,
maxSlippage: type(uint256).max
})
);

// 1/2 term matures and accrues interest
advanceTime(timeElapsed, variableInterest);

// Close the long.
closeLong(bob, maturityTime, bondAmount);
shareReservesFlatFee = hyperdrive.getPoolInfo().shareReserves;
}
uint256 normalizedTimeRemaining = (timeElapsed).divDown(
POSITION_DURATION
);
uint256 expectedFeeSubtractedFromShareReserves = bondsPurchased
.mulDown(flatFee)
.mulDown(normalizedTimeRemaining);

// (Share Reserves Without Any Fees bc They All Went to Governance) + (10% Flat X 100% Governance Fees) - (Share Reserves With Flat Fee) = 0
assertEq(
shareReservesNoFees + governanceFees - shareReservesFlatFee,
0
);
assertEq(shareReservesFlatFee - shareReservesNoFees, governanceFees);
assertEq(governanceFees, expectedFeeSubtractedFromShareReserves);
}

// This test demonstrates that the governance fees from curve fee are NOT included in the shareReserves.
function test_curve_gov_fee_close_long() public {
uint256 initialSharePrice = 1e18;
uint256 curveFee = 0.1e18;
uint256 flatFee = 0e18;
uint256 governanceFee = 1e18;
uint256 timeElapsed = 73 days;

uint256 governanceFeesFromCloseLong = 0;
uint256 governanceFeesFromOpenLong = 0;
uint256 shareReservesNoFees = 0;
uint256 bondsPurchased = 0;
uint256 spotPrice = 0;
// Initialize the market with 10% curve fee and 100% governance fee
{
uint256 apr = 0.01e18;
deploy(
alice,
apr,
initialSharePrice,
curveFee,
flatFee,
governanceFee
);
uint256 contribution = 500_000_000e18;
initialize(alice, apr, contribution);

// Open a long position.
uint256 basePaid = .01e18;
(uint256 maturityTime, uint256 bondAmount) = openLong(
bob,
basePaid,
DepositOverrides({
asUnderlying: true,
depositAmount: basePaid,
minSlippage: 0,
maxSlippage: type(uint256).max
})
);
bondsPurchased = bondAmount;
// Get the fees accrued from opening the long.
governanceFeesFromOpenLong = IMockHyperdrive(address(hyperdrive))
.getGovernanceFeesAccrued();

// 1/2 term matures and no interest accrues
advanceTime(timeElapsed, 0);

spotPrice = HyperdriveUtils.calculateSpotPrice(hyperdrive);

// Close the long.
closeLong(bob, maturityTime, bondAmount);

// Get the fees after closing the long.
governanceFeesFromCloseLong =
IMockHyperdrive(address(hyperdrive))
.getGovernanceFeesAccrued() -
governanceFeesFromOpenLong;
shareReservesNoFees = hyperdrive.getPoolInfo().shareReserves;
}

// Test that expected Fees ~ actual Fees
{
uint256 normalizedTimeRemaining = FixedPointMath.ONE_18 -
(timeElapsed).divDown(POSITION_DURATION);

// Calculate curve fee
uint256 expectedFeeSubtractedFromShareReserves = FixedPointMath
.ONE_18 - spotPrice;
expectedFeeSubtractedFromShareReserves = expectedFeeSubtractedFromShareReserves
.mulDown(curveFee)
.mulDown(bondsPurchased)
.mulDown(normalizedTimeRemaining);

// actual curve fee from close long should equal the expected curve fee from close long
assertEq(
governanceFeesFromCloseLong,
expectedFeeSubtractedFromShareReserves
);
}

// Initialize the market with 10% curve fee and 0% governance fee
uint256 shareReservesCurveFee = 0;
{
uint256 apr = 0.01e18;
deploy(alice, apr, initialSharePrice, curveFee, flatFee, 0);
uint256 contribution = 500_000_000e18;
initialize(alice, apr, contribution);

// Open a long position.
uint256 basePaid = .01e18;
(uint256 maturityTime, uint256 bondAmount) = openLong(
bob,
basePaid,
DepositOverrides({
asUnderlying: true,
depositAmount: basePaid,
minSlippage: 0,
maxSlippage: type(uint256).max
})
);

// 1/2 term matures and no interest accrues
advanceTime(timeElapsed, 0);

// Close the long.
closeLong(bob, maturityTime, bondAmount);
shareReservesCurveFee = hyperdrive.getPoolInfo().shareReserves;
}

// The share reserves with curve fee should be greater than the share reserves without any fees + the fees from open long
assertGt(
shareReservesCurveFee,
shareReservesNoFees + governanceFeesFromOpenLong
);

// The share reserves with curve fee should be greater than the share reserves without any fees + the fees from close long
assertGt(
shareReservesCurveFee,
shareReservesNoFees + governanceFeesFromCloseLong
);

// (Share Reserves Without Any Fees bc They All Went to Governance) + (10% Curve X 100% Governance Fees) - (Share Reserves With Curve Fee) ~ 0
assertApproxEqAbs(
int256(
shareReservesNoFees +
governanceFeesFromOpenLong +
governanceFeesFromCloseLong
) - int256(shareReservesCurveFee),
0,
1e7
);

// The difference between the share reserves should be equal to the actual fees
assertApproxEqAbs(
shareReservesCurveFee - shareReservesNoFees,
governanceFeesFromOpenLong + governanceFeesFromCloseLong,
1e7
);
}

function test_collectFees_long() public {
uint256 apr = 0.05e18;
// Initialize the pool with a large amount of capital.
Expand Down

0 comments on commit 662cc79

Please sign in to comment.