diff --git a/lib/morpho-blue b/lib/morpho-blue index f463e40..55d2d99 160000 --- a/lib/morpho-blue +++ b/lib/morpho-blue @@ -1 +1 @@ -Subproject commit f463e40f776acd0f26d0d380b51cfd02949c8c23 +Subproject commit 55d2d99304fb3fb930c688462ae2ccabb1d533ad diff --git a/src/ChainlinkOracle.sol b/src/ChainlinkOracle.sol index 9f36cdc..f020ca2 100644 --- a/src/ChainlinkOracle.sol +++ b/src/ChainlinkOracle.sol @@ -1,6 +1,7 @@ // 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 {AggregatorV3Interface, ChainlinkDataFeedLib} from "./libraries/ChainlinkDataFeedLib.sol"; @@ -12,27 +13,32 @@ import {Math} from "../lib/openzeppelin-contracts/contracts/utils/math/Math.sol" /// @author Morpho Labs /// @custom:contact security@morpho.org /// @notice Morpho Blue oracle using Chainlink-compliant feeds. -contract ChainlinkOracle is IOracle { +contract ChainlinkOracle is IChainlinkOracle { using Math for uint256; using VaultLib for IERC4626; using ChainlinkDataFeedLib for AggregatorV3Interface; /* IMMUTABLES */ - /// @notice Vault. + /// @inheritdoc IChainlinkOracle IERC4626 public immutable VAULT; - /// @notice Vault conversion sample. The sample amount of shares used to convert to the underlying asset. - /// @notice Should be chosen such that converting `VAULT_CONVERSION_SAMPLE` to assets has enough precision. + + /// @inheritdoc IChainlinkOracle uint256 public immutable VAULT_CONVERSION_SAMPLE; - /// @notice First base feed. + + /// @inheritdoc IChainlinkOracle AggregatorV3Interface public immutable BASE_FEED_1; - /// @notice Second base feed. + + /// @inheritdoc IChainlinkOracle AggregatorV3Interface public immutable BASE_FEED_2; - /// @notice First quote feed. + + /// @inheritdoc IChainlinkOracle AggregatorV3Interface public immutable QUOTE_FEED_1; - /// @notice Second quote feed. + + /// @inheritdoc IChainlinkOracle AggregatorV3Interface public immutable QUOTE_FEED_2; - /// @notice Price scale factor, computed at contract creation. + + /// @inheritdoc IChainlinkOracle uint256 public immutable SCALE_FACTOR; /* CONSTRUCTOR */ @@ -50,7 +56,9 @@ contract ChainlinkOracle is IOracle { /// @param baseFeed2 Second base feed. Pass address zero if the price = 1. /// @param quoteFeed1 First quote feed. Pass address zero if the price = 1. /// @param quoteFeed2 Second quote feed. Pass address zero if the price = 1. - /// @param vaultConversionSample Vault conversion sample. Pass 1 if the oracle does not use a vault. + /// @param vaultConversionSample The sample amount of vault shares used to convert to the underlying asset. + /// Pass 1 if the oracle does not use a vault. Should be chosen such that converting `vaultConversionSample` to + /// assets has enough precision. /// @param baseTokenDecimals Base token decimals. /// @param quoteTokenDecimals Quote token decimals. constructor( diff --git a/src/interfaces/AggregatorV3Interface.sol b/src/interfaces/AggregatorV3Interface.sol index e12ac3e..a892379 100644 --- a/src/interfaces/AggregatorV3Interface.sol +++ b/src/interfaces/AggregatorV3Interface.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; +pragma solidity >=0.5.0; /// @dev From -/// https://github.com/smartcontractkit/chainlink/blob/develop/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol. +/// https://github.com/smartcontractkit/chainlink/blob/master/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol interface AggregatorV3Interface { function decimals() external view returns (uint8); diff --git a/src/interfaces/IChainlinkOracle.sol b/src/interfaces/IChainlinkOracle.sol new file mode 100644 index 0000000..8f4162a --- /dev/null +++ b/src/interfaces/IChainlinkOracle.sol @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +import {IERC4626} from "./IERC4626.sol"; +import {AggregatorV3Interface} from "./AggregatorV3Interface.sol"; +import {IOracle} from "../../lib/morpho-blue/src/interfaces/IOracle.sol"; + +/// @title IChainlinkOracle +/// @author Morpho Labs +/// @custom:contact security@morpho.org +/// @notice Interface of ChainlinkOracle. +interface IChainlinkOracle is IOracle { + /// @notice Returns the address of the ERC4626 vault. + function VAULT() external view returns (IERC4626); + + /// @notice Returns the vault conversion sample. + function VAULT_CONVERSION_SAMPLE() external view returns (uint256); + + /// @notice Returns the address of the first Chainlink base feed. + function BASE_FEED_1() external view returns (AggregatorV3Interface); + + /// @notice Returns the address of the second Chainlink base feed. + function BASE_FEED_2() external view returns (AggregatorV3Interface); + + /// @notice Returns the address of the first Chainlink quote feed. + function QUOTE_FEED_1() external view returns (AggregatorV3Interface); + + /// @notice Returns the address of the second Chainlink quote feed. + function QUOTE_FEED_2() external view returns (AggregatorV3Interface); + + /// @notice Returns the price scale factor, calculated at contract creation. + function SCALE_FACTOR() external view returns (uint256); +} diff --git a/src/interfaces/IERC4626.sol b/src/interfaces/IERC4626.sol index 224b4cd..b848fd6 100644 --- a/src/interfaces/IERC4626.sol +++ b/src/interfaces/IERC4626.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.0; +pragma solidity >=0.5.0; interface IERC4626 { function convertToAssets(uint256) external view returns (uint256); diff --git a/test/ChainlinkOracleTest.sol b/test/ChainlinkOracleTest.sol index 42f57b9..1460244 100644 --- a/test/ChainlinkOracleTest.sol +++ b/test/ChainlinkOracleTest.sol @@ -57,7 +57,7 @@ contract ChainlinkOracleTest is Test { } function testOracleWbtcEth() public { - ChainlinkOracle oracle = new ChainlinkOracle(vaultZero,wBtcBtcFeed, btcEthFeed, feedZero, feedZero, 1, 8, 18); + 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))); @@ -98,8 +98,9 @@ contract ChainlinkOracleTest is Test { 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); + 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();