Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
137 changes: 114 additions & 23 deletions contracts/Hyperdrive.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,37 +21,37 @@ abstract contract Hyperdrive is MultiToken, IHyperdrive {

/// Tokens ///

// @dev The base asset.
// @notice The base asset.
IERC20 public immutable baseToken;

/// Time ///

// @dev The amount of seconds between share price checkpoints.
// @notice The amount of seconds between share price checkpoints.
uint256 public immutable checkpointDuration;

// @dev The amount of seconds that elapse before a bond can be redeemed.
// @notice The amount of seconds that elapse before a bond can be redeemed.
uint256 public immutable positionDuration;

// @dev A parameter that decreases slippage around a target rate.
// @notice A parameter that decreases slippage around a target rate.
uint256 public immutable timeStretch;

/// Market state ///

// @dev The share price at the time the pool was created.
// @notice The share price at the time the pool was created.
uint256 public immutable initialSharePrice;

/// @dev Checkpoints of historical share prices.
/// @notice Checkpoints of historical share prices.
mapping(uint256 => uint256) public checkpoints;

/// @dev The share reserves. The share reserves multiplied by the share price
/// give the base reserves, so shares are a mechanism of ensuring that
/// interest is properly awarded over time.
/// @notice The share reserves. The share reserves multiplied by the share
/// price give the base reserves, so shares are a mechanism of
/// ensuring that interest is properly awarded over time.
uint256 public shareReserves;

/// @dev The bond reserves. In Hyperdrive, the bond reserves aren't backed by
/// pre-minted bonds and are instead used as a virtual value that
/// ensures that the spot rate changes according to the laws of supply
/// and demand.
/// @notice The bond reserves. In Hyperdrive, the bond reserves aren't
/// backed by pre-minted bonds and are instead used as a virtual
/// value that ensures that the spot rate changes according to the
/// laws of supply and demand.
uint256 public bondReserves;

/// @notice The amount of longs that are still open.
Expand All @@ -60,6 +60,12 @@ abstract contract Hyperdrive is MultiToken, IHyperdrive {
/// @notice The amount of shorts that are still open.
uint256 public shortsOutstanding;

/// @notice The average maturity time of long positions.
uint256 public longAverageMaturityTime;

/// @notice The average maturity time of short positions.
uint256 public shortAverageMaturityTime;

/// @notice The amount of long withdrawal shares that haven't been paid out.
uint256 public longWithdrawalSharesOutstanding;

Expand All @@ -72,10 +78,16 @@ abstract contract Hyperdrive is MultiToken, IHyperdrive {
/// @notice The proceeds that have accrued to the short withdrawal shares.
uint256 public shortWithdrawalShareProceeds;

// @notice the fee paramater to apply to the curve portion of the hyperdrive trade equation.
// TODO: Should this be immutable?
//
/// @notice The fee paramater to apply to the curve portion of the
/// hyperdrive trade equation.
uint256 public curveFee;

// @notice the fee paramater to apply to the flat portion of the hyperdrive trade equation.
// TODO: Should this be immutable?
//
/// @notice The fee paramater to apply to the flat portion of the hyperdrive
/// trade equation.
uint256 public flatFee;

/// @notice Initializes a Hyperdrive pool.
Expand Down Expand Up @@ -451,6 +463,15 @@ abstract contract Hyperdrive is MultiToken, IHyperdrive {
// Enforce min user outputs
if (_minOutput > bondProceeds) revert Errors.OutputLimit();

// Update the average maturity time of long positions.
longAverageMaturityTime = _calculateAverageMaturityTime(
longsOutstanding,
bondProceeds,
longAverageMaturityTime,
maturityTime,
true
);

// Apply the trading deltas to the reserves and update the amount of
// longs outstanding.
shareReserves += shares;
Expand Down Expand Up @@ -632,6 +653,15 @@ abstract contract Hyperdrive is MultiToken, IHyperdrive {
deposit(userDeposit); // max_loss + interest
}

// Update the average maturity time of long positions.
shortAverageMaturityTime = _calculateAverageMaturityTime(
shortsOutstanding,
_bondAmount,
shortAverageMaturityTime,
maturityTime,
true
);

// Apply the trading deltas to the reserves and increase the bond buffer
// by the amount of bonds that were shorted. We don't need to add the
// margin or pre-paid interest to the reserves because of the way that
Expand Down Expand Up @@ -707,7 +737,8 @@ abstract contract Hyperdrive is MultiToken, IHyperdrive {
_bondAmount,
poolBondDelta,
sharePayment,
sharePrice
sharePrice,
_maturityTime
);
} else {
// Perform a checkpoint for the short's maturity time. This ensures
Expand Down Expand Up @@ -793,8 +824,12 @@ abstract contract Hyperdrive is MultiToken, IHyperdrive {
/// @return bondReserves_ The bond reserves.
/// @return lpTotalSupply The total supply of LP shares.
/// @return sharePrice The share price.
/// @return longsOutstanding_ The longs that haven't been closed.
/// @return shortsOutstanding_ The shorts that haven't been closed.
/// @return longsOutstanding_ The outstanding longs that haven't matured.
/// @return longAverageMaturityTime_ The average maturity time of the
/// outstanding longs.
/// @return shortsOutstanding_ The outstanding shorts that haven't matured.
/// @return shortAverageMaturityTime_ The average maturity time of the
/// outstanding shorts.
function getPoolInfo()
external
view
Expand All @@ -804,7 +839,9 @@ abstract contract Hyperdrive is MultiToken, IHyperdrive {
uint256 lpTotalSupply,
uint256 sharePrice,
uint256 longsOutstanding_,
uint256 shortsOutstanding_
uint256 longAverageMaturityTime_,
uint256 shortsOutstanding_,
uint256 shortAverageMaturityTime_
)
{
return (
Expand All @@ -813,7 +850,9 @@ abstract contract Hyperdrive is MultiToken, IHyperdrive {
totalSupply[AssetId._LP_ASSET_ID],
pricePerShare(),
longsOutstanding,
shortsOutstanding
longAverageMaturityTime,
shortsOutstanding,
shortAverageMaturityTime
);
}

Expand All @@ -828,14 +867,23 @@ abstract contract Hyperdrive is MultiToken, IHyperdrive {
/// @param _shareProceeds The proceeds in shares received from closing the
/// long.
/// @param _sharePrice The current share price.
/// @param _maturityTime The maturity time of the longs.
/// @param _maturityTime The maturity time of the long.
function _applyCloseLong(
uint256 _bondAmount,
uint256 _poolBondDelta,
uint256 _shareProceeds,
uint256 _sharePrice,
uint256 _maturityTime
) internal {
// Update the long average maturity time.
longAverageMaturityTime = _calculateAverageMaturityTime(
longsOutstanding,
_bondAmount,
longAverageMaturityTime,
_maturityTime,
false
);

// Reduce the amount of outstanding longs.
longsOutstanding -= _bondAmount;

Expand Down Expand Up @@ -917,12 +965,23 @@ abstract contract Hyperdrive is MultiToken, IHyperdrive {
/// pool.
/// @param _sharePayment The payment in shares required to close the short.
/// @param _sharePrice The current share price.
/// @param _maturityTime The maturity time of the short.
function _applyCloseShort(
uint256 _bondAmount,
uint256 _poolBondDelta,
uint256 _sharePayment,
uint256 _sharePrice
uint256 _sharePrice,
uint256 _maturityTime
) internal {
// Update the short average maturity time.
shortAverageMaturityTime = _calculateAverageMaturityTime(
shortsOutstanding,
_bondAmount,
shortAverageMaturityTime,
_maturityTime,
false
);

// Decrease the amount of shorts outstanding.
shortsOutstanding -= _bondAmount;

Expand Down Expand Up @@ -1032,7 +1091,8 @@ abstract contract Hyperdrive is MultiToken, IHyperdrive {
maturedShortsAmount,
0,
maturedShortsAmount,
_sharePrice
_sharePrice,
_checkpointTime
);
}

Expand Down Expand Up @@ -1068,6 +1128,37 @@ abstract contract Hyperdrive is MultiToken, IHyperdrive {
return proceeds;
}

/// @dev Calculate a new average maturity time when positions are opened or
/// closed.
/// @param _positionsOutstanding The amount of positions outstanding.
/// @param _positionAmount The position balance being opened or closed.
/// @param _averageMaturityTime The average maturity time of positions.
/// @param _positionMaturityTime The maturity time of the position being
/// opened or closed.
/// @param _isOpen A flag indicating that the position is being opened if
/// true and that the position is being closed if false.
/// @return averageMaturityTime The updated average maturity time.
function _calculateAverageMaturityTime(
uint256 _positionsOutstanding,
uint256 _positionAmount,
uint256 _averageMaturityTime,
uint256 _positionMaturityTime,
bool _isOpen
) internal pure returns (uint256 averageMaturityTime) {
if (_isOpen) {
return
(_positionsOutstanding.mulDown(_averageMaturityTime))
.add(_positionAmount.mulDown(_positionMaturityTime))
.divDown(_positionsOutstanding.add(_positionAmount));
} else {
if (_positionsOutstanding == _positionAmount) return 0;
return
(_positionsOutstanding.mulDown(_averageMaturityTime))
.sub(_positionAmount.mulDown(_positionMaturityTime))
.divDown(_positionsOutstanding.sub(_positionAmount));
}
}

/// @dev Calculates the normalized time remaining of a position.
/// @param _maturityTime The maturity time of the position.
/// @return timeRemaining The normalized time remaining (in [0, 1]).
Expand Down
32 changes: 29 additions & 3 deletions test/Hyperdrive.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ contract HyperdriveTest is Test {
hyperdrive.positionDuration(),
hyperdrive.timeStretch()
);
assertApproxEqAbs(poolApr, apr, 1e1); // 17 decimals of precision
assertApproxEqAbs(poolApr, apr, 1); // 17 decimals of precision

// Ensure that Alice's base balance has been depleted and that Alice
// received some LP tokens.
Expand Down Expand Up @@ -225,10 +225,16 @@ contract HyperdriveTest is Test {
poolInfoAfter.longsOutstanding,
poolInfoBefore.longsOutstanding + bondAmount
);
assertApproxEqAbs(
poolInfoAfter.longAverageMaturityTime,
maturityTime,
1
);
assertEq(
poolInfoAfter.shortsOutstanding,
poolInfoBefore.shortsOutstanding
);
assertEq(poolInfoAfter.shortAverageMaturityTime, 0);
}

/// Close Long ///
Expand Down Expand Up @@ -370,10 +376,12 @@ contract HyperdriveTest is Test {
poolInfoAfter.longsOutstanding,
poolInfoBefore.longsOutstanding - bondAmount
);
assertEq(poolInfoAfter.longAverageMaturityTime, 0);
assertEq(
poolInfoAfter.shortsOutstanding,
poolInfoBefore.shortsOutstanding
);
assertEq(poolInfoAfter.shortAverageMaturityTime, 0);
}

// TODO: Clean up these tests.
Expand Down Expand Up @@ -440,10 +448,12 @@ contract HyperdriveTest is Test {
poolInfoAfter.longsOutstanding,
poolInfoBefore.longsOutstanding - bondAmount
);
assertEq(poolInfoAfter.longAverageMaturityTime, 0);
assertEq(
poolInfoAfter.shortsOutstanding,
poolInfoBefore.shortsOutstanding
);
assertEq(poolInfoAfter.shortAverageMaturityTime, 0);
}

/// Open Short ///
Expand Down Expand Up @@ -547,10 +557,16 @@ contract HyperdriveTest is Test {
poolInfoAfter.longsOutstanding,
poolInfoBefore.longsOutstanding
);
assertEq(poolInfoAfter.longAverageMaturityTime, 0);
assertEq(
poolInfoAfter.shortsOutstanding,
poolInfoBefore.shortsOutstanding + bondAmount
);
assertApproxEqAbs(
poolInfoAfter.shortAverageMaturityTime,
maturityTime,
1
);
}

/// Close Short ///
Expand Down Expand Up @@ -688,10 +704,12 @@ contract HyperdriveTest is Test {
poolInfoAfter.longsOutstanding,
poolInfoBefore.longsOutstanding
);
assertEq(poolInfoAfter.longAverageMaturityTime, 0);
assertEq(
poolInfoAfter.shortsOutstanding,
poolInfoBefore.shortsOutstanding - bondAmount
);
assertEq(poolInfoAfter.shortAverageMaturityTime, 0);
}

// TODO: Clean up these tests.
Expand Down Expand Up @@ -759,10 +777,12 @@ contract HyperdriveTest is Test {
poolInfoAfter.longsOutstanding,
poolInfoBefore.longsOutstanding
);
assertEq(poolInfoAfter.longAverageMaturityTime, 0);
assertEq(
poolInfoAfter.shortsOutstanding,
poolInfoBefore.shortsOutstanding - bondAmount
);
assertEq(poolInfoAfter.shortAverageMaturityTime, 0);
}

/// Utils ///
Expand All @@ -784,7 +804,9 @@ contract HyperdriveTest is Test {
uint256 lpTotalSupply;
uint256 sharePrice;
uint256 longsOutstanding;
uint256 longAverageMaturityTime;
uint256 shortsOutstanding;
uint256 shortAverageMaturityTime;
}

function getPoolInfo() internal view returns (PoolInfo memory) {
Expand All @@ -794,7 +816,9 @@ contract HyperdriveTest is Test {
uint256 lpTotalSupply,
uint256 sharePrice,
uint256 longsOutstanding,
uint256 shortsOutstanding
uint256 longAverageMaturityTime,
uint256 shortsOutstanding,
uint256 shortAverageMaturityTime
) = hyperdrive.getPoolInfo();
return
PoolInfo({
Expand All @@ -803,7 +827,9 @@ contract HyperdriveTest is Test {
lpTotalSupply: lpTotalSupply,
sharePrice: sharePrice,
longsOutstanding: longsOutstanding,
shortsOutstanding: shortsOutstanding
longAverageMaturityTime: longAverageMaturityTime,
shortsOutstanding: shortsOutstanding,
shortAverageMaturityTime: shortAverageMaturityTime
});
}
}