|
| 1 | +// SPDX-License-Identifier: MIT |
| 2 | +pragma solidity ^0.8.0; |
| 3 | + |
| 4 | +import {ERC20} from "solmate/src/tokens/ERC20.sol"; |
| 5 | +import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol"; |
| 6 | +import {Currency, CurrencyLibrary} from "@uniswap/v4-core/src/types/Currency.sol"; |
| 7 | +import {BaseTokenWrapperHook} from "../base/hooks/BaseTokenWrapperHook.sol"; |
| 8 | +import {IWstETH} from "../interfaces/external/IWstETH.sol"; |
| 9 | + |
| 10 | +/// @title Wrapped Staked ETH (wstETH) Hook |
| 11 | +/// @notice Hook for wrapping/unwrapping stETH/wstETH in Uniswap V4 pools |
| 12 | +/// @dev Implements dynamic exchange rate wrapping/unwrapping between stETH and wstETH |
| 13 | +/// @dev wstETH represents stETH with accrued staking rewards, maintaining a dynamic exchange rate |
| 14 | +contract WstETHHook is BaseTokenWrapperHook { |
| 15 | + /// @notice The wstETH contract used for wrapping/unwrapping operations |
| 16 | + IWstETH public immutable wstETH; |
| 17 | + |
| 18 | + /// @notice Creates a new wstETH wrapper hook |
| 19 | + /// @param _manager The Uniswap V4 pool manager |
| 20 | + /// @param _wsteth The wstETH contract address |
| 21 | + /// @dev Initializes with wstETH as wrapper token and stETH as underlying token |
| 22 | + constructor(IPoolManager _manager, IWstETH _wsteth) |
| 23 | + BaseTokenWrapperHook( |
| 24 | + _manager, |
| 25 | + Currency.wrap(address(_wsteth)), // wrapper token is wsteth |
| 26 | + Currency.wrap(_wsteth.stETH()) // underlying token is stETH |
| 27 | + ) |
| 28 | + { |
| 29 | + wstETH = _wsteth; |
| 30 | + ERC20(Currency.unwrap(underlyingCurrency)).approve(address(wstETH), type(uint256).max); |
| 31 | + } |
| 32 | + |
| 33 | + /// @inheritdoc BaseTokenWrapperHook |
| 34 | + /// @notice Wraps stETH to wstETH |
| 35 | + /// @param underlyingAmount Amount of stETH to wrap |
| 36 | + /// @return Amount of wstETH received |
| 37 | + function _deposit(uint256 underlyingAmount) internal override returns (uint256) { |
| 38 | + return wstETH.wrap(underlyingAmount); |
| 39 | + } |
| 40 | + |
| 41 | + /// @inheritdoc BaseTokenWrapperHook |
| 42 | + /// @notice Unwraps wstETH to stETH |
| 43 | + /// @param wrapperAmount Amount of wstETH to unwrap |
| 44 | + /// @return Amount of stETH received |
| 45 | + function _withdraw(uint256 wrapperAmount) internal override returns (uint256) { |
| 46 | + return wstETH.unwrap(wrapperAmount); |
| 47 | + } |
| 48 | + |
| 49 | + /// @inheritdoc BaseTokenWrapperHook |
| 50 | + /// @notice Calculates how much stETH is needed to receive a specific amount of wstETH |
| 51 | + /// @param wrappedAmount Desired amount of wstETH |
| 52 | + /// @return Amount of stETH required |
| 53 | + /// @dev Uses current stETH/wstETH exchange rate for calculation |
| 54 | + function _getWrapInputRequired(uint256 wrappedAmount) internal view override returns (uint256) { |
| 55 | + return wstETH.getStETHByWstETH(wrappedAmount); |
| 56 | + } |
| 57 | + |
| 58 | + /// @inheritdoc BaseTokenWrapperHook |
| 59 | + /// @notice Calculates how much wstETH is needed to receive a specific amount of stETH |
| 60 | + /// @param underlyingAmount Desired amount of stETH |
| 61 | + /// @return Amount of wstETH required |
| 62 | + /// @dev Uses current stETH/wstETH exchange rate for calculation |
| 63 | + function _getUnwrapInputRequired(uint256 underlyingAmount) internal view override returns (uint256) { |
| 64 | + return wstETH.getWstETHByStETH(underlyingAmount); |
| 65 | + } |
| 66 | +} |
0 commit comments