-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: base network ezETH/WETH uniswap flashswap strategy and spot/res…
…erve oracles and tests
- Loading branch information
Showing
10 changed files
with
956 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
// SPDX-License-Identifier: BUSL-1.1 | ||
pragma solidity 0.8.21; | ||
|
||
import { IonPool } from "./../../../IonPool.sol"; | ||
import { IonPool } from "./../../../IonPool.sol"; | ||
import { GemJoin } from "./../../../join/GemJoin.sol"; | ||
import { Whitelist } from "./../../../Whitelist.sol"; | ||
import { IonHandlerBase } from "./../../IonHandlerBase.sol"; | ||
import { IWETH9 } from "./../../../interfaces/IWETH9.sol"; | ||
import { UniswapFlashswapHandler } from "./../../UniswapFlashswapHandler.sol"; | ||
import { IUniswapV3Pool } from "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol"; | ||
|
||
/** | ||
* @notice Handler for the ezETH collateral in the ezETH/WETH market on Base. | ||
* | ||
* @custom:security-contact security@molecularlabs.io | ||
*/ | ||
contract BaseEzEthWethHandler is UniswapFlashswapHandler { | ||
/** | ||
* @notice Creates a new `EzEthWethHandler` instance. | ||
* @param _ilkIndex Ilk index of the pool. | ||
* @param _ionPool address. | ||
* @param _gemJoin address. | ||
* @param _whitelist address. | ||
* @param _pool address of the ezETH/WETH Aerodrome pool. | ||
* @param _wethIsToken0 Whether WETH is token0 or token1. | ||
* @param _weth The WETH address of this chain. | ||
*/ | ||
constructor( | ||
uint8 _ilkIndex, | ||
IonPool _ionPool, | ||
GemJoin _gemJoin, | ||
Whitelist _whitelist, | ||
IUniswapV3Pool _pool, | ||
bool _wethIsToken0, | ||
IWETH9 _weth | ||
) | ||
IonHandlerBase(_ilkIndex, _ionPool, _gemJoin, _whitelist, _weth) | ||
UniswapFlashswapHandler(_pool, _wethIsToken0) | ||
{ } | ||
} |
71 changes: 71 additions & 0 deletions
71
src/oracles/reserve/lrt/base/BaseEzEthWethReserveOracle.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.21; | ||
|
||
import { WadRayMath } from "../../../../libraries/math/WadRayMath.sol"; | ||
import { ReserveOracle } from "../../ReserveOracle.sol"; | ||
import { BASE_EZETH_ETH_EXCHANGE_RATE_CHAINLINK, BASE_SEQUENCER_UPTIME_FEED } from "../../../../Constants.sol"; | ||
import { SafeCast } from "openzeppelin-contracts/contracts/utils/math/SafeCast.sol"; | ||
|
||
/** | ||
* @notice Reserve Oracle for ezETH denominated in WETH. | ||
* | ||
* @custom:security-contact security@molecularlabs.io | ||
*/ | ||
contract BaseEzEthWethReserveOracle is ReserveOracle { | ||
using WadRayMath for uint256; | ||
using SafeCast for int256; | ||
|
||
error SequencerDown(); | ||
error GracePeriodNotOver(); | ||
error MaxTimeFromLastUpdateExceeded(uint256, uint256); | ||
|
||
uint256 public immutable MAX_TIME_FROM_LAST_UPDATE; // seconds | ||
uint256 public immutable GRACE_PERIOD; | ||
|
||
/** | ||
* @notice Creates a new `BaseEzEthWethReserveOracle` instance. Provides | ||
* the amount of WETH equal to one ezETH (ETH / ezETH). | ||
* @dev The value of ezETH denominated in WETH by Chainlink. | ||
* @param _feeds List of alternative data sources for the WETH/ezETH exchange rate. | ||
* @param _quorum The amount of alternative data sources to aggregate. | ||
* @param _maxChange Maximum percent change between exchange rate updates. [RAY] | ||
*/ | ||
constructor( | ||
uint8 _ilkIndex, | ||
address[] memory _feeds, | ||
uint8 _quorum, | ||
uint256 _maxChange, | ||
uint256 _maxTimeFromLastUpdate, | ||
uint256 _gracePeriod | ||
) | ||
ReserveOracle(_ilkIndex, _feeds, _quorum, _maxChange) | ||
{ | ||
MAX_TIME_FROM_LAST_UPDATE = _maxTimeFromLastUpdate; | ||
GRACE_PERIOD = _gracePeriod; | ||
_initializeExchangeRate(); | ||
} | ||
|
||
function _getProtocolExchangeRate() internal view override returns (uint256) { | ||
( | ||
/*uint80 roundID*/ | ||
, | ||
int256 answer, | ||
uint256 startedAt, | ||
/*uint256 updatedAt*/ | ||
, | ||
/*uint80 answeredInRound*/ | ||
) = BASE_SEQUENCER_UPTIME_FEED.latestRoundData(); | ||
|
||
if (answer == 1) revert SequencerDown(); | ||
if (block.timestamp - startedAt <= GRACE_PERIOD) revert GracePeriodNotOver(); | ||
|
||
(, int256 ethPerEzEth,, uint256 ethPerEzEthUpdatedAt,) = | ||
BASE_EZETH_ETH_EXCHANGE_RATE_CHAINLINK.latestRoundData(); | ||
|
||
if (block.timestamp - ethPerEzEthUpdatedAt > MAX_TIME_FROM_LAST_UPDATE) { | ||
revert MaxTimeFromLastUpdateExceeded(block.timestamp - ethPerEzEthUpdatedAt, MAX_TIME_FROM_LAST_UPDATE); | ||
} else { | ||
return ethPerEzEth.toUint256(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity 0.8.21; | ||
|
||
import { SpotOracle } from "../../../../oracles/spot/SpotOracle.sol"; | ||
import { WadRayMath } from "../../../../libraries/math/WadRayMath.sol"; | ||
import { BASE_SEQUENCER_UPTIME_FEED, BASE_EZETH_ETH_PRICE_CHAINLINK } from "../../../../Constants.sol"; | ||
|
||
import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol"; | ||
|
||
/** | ||
* @notice The ezETH spot oracle denominated in WETH on Base. | ||
* | ||
* @custom:security-contact security@molecularlabs.io | ||
*/ | ||
contract BaseEzEthWethSpotOracle is SpotOracle { | ||
using WadRayMath for uint256; | ||
using SafeCast for int256; | ||
|
||
error SequencerDown(); | ||
error GracePeriodNotOver(); | ||
|
||
/** | ||
* @notice The maximum delay for the oracle update in seconds before the | ||
* data is considered stale. | ||
*/ | ||
uint256 public immutable MAX_TIME_FROM_LAST_UPDATE; // seconds | ||
|
||
/** | ||
* @notice Amount of time to wait after the sequencer restarts. | ||
*/ | ||
uint256 public immutable GRACE_PERIOD; | ||
|
||
/** | ||
* @notice Creates a new `WeEthWethSpotOracle` instance. | ||
* @param _ltv The loan to value ratio for the weETH/WETH market. | ||
* @param _reserveOracle The associated reserve oracle. | ||
* @param _maxTimeFromLastUpdate The maximum delay for the oracle update in seconds | ||
*/ | ||
constructor( | ||
uint256 _ltv, | ||
address _reserveOracle, | ||
uint256 _maxTimeFromLastUpdate, | ||
uint256 _gracePeriod | ||
) | ||
SpotOracle(_ltv, _reserveOracle) | ||
{ | ||
MAX_TIME_FROM_LAST_UPDATE = _maxTimeFromLastUpdate; | ||
GRACE_PERIOD = _gracePeriod; | ||
} | ||
|
||
/** | ||
* @notice Gets the price of weETH in WETH. | ||
* @dev Redstone oracle returns ETH per weETH with 8 decimals. This | ||
* @return wethPerWeEth price of weETH in WETH. [WAD] | ||
*/ | ||
function getPrice() public view override returns (uint256) { | ||
( | ||
/*uint80 roundID*/ | ||
, | ||
int256 answer, | ||
uint256 startedAt, | ||
/*uint256 updatedAt*/ | ||
, | ||
/*uint80 answeredInRound*/ | ||
) = BASE_SEQUENCER_UPTIME_FEED.latestRoundData(); | ||
|
||
if (answer == 1) revert SequencerDown(); | ||
if (block.timestamp - startedAt <= GRACE_PERIOD) revert GracePeriodNotOver(); | ||
|
||
( | ||
/*uint80 roundID*/ | ||
, | ||
int256 ethPerEzEth, | ||
/*uint startedAt*/ | ||
, | ||
uint256 ethPerEzEthUpdatedAt, | ||
/*uint80 answeredInRound*/ | ||
) = BASE_EZETH_ETH_PRICE_CHAINLINK.latestRoundData(); // [WAD] | ||
|
||
if (block.timestamp - ethPerEzEthUpdatedAt > MAX_TIME_FROM_LAST_UPDATE) { | ||
return 0; // collateral valuation is zero if oracle data is stale | ||
} else { | ||
return ethPerEzEth.toUint256(); // [wad] | ||
} | ||
} | ||
} |
Oops, something went wrong.