diff --git a/contracts/external/redstone/IRedstoneOracle.sol b/contracts/external/redstone/IRedstoneOracle.sol new file mode 100644 index 00000000..f7fafd8f --- /dev/null +++ b/contracts/external/redstone/IRedstoneOracle.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +interface IRedstoneOracle { + function priceOf(address asset) external view returns (uint256); + + function priceOfETH() external view returns (uint256); + + function getDataFeedIdForAsset(address asset) external view returns (bytes32); + + function getDataFeedIds() external view returns (bytes32[] memory dataFeedIds); +} diff --git a/contracts/oracles/default/RedstoneAdapterPriceOracle.sol b/contracts/oracles/default/RedstoneAdapterPriceOracle.sol new file mode 100644 index 00000000..6ed7a9c1 --- /dev/null +++ b/contracts/oracles/default/RedstoneAdapterPriceOracle.sol @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.0; + +import "openzeppelin-contracts-upgradeable/contracts/token/ERC20/ERC20Upgradeable.sol"; + +import "../../external/redstone/IRedstoneOracle.sol"; +import "../BasePriceOracle.sol"; + +/** + * @title RedstoneAdapterPriceOracle + * @notice Returns prices from Redstone. + * @dev Implements `BasePriceOracle`. + * @author Veliko Minkov (https://github.com/vminkov) + */ +contract RedstoneAdapterPriceOracle is BasePriceOracle { + /** + * @notice The Redstone oracle contract + */ + IRedstoneOracle public REDSTONE_ORACLE; + + /** + * @dev Constructor to set admin, wtoken address and native token USD price feed address + * @param redstoneOracle The Redstone oracle contract address + */ + constructor(address redstoneOracle) { + REDSTONE_ORACLE = IRedstoneOracle(redstoneOracle); + } + + /** + * @notice Internal function returning the price in of `underlying`. + * @dev will return a price denominated in the native token + */ + function _price(address underlying) internal view returns (uint256) { + uint256 priceInUsd = REDSTONE_ORACLE.priceOf(underlying); + uint256 priceOfNativeInUsd = REDSTONE_ORACLE.priceOfETH(); + return (priceInUsd * 1e18) / priceOfNativeInUsd; + } + + /** + * @notice Returns the price in of `underlying` either in the + * native token (implements `BasePriceOracle`). + */ + function price(address underlying) external view override returns (uint256) { + return _price(underlying); + } + + /** + * @notice Returns the price in WNATIVE of the token underlying `cToken`. + * @dev Implements the `BasePriceOracle` interface for Ionic pools (and Compound v2). + * @return Price in WNATIVE of the token underlying `cToken`, scaled by `10 ** (36 - underlyingDecimals)`. + */ + function getUnderlyingPrice(ICErc20 cToken) external view override returns (uint256) { + // Get underlying token address + address underlying = cToken.underlying(); + + uint256 oraclePrice = _price(underlying); + + uint256 underlyingDecimals = uint256(ERC20Upgradeable(underlying).decimals()); + return + underlyingDecimals <= 18 + ? uint256(oraclePrice) * (10**(18 - underlyingDecimals)) + : uint256(oraclePrice) / (10**(underlyingDecimals - 18)); + } +} diff --git a/contracts/test/oracles/RedstoneAdapterOracleTest.t.sol b/contracts/test/oracles/RedstoneAdapterOracleTest.t.sol new file mode 100644 index 00000000..5c2745df --- /dev/null +++ b/contracts/test/oracles/RedstoneAdapterOracleTest.t.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.0; + +import { TransparentUpgradeableProxy } from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; + +import { RedstoneAdapterPriceOracle } from "../../oracles/default/RedstoneAdapterPriceOracle.sol"; + +import { BaseTest } from "../config/BaseTest.t.sol"; + +contract RedstoneAdapterOracleTest is BaseTest { + RedstoneAdapterPriceOracle public oracle; + address public redstoneOracleAddress; + address MODE_USDC = 0xd988097fb8612cc24eeC14542bC03424c656005f; + address MODE_EZETH = 0x2416092f143378750bb29b79eD961ab195CcEea5; + address MODE_WBTC = 0xcDd475325D6F564d27247D1DddBb0DAc6fA0a5CF; + + function afterForkSetUp() internal override { + if (block.chainid == MODE_MAINNET) { + redstoneOracleAddress = 0x7C1DAAE7BB0688C9bfE3A918A4224041c7177256; + } + + oracle = new RedstoneAdapterPriceOracle(redstoneOracleAddress); + } + + function testPrintPricesMode() public fork(MODE_MAINNET) { + emit log_named_uint("ezETH price (18 dec)", oracle.price(MODE_EZETH)); + emit log_named_uint("WBTC price (8 dec)", oracle.price(MODE_WBTC)); + } +}