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
54 changes: 54 additions & 0 deletions contracts/src/HyperdriveBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,60 @@ abstract contract HyperdriveBase is MultiToken, HyperdriveStorage {
using FixedPointMath for uint256;
using SafeCast for uint256;

event Initialize(
address indexed provider,
uint256 lpAmount,
uint256 baseAmount,
uint256 apr
);

event AddLiquidity(
address indexed provider,
uint256 lpAmount,
uint256 baseAmount
);

event RemoveLiquidity(
address indexed provider,
uint256 lpAmount,
uint256 baseAmount,
uint256 withdrawalShareAmount
);

event RedeemWithdrawalShares(
address indexed provider,
uint256 withdrawalShareAmount,
uint256 baseAmount
);

event OpenLong(
address indexed trader,
uint256 maturityTime,
uint256 baseAmount,
uint256 bondAmount
);

event OpenShort(
address indexed trader,
uint256 maturityTime,
uint256 baseAmount,
uint256 bondAmount
);

event CloseLong(
address indexed trader,
uint256 maturityTime,
uint256 baseAmount,
uint256 bondAmount
);

event CloseShort(
address indexed trader,
uint256 maturityTime,
uint256 baseAmount,
uint256 bondAmount
);

/// @notice Initializes a Hyperdrive pool.
/// @param _config The configuration of the Hyperdrive pool.
/// @param _dataProvider The address of the data provider.
Expand Down
184 changes: 117 additions & 67 deletions contracts/src/HyperdriveLP.sol
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ abstract contract HyperdriveLP is HyperdriveTWAP {

// Mint LP shares to the initializer.
_mint(AssetId._LP_ASSET_ID, _destination, initialLpShares);

// Emit an Initialize event.
emit Initialize(_destination, initialLpShares, _contribution, _apr);
}

/// @notice Allows LPs to supply liquidity for LP shares.
Expand Down Expand Up @@ -192,6 +195,9 @@ abstract contract HyperdriveLP is HyperdriveTWAP {

// Mint LP shares to the supplier.
_mint(AssetId._LP_ASSET_ID, _destination, lpShares);

// Emit an AddLiquidity event.
emit AddLiquidity(_destination, lpShares, _contribution);
}

/// @notice Allows an LP to burn shares and withdraw from the pool.
Expand Down Expand Up @@ -221,85 +227,31 @@ abstract contract HyperdriveLP is HyperdriveTWAP {
_applyCheckpoint(_latestCheckpoint(), sharePrice);

// Burn the LP shares.
uint256 activeLpTotalSupply = _totalSupply[AssetId._LP_ASSET_ID];
uint256 totalActiveLpSupply = _totalSupply[AssetId._LP_ASSET_ID];
uint256 withdrawalSharesOutstanding = _totalSupply[
AssetId._WITHDRAWAL_SHARE_ASSET_ID
] - _withdrawPool.readyToWithdraw;
uint256 lpTotalSupply = activeLpTotalSupply +
uint256 totalLpSupply = totalActiveLpSupply +
withdrawalSharesOutstanding;
_burn(AssetId._LP_ASSET_ID, msg.sender, _shares);

// Calculate the starting present value of the pool.
HyperdriveMath.PresentValueParams memory params = HyperdriveMath
.PresentValueParams({
shareReserves: _marketState.shareReserves,
bondReserves: _marketState.bondReserves,
sharePrice: sharePrice,
initialSharePrice: _initialSharePrice,
timeStretch: _timeStretch,
longsOutstanding: _marketState.longsOutstanding,
longAverageTimeRemaining: _calculateTimeRemaining(
uint256(_marketState.longAverageMaturityTime).divUp(1e36) // scale to seconds
),
shortsOutstanding: _marketState.shortsOutstanding,
shortAverageTimeRemaining: _calculateTimeRemaining(
uint256(_marketState.shortAverageMaturityTime).divUp(1e36) // scale to seconds
),
shortBaseVolume: _marketState.shortBaseVolume
});
uint256 startingPresentValue = HyperdriveMath.calculatePresentValue(
params
);

// The LP is given their share of the idle capital in the pool. This
// is removed from the pool's reserves and paid out immediately. We use
// the average opening share price of longs to avoid double counting
// the variable rate interest accrued on long positions. The idle amount
// is given by:
//
// idle = (z - (o_l / c_0)) * (dl / l_a)
uint256 shareProceeds = _marketState.shareReserves;
if (_marketState.longsOutstanding > 0) {
shareProceeds -= uint256(_marketState.longsOutstanding).divDown(
_marketState.longOpenSharePrice
);
}
shareProceeds = shareProceeds.mulDivDown(_shares, activeLpTotalSupply);
_updateLiquidity(-int256(shareProceeds));
params.shareReserves = _marketState.shareReserves;
params.bondReserves = _marketState.bondReserves;
uint256 endingPresentValue = HyperdriveMath.calculatePresentValue(
params
);

// Calculate the amount of withdrawal shares that should be minted. We
// solve for this value by solving the present value equation as
// follows:
//
// PV0 / l0 = PV1 / (l0 - dl + dw) => dw = (PV1 / PV0) * l0 - (l0 - dl)
int256 withdrawalShares = int256(
lpTotalSupply.mulDivDown(endingPresentValue, startingPresentValue)
);
withdrawalShares -= int256(lpTotalSupply) - int256(_shares);
if (withdrawalShares < 0) {
// We backtrack by calculating the amount of the idle that should
// be returned to the pool using the original present value ratio.
uint256 overestimatedProceeds = uint256(-withdrawalShares)
.mulDivDown(startingPresentValue, lpTotalSupply);
_updateLiquidity(int256(overestimatedProceeds));
_applyWithdrawalProceeds(
overestimatedProceeds,
withdrawalSharesOutstanding,
sharePrice
// Remove the liquidity from the pool.
(
uint256 shareProceeds,
uint256 withdrawalShares
) = _applyRemoveLiquidity(
_shares,
sharePrice,
totalLpSupply,
totalActiveLpSupply,
withdrawalSharesOutstanding
);
withdrawalShares = 0;
}

// Mint the withdrawal shares to the LP.
_mint(
AssetId._WITHDRAWAL_SHARE_ASSET_ID,
_destination,
uint256(withdrawalShares)
withdrawalShares
);

// Withdraw the shares from the yield source.
Expand All @@ -312,6 +264,15 @@ abstract contract HyperdriveLP is HyperdriveTWAP {
// Enforce min user outputs
if (_minOutput > baseProceeds) revert Errors.OutputLimit();

// Emit a RemoveLiquidity event.
uint256 shares = _shares;
emit RemoveLiquidity(
_destination,
shares,
baseProceeds,
uint256(withdrawalShares)
);

return (baseProceeds, uint256(withdrawalShares));
}

Expand Down Expand Up @@ -362,6 +323,9 @@ abstract contract HyperdriveLP is HyperdriveTWAP {

// Enforce min user outputs
if (_minOutput > _proceeds) revert Errors.OutputLimit();

// Emit a RedeemWithdrawalShares event.
emit RedeemWithdrawalShares(_destination, _shares, _proceeds);
}

/// @dev Updates the pool's liquidity and holds the pool's APR constant.
Expand Down Expand Up @@ -389,6 +353,92 @@ abstract contract HyperdriveLP is HyperdriveTWAP {
}
}

/// @dev Removes liquidity from the pool and calculates the amount of
/// withdrawal shares that should be minted.
/// @param _shares The amount of shares to remove.
/// @param _sharePrice The current price of a share.
/// @param _totalLpSupply The total amount of LP shares.
/// @param _totalActiveLpSupply The total amount of active LP shares.
/// @param _withdrawalSharesOutstanding The total amount of withdrawal
/// shares outstanding.
/// @return shareProceeds The share proceeds that will be paid to the LP.
/// @return The amount of withdrawal shares that should be minted.
function _applyRemoveLiquidity(
uint256 _shares,
uint256 _sharePrice,
uint256 _totalLpSupply,
uint256 _totalActiveLpSupply,
uint256 _withdrawalSharesOutstanding
) internal returns (uint256 shareProceeds, uint256) {
// Calculate the starting present value of the pool.
HyperdriveMath.PresentValueParams memory params = HyperdriveMath
.PresentValueParams({
shareReserves: _marketState.shareReserves,
bondReserves: _marketState.bondReserves,
sharePrice: _sharePrice,
initialSharePrice: _initialSharePrice,
timeStretch: _timeStretch,
longsOutstanding: _marketState.longsOutstanding,
longAverageTimeRemaining: _calculateTimeRemaining(
uint256(_marketState.longAverageMaturityTime).divUp(1e36) // scale to seconds
),
shortsOutstanding: _marketState.shortsOutstanding,
shortAverageTimeRemaining: _calculateTimeRemaining(
uint256(_marketState.shortAverageMaturityTime).divUp(1e36) // scale to seconds
),
shortBaseVolume: _marketState.shortBaseVolume
});
uint256 startingPresentValue = HyperdriveMath.calculatePresentValue(
params
);

// The LP is given their share of the idle capital in the pool. This
// is removed from the pool's reserves and paid out immediately. We use
// the average opening share price of longs to avoid double counting
// the variable rate interest accrued on long positions. The idle amount
// is given by:
//
// idle = (z - (o_l / c_0)) * (dl / l_a)
shareProceeds = _marketState.shareReserves;
if (_marketState.longsOutstanding > 0) {
shareProceeds -= uint256(_marketState.longsOutstanding).divDown(
_marketState.longOpenSharePrice
);
}
shareProceeds = shareProceeds.mulDivDown(_shares, _totalActiveLpSupply);
_updateLiquidity(-int256(shareProceeds));
params.shareReserves = _marketState.shareReserves;
params.bondReserves = _marketState.bondReserves;
uint256 endingPresentValue = HyperdriveMath.calculatePresentValue(
params
);

// Calculate the amount of withdrawal shares that should be minted. We
// solve for this value by solving the present value equation as
// follows:
//
// PV0 / l0 = PV1 / (l0 - dl + dw) => dw = (PV1 / PV0) * l0 - (l0 - dl)
int256 withdrawalShares = int256(
_totalLpSupply.mulDivDown(endingPresentValue, startingPresentValue)
);
withdrawalShares -= int256(_totalLpSupply) - int256(_shares);
if (withdrawalShares < 0) {
// We backtrack by calculating the amount of the idle that should
// be returned to the pool using the original present value ratio.
uint256 overestimatedProceeds = uint256(-withdrawalShares)
.mulDivDown(startingPresentValue, _totalLpSupply);
_updateLiquidity(int256(overestimatedProceeds));
_applyWithdrawalProceeds(
overestimatedProceeds,
_withdrawalSharesOutstanding,
_sharePrice
);
withdrawalShares = 0;
}

return (shareProceeds, uint256(withdrawalShares));
}

/// @dev Pays out the maximum amount of withdrawal shares given a specified
/// amount of withdrawal proceeds.
/// @param _withdrawalProceeds The amount of withdrawal proceeds to pay out.
Expand Down
8 changes: 8 additions & 0 deletions contracts/src/HyperdriveLong.sol
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ abstract contract HyperdriveLong is HyperdriveLP {
_destination,
bondProceeds
);

// Emit an OpenLong event.
uint256 baseAmount = _baseAmount; // Avoid stack too deep error.
emit OpenLong(_destination, maturityTime, baseAmount, bondProceeds);

return (bondProceeds);
}

Expand Down Expand Up @@ -155,6 +160,9 @@ abstract contract HyperdriveLong is HyperdriveLP {
// Enforce min user outputs
if (_minOutput > baseProceeds) revert Errors.OutputLimit();

// Emit a CloseLong event.
emit CloseLong(_destination, _maturityTime, baseProceeds, _bondAmount);

return (baseProceeds);
}

Expand Down
12 changes: 11 additions & 1 deletion contracts/src/HyperdriveShort.sol
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,10 @@ abstract contract HyperdriveShort is HyperdriveLP {
_bondAmount
);

// Emit an OpenShort event.
uint256 bondAmount = _bondAmount; // Avoid stack too deep error.
emit OpenShort(_destination, maturityTime, traderDeposit, bondAmount);

return (traderDeposit);
}

Expand Down Expand Up @@ -185,7 +189,13 @@ abstract contract HyperdriveShort is HyperdriveLP {

// Enforce min user outputs
if (baseProceeds < _minOutput) revert Errors.OutputLimit();
return (baseProceeds);

// Emit a CloseShort event.
uint256 maturityTime = _maturityTime; // Avoid stack too deep error.
uint256 bondAmount = _bondAmount; // Avoid stack too deep error.
emit CloseShort(_destination, maturityTime, baseProceeds, bondAmount);

return baseProceeds;
}

/// @dev Applies an open short to the state. This includes updating the
Expand Down
2 changes: 1 addition & 1 deletion foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ fs_permissions = [{ access = "write", path = "./artifacts/script_addresses.json"
deny_warnings = true
# optimizer settings
optimizer = true
optimizer_runs = 8000
optimizer_runs = 4500
# Enable via-ir
via_ir = true
# Enable gas-reporting for all contracts
Expand Down
Loading