Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into zombie-interest
Browse files Browse the repository at this point in the history
  • Loading branch information
jrhea committed Dec 12, 2023
2 parents 94297e4 + 844fc0e commit f086b1f
Show file tree
Hide file tree
Showing 28 changed files with 176 additions and 468 deletions.
12 changes: 11 additions & 1 deletion contracts/src/external/HyperdriveTarget0.sol
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,17 @@ abstract contract HyperdriveTarget0 is
_revert(abi.encode(_checkpoints[_checkpointId]));
}

/// @notice Gets the checkpoint exposure at a specified time.
/// @param _checkpointTime The checkpoint time.
/// @return The checkpoint exposure.
function getCheckpointExposure(
uint256 _checkpointTime
) external view returns (int256) {
_revert(
abi.encode(_nonNettedLongs(_checkpointTime + _positionDuration))
);
}

/// @notice Gets the pool's configuration parameters.
/// @dev These parameters are immutable, so this should only need to be
/// called once.
Expand All @@ -236,7 +247,6 @@ abstract contract HyperdriveTarget0 is
initialSharePrice: _initialSharePrice,
minimumShareReserves: _minimumShareReserves,
minimumTransactionAmount: _minimumTransactionAmount,
precisionThreshold: _precisionThreshold,
positionDuration: _positionDuration,
checkpointDuration: _checkpointDuration,
timeStretch: _timeStretch,
Expand Down
10 changes: 1 addition & 9 deletions contracts/src/interfaces/IHyperdrive.sol
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,6 @@ interface IHyperdrive is IHyperdriveRead, IHyperdriveCore, IMultiToken {
/// as well as the share price at closing of matured longs and
/// shorts.
uint128 sharePrice;
/// @dev If exposure is positive, then we have net long exposure, otherwise
/// we have net short exposure in the checkpoint.
int128 exposure;
}

struct WithdrawPool {
Expand Down Expand Up @@ -162,9 +159,6 @@ interface IHyperdrive is IHyperdriveRead, IHyperdriveCore, IMultiToken {
/// @dev The minimum amount of tokens that a position can be opened or
/// closed with.
uint256 minimumTransactionAmount;
/// @dev The amount of precision expected to lose due to exponentiation
/// implementation.
uint256 precisionThreshold;
/// @dev The duration of a position prior to maturity.
uint256 positionDuration;
/// @dev The duration of a checkpoint.
Expand Down Expand Up @@ -231,9 +225,6 @@ interface IHyperdrive is IHyperdriveRead, IHyperdriveCore, IMultiToken {
/// ### Hyperdrive ###
/// ##################
error ApprovalFailed();
// TODO: We should rename this so that it's clear that it pertains to
// solvency.
error BaseBufferExceedsShareReserves();
error BelowMinimumContribution();
error BelowMinimumShareReserves();
error InvalidApr();
Expand All @@ -247,6 +238,7 @@ interface IHyperdrive is IHyperdriveRead, IHyperdriveCore, IMultiToken {
error InvalidShareReserves();
error InvalidFeeAmounts();
error InvalidFeeDestination();
error InsufficientLiquidity();
error NegativeInterest();
error NegativePresentValue();
error NoAssetsToWithdraw();
Expand Down
4 changes: 4 additions & 0 deletions contracts/src/interfaces/IHyperdriveRead.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ interface IHyperdriveRead is IMultiTokenRead {
uint256 _checkpointId
) external view returns (IHyperdrive.Checkpoint memory);

function getCheckpointExposure(
uint256 _checkpointTime
) external view returns (int256);

function getWithdrawPool()
external
view
Expand Down
112 changes: 39 additions & 73 deletions contracts/src/internal/HyperdriveBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,36 @@ abstract contract HyperdriveBase is HyperdriveStorage {
);
}

/// @dev Gets the amount of non-netted longs with a given maturity.
/// @param _maturityTime The maturity time of the longs.
/// @return The amount of non-netted longs. This is a signed value that
/// can be negative. This is convenient for updating the long
/// exposure when closing positions.
function _nonNettedLongs(
uint256 _maturityTime
) internal view returns (int256) {
// The amount of non-netted longs is the difference between the amount
// of longs and the amount of shorts with a given maturity time. If the
// difference is negative, the amount of non-netted longs is zero.
return
int256(
_totalSupply[
AssetId.encodeAssetId(
AssetId.AssetIdPrefix.Long,
_maturityTime
)
]
) -
int256(
_totalSupply[
AssetId.encodeAssetId(
AssetId.AssetIdPrefix.Short,
_maturityTime
)
]
);
}

/// @dev Gets the present value parameters from the current state.
/// @param _sharePrice The current share price.
/// @return presentValue The present value parameters.
Expand Down Expand Up @@ -293,83 +323,19 @@ abstract contract HyperdriveBase is HyperdriveStorage {
int256(_minimumShareReserves.mulDown(_sharePrice));
}

/// @dev Calculates the checkpoint exposure when a position is closed
/// @param _bondAmount The amount of bonds that the user is closing.
/// @param _shareCurveDelta The amount of shares the trader pays the curve.
/// @param _bondReservesDelta The amount of bonds that the reserves will
/// change by.
/// @param _shareReservesDelta The amount of shares that the reserves will
/// change by.
/// @param _maturityTime The maturity time of the position being closed.
/// @param _sharePrice The current share price.
/// @param _isLong True if the position being closed is long.
function _updateCheckpointExposureOnClose(
uint256 _bondAmount,
uint256 _shareCurveDelta,
uint256 _bondReservesDelta,
uint256 _shareReservesDelta,
uint256 _maturityTime,
uint256 _sharePrice,
bool _isLong
) internal {
uint256 checkpointTime = _maturityTime - _positionDuration;
uint256 checkpointLongs = _totalSupply[
AssetId.encodeAssetId(AssetId.AssetIdPrefix.Long, _maturityTime)
];
uint256 checkpointShorts = _totalSupply[
AssetId.encodeAssetId(AssetId.AssetIdPrefix.Short, _maturityTime)
];

// We can zero out exposure when there are no more open positions
if (checkpointLongs == 0 && checkpointShorts == 0) {
_checkpoints[checkpointTime].exposure = 0;
} else {
// The exposure delta is flat + curve amount + the bonds the
// user is closing:
//
// (dz_user*c - dz*c) + (dy - dz*c) + dy_user
// = dz_user*c + dy - 2*dz*c + dy_user
int128 delta = int128(
(_shareReservesDelta.mulDown(_sharePrice) +
_bondReservesDelta -
2 *
_shareCurveDelta.mulDown(_sharePrice) +
_bondAmount).toUint128()
);

// If the position being closed is long, then the exposure
// decreases by the delta. If it's short, then the exposure
// increases by the delta.
if (_isLong) {
_checkpoints[checkpointTime].exposure -= delta;
} else {
_checkpoints[checkpointTime].exposure += delta;
}
}
}

/// @dev Updates the global long exposure.
/// @param _before The long exposure before the update.
/// @param _after The long exposure after the update.
function _updateLongExposure(int256 _before, int256 _after) internal {
// LongExposure is decreasing (OpenShort/CloseLong)
if (_before > _after && _before >= 0) {
int256 delta = int256(_before - _after.max(0));
// Since the longExposure can't be negative, we need to make sure we
// don't underflow.
_marketState.longExposure -= uint128(
delta.min(int128(_marketState.longExposure)).toInt128()
);
}
// LongExposure is increasing (OpenLong/CloseShort)
else if (_after > _before) {
if (_before >= 0) {
_marketState.longExposure += uint128(
_after.toInt128() - _before.toInt128()
);
} else {
_marketState.longExposure += uint128(_after.max(0).toInt128());
}
// The global long exposure is the sum of the non-netted longs in each
// checkpoint. To update this value, we subtract the current value
// (`_before.max(0)`) and add the new value (`_after.max(0)`).
int128 delta = (int256(_after.max(0)) - int256(_before.max(0)))
.toInt128();
if (delta > 0) {
_marketState.longExposure += uint128(delta);
} else if (delta < 0) {
_marketState.longExposure -= uint128(-delta);
}
}

Expand Down
16 changes: 8 additions & 8 deletions contracts/src/internal/HyperdriveCheckpoint.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ abstract contract HyperdriveCheckpoint is
HyperdriveShort
{
using FixedPointMath for uint256;
using FixedPointMath for int256;
using SafeCast for uint256;

/// @dev Attempts to mint a checkpoint with the specified checkpoint time.
Expand Down Expand Up @@ -169,16 +170,15 @@ abstract contract HyperdriveCheckpoint is
positionsClosed = true;
}

// Update the checkpoint exposure and global long exposure.
// If we closed any positions, update the global long exposure and
// distribute any excess idle to the withdrawal pool.
if (positionsClosed) {
uint256 maturityTime = _checkpointTime - _positionDuration;
int128 checkpointExposureBefore = int128(
_checkpoints[maturityTime].exposure
);
_checkpoints[maturityTime].exposure = 0;
// Update the global long exposure. Since we've closed some matured
// positions, we can reduce the long exposure for the matured
// checkpoint to zero.
_updateLongExposure(
checkpointExposureBefore,
_checkpoints[maturityTime].exposure
int256(maturedLongsAmount) - int256(maturedShortsAmount),
0
);

// Distribute the excess idle to the withdrawal pool.
Expand Down
51 changes: 14 additions & 37 deletions contracts/src/internal/HyperdriveLong.sol
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,6 @@ abstract contract HyperdriveLong is HyperdriveLP {
bondProceeds,
bondReservesDelta,
sharePrice,
latestCheckpoint,
maturityTime
);

Expand Down Expand Up @@ -158,7 +157,6 @@ abstract contract HyperdriveLong is HyperdriveLP {
uint256 bondReservesDelta,
uint256 shareProceeds,
uint256 shareReservesDelta,
uint256 shareCurveDelta,
int256 shareAdjustmentDelta,
uint256 totalGovernanceFee
) = _calculateCloseLong(_bondAmount, sharePrice, _maturityTime);
Expand All @@ -178,23 +176,12 @@ abstract contract HyperdriveLong is HyperdriveLP {
maturityTime
);

// Update the checkpoint exposure and global long exposure.
uint256 checkpointTime = maturityTime - _positionDuration;
int128 checkpointExposureBefore = int128(
_checkpoints[checkpointTime].exposure
);
_updateCheckpointExposureOnClose(
_bondAmount,
shareCurveDelta,
bondReservesDelta,
shareReservesDelta,
maturityTime,
sharePrice,
true
);
// Update the global long exposure. Since we're closing a long, the
// number of non-netted longs decreases by the bond amount.
int256 nonNettedLongs = _nonNettedLongs(maturityTime);
_updateLongExposure(
checkpointExposureBefore,
_checkpoints[checkpointTime].exposure
nonNettedLongs + int256(_bondAmount),
nonNettedLongs
);

// Distribute the excess idle to the withdrawal pool.
Expand Down Expand Up @@ -255,14 +242,12 @@ abstract contract HyperdriveLong is HyperdriveLP {
/// @param _bondProceeds The amount of bonds purchased by the trader.
/// @param _bondReservesDelta The amount of bonds sold by the curve.
/// @param _sharePrice The share price.
/// @param _checkpointTime The time of the latest checkpoint.
/// @param _maturityTime The maturity time of the long.
function _applyOpenLong(
uint256 _shareReservesDelta,
uint256 _bondProceeds,
uint256 _bondReservesDelta,
uint256 _sharePrice,
uint256 _checkpointTime,
uint256 _maturityTime
) internal {
// Update the average maturity time of long positions.
Expand All @@ -285,23 +270,17 @@ abstract contract HyperdriveLong is HyperdriveLP {
longsOutstanding_ += _bondProceeds.toUint128();
_marketState.longsOutstanding = longsOutstanding_;

// Increase the exposure by the amount the LPs must reserve to cover the
// long. We are overly conservative, so this is equal to the amount of
// fixed interest the long is owed at maturity plus the face value of
// the long.
IHyperdrive.Checkpoint storage checkpoint = _checkpoints[
_checkpointTime
];
int128 checkpointExposureBefore = int128(checkpoint.exposure);
uint128 exposureDelta = (2 *
_bondProceeds -
_shareReservesDelta.mulDown(_sharePrice)).toUint128();
checkpoint.exposure += int128(exposureDelta);
_updateLongExposure(checkpointExposureBefore, checkpoint.exposure);
// Update the global long exposure. Since we're opening a long, the
// number of non-netted longs increases by the bond amount.
int256 nonNettedLongs = _nonNettedLongs(_maturityTime);
_updateLongExposure(
nonNettedLongs,
nonNettedLongs + int256(_bondProceeds)
);

// We need to check solvency because longs increase the system's exposure.
if (!_isSolvent(_sharePrice)) {
revert IHyperdrive.BaseBufferExceedsShareReserves();
revert IHyperdrive.InsufficientLiquidity();
}

// Distribute the excess idle to the withdrawal pool.
Expand Down Expand Up @@ -484,8 +463,6 @@ abstract contract HyperdriveLong is HyperdriveLP {
/// @return bondReservesDelta The bonds added to the reserves.
/// @return shareProceeds The proceeds in shares of selling the bonds.
/// @return shareReservesDelta The shares removed from the reserves.
/// @return shareCurveDelta The curve portion of the payment that LPs need
/// to make to the trader in shares.
/// @return shareAdjustmentDelta The change in the share adjustment.
/// @return totalGovernanceFee The governance fee in shares.
function _calculateCloseLong(
Expand All @@ -499,14 +476,14 @@ abstract contract HyperdriveLong is HyperdriveLP {
uint256 bondReservesDelta,
uint256 shareProceeds,
uint256 shareReservesDelta,
uint256 shareCurveDelta,
int256 shareAdjustmentDelta,
uint256 totalGovernanceFee
)
{
// Calculate the effect that closing the long should have on the pool's
// reserves as well as the amount of shares the trader receives for
// selling their bonds.
uint256 shareCurveDelta;
{
// Calculate the effect that closing the long should have on the
// pool's reserves as well as the amount of shares the trader
Expand Down
Loading

0 comments on commit f086b1f

Please sign in to comment.