Skip to content

Commit 53839ce

Browse files
committed
fix: issues after refactor
1 parent 9635b2b commit 53839ce

File tree

8 files changed

+175
-131
lines changed

8 files changed

+175
-131
lines changed

src/morpho-chainlink-v2/ChainlinkOracle.sol renamed to src/morpho-chainlink-v2/MorphoChainlinkOracleV2.sol

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,50 @@
11
// SPDX-License-Identifier: GPL-2.0-or-later
22
pragma solidity 0.8.21;
33

4-
import {IChainlinkOracle} from "./interfaces/IChainlinkOracle.sol";
54
import {IOracle} from "../../lib/morpho-blue/src/interfaces/IOracle.sol";
5+
import {IMorphoChainlinkOracleV2} from "./interfaces/IMorphoChainlinkOracleV2.sol";
66

7-
import {AggregatorV3Interface, ChainlinkDataFeedLib} from "./libraries/ChainlinkDataFeedLib.sol";
8-
import {IERC4626, VaultLib} from "./libraries/VaultLib.sol";
97
import {ErrorsLib} from "./libraries/ErrorsLib.sol";
8+
import {IERC4626, VaultLib} from "../morpho-chainlink-v1/libraries/VaultLib.sol";
109
import {Math} from "../../lib/openzeppelin-contracts/contracts/utils/math/Math.sol";
10+
import {AggregatorV3Interface, ChainlinkDataFeedLib} from "../morpho-chainlink-v1/libraries/ChainlinkDataFeedLib.sol";
1111

12-
/// @title ChainlinkOracle
12+
/// @title MorphoChainlinkOracleV2
1313
/// @author Morpho Labs
1414
/// @custom:contact security@morpho.org
1515
/// @notice Morpho Blue oracle using Chainlink-compliant feeds.
16-
contract ChainlinkOracle is IChainlinkOracle {
16+
contract MorphoChainlinkOracleV2 is IMorphoChainlinkOracleV2 {
1717
using Math for uint256;
1818
using VaultLib for IERC4626;
1919
using ChainlinkDataFeedLib for AggregatorV3Interface;
2020

2121
/* IMMUTABLES */
2222

23-
/// @inheritdoc IChainlinkOracle
23+
/// @inheritdoc IMorphoChainlinkOracleV2
2424
IERC4626 public immutable BASE_VAULT;
2525

26-
/// @inheritdoc IChainlinkOracle
26+
/// @inheritdoc IMorphoChainlinkOracleV2
2727
uint256 public immutable BASE_VAULT_CONVERSION_SAMPLE;
2828

29-
/// @inheritdoc IChainlinkOracle
29+
/// @inheritdoc IMorphoChainlinkOracleV2
3030
IERC4626 public immutable QUOTE_VAULT;
3131

32-
/// @inheritdoc IChainlinkOracle
32+
/// @inheritdoc IMorphoChainlinkOracleV2
3333
uint256 public immutable QUOTE_VAULT_CONVERSION_SAMPLE;
3434

35-
/// @inheritdoc IChainlinkOracle
35+
/// @inheritdoc IMorphoChainlinkOracleV2
3636
AggregatorV3Interface public immutable BASE_FEED_1;
3737

38-
/// @inheritdoc IChainlinkOracle
38+
/// @inheritdoc IMorphoChainlinkOracleV2
3939
AggregatorV3Interface public immutable BASE_FEED_2;
4040

41-
/// @inheritdoc IChainlinkOracle
41+
/// @inheritdoc IMorphoChainlinkOracleV2
4242
AggregatorV3Interface public immutable QUOTE_FEED_1;
4343

44-
/// @inheritdoc IChainlinkOracle
44+
/// @inheritdoc IMorphoChainlinkOracleV2
4545
AggregatorV3Interface public immutable QUOTE_FEED_2;
4646

47-
/// @inheritdoc IChainlinkOracle
47+
/// @inheritdoc IMorphoChainlinkOracleV2
4848
uint256 public immutable SCALE_FACTOR;
4949

5050
/* CONSTRUCTOR */

src/morpho-chainlink-v2/interfaces/AggregatorV3Interface.sol

Lines changed: 0 additions & 22 deletions
This file was deleted.

src/morpho-chainlink-v2/interfaces/IERC4626.sol

Lines changed: 0 additions & 6 deletions
This file was deleted.

src/morpho-chainlink-v2/interfaces/IChainlinkOracle.sol renamed to src/morpho-chainlink-v2/interfaces/IMorphoChainlinkOracleV2.sol

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
// SPDX-License-Identifier: GPL-2.0-or-later
22
pragma solidity >=0.5.0;
33

4-
import {IERC4626} from "./IERC4626.sol";
5-
import {AggregatorV3Interface} from "./AggregatorV3Interface.sol";
4+
import {IERC4626} from "../../morpho-chainlink-v1/interfaces/IERC4626.sol";
65
import {IOracle} from "../../../lib/morpho-blue/src/interfaces/IOracle.sol";
6+
import {AggregatorV3Interface} from "../../morpho-chainlink-v1/interfaces/AggregatorV3Interface.sol";
77

8-
/// @title IChainlinkOracle
8+
/// @title IMorphoChainlinkOracleV2
99
/// @author Morpho Labs
1010
/// @custom:contact security@morpho.org
11-
/// @notice Interface of ChainlinkOracle.
12-
interface IChainlinkOracle is IOracle {
11+
/// @notice Interface of MorphoChainlinkOracleV2.
12+
interface IMorphoChainlinkOracleV2 is IOracle {
1313
/// @notice Returns the address of the base ERC4626 vault.
1414
function BASE_VAULT() external view returns (IERC4626);
1515

src/morpho-chainlink-v2/libraries/ChainlinkDataFeedLib.sol

Lines changed: 0 additions & 36 deletions
This file was deleted.

src/morpho-chainlink-v2/libraries/VaultLib.sol

Lines changed: 0 additions & 18 deletions
This file was deleted.

test/ChainlinkOracleV1Test.sol

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
// SPDX-License-Identifier: GPL-2.0-or-later
2+
pragma solidity ^0.8.0;
3+
4+
import "../lib/forge-std/src/Test.sol";
5+
import "../src/morpho-chainlink-v1/ChainlinkOracle.sol";
6+
import "./mocks/ChainlinkAggregatorMock.sol";
7+
import "./helpers/Constants.sol";
8+
9+
contract ChainlinkOracleTest is Test {
10+
function setUp() public {
11+
vm.createSelectFork(vm.envString("ETH_RPC_URL"));
12+
}
13+
14+
function testOracleWbtcUsdc() public {
15+
ChainlinkOracle oracle = new ChainlinkOracle(vaultZero, wBtcBtcFeed, btcUsdFeed, usdcUsdFeed, feedZero, 1, 8, 6);
16+
(, int256 firstBaseAnswer,,,) = wBtcBtcFeed.latestRoundData();
17+
(, int256 secondBaseAnswer,,,) = btcUsdFeed.latestRoundData();
18+
(, int256 quoteAnswer,,,) = usdcUsdFeed.latestRoundData();
19+
assertEq(
20+
oracle.price(),
21+
(uint256(firstBaseAnswer) * uint256(secondBaseAnswer) * 10 ** (36 + 8 + 6 - 8 - 8 - 8))
22+
/ uint256(quoteAnswer)
23+
);
24+
}
25+
26+
function testOracleUsdcWbtc() public {
27+
ChainlinkOracle oracle = new ChainlinkOracle(vaultZero, usdcUsdFeed, feedZero, wBtcBtcFeed, btcUsdFeed, 1, 6, 8);
28+
(, int256 baseAnswer,,,) = usdcUsdFeed.latestRoundData();
29+
(, int256 firstQuoteAnswer,,,) = wBtcBtcFeed.latestRoundData();
30+
(, int256 secondQuoteAnswer,,,) = btcUsdFeed.latestRoundData();
31+
assertEq(
32+
oracle.price(),
33+
(uint256(baseAnswer) * 10 ** (36 + 8 + 8 + 8 - 6 - 8))
34+
/ (uint256(firstQuoteAnswer) * uint256(secondQuoteAnswer))
35+
);
36+
}
37+
38+
function testOracleWbtcEth() public {
39+
ChainlinkOracle oracle = new ChainlinkOracle(vaultZero, wBtcBtcFeed, btcEthFeed, feedZero, feedZero, 1, 8, 18);
40+
(, int256 firstBaseAnswer,,,) = wBtcBtcFeed.latestRoundData();
41+
(, int256 secondBaseAnswer,,,) = btcEthFeed.latestRoundData();
42+
assertEq(oracle.price(), (uint256(firstBaseAnswer) * uint256(secondBaseAnswer) * 10 ** (36 + 18 - 8 - 8 - 18)));
43+
}
44+
45+
function testOracleStEthUsdc() public {
46+
ChainlinkOracle oracle = new ChainlinkOracle(vaultZero, stEthEthFeed, feedZero, usdcEthFeed, feedZero, 1, 18, 6);
47+
(, int256 baseAnswer,,,) = stEthEthFeed.latestRoundData();
48+
(, int256 quoteAnswer,,,) = usdcEthFeed.latestRoundData();
49+
assertEq(oracle.price(), uint256(baseAnswer) * 10 ** (36 + 18 + 6 - 18 - 18) / uint256(quoteAnswer));
50+
}
51+
52+
function testOracleEthUsd() public {
53+
ChainlinkOracle oracle = new ChainlinkOracle(vaultZero, ethUsdFeed, feedZero, feedZero, feedZero, 1, 18, 0);
54+
(, int256 expectedPrice,,,) = ethUsdFeed.latestRoundData();
55+
assertEq(oracle.price(), uint256(expectedPrice) * 10 ** (36 - 18 - 8));
56+
}
57+
58+
function testOracleStEthEth() public {
59+
ChainlinkOracle oracle = new ChainlinkOracle(vaultZero, stEthEthFeed, feedZero, feedZero, feedZero, 1, 18, 18);
60+
(, int256 expectedPrice,,,) = stEthEthFeed.latestRoundData();
61+
assertEq(oracle.price(), uint256(expectedPrice) * 10 ** (36 + 18 - 18 - 18));
62+
assertApproxEqRel(oracle.price(), 1e36, 0.01 ether);
63+
}
64+
65+
function testOracleEthStEth() public {
66+
ChainlinkOracle oracle = new ChainlinkOracle(vaultZero, feedZero, feedZero, stEthEthFeed, feedZero, 1, 18, 18);
67+
(, int256 expectedPrice,,,) = stEthEthFeed.latestRoundData();
68+
assertEq(oracle.price(), 10 ** (36 + 18 + 18 - 18) / uint256(expectedPrice));
69+
assertApproxEqRel(oracle.price(), 1e36, 0.01 ether);
70+
}
71+
72+
function testOracleUsdcUsd() public {
73+
ChainlinkOracle oracle = new ChainlinkOracle(vaultZero, usdcUsdFeed, feedZero, feedZero, feedZero, 1, 6, 0);
74+
assertApproxEqRel(oracle.price(), 1e36 / 1e6, 0.01 ether);
75+
}
76+
77+
function testNegativeAnswer(int256 price) public {
78+
price = bound(price, type(int256).min, -1);
79+
ChainlinkAggregatorMock aggregator = new ChainlinkAggregatorMock();
80+
ChainlinkOracle oracle = new ChainlinkOracle(
81+
vaultZero, AggregatorV3Interface(address(aggregator)), feedZero, feedZero, feedZero, 1, 18, 0
82+
);
83+
aggregator.setAnwser(price);
84+
vm.expectRevert(bytes(ErrorsLib.NEGATIVE_ANSWER));
85+
oracle.price();
86+
}
87+
88+
function testSDaiEthOracle() public {
89+
ChainlinkOracle oracle =
90+
new ChainlinkOracle(sDaiVault, daiEthFeed, feedZero, feedZero, feedZero, 10 ** 18, 18, 18);
91+
(, int256 expectedPrice,,,) = daiEthFeed.latestRoundData();
92+
assertEq(
93+
oracle.price(),
94+
sDaiVault.convertToAssets(1e18) * uint256(expectedPrice) * 10 ** (36 + 18 + 0 - 18 - 18 - 18)
95+
);
96+
}
97+
98+
function testSDaiUsdcOracle() public {
99+
ChainlinkOracle oracle =
100+
new ChainlinkOracle(sDaiVault, daiEthFeed, feedZero, usdcEthFeed, feedZero, 10 ** 18, 18, 6);
101+
(, int256 baseAnswer,,,) = daiEthFeed.latestRoundData();
102+
(, int256 quoteAnswer,,,) = usdcEthFeed.latestRoundData();
103+
assertEq(
104+
oracle.price(),
105+
sDaiVault.convertToAssets(1e18) * uint256(baseAnswer) * 10 ** (36 + 6 + 18 - 18 - 18 - 18)
106+
/ uint256(quoteAnswer)
107+
);
108+
// DAI has 12 more decimals than USDC.
109+
uint256 expectedPrice = 10 ** (36 - 12);
110+
// Admit a 50% interest gain before breaking this test.
111+
uint256 deviation = 0.5 ether;
112+
assertApproxEqRel(oracle.price(), expectedPrice, deviation);
113+
}
114+
115+
function testConstructorZeroVaultConversionSample() public {
116+
vm.expectRevert(bytes(ErrorsLib.VAULT_CONVERSION_SAMPLE_IS_ZERO));
117+
new ChainlinkOracle(sDaiVault, daiEthFeed, feedZero, usdcEthFeed, feedZero, 0, 18, 6);
118+
}
119+
120+
function testConstructorVaultZeroNonOneSample(uint256 vaultConversionSample) public {
121+
vaultConversionSample = bound(vaultConversionSample, 2, type(uint256).max);
122+
123+
vm.expectRevert(bytes(ErrorsLib.VAULT_CONVERSION_SAMPLE_IS_NOT_ONE));
124+
new ChainlinkOracle(vaultZero, daiEthFeed, feedZero, usdcEthFeed, feedZero, vaultConversionSample, 18, 6);
125+
}
126+
}

0 commit comments

Comments
 (0)