From 272a65b071c42bc2e75fe63cb45956824991d429 Mon Sep 17 00:00:00 2001 From: Jamie Pickett Date: Sun, 17 Mar 2024 23:49:18 -0400 Subject: [PATCH] added in myso oracle contracts --- .../interfaces/oracles/IWSTETH.sol | 13 ++ .../oracles/custom/MysoOracle.sol | 121 ++++++++++++++++++ package-lock.json | 6 +- 3 files changed, 137 insertions(+), 3 deletions(-) create mode 100644 contracts/peer-to-peer/interfaces/oracles/IWSTETH.sol create mode 100644 contracts/peer-to-peer/oracles/custom/MysoOracle.sol diff --git a/contracts/peer-to-peer/interfaces/oracles/IWSTETH.sol b/contracts/peer-to-peer/interfaces/oracles/IWSTETH.sol new file mode 100644 index 00000000..2213234a --- /dev/null +++ b/contracts/peer-to-peer/interfaces/oracles/IWSTETH.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +interface IWSTETH { + /** + * @notice gets amount of stEth for given wstEth + * @param _wstETHAmount amount of wstEth + * @return amount of stEth + */ + function getStETHByWstETH( + uint256 _wstETHAmount + ) external view returns (uint256); +} diff --git a/contracts/peer-to-peer/oracles/custom/MysoOracle.sol b/contracts/peer-to-peer/oracles/custom/MysoOracle.sol new file mode 100644 index 00000000..0aad4690 --- /dev/null +++ b/contracts/peer-to-peer/oracles/custom/MysoOracle.sol @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.19; + +import {ChainlinkBase} from "../chainlink/ChainlinkBase.sol"; +import {Errors} from "../../../Errors.sol"; +import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; +import {Ownable2Step} from "@openzeppelin/contracts/access/Ownable2Step.sol"; +import {IWSTETH} from "../../interfaces/oracles/IWSTETH.sol"; +import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; + +/** + * @dev supports oracles which are compatible with v2v3 or v3 interfaces + */ +contract MysoOracle is ChainlinkBase, Ownable2Step { + struct MysoPrice { + uint112 priceUntilTimestampPassed; + uint112 priceOnceTimestampPassed; + uint32 timestampLatestProposedPriceBecomesValid; + } + + // solhint-disable var-name-mixedcase + address internal constant MYSO = 0x00000000000000000000000000000000DeaDBeef; // TODO: put in real myso address + address internal constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; // weth + address internal constant WSTETH = + 0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0; //wsteth + address internal constant STETH = + 0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84; //steth + uint256 internal constant MYSO_IOO_BASE_CURRENCY_UNIT = 1e18; // 18 decimals for ETH based oracles + address internal constant ETH_USD_CHAINLINK = + 0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419; //eth usd chainlink + + uint256 internal constant MYSO_PRICE_TIME_LOCK = 1 days; + + MysoPrice public mysoPrice; + + /** + * @dev constructor for MysoOracle + * @param _tokenAddrs array of token addresses + * @param _oracleAddrs array of oracle addresses + * @param _mysoUsdPrice initial price of myso in usd (use 8 decimals like chainlink) (eg. 0.50 USD = 0.5 * 1e8) + */ + constructor( + address[] memory _tokenAddrs, + address[] memory _oracleAddrs, + uint112 _mysoUsdPrice + ) ChainlinkBase(_tokenAddrs, _oracleAddrs, MYSO_IOO_BASE_CURRENCY_UNIT) { + mysoPrice = MysoPrice( + _mysoUsdPrice, + _mysoUsdPrice, + uint32(block.timestamp) + ); + } + + /** + * @dev updates timestampLatestProposedPriceBecomesValid and priceOnceTimestampPassed + * only updates priceUntilTimestampPassed if the prior time lock had passed + * @param _newMysoUsdPrice initial price of myso in usd (use 8 decimals like chainlink) (eg. 0.50 USD = 0.5 * 1e8) + */ + + function setMysoPrice(uint112 _newMysoUsdPrice) external onlyOwner { + if ( + block.timestamp < mysoPrice.timestampLatestProposedPriceBecomesValid + ) { + // if the priceOnceTimestampPassed is not yet active, update that price, + // leave priceUntilTimestampPassed the same but reset the time lock + mysoPrice = MysoPrice( + mysoPrice.priceUntilTimestampPassed, + _newMysoUsdPrice, + uint32(block.timestamp + MYSO_PRICE_TIME_LOCK) + ); + } else { + // if the priceOnceTimestampPassed is not yet active, update the priceUntilTimestampPassed with old priceOnceTimestampPassed, + // update the priceOnceTimestampPassed with new price, and reset the time lock + mysoPrice = MysoPrice( + mysoPrice.priceOnceTimestampPassed, + _newMysoUsdPrice, + uint32(block.timestamp + MYSO_PRICE_TIME_LOCK) + ); + } + } + + function _getPriceOfToken( + address token + ) internal view virtual override returns (uint256 tokenPriceRaw) { + if (token == MYSO) { + tokenPriceRaw = _getMysoPriceInEth(); + } else if (token == WETH) { + tokenPriceRaw = 1e18; + } else if (token == WSTETH) { + tokenPriceRaw = _getWstEthPrice(); + } else { + tokenPriceRaw = super._getPriceOfToken(token); + } + } + + function _getWstEthPrice() internal view returns (uint256 wstEthPriceRaw) { + uint256 stEthAmountPerWstEth = IWSTETH(WSTETH).getStETHByWstETH(1e18); + uint256 stEthPriceInEth = _getPriceOfToken(STETH); + wstEthPriceRaw = Math.mulDiv( + stEthPriceInEth, + stEthAmountPerWstEth, + 1e18 + ); + } + + function _getMysoPriceInEth() + internal + view + returns (uint256 mysoPriceInEth) + { + uint256 mysoPriceInUsd = block.timestamp < + mysoPrice.timestampLatestProposedPriceBecomesValid + ? mysoPrice.priceUntilTimestampPassed + : mysoPrice.priceOnceTimestampPassed; + uint256 ethPriceInUsd = _checkAndReturnLatestRoundData( + ETH_USD_CHAINLINK + ); + mysoPriceInEth = Math.mulDiv(mysoPriceInUsd, 1e18, ethPriceInUsd); + } +} diff --git a/package-lock.json b/package-lock.json index b8f50b7a..6d8a1f85 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10817,7 +10817,7 @@ "fs-readdir-recursive": "^1.1.0", "lodash": "^4.17.14", "markdown-table": "^1.1.3", - "mocha": "^7.1.1", + "mocha": "10.2.0", "req-cwd": "^2.0.0", "request": "^2.88.0", "request-promise-native": "^1.0.5", @@ -11512,7 +11512,7 @@ "keccak": "^3.0.2", "lodash": "^4.17.11", "mnemonist": "^0.38.0", - "mocha": "^10.0.0", + "mocha": "10.2.0", "p-map": "^4.0.0", "qs": "^6.7.0", "raw-body": "^2.4.1", @@ -13646,7 +13646,7 @@ "globby": "^10.0.1", "jsonschema": "^1.2.4", "lodash": "^4.17.15", - "mocha": "7.1.2", + "mocha": "10.2.0", "node-emoji": "^1.10.0", "pify": "^4.0.1", "recursive-readdir": "^2.2.2",