-
Notifications
You must be signed in to change notification settings - Fork 33
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
175 additions
and
131 deletions.
There are no files selected for viewing
28 changes: 14 additions & 14 deletions
28
src/morpho-chainlink-v2/ChainlinkOracle.sol → ...-chainlink-v2/MorphoChainlinkOracleV2.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 |
---|---|---|
@@ -1,50 +1,50 @@ | ||
// SPDX-License-Identifier: GPL-2.0-or-later | ||
pragma solidity 0.8.21; | ||
|
||
import {IChainlinkOracle} from "./interfaces/IChainlinkOracle.sol"; | ||
import {IOracle} from "../../lib/morpho-blue/src/interfaces/IOracle.sol"; | ||
import {IMorphoChainlinkOracleV2} from "./interfaces/IMorphoChainlinkOracleV2.sol"; | ||
|
||
import {AggregatorV3Interface, ChainlinkDataFeedLib} from "./libraries/ChainlinkDataFeedLib.sol"; | ||
import {IERC4626, VaultLib} from "./libraries/VaultLib.sol"; | ||
import {ErrorsLib} from "./libraries/ErrorsLib.sol"; | ||
import {IERC4626, VaultLib} from "../morpho-chainlink-v1/libraries/VaultLib.sol"; | ||
import {Math} from "../../lib/openzeppelin-contracts/contracts/utils/math/Math.sol"; | ||
import {AggregatorV3Interface, ChainlinkDataFeedLib} from "../morpho-chainlink-v1/libraries/ChainlinkDataFeedLib.sol"; | ||
|
||
/// @title ChainlinkOracle | ||
/// @title MorphoChainlinkOracleV2 | ||
/// @author Morpho Labs | ||
/// @custom:contact [email protected] | ||
/// @notice Morpho Blue oracle using Chainlink-compliant feeds. | ||
contract ChainlinkOracle is IChainlinkOracle { | ||
contract MorphoChainlinkOracleV2 is IMorphoChainlinkOracleV2 { | ||
using Math for uint256; | ||
using VaultLib for IERC4626; | ||
using ChainlinkDataFeedLib for AggregatorV3Interface; | ||
|
||
/* IMMUTABLES */ | ||
|
||
/// @inheritdoc IChainlinkOracle | ||
/// @inheritdoc IMorphoChainlinkOracleV2 | ||
IERC4626 public immutable BASE_VAULT; | ||
|
||
/// @inheritdoc IChainlinkOracle | ||
/// @inheritdoc IMorphoChainlinkOracleV2 | ||
uint256 public immutable BASE_VAULT_CONVERSION_SAMPLE; | ||
|
||
/// @inheritdoc IChainlinkOracle | ||
/// @inheritdoc IMorphoChainlinkOracleV2 | ||
IERC4626 public immutable QUOTE_VAULT; | ||
|
||
/// @inheritdoc IChainlinkOracle | ||
/// @inheritdoc IMorphoChainlinkOracleV2 | ||
uint256 public immutable QUOTE_VAULT_CONVERSION_SAMPLE; | ||
|
||
/// @inheritdoc IChainlinkOracle | ||
/// @inheritdoc IMorphoChainlinkOracleV2 | ||
AggregatorV3Interface public immutable BASE_FEED_1; | ||
|
||
/// @inheritdoc IChainlinkOracle | ||
/// @inheritdoc IMorphoChainlinkOracleV2 | ||
AggregatorV3Interface public immutable BASE_FEED_2; | ||
|
||
/// @inheritdoc IChainlinkOracle | ||
/// @inheritdoc IMorphoChainlinkOracleV2 | ||
AggregatorV3Interface public immutable QUOTE_FEED_1; | ||
|
||
/// @inheritdoc IChainlinkOracle | ||
/// @inheritdoc IMorphoChainlinkOracleV2 | ||
AggregatorV3Interface public immutable QUOTE_FEED_2; | ||
|
||
/// @inheritdoc IChainlinkOracle | ||
/// @inheritdoc IMorphoChainlinkOracleV2 | ||
uint256 public immutable SCALE_FACTOR; | ||
|
||
/* CONSTRUCTOR */ | ||
|
22 changes: 0 additions & 22 deletions
22
src/morpho-chainlink-v2/interfaces/AggregatorV3Interface.sol
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
10 changes: 5 additions & 5 deletions
10
...inlink-v2/interfaces/IChainlinkOracle.sol → ...2/interfaces/IMorphoChainlinkOracleV2.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 |
---|---|---|
@@ -1,15 +1,15 @@ | ||
// SPDX-License-Identifier: GPL-2.0-or-later | ||
pragma solidity >=0.5.0; | ||
|
||
import {IERC4626} from "./IERC4626.sol"; | ||
import {AggregatorV3Interface} from "./AggregatorV3Interface.sol"; | ||
import {IERC4626} from "../../morpho-chainlink-v1/interfaces/IERC4626.sol"; | ||
import {IOracle} from "../../../lib/morpho-blue/src/interfaces/IOracle.sol"; | ||
import {AggregatorV3Interface} from "../../morpho-chainlink-v1/interfaces/AggregatorV3Interface.sol"; | ||
|
||
/// @title IChainlinkOracle | ||
/// @title IMorphoChainlinkOracleV2 | ||
/// @author Morpho Labs | ||
/// @custom:contact [email protected] | ||
/// @notice Interface of ChainlinkOracle. | ||
interface IChainlinkOracle is IOracle { | ||
/// @notice Interface of MorphoChainlinkOracleV2. | ||
interface IMorphoChainlinkOracleV2 is IOracle { | ||
/// @notice Returns the address of the base ERC4626 vault. | ||
function BASE_VAULT() external view returns (IERC4626); | ||
|
||
|
36 changes: 0 additions & 36 deletions
36
src/morpho-chainlink-v2/libraries/ChainlinkDataFeedLib.sol
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
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,126 @@ | ||
// SPDX-License-Identifier: GPL-2.0-or-later | ||
pragma solidity ^0.8.0; | ||
|
||
import "../lib/forge-std/src/Test.sol"; | ||
import "../src/morpho-chainlink-v1/ChainlinkOracle.sol"; | ||
import "./mocks/ChainlinkAggregatorMock.sol"; | ||
import "./helpers/Constants.sol"; | ||
|
||
contract ChainlinkOracleTest is Test { | ||
function setUp() public { | ||
vm.createSelectFork(vm.envString("ETH_RPC_URL")); | ||
} | ||
|
||
function testOracleWbtcUsdc() public { | ||
ChainlinkOracle oracle = new ChainlinkOracle(vaultZero, wBtcBtcFeed, btcUsdFeed, usdcUsdFeed, feedZero, 1, 8, 6); | ||
(, int256 firstBaseAnswer,,,) = wBtcBtcFeed.latestRoundData(); | ||
(, int256 secondBaseAnswer,,,) = btcUsdFeed.latestRoundData(); | ||
(, int256 quoteAnswer,,,) = usdcUsdFeed.latestRoundData(); | ||
assertEq( | ||
oracle.price(), | ||
(uint256(firstBaseAnswer) * uint256(secondBaseAnswer) * 10 ** (36 + 8 + 6 - 8 - 8 - 8)) | ||
/ uint256(quoteAnswer) | ||
); | ||
} | ||
|
||
function testOracleUsdcWbtc() public { | ||
ChainlinkOracle oracle = new ChainlinkOracle(vaultZero, usdcUsdFeed, feedZero, wBtcBtcFeed, btcUsdFeed, 1, 6, 8); | ||
(, int256 baseAnswer,,,) = usdcUsdFeed.latestRoundData(); | ||
(, int256 firstQuoteAnswer,,,) = wBtcBtcFeed.latestRoundData(); | ||
(, int256 secondQuoteAnswer,,,) = btcUsdFeed.latestRoundData(); | ||
assertEq( | ||
oracle.price(), | ||
(uint256(baseAnswer) * 10 ** (36 + 8 + 8 + 8 - 6 - 8)) | ||
/ (uint256(firstQuoteAnswer) * uint256(secondQuoteAnswer)) | ||
); | ||
} | ||
|
||
function testOracleWbtcEth() public { | ||
ChainlinkOracle oracle = new ChainlinkOracle(vaultZero, wBtcBtcFeed, btcEthFeed, feedZero, feedZero, 1, 8, 18); | ||
(, int256 firstBaseAnswer,,,) = wBtcBtcFeed.latestRoundData(); | ||
(, int256 secondBaseAnswer,,,) = btcEthFeed.latestRoundData(); | ||
assertEq(oracle.price(), (uint256(firstBaseAnswer) * uint256(secondBaseAnswer) * 10 ** (36 + 18 - 8 - 8 - 18))); | ||
} | ||
|
||
function testOracleStEthUsdc() public { | ||
ChainlinkOracle oracle = new ChainlinkOracle(vaultZero, stEthEthFeed, feedZero, usdcEthFeed, feedZero, 1, 18, 6); | ||
(, int256 baseAnswer,,,) = stEthEthFeed.latestRoundData(); | ||
(, int256 quoteAnswer,,,) = usdcEthFeed.latestRoundData(); | ||
assertEq(oracle.price(), uint256(baseAnswer) * 10 ** (36 + 18 + 6 - 18 - 18) / uint256(quoteAnswer)); | ||
} | ||
|
||
function testOracleEthUsd() public { | ||
ChainlinkOracle oracle = new ChainlinkOracle(vaultZero, ethUsdFeed, feedZero, feedZero, feedZero, 1, 18, 0); | ||
(, int256 expectedPrice,,,) = ethUsdFeed.latestRoundData(); | ||
assertEq(oracle.price(), uint256(expectedPrice) * 10 ** (36 - 18 - 8)); | ||
} | ||
|
||
function testOracleStEthEth() public { | ||
ChainlinkOracle oracle = new ChainlinkOracle(vaultZero, stEthEthFeed, feedZero, feedZero, feedZero, 1, 18, 18); | ||
(, int256 expectedPrice,,,) = stEthEthFeed.latestRoundData(); | ||
assertEq(oracle.price(), uint256(expectedPrice) * 10 ** (36 + 18 - 18 - 18)); | ||
assertApproxEqRel(oracle.price(), 1e36, 0.01 ether); | ||
} | ||
|
||
function testOracleEthStEth() public { | ||
ChainlinkOracle oracle = new ChainlinkOracle(vaultZero, feedZero, feedZero, stEthEthFeed, feedZero, 1, 18, 18); | ||
(, int256 expectedPrice,,,) = stEthEthFeed.latestRoundData(); | ||
assertEq(oracle.price(), 10 ** (36 + 18 + 18 - 18) / uint256(expectedPrice)); | ||
assertApproxEqRel(oracle.price(), 1e36, 0.01 ether); | ||
} | ||
|
||
function testOracleUsdcUsd() public { | ||
ChainlinkOracle oracle = new ChainlinkOracle(vaultZero, usdcUsdFeed, feedZero, feedZero, feedZero, 1, 6, 0); | ||
assertApproxEqRel(oracle.price(), 1e36 / 1e6, 0.01 ether); | ||
} | ||
|
||
function testNegativeAnswer(int256 price) public { | ||
price = bound(price, type(int256).min, -1); | ||
ChainlinkAggregatorMock aggregator = new ChainlinkAggregatorMock(); | ||
ChainlinkOracle oracle = new ChainlinkOracle( | ||
vaultZero, AggregatorV3Interface(address(aggregator)), feedZero, feedZero, feedZero, 1, 18, 0 | ||
); | ||
aggregator.setAnwser(price); | ||
vm.expectRevert(bytes(ErrorsLib.NEGATIVE_ANSWER)); | ||
oracle.price(); | ||
} | ||
|
||
function testSDaiEthOracle() public { | ||
ChainlinkOracle oracle = | ||
new ChainlinkOracle(sDaiVault, daiEthFeed, feedZero, feedZero, feedZero, 10 ** 18, 18, 18); | ||
(, int256 expectedPrice,,,) = daiEthFeed.latestRoundData(); | ||
assertEq( | ||
oracle.price(), | ||
sDaiVault.convertToAssets(1e18) * uint256(expectedPrice) * 10 ** (36 + 18 + 0 - 18 - 18 - 18) | ||
); | ||
} | ||
|
||
function testSDaiUsdcOracle() public { | ||
ChainlinkOracle oracle = | ||
new ChainlinkOracle(sDaiVault, daiEthFeed, feedZero, usdcEthFeed, feedZero, 10 ** 18, 18, 6); | ||
(, int256 baseAnswer,,,) = daiEthFeed.latestRoundData(); | ||
(, int256 quoteAnswer,,,) = usdcEthFeed.latestRoundData(); | ||
assertEq( | ||
oracle.price(), | ||
sDaiVault.convertToAssets(1e18) * uint256(baseAnswer) * 10 ** (36 + 6 + 18 - 18 - 18 - 18) | ||
/ uint256(quoteAnswer) | ||
); | ||
// DAI has 12 more decimals than USDC. | ||
uint256 expectedPrice = 10 ** (36 - 12); | ||
// Admit a 50% interest gain before breaking this test. | ||
uint256 deviation = 0.5 ether; | ||
assertApproxEqRel(oracle.price(), expectedPrice, deviation); | ||
} | ||
|
||
function testConstructorZeroVaultConversionSample() public { | ||
vm.expectRevert(bytes(ErrorsLib.VAULT_CONVERSION_SAMPLE_IS_ZERO)); | ||
new ChainlinkOracle(sDaiVault, daiEthFeed, feedZero, usdcEthFeed, feedZero, 0, 18, 6); | ||
} | ||
|
||
function testConstructorVaultZeroNonOneSample(uint256 vaultConversionSample) public { | ||
vaultConversionSample = bound(vaultConversionSample, 2, type(uint256).max); | ||
|
||
vm.expectRevert(bytes(ErrorsLib.VAULT_CONVERSION_SAMPLE_IS_NOT_ONE)); | ||
new ChainlinkOracle(vaultZero, daiEthFeed, feedZero, usdcEthFeed, feedZero, vaultConversionSample, 18, 6); | ||
} | ||
} |
Oops, something went wrong.