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
7 changes: 7 additions & 0 deletions contracts/src/HyperdriveLong.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ abstract contract HyperdriveLong is HyperdriveLP {
/// @notice Opens a long position.
/// @param _baseAmount The amount of base to use when trading.
/// @param _minOutput The minium number of bonds to receive.
/// @param _minSharePrice The minium share price at which to open the long.
Comment thread
jalextowle marked this conversation as resolved.
/// This allows traders to protect themselves from opening a long in
/// a checkpoint where negative interest has accrued.
/// @param _destination The address which will receive the bonds
/// @param _asUnderlying A flag indicating whether the sender will pay in
/// base or using another currency. Implementations choose which
Expand All @@ -32,6 +35,7 @@ abstract contract HyperdriveLong is HyperdriveLP {
function openLong(
uint256 _baseAmount,
uint256 _minOutput,
uint256 _minSharePrice,
address _destination,
bool _asUnderlying
)
Expand All @@ -52,6 +56,9 @@ abstract contract HyperdriveLong is HyperdriveLP {
_baseAmount,
_asUnderlying
);
if (sharePrice < _minSharePrice) {
revert IHyperdrive.MinimumSharePrice();
}

// Perform a checkpoint.
uint256 latestCheckpoint = _latestCheckpoint();
Expand Down
7 changes: 7 additions & 0 deletions contracts/src/HyperdriveShort.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ abstract contract HyperdriveShort is HyperdriveLP {
/// @notice Opens a short position.
/// @param _bondAmount The amount of bonds to short.
/// @param _maxDeposit The most the user expects to deposit for this trade
/// @param _minSharePrice The minium share price at which to open the long.
Comment thread
jalextowle marked this conversation as resolved.
/// This allows traders to protect themselves from opening a long in
/// a checkpoint where negative interest has accrued.
/// @param _destination The address which gets credited with share tokens
/// @param _asUnderlying A flag indicating whether the sender will pay in
/// base or using another currency. Implementations choose which
Expand All @@ -32,6 +35,7 @@ abstract contract HyperdriveShort is HyperdriveLP {
function openShort(
uint256 _bondAmount,
uint256 _maxDeposit,
uint256 _minSharePrice,
address _destination,
bool _asUnderlying
)
Expand All @@ -52,6 +56,9 @@ abstract contract HyperdriveShort is HyperdriveLP {
// Since the short will receive interest from the beginning of the
// checkpoint, they will receive this backdated interest back at closing.
uint256 sharePrice = _pricePerShare();
if (sharePrice < _minSharePrice) {
revert IHyperdrive.MinimumSharePrice();
}
uint256 latestCheckpoint = _latestCheckpoint();
uint256 openSharePrice = _applyCheckpoint(latestCheckpoint, sharePrice);

Expand Down
3 changes: 3 additions & 0 deletions contracts/src/interfaces/IHyperdrive.sol
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,8 @@ interface IHyperdrive is IHyperdriveRead, IHyperdriveWrite, 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();
Expand All @@ -227,6 +229,7 @@ interface IHyperdrive is IHyperdriveRead, IHyperdriveWrite, IMultiToken {
error UnexpectedAssetId();
error UnexpectedSender();
error UnsupportedToken();
error MinimumSharePrice();
error MinimumTransactionAmount();
error ZeroLpTotalSupply();

Expand Down
2 changes: 2 additions & 0 deletions contracts/src/interfaces/IHyperdriveWrite.sol
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ interface IHyperdriveWrite is IMultiTokenWrite {
function openLong(
uint256 _baseAmount,
uint256 _minOutput,
uint256 _minSharePrice,
address _destination,
bool _asUnderlying
) external payable returns (uint256 maturityTime, uint256 bondProceeds);
Expand All @@ -57,6 +58,7 @@ interface IHyperdriveWrite is IMultiTokenWrite {
function openShort(
uint256 _bondAmount,
uint256 _maxDeposit,
uint256 _minSharePrice,
address _destination,
bool _asUnderlying
) external payable returns (uint256 maturityTime, uint256 traderDeposit);
Expand Down
16 changes: 14 additions & 2 deletions crates/test-utils/src/agent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,13 @@ impl Agent<ChainClient, ChaCha8Rng> {
};
let tx = self
.hyperdrive
.open_long(base_paid.into(), min_output.into(), self.address, true)
.open_long(
base_paid.into(),
min_output.into(),
fixed!(0).into(), // TODO: This is fine for testing, but not prod.
self.address,
true,
)
.from(self.address);
let logs = tx
.send()
Expand Down Expand Up @@ -241,7 +247,13 @@ impl Agent<ChainClient, ChaCha8Rng> {
};
let tx = self
.hyperdrive
.open_short(bond_amount.into(), max_deposit.into(), self.address, true)
.open_short(
bond_amount.into(),
max_deposit.into(),
fixed!(0).into(), // TODO: This is fine for testing, but not prod.
self.address,
true,
)
.from(self.address);
let logs = tx
.send()
Expand Down
5 changes: 4 additions & 1 deletion script/DevnetSmokeTest.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ contract DevnetSmokeTest is Script {
(, uint256 basePaid) = HYPERDRIVE.openShort(
bondAmount,
type(uint256).max,
0,
msg.sender,
true
);
Expand All @@ -52,7 +53,7 @@ contract DevnetSmokeTest is Script {
uint256 basePaid = 300_000e18;
BASE.mint(msg.sender, basePaid);
BASE.approve(address(HYPERDRIVE), basePaid);
HYPERDRIVE.openLong(basePaid, 0, msg.sender, true);
HYPERDRIVE.openLong(basePaid, 0, 0, msg.sender, true);
}

console.log("Ending pool info:");
Expand All @@ -70,6 +71,7 @@ contract DevnetSmokeTest is Script {
(uint256 maturityTime, uint256 bondAmount) = HYPERDRIVE.openLong(
10_000e18,
0,
0,
msg.sender,
true
);
Expand Down Expand Up @@ -134,6 +136,7 @@ contract DevnetSmokeTest is Script {
(uint256 maturityTime, uint256 bondAmount) = HYPERDRIVE.openShort(
10_000e18,
type(uint256).max,
0,
msg.sender,
true
);
Expand Down
4 changes: 4 additions & 0 deletions test/integrations/ERC4626Validation.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,7 @@ abstract contract ERC4626ValidationTest is HyperdriveTest {
(maturityTime, bondAmount) = hyperdrive.openLong(
baseAmount,
0,
0,
trader,
asUnderlying
);
Expand All @@ -518,6 +519,7 @@ abstract contract ERC4626ValidationTest is HyperdriveTest {
(maturityTime, bondAmount) = hyperdrive.openLong(
baseAmount,
0,
0,
trader,
asUnderlying
);
Expand All @@ -539,6 +541,7 @@ abstract contract ERC4626ValidationTest is HyperdriveTest {
(maturityTime, baseAmount) = hyperdrive.openShort(
bondAmount,
type(uint256).max,
0,
trader,
asUnderlying
);
Expand All @@ -547,6 +550,7 @@ abstract contract ERC4626ValidationTest is HyperdriveTest {
(maturityTime, baseAmount) = hyperdrive.openShort(
bondAmount,
type(uint256).max,
0,
trader,
asUnderlying
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ contract NegativeInterestLongFeeTest is HyperdriveTest {
DepositOverrides({
asUnderlying: true,
depositAmount: basePaid,
minSharePrice: 0,
minSlippage: 0, // TODO: This should never go below the base amount. Investigate this.
maxSlippage: type(uint256).max
})
Expand Down Expand Up @@ -331,6 +332,7 @@ contract NegativeInterestLongFeeTest is HyperdriveTest {
DepositOverrides({
asUnderlying: true,
depositAmount: basePaid,
minSharePrice: 0,
minSlippage: 0, // TODO: This should never go below the base amount. Investigate this.
maxSlippage: type(uint256).max
})
Expand Down Expand Up @@ -522,6 +524,7 @@ contract NegativeInterestLongFeeTest is HyperdriveTest {
DepositOverrides({
asUnderlying: true,
depositAmount: basePaid,
minSharePrice: 0,
minSlippage: 0, // TODO: This should never go below the base amount. Investigate this.
maxSlippage: type(uint256).max
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,7 @@ contract NegativeInterestShortFeeTest is HyperdriveTest {
asUnderlying: true,
// NOTE: Roughly double deposit amount needed to cover 100% flat fee
depositAmount: shortAmount * 2,
minSharePrice: 0,
minSlippage: 0,
maxSlippage: type(uint256).max
})
Expand Down Expand Up @@ -521,6 +522,7 @@ contract NegativeInterestShortFeeTest is HyperdriveTest {
asUnderlying: true,
// NOTE: Roughly double deposit amount needed to cover 100% flat fee
depositAmount: shortAmount * 2,
minSharePrice: 0,
minSlippage: 0,
maxSlippage: type(uint256).max
})
Expand Down
9 changes: 7 additions & 2 deletions test/integrations/hyperdrive/ReentrancyTest.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -133,15 +133,15 @@ contract ReentrancyTest is HyperdriveTest {
);
data[4] = abi.encodeCall(
hyperdrive.openLong,
(BASE_PAID, 0, _trader, true)
(BASE_PAID, 0, 0, _trader, true)
);
data[5] = abi.encodeCall(
hyperdrive.closeLong,
(block.timestamp, BOND_AMOUNT, 0, _trader, true)
);
data[6] = abi.encodeCall(
hyperdrive.openShort,
(BOND_AMOUNT, type(uint256).max, _trader, true)
(BOND_AMOUNT, type(uint256).max, 0, _trader, true)
);
data[7] = abi.encodeCall(
hyperdrive.closeShort,
Expand Down Expand Up @@ -187,6 +187,7 @@ contract ReentrancyTest is HyperdriveTest {
DepositOverrides({
asUnderlying: true,
depositAmount: CONTRIBUTION + 1,
minSharePrice: 0,
minSlippage: 0,
maxSlippage: type(uint256).max
})
Expand Down Expand Up @@ -214,6 +215,7 @@ contract ReentrancyTest is HyperdriveTest {
DepositOverrides({
asUnderlying: true,
depositAmount: CONTRIBUTION + 1,
minSharePrice: 0,
minSlippage: 0,
maxSlippage: type(uint256).max
})
Expand Down Expand Up @@ -279,6 +281,7 @@ contract ReentrancyTest is HyperdriveTest {
DepositOverrides({
asUnderlying: true,
depositAmount: BASE_PAID + 1,
minSharePrice: 0,
minSlippage: 0,
maxSlippage: type(uint256).max
})
Expand Down Expand Up @@ -318,6 +321,7 @@ contract ReentrancyTest is HyperdriveTest {
asUnderlying: true,
// NOTE: Roughly double deposit amount needed to cover 100% flat fee
depositAmount: BOND_AMOUNT * 2,
minSharePrice: 0,
minSlippage: 0,
maxSlippage: type(uint256).max
})
Expand All @@ -335,6 +339,7 @@ contract ReentrancyTest is HyperdriveTest {
asUnderlying: true,
// NOTE: Roughly double deposit amount needed to cover 100% flat fee
depositAmount: BOND_AMOUNT * 2,
minSharePrice: 0,
minSlippage: 0,
maxSlippage: type(uint256).max
})
Expand Down
35 changes: 27 additions & 8 deletions test/integrations/hyperdrive/RoundTripTest.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -181,14 +181,33 @@ contract RoundTripTest is HyperdriveTest {
function test_long_multiblock_round_trip_end_of_checkpoint_edge_cases()
external
{
uint256 apr = 115792089237316195423570985008687907853269984665640564039457583990320674062335;
uint256 timeStretchApr = 886936259672610464646559504023817532562726574141720139630650341263;
uint256 basePaid = 65723876150308947051900890891865009457038319412461;
_test_long_multiblock_round_trip_end_of_checkpoint(
apr,
timeStretchApr,
basePaid
);
uint256 snapshotId = vm.snapshot();
{
uint256 apr = 115792089237316195423570985008687907853269984665640564039457583990320674062335;
uint256 timeStretchApr = 886936259672610464646559504023817532562726574141720139630650341263;
uint256 basePaid = 65723876150308947051900890891865009457038319412461;
_test_long_multiblock_round_trip_end_of_checkpoint(
apr,
timeStretchApr,
basePaid
);
}
vm.revertTo(snapshotId);

// TODO: This test fails because the calculateMaxLong seems to be misbehaving.
// See issue #595
// snapshotId = vm.snapshot();
// {
// uint256 apr = 63203229717248733662763783222570;
// uint256 timeStretchApr = 3408059979187494427077136;
// uint256 basePaid = 57669888194155013968076316270639259357724635816572534634741412969387347636732;
// _test_long_multiblock_round_trip_end_of_checkpoint(
// apr,
// timeStretchApr,
// basePaid
// );
// }
// vm.revertTo(snapshotId);

// TODO: This test fails because the calculateMaxLong seems to be misbehaving.
// See issue #595
Expand Down
4 changes: 4 additions & 0 deletions test/units/hyperdrive/CloseShortTest.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,7 @@ contract CloseShortTest is HyperdriveTest {
asUnderlying: false,
// NOTE: Roughly double deposit amount needed to cover 100% flat fee
depositAmount: 10e18 * 2,
minSharePrice: 0,
minSlippage: 0,
maxSlippage: type(uint128).max
})
Expand Down Expand Up @@ -501,6 +502,7 @@ contract CloseShortTest is HyperdriveTest {
asUnderlying: false,
// NOTE: Roughly double deposit amount needed to cover 100% flat fee
depositAmount: 10e18 * 2,
minSharePrice: 0,
minSlippage: 0,
maxSlippage: type(uint128).max
})
Expand Down Expand Up @@ -539,6 +541,7 @@ contract CloseShortTest is HyperdriveTest {
asUnderlying: false,
// NOTE: Roughly double deposit amount needed to cover 100% flat fee
depositAmount: 10e18 * 2,
minSharePrice: 0,
minSlippage: 0,
maxSlippage: type(uint128).max
})
Expand Down Expand Up @@ -567,6 +570,7 @@ contract CloseShortTest is HyperdriveTest {
asUnderlying: false,
// NOTE: Roughly double deposit amount needed to cover 100% flat fee
depositAmount: 10e18 * 2,
minSharePrice: 0,
minSlippage: 0,
maxSlippage: type(uint128).max
})
Expand Down
2 changes: 1 addition & 1 deletion test/units/hyperdrive/ExtremeInputs.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ contract ExtremeInputs is HyperdriveTest {
baseToken.mint(shortAmount);
baseToken.approve(address(hyperdrive), shortAmount);
vm.expectRevert(IHyperdrive.BaseBufferExceedsShareReserves.selector);
hyperdrive.openShort(shortAmount, type(uint256).max, bob, true);
hyperdrive.openShort(shortAmount, type(uint256).max, 0, bob, true);
}

// This test stresses the edge cases of the `_updateLiquidity` function
Expand Down
4 changes: 4 additions & 0 deletions test/units/hyperdrive/FeeTest.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ contract FeeTest is HyperdriveTest {
DepositOverrides({
asUnderlying: true,
depositAmount: basePaid,
minSharePrice: 0,
minSlippage: 0,
maxSlippage: type(uint256).max
})
Expand Down Expand Up @@ -117,6 +118,7 @@ contract FeeTest is HyperdriveTest {
DepositOverrides({
asUnderlying: true,
depositAmount: basePaid,
minSharePrice: 0,
minSlippage: 0,
maxSlippage: type(uint256).max
})
Expand Down Expand Up @@ -180,6 +182,7 @@ contract FeeTest is HyperdriveTest {
DepositOverrides({
asUnderlying: true,
depositAmount: basePaid,
minSharePrice: 0,
minSlippage: 0,
maxSlippage: type(uint256).max
})
Expand Down Expand Up @@ -241,6 +244,7 @@ contract FeeTest is HyperdriveTest {
DepositOverrides({
asUnderlying: true,
depositAmount: basePaid,
minSharePrice: 0,
minSlippage: 0,
maxSlippage: type(uint256).max
})
Expand Down
Loading