Skip to content

Commit b20ad57

Browse files
Merge pull request #71 from morpho-org/refactor/cl-interface
Use minimal CL interface
2 parents 935744b + bb11beb commit b20ad57

10 files changed

+57
-75
lines changed

src/ChainlinkOracle.sol

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ pragma solidity 0.8.21;
44
import {IChainlinkOracle} from "./interfaces/IChainlinkOracle.sol";
55
import {IOracle} from "../lib/morpho-blue/src/interfaces/IOracle.sol";
66

7-
import {AggregatorV3Interface, ChainlinkDataFeedLib} from "./libraries/ChainlinkDataFeedLib.sol";
7+
import {MinimalAggregatorV3Interface, ChainlinkDataFeedLib} from "./libraries/ChainlinkDataFeedLib.sol";
88
import {IERC4626, VaultLib} from "./libraries/VaultLib.sol";
99
import {ErrorsLib} from "./libraries/ErrorsLib.sol";
1010
import {Math} from "../lib/openzeppelin-contracts/contracts/utils/math/Math.sol";
@@ -16,7 +16,7 @@ import {Math} from "../lib/openzeppelin-contracts/contracts/utils/math/Math.sol"
1616
contract ChainlinkOracle is IChainlinkOracle {
1717
using Math for uint256;
1818
using VaultLib for IERC4626;
19-
using ChainlinkDataFeedLib for AggregatorV3Interface;
19+
using ChainlinkDataFeedLib for MinimalAggregatorV3Interface;
2020

2121
/* IMMUTABLES */
2222

@@ -27,16 +27,16 @@ contract ChainlinkOracle is IChainlinkOracle {
2727
uint256 public immutable VAULT_CONVERSION_SAMPLE;
2828

2929
/// @inheritdoc IChainlinkOracle
30-
AggregatorV3Interface public immutable BASE_FEED_1;
30+
MinimalAggregatorV3Interface public immutable BASE_FEED_1;
3131

3232
/// @inheritdoc IChainlinkOracle
33-
AggregatorV3Interface public immutable BASE_FEED_2;
33+
MinimalAggregatorV3Interface public immutable BASE_FEED_2;
3434

3535
/// @inheritdoc IChainlinkOracle
36-
AggregatorV3Interface public immutable QUOTE_FEED_1;
36+
MinimalAggregatorV3Interface public immutable QUOTE_FEED_1;
3737

3838
/// @inheritdoc IChainlinkOracle
39-
AggregatorV3Interface public immutable QUOTE_FEED_2;
39+
MinimalAggregatorV3Interface public immutable QUOTE_FEED_2;
4040

4141
/// @inheritdoc IChainlinkOracle
4242
uint256 public immutable SCALE_FACTOR;
@@ -63,10 +63,10 @@ contract ChainlinkOracle is IChainlinkOracle {
6363
/// @param quoteTokenDecimals Quote token decimals.
6464
constructor(
6565
IERC4626 vault,
66-
AggregatorV3Interface baseFeed1,
67-
AggregatorV3Interface baseFeed2,
68-
AggregatorV3Interface quoteFeed1,
69-
AggregatorV3Interface quoteFeed2,
66+
MinimalAggregatorV3Interface baseFeed1,
67+
MinimalAggregatorV3Interface baseFeed2,
68+
MinimalAggregatorV3Interface quoteFeed1,
69+
MinimalAggregatorV3Interface quoteFeed2,
7070
uint256 vaultConversionSample,
7171
uint256 baseTokenDecimals,
7272
uint256 quoteTokenDecimals
Lines changed: 9 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
// SPDX-License-Identifier: GPL-2.0-or-later
22
pragma solidity 0.8.21;
33

4-
import {IStEth} from "../interfaces/IStEth.sol";
5-
import {AggregatorV3Interface} from "../interfaces/AggregatorV3Interface.sol";
4+
import {IWstEth} from "../interfaces/IWstEth.sol";
5+
import {MinimalAggregatorV3Interface} from "../interfaces/MinimalAggregatorV3Interface.sol";
66

77
import {ErrorsLib} from "../libraries/ErrorsLib.sol";
88

@@ -11,29 +11,20 @@ import {ErrorsLib} from "../libraries/ErrorsLib.sol";
1111
/// @custom:contact security@morpho.org
1212
/// @notice wstETH/ETH exchange rate price feed.
1313
/// @dev This contract should only be used as price feed for `ChainlinkOracle`.
14-
contract WstEthEthExchangeRateChainlinkAdapter is AggregatorV3Interface {
14+
contract WstEthEthExchangeRateChainlinkAdapter is MinimalAggregatorV3Interface {
1515
uint8 public constant decimals = 18;
1616
string public constant description = "wstETH/ETH exchange rate";
1717

18-
IStEth public immutable ST_ETH;
18+
IWstEth public immutable WST_ETH;
1919

20-
constructor(address stEth) {
21-
require(stEth != address(0), ErrorsLib.ZERO_ADDRESS);
22-
ST_ETH = IStEth(stEth);
23-
}
24-
25-
/// @notice Reverts as no Chainlink aggregator is used.
26-
function version() external pure returns (uint256) {
27-
revert();
28-
}
20+
constructor(address wstEth) {
21+
require(wstEth != address(0), ErrorsLib.ZERO_ADDRESS);
2922

30-
/// @notice Reverts as it's not necessary for the ChainlinkOracle contract.
31-
function getRoundData(uint80) external pure returns (uint80, int256, uint256, uint256, uint80) {
32-
revert();
23+
WST_ETH = IWstEth(wstEth);
3324
}
3425

3526
function latestRoundData() external view returns (uint80, int256, uint256, uint256, uint80) {
36-
uint256 answer = ST_ETH.getPooledEthByShares(10 ** decimals);
37-
return (0, int256(answer), 0, 0, 0);
27+
// It is assumed that `stEthPerToken` returns a price with 18 decimals precision.
28+
return (0, int256(WST_ETH.stEthPerToken()), 0, 0, 0);
3829
}
3930
}

src/interfaces/IChainlinkOracle.sol

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
pragma solidity >=0.5.0;
33

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

88
/// @title IChainlinkOracle
99
/// @author Morpho Labs
@@ -17,16 +17,16 @@ interface IChainlinkOracle is IOracle {
1717
function VAULT_CONVERSION_SAMPLE() external view returns (uint256);
1818

1919
/// @notice Returns the address of the first Chainlink base feed.
20-
function BASE_FEED_1() external view returns (AggregatorV3Interface);
20+
function BASE_FEED_1() external view returns (MinimalAggregatorV3Interface);
2121

2222
/// @notice Returns the address of the second Chainlink base feed.
23-
function BASE_FEED_2() external view returns (AggregatorV3Interface);
23+
function BASE_FEED_2() external view returns (MinimalAggregatorV3Interface);
2424

2525
/// @notice Returns the address of the first Chainlink quote feed.
26-
function QUOTE_FEED_1() external view returns (AggregatorV3Interface);
26+
function QUOTE_FEED_1() external view returns (MinimalAggregatorV3Interface);
2727

2828
/// @notice Returns the address of the second Chainlink quote feed.
29-
function QUOTE_FEED_2() external view returns (AggregatorV3Interface);
29+
function QUOTE_FEED_2() external view returns (MinimalAggregatorV3Interface);
3030

3131
/// @notice Returns the price scale factor, calculated at contract creation.
3232
function SCALE_FACTOR() external view returns (uint256);

src/interfaces/IStEth.sol

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

src/interfaces/IWstEth.sol

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// SPDX-License-Identifier: GPL-2.0-or-later
2+
pragma solidity >=0.5.0;
3+
4+
interface IWstEth {
5+
function stEthPerToken() external view returns (uint256);
6+
}

src/interfaces/AggregatorV3Interface.sol renamed to src/interfaces/MinimalAggregatorV3Interface.sol

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,11 @@
11
// SPDX-License-Identifier: MIT
22
pragma solidity >=0.5.0;
33

4-
/// @dev From
4+
/// @dev Inspired by
55
/// https://github.com/smartcontractkit/chainlink/blob/master/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol
6-
interface AggregatorV3Interface {
6+
interface MinimalAggregatorV3Interface {
77
function decimals() external view returns (uint8);
88

9-
function description() external view returns (string memory);
10-
11-
function version() external view returns (uint256);
12-
13-
function getRoundData(uint80 _roundId)
14-
external
15-
view
16-
returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
17-
189
function latestRoundData()
1910
external
2011
view

src/libraries/ChainlinkDataFeedLib.sol

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

4-
import {AggregatorV3Interface} from "../interfaces/AggregatorV3Interface.sol";
4+
import {MinimalAggregatorV3Interface} from "../interfaces/MinimalAggregatorV3Interface.sol";
55

66
import {ErrorsLib} from "./ErrorsLib.sol";
77

@@ -17,7 +17,7 @@ library ChainlinkDataFeedLib {
1717
/// - Staleness is not checked because it's assumed that the Chainlink feed keeps its promises on this.
1818
/// - The price is not checked to be in the min/max bounds because it's assumed that the Chainlink feed keeps its
1919
/// promises on this.
20-
function getPrice(AggregatorV3Interface feed) internal view returns (uint256) {
20+
function getPrice(MinimalAggregatorV3Interface feed) internal view returns (uint256) {
2121
if (address(feed) == address(0)) return 1;
2222

2323
(, int256 answer,,,) = feed.latestRoundData();
@@ -28,7 +28,7 @@ library ChainlinkDataFeedLib {
2828

2929
/// @dev Returns the number of decimals of a `feed`.
3030
/// @dev When `feed` is the address zero, returns 0.
31-
function getDecimals(AggregatorV3Interface feed) internal view returns (uint256) {
31+
function getDecimals(MinimalAggregatorV3Interface feed) internal view returns (uint256) {
3232
if (address(feed) == address(0)) return 0;
3333

3434
return feed.decimals();

test/ChainlinkOracleTest.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ contract ChainlinkOracleTest is Test {
7979
price = bound(price, type(int256).min, -1);
8080
ChainlinkAggregatorMock aggregator = new ChainlinkAggregatorMock();
8181
ChainlinkOracle oracle = new ChainlinkOracle(
82-
vaultZero, AggregatorV3Interface(address(aggregator)), feedZero, feedZero, feedZero, 1, 18, 0
82+
vaultZero, MinimalAggregatorV3Interface(address(aggregator)), feedZero, feedZero, feedZero, 1, 18, 0
8383
);
8484
aggregator.setAnwser(price);
8585
vm.expectRevert(bytes(ErrorsLib.NEGATIVE_ANSWER));

test/WstEthEthExchangeRateChainlinkAdapterTest.sol

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@ import "../src/ChainlinkOracle.sol";
99
import "./helpers/Constants.sol";
1010

1111
contract WstEthEthExchangeRateChainlinkAdapterTest is Test {
12-
IStEth internal constant ST_ETH = IStEth(0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84);
12+
IWstEth internal constant WST_ETH = IWstEth(0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0);
1313

1414
WstEthEthExchangeRateChainlinkAdapter internal oracle;
1515
ChainlinkOracle internal chainlinkOracle;
1616

1717
function setUp() public {
1818
vm.createSelectFork(vm.envString("ETH_RPC_URL"));
19-
oracle = new WstEthEthExchangeRateChainlinkAdapter(address(ST_ETH));
19+
oracle = new WstEthEthExchangeRateChainlinkAdapter(address(WST_ETH));
2020
chainlinkOracle = new ChainlinkOracle(vaultZero, oracle, feedZero, feedZero, feedZero, 1, 18, 18);
2121
}
2222

@@ -33,19 +33,11 @@ contract WstEthEthExchangeRateChainlinkAdapterTest is Test {
3333
new WstEthEthExchangeRateChainlinkAdapter(address(0));
3434
}
3535

36-
function testReverts() public {
37-
vm.expectRevert();
38-
oracle.version();
39-
40-
vm.expectRevert();
41-
oracle.getRoundData(0);
42-
}
43-
4436
function testLatestRoundData() public {
4537
(uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) =
4638
oracle.latestRoundData();
4739
assertEq(roundId, 0);
48-
assertEq(uint256(answer), ST_ETH.getPooledEthByShares(10 ** 18));
40+
assertEq(uint256(answer), WST_ETH.stEthPerToken());
4941
assertEq(startedAt, 0);
5042
assertEq(updatedAt, 0);
5143
assertEq(answeredInRound, 0);

test/helpers/Constants.sol

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,33 @@
11
// SPDX-License-Identifier: GPL-2.0-or-later
22
pragma solidity ^0.8.0;
33

4-
import {IERC4626, AggregatorV3Interface} from "../../src/ChainlinkOracle.sol";
4+
import {IERC4626, MinimalAggregatorV3Interface} from "../../src/ChainlinkOracle.sol";
55

6-
AggregatorV3Interface constant feedZero = AggregatorV3Interface(address(0));
6+
MinimalAggregatorV3Interface constant feedZero = MinimalAggregatorV3Interface(address(0));
77
// 8 decimals of precision
8-
AggregatorV3Interface constant btcUsdFeed = AggregatorV3Interface(0xF4030086522a5bEEa4988F8cA5B36dbC97BeE88c);
8+
MinimalAggregatorV3Interface constant btcUsdFeed =
9+
MinimalAggregatorV3Interface(0xF4030086522a5bEEa4988F8cA5B36dbC97BeE88c);
910
// 8 decimals of precision
10-
AggregatorV3Interface constant usdcUsdFeed = AggregatorV3Interface(0x8fFfFfd4AfB6115b954Bd326cbe7B4BA576818f6);
11+
MinimalAggregatorV3Interface constant usdcUsdFeed =
12+
MinimalAggregatorV3Interface(0x8fFfFfd4AfB6115b954Bd326cbe7B4BA576818f6);
1113
// 18 decimals of precision
12-
AggregatorV3Interface constant btcEthFeed = AggregatorV3Interface(0xdeb288F737066589598e9214E782fa5A8eD689e8);
14+
MinimalAggregatorV3Interface constant btcEthFeed =
15+
MinimalAggregatorV3Interface(0xdeb288F737066589598e9214E782fa5A8eD689e8);
1316
// 8 decimals of precision
14-
AggregatorV3Interface constant wBtcBtcFeed = AggregatorV3Interface(0xfdFD9C85aD200c506Cf9e21F1FD8dd01932FBB23);
17+
MinimalAggregatorV3Interface constant wBtcBtcFeed =
18+
MinimalAggregatorV3Interface(0xfdFD9C85aD200c506Cf9e21F1FD8dd01932FBB23);
1519
// 18 decimals of precision
16-
AggregatorV3Interface constant stEthEthFeed = AggregatorV3Interface(0x86392dC19c0b719886221c78AB11eb8Cf5c52812);
20+
MinimalAggregatorV3Interface constant stEthEthFeed =
21+
MinimalAggregatorV3Interface(0x86392dC19c0b719886221c78AB11eb8Cf5c52812);
1722
// 18 decimals of precision
18-
AggregatorV3Interface constant usdcEthFeed = AggregatorV3Interface(0x986b5E1e1755e3C2440e960477f25201B0a8bbD4);
23+
MinimalAggregatorV3Interface constant usdcEthFeed =
24+
MinimalAggregatorV3Interface(0x986b5E1e1755e3C2440e960477f25201B0a8bbD4);
1925
// 8 decimals of precision
20-
AggregatorV3Interface constant ethUsdFeed = AggregatorV3Interface(0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419);
26+
MinimalAggregatorV3Interface constant ethUsdFeed =
27+
MinimalAggregatorV3Interface(0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419);
2128
// 18 decimals of precision
22-
AggregatorV3Interface constant daiEthFeed = AggregatorV3Interface(0x773616E4d11A78F511299002da57A0a94577F1f4);
29+
MinimalAggregatorV3Interface constant daiEthFeed =
30+
MinimalAggregatorV3Interface(0x773616E4d11A78F511299002da57A0a94577F1f4);
2331

2432
IERC4626 constant vaultZero = IERC4626(address(0));
2533
IERC4626 constant sDaiVault = IERC4626(0x83F20F44975D03b1b09e64809B757c47f942BEeA);

0 commit comments

Comments
 (0)