Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Uni v3 Zap #1179

Merged
merged 34 commits into from
Oct 12, 2024
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
c6e6425
forge install: v3-periphery
jalextowle Sep 24, 2024
234a1a0
Added a basic Uniswap v3 zap contract
jalextowle Sep 25, 2024
876ec87
Added a Uni v3 zap for `closeLong` and improved the zap for `openLong`
jalextowle Sep 25, 2024
459cb9e
Added a zap for `addLiquidity`
jalextowle Sep 25, 2024
573a142
Cleaned up the validation logic for Uni v3 zaps
jalextowle Sep 25, 2024
7a28332
Adds zaps for `removeLiquidity` and `redeemWithdrawalShares`
jalextowle Sep 25, 2024
f66af29
Added the remaining zaps
jalextowle Sep 25, 2024
c6458ee
Started writing tests for `addLiquidityZap`
jalextowle Sep 26, 2024
513269e
Updated the zap to use multi-hop routing
jalextowle Sep 26, 2024
35b2f5e
Wrote some more tests
jalextowle Sep 27, 2024
908b1f9
Refactored the `addLiquidityZap` tests
jalextowle Sep 27, 2024
8b64cc1
Added tests for `openLongZap`
jalextowle Sep 28, 2024
1057043
Added a full test suite for `openLongZap`
jalextowle Sep 30, 2024
80e8286
Broke out the zap tests and added test for `openShortZap`
jalextowle Oct 1, 2024
21a0a9c
Added support for zaps out starting with ETH
jalextowle Oct 1, 2024
a003ebd
Added tests for `removeLiquidity`
jalextowle Oct 2, 2024
887f3c8
Added tests for `redeemWithdrawalSharesZap`
jalextowle Oct 2, 2024
8038319
Added tests for `redeemWithdrawalSharesZap`
jalextowle Oct 2, 2024
231eb82
Added tests for `closeLongZap`
jalextowle Oct 2, 2024
1fced1f
Added tests for `closeShortZap`
jalextowle Oct 2, 2024
2d42990
Cleaned up the tests
jalextowle Oct 2, 2024
d779071
Bump the version
jalextowle Oct 2, 2024
24d7bd9
Merge remote-tracking branch 'origin/main' into jalextowle/zaps/uni-v…
jalextowle Oct 2, 2024
463399b
Remaining cleanup
jalextowle Oct 3, 2024
ec5a0b6
Fixed the UniV3Zap
jalextowle Oct 9, 2024
d3b11bb
Added wrapping tests
jalextowle Oct 9, 2024
2d9132e
Some cleanup
jalextowle Oct 9, 2024
6777136
Merge remote-tracking branch 'origin/main' into jalextowle/zaps/uni-v…
jalextowle Oct 9, 2024
e847de5
Some cleanup
jalextowle Oct 9, 2024
a03407d
Addressed review feedback from @jrhea
jalextowle Oct 10, 2024
1f0fde6
Addressed remaining review feedback from @jrhea
jalextowle Oct 10, 2024
c3e5ad9
Appeased the linter gods
jalextowle Oct 10, 2024
b8fbf92
Merge remote-tracking branch 'origin/main' into jalextowle/zaps/uni-v…
jalextowle Oct 10, 2024
4b70d1c
Addressed review feedback from @mcclurejt
jalextowle Oct 12, 2024
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
58 changes: 58 additions & 0 deletions contracts/src/interfaces/ISwapRouter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.22;

interface ISwapRouter {
struct ExactInputSingleParams {
address tokenIn;
address tokenOut;
uint24 fee;
address recipient;
uint256 deadline;
uint256 amountIn;
uint256 amountOutMinimum;
uint160 sqrtPriceLimitX96;
}

function exactInputSingle(
ExactInputSingleParams calldata params
) external payable returns (uint256 amountOut);

struct ExactInputParams {
bytes path;
address recipient;
uint256 deadline;
uint256 amountIn;
uint256 amountOutMinimum;
}

function exactInput(
ExactInputParams calldata params
) external payable returns (uint256 amountOut);

struct ExactOutputSingleParams {
address tokenIn;
address tokenOut;
uint24 fee;
address recipient;
uint256 deadline;
uint256 amountOut;
uint256 amountInMaximum;
uint160 sqrtPriceLimitX96;
}

function exactOutputSingle(
ExactOutputSingleParams calldata params
) external payable returns (uint256 amountIn);

struct ExactOutputParams {
bytes path;
address recipient;
uint256 deadline;
uint256 amountOut;
uint256 amountInMaximum;
}

function exactOutput(
ExactOutputParams calldata params
) external payable returns (uint256 amountIn);
}
284 changes: 284 additions & 0 deletions contracts/src/interfaces/IUniV3Zap.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,284 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;

import { IHyperdrive } from "./IHyperdrive.sol";
import { ISwapRouter } from "./ISwapRouter.sol";

/// @title IUniV3Zap
/// @author DELV
/// @notice The interface for the UniV3Zap contract.
/// @custom:disclaimer The language used in this code is for coding convenience
/// only, and is not intended to, and does not, have any
/// particular legal or regulatory significance.
interface IUniV3Zap {
/// Errors ///

/// @notice Thrown when attempting to zap to an invalid input token.
error InvalidInputToken();

/// @notice Thrown when attempting to zap to an invalid output token.
error InvalidOutputToken();

/// @notice Thrown when attempting to zap to an invalid recipient.
error InvalidRecipient();

/// @notice Thrown when attempting to zap with an invalid source amount. If
/// the source asset doesn't needs to be wrapped, this needs to be
/// the same as the swap's input amount.
error InvalidSourceAmount();

/// @notice Thrown when attempting to zap with an invalid source asset. If
/// the source asset needs to be wrapped, this shouldn't be the same
/// address as the input token to the swap. Otherwise, they should
/// be the same.
error InvalidSourceAsset();

/// @notice Thrown when receiving ether outside of an zap.
error InvalidTransfer();

/// @notice Thrown when ether is sent to an instance that doesn't accept
/// ether as a deposit asset.
error NotPayable();

/// @notice Thrown when we should be wrapping assets, but the zap isn't
/// configured to wrap.
error ShouldWrapAssets();

/// @notice Thrown when an ether transfer fails.
error TransferFailed();

/// Structs ///

/// @dev The options parameter provided to all of the functions that zap
/// funds into Hyperdrive.
struct ZapInOptions {
/// @dev The Uniswap swap parameters to use when swapping assets to the
/// deposit asset.
ISwapRouter.ExactInputParams swapParams;
/// @dev In most cases, this should be equal to the input token of the
/// swap. In some cases, this will be a rebasing token like stETH
/// that needs to be wrapped to make it suitable for swapping on
/// Uniswap.
address sourceAsset;
/// @dev The amount of source tokens that should be swapped. In most
/// cases, this should be equal to the `swapParams.amountIn`, but
/// in the case of wrapped tokens, this amount will supersede that
/// quantity.
uint256 sourceAmount;
/// @dev A flag that indicates whether or not the source token should
/// be wrapped into the input token. Uniswap v3 demands complete
/// precision on the input token amounts, which makes it hard to
/// work with rebasing tokens that have imprecise transfer
/// functions. Wrapping tokens provides a workaround for these
/// issues.
bool shouldWrap;
/// @dev A flag that indicates whether or not the Hyperdrive vault
/// shares token is a vault shares token. This is used to ensure
/// that the input into Hyperdrive properly handles rebasing tokens.
bool isRebasing;
}

/// Metadata ///

/// @notice Returns the name of this zap.
/// @return The name of this zap.
function name() external view returns (string memory);

/// @notice Returns the kind of this zap.
/// @return The kind of this zap.
function kind() external view returns (string memory);

/// @notice Returns the version of this zap.
/// @return The version of this zap.
function version() external view returns (string memory);

/// LPs ///

/// @notice Executes a swap on Uniswap and uses the proceeds to add
/// liquidity on Hyperdrive.
/// @param _hyperdrive The Hyperdrive pool to open the long on.
/// @param _minLpSharePrice The minimum LP share price the LP is willing
/// to accept for their shares. LPs incur negative slippage when
/// adding liquidity if there is a net curve position in the market,
/// so this allows LPs to protect themselves from high levels of
/// slippage. The units of this quantity are either base or vault
/// shares, depending on the value of `_options.asBase`.
/// @param _minApr The minimum APR at which the LP is willing to supply.
/// @param _maxApr The maximum APR at which the LP is willing to supply.
/// @param _hyperdriveOptions The options that configure how the Hyperdrive
/// operation is settled.
/// @param _zapInOptions The options that configure how the zap will be
/// settled.
/// @return lpShares The LP shares received by the LP.
function addLiquidityZap(
IHyperdrive _hyperdrive,
uint256 _minLpSharePrice,
uint256 _minApr,
uint256 _maxApr,
IHyperdrive.Options calldata _hyperdriveOptions,
IUniV3Zap.ZapInOptions calldata _zapInOptions
) external payable returns (uint256 lpShares);

/// @notice Removes liquidity on Hyperdrive and converts the proceeds to the
/// traders preferred asset by executing a swap on Uniswap v3.
/// @param _hyperdrive The Hyperdrive pool to open the long on.
/// @param _lpShares The LP shares to burn.
/// @param _minOutputPerShare The minimum amount the LP expects to receive
/// for each withdrawal share that is burned. The units of this
/// quantity are either base or vault shares, depending on the value
/// of `_options.asBase`.
/// @param _options The options that configure how the operation is settled.
/// @param _swapParams The Uniswap swap parameters for a multi-hop fill.
/// @param _shouldWrap A flag indicating whether or not the proceeds need to
/// be wrapped.
/// @return proceeds The proceeds of removing liquidity. These proceeds will
/// be in units determined by the Uniswap swap parameters.
/// @return withdrawalShares The base that the LP receives buys out some of
/// their LP shares, but it may not be sufficient to fully buy the
/// LP out. In this case, the LP receives withdrawal shares equal in
/// value to the present value they are owed. As idle capital
/// becomes available, the pool will buy back these shares.
function removeLiquidityZap(
IHyperdrive _hyperdrive,
uint256 _lpShares,
uint256 _minOutputPerShare,
IHyperdrive.Options calldata _options,
ISwapRouter.ExactInputParams calldata _swapParams,
bool _shouldWrap
) external returns (uint256 proceeds, uint256 withdrawalShares);

/// @notice Redeem withdrawal shares on Hyperdrive and converts the proceeds
/// to the traders preferred asset by executing a swap on Uniswap
/// v3.
/// @param _hyperdrive The Hyperdrive pool to open the long on.
/// @param _withdrawalShares The withdrawal shares to redeem.
/// @param _minOutputPerShare The minimum amount the LP expects to
/// receive for each withdrawal share that is burned. The units of
/// this quantity are either base or vault shares, depending on the
/// value of `_options.asBase`.
/// @param _options The options that configure how the operation is settled.
/// @param _swapParams The Uniswap swap parameters for a multi-hop fill.
/// @param _shouldWrap A flag indicating whether or not the proceeds need to
/// be wrapped.
/// @return proceeds The proceeds of redeeming withdrawal shares. These
/// proceeds will be in units determined by the Uniswap swap
/// parameters.
/// @return withdrawalSharesRedeemed The amount of withdrawal shares that
/// were redeemed.
function redeemWithdrawalSharesZap(
IHyperdrive _hyperdrive,
uint256 _withdrawalShares,
uint256 _minOutputPerShare,
IHyperdrive.Options calldata _options,
ISwapRouter.ExactInputParams calldata _swapParams,
bool _shouldWrap
) external returns (uint256 proceeds, uint256 withdrawalSharesRedeemed);

/// Longs ///

/// @notice Executes a swap on Uniswap and uses the proceeds to open a long
/// on Hyperdrive.
/// @param _hyperdrive The Hyperdrive pool to open the long on.
/// @param _minOutput The minimum number of bonds to receive.
/// @param _minVaultSharePrice The minimum vault share price at which to
/// open the long. This allows traders to protect themselves from
/// opening a long in a checkpoint where negative interest has
/// accrued.
/// @param _hyperdriveOptions The options that configure how the Hyperdrive
/// operation is settled.
/// @param _zapInOptions The options that configure how the zap will be
/// settled.
/// @return maturityTime The maturity time of the bonds.
/// @return longAmount The amount of bonds the trader received.
function openLongZap(
IHyperdrive _hyperdrive,
uint256 _minOutput,
uint256 _minVaultSharePrice,
IHyperdrive.Options calldata _hyperdriveOptions,
IUniV3Zap.ZapInOptions calldata _zapInOptions
) external payable returns (uint256 maturityTime, uint256 longAmount);

/// @notice Closes a long on Hyperdrive and converts the proceeds to the
/// traders preferred asset by executing a swap on Uniswap v3.
/// @param _hyperdrive The Hyperdrive pool to open the long on.
/// @param _maturityTime The maturity time of the long.
/// @param _bondAmount The amount of longs to close.
/// @param _minOutput The minimum proceeds the trader will accept. The units
/// of this quantity are either base or vault shares, depending on
/// the value of `_options.asBase`.
/// @param _options The options that configure how the Hyperdrive trade is
/// settled.
/// @param _swapParams The Uniswap swap parameters for a multi-hop fill.
/// @param _shouldWrap A flag indicating whether or not the proceeds need to
/// be wrapped.
/// @return proceeds The proceeds of closing the long. These proceeds will
/// be in units determined by the Uniswap swap parameters.
function closeLongZap(
IHyperdrive _hyperdrive,
uint256 _maturityTime,
uint256 _bondAmount,
uint256 _minOutput,
IHyperdrive.Options calldata _options,
ISwapRouter.ExactInputParams calldata _swapParams,
bool _shouldWrap
) external returns (uint256 proceeds);

/// Shorts ///

/// @notice Executes a swap on Uniswap and uses the proceeds to open a short
/// on Hyperdrive.
/// @param _hyperdrive The Hyperdrive pool to open the long on.
/// @param _maxDeposit The most the user expects to deposit for this trade.
/// The units of this quantity are either base or vault shares,
/// depending on the value of `_options.asBase`.
/// @param _minVaultSharePrice The minimum vault share price at which to open
/// the short. This allows traders to protect themselves from opening
/// a short in a checkpoint where negative interest has accrued.
/// @param _hyperdriveOptions The options that configure how the Hyperdrive
/// operation is settled.
/// @param _zapInOptions The options that configure how the zap will be
/// settled.
/// @return maturityTime The maturity time of the bonds.
/// @return deposit The amount the user deposited for this trade. The units
/// of this quantity are either base or vault shares, depending on
/// the value of `_options.asBase`.
function openShortZap(
IHyperdrive _hyperdrive,
uint256 _bondAmount,
uint256 _maxDeposit,
uint256 _minVaultSharePrice,
IHyperdrive.Options calldata _hyperdriveOptions,
IUniV3Zap.ZapInOptions calldata _zapInOptions
) external payable returns (uint256 maturityTime, uint256 deposit);

/// @notice Closes a short on Hyperdrive and converts the proceeds to the
/// traders preferred asset by executing a swap on Uniswap v3.
/// @param _hyperdrive The Hyperdrive pool to open the long on.
/// @param _maturityTime The maturity time of the short.
/// @param _bondAmount The amount of shorts to close.
/// @param _minOutput The minimum output of this trade. The units of this
/// quantity are either base or vault shares, depending on the value
/// of `_options.asBase`.
/// @param _options The options that configure how the Hyperdrive trade is
/// settled.
/// @param _swapParams The Uniswap swap parameters for a multi-hop fill.
/// @param _shouldWrap A flag indicating whether or not the proceeds need to
/// be wrapped.
/// @return proceeds The proceeds of closing the short. These proceeds will
/// be in units determined by the Uniswap swap parameters.
function closeShortZap(
IHyperdrive _hyperdrive,
uint256 _maturityTime,
uint256 _bondAmount,
uint256 _minOutput,
IHyperdrive.Options calldata _options,
ISwapRouter.ExactInputParams calldata _swapParams,
bool _shouldWrap
) external returns (uint256 proceeds);

/// Getters ///

/// @notice Returns the Uniswap swap router.
/// @return The Uniswap swap router.
function swapRouter() external view returns (ISwapRouter);
}
10 changes: 10 additions & 0 deletions contracts/src/interfaces/IWETH.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;

import { IERC20 } from "./IERC20.sol";

interface IWETH is IERC20 {
function deposit() external payable;

function withdraw(uint256 wad) external;
}
8 changes: 8 additions & 0 deletions contracts/src/interfaces/IWrappedERC20.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;

import { IERC20 } from "./IERC20.sol";

interface IWrappedERC20 is IERC20 {
function wrap(uint256 _amount) external returns (uint256);
}
5 changes: 4 additions & 1 deletion contracts/src/libraries/Constants.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ pragma solidity ^0.8.20;
address constant ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;

/// @dev The version of the contracts.
string constant VERSION = "v1.0.19";
string constant VERSION = "v1.0.20";

/// @dev The number of targets that must be deployed for a full deployment.
uint256 constant NUM_TARGETS = 5;
Expand Down Expand Up @@ -111,3 +111,6 @@ string constant STAKING_USDS_HYPERDRIVE_KIND = "StakingUSDSHyperdrive";

/// @dev The kind of StkWellSHyperdrive.
string constant STK_WELL_HYPERDRIVE_KIND = "StkWellHyperdrive";

/// @dev The kind of UniV3Zap.
string constant UNI_V3_ZAP_KIND = "UniV3Zap";
Loading
Loading