Skip to content

Commit

Permalink
fix: issues after refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
MathisGD committed Feb 18, 2024
1 parent 9635b2b commit 53839ce
Show file tree
Hide file tree
Showing 8 changed files with 175 additions and 131 deletions.
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 */
Expand Down
22 changes: 0 additions & 22 deletions src/morpho-chainlink-v2/interfaces/AggregatorV3Interface.sol

This file was deleted.

6 changes: 0 additions & 6 deletions src/morpho-chainlink-v2/interfaces/IERC4626.sol

This file was deleted.

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);

Expand Down
36 changes: 0 additions & 36 deletions src/morpho-chainlink-v2/libraries/ChainlinkDataFeedLib.sol

This file was deleted.

18 changes: 0 additions & 18 deletions src/morpho-chainlink-v2/libraries/VaultLib.sol

This file was deleted.

126 changes: 126 additions & 0 deletions test/ChainlinkOracleV1Test.sol
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);
}
}
Loading

0 comments on commit 53839ce

Please sign in to comment.