-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathChainlinkOracle.sol
74 lines (66 loc) · 3.72 KB
/
ChainlinkOracle.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
import {BaseAdapter, Errors, IPriceOracle} from "../BaseAdapter.sol";
import {AggregatorV3Interface} from "./AggregatorV3Interface.sol";
import {ScaleUtils, Scale} from "../../lib/ScaleUtils.sol";
/// @title ChainlinkOracle
/// @custom:security-contact security@euler.xyz
/// @author Euler Labs (https://www.eulerlabs.com/)
/// @notice PriceOracle adapter for Chainlink push-based price feeds.
/// @dev Integration Note: `maxStaleness` is an immutable parameter set in the constructor.
/// If the aggregator's heartbeat changes, this adapter may exhibit unintended behavior.
contract ChainlinkOracle is BaseAdapter {
/// @inheritdoc IPriceOracle
string public constant name = "ChainlinkOracle";
/// @notice The minimum permitted value for `maxStaleness`.
uint256 internal constant MAX_STALENESS_LOWER_BOUND = 1 minutes;
/// @notice The maximum permitted value for `maxStaleness`.
uint256 internal constant MAX_STALENESS_UPPER_BOUND = 72 hours;
/// @notice The address of the base asset corresponding to the feed.
address public immutable base;
/// @notice The address of the quote asset corresponding to the feed.
address public immutable quote;
/// @notice The address of the Chainlink price feed.
/// @dev https://docs.chain.link/data-feeds/price-feeds/addresses
address public immutable feed;
/// @notice The maximum allowed age of the price.
/// @dev Reverts if block.timestamp - updatedAt > maxStaleness.
uint256 public immutable maxStaleness;
/// @notice The scale factors used for decimal conversions.
Scale internal immutable scale;
/// @notice Deploy a ChainlinkOracle.
/// @param _base The address of the base asset corresponding to the feed.
/// @param _quote The address of the quote asset corresponding to the feed.
/// @param _feed The address of the Chainlink price feed.
/// @param _maxStaleness The maximum allowed age of the price.
/// @dev Consider setting `_maxStaleness` to slightly more than the feed's heartbeat
/// to account for possible network delays when the heartbeat is triggered.
constructor(address _base, address _quote, address _feed, uint256 _maxStaleness) {
if (_maxStaleness < MAX_STALENESS_LOWER_BOUND || _maxStaleness > MAX_STALENESS_UPPER_BOUND) {
revert Errors.PriceOracle_InvalidConfiguration();
}
base = _base;
quote = _quote;
feed = _feed;
maxStaleness = _maxStaleness;
// The scale factor is used to correctly convert decimals.
uint8 baseDecimals = _getDecimals(base);
uint8 quoteDecimals = _getDecimals(quote);
uint8 feedDecimals = AggregatorV3Interface(feed).decimals();
scale = ScaleUtils.calcScale(baseDecimals, quoteDecimals, feedDecimals);
}
/// @notice Get the quote from the Chainlink feed.
/// @param inAmount The amount of `base` to convert.
/// @param _base The token that is being priced.
/// @param _quote The token that is the unit of account.
/// @return The converted amount using the Chainlink feed.
function _getQuote(uint256 inAmount, address _base, address _quote) internal view override returns (uint256) {
bool inverse = ScaleUtils.getDirectionOrRevert(_base, base, _quote, quote);
(, int256 answer,, uint256 updatedAt,) = AggregatorV3Interface(feed).latestRoundData();
if (answer <= 0) revert Errors.PriceOracle_InvalidAnswer();
uint256 staleness = block.timestamp - updatedAt;
if (staleness > maxStaleness) revert Errors.PriceOracle_TooStale(staleness, maxStaleness);
uint256 price = uint256(answer);
return ScaleUtils.calcOutAmount(inAmount, price, scale, inverse);
}
}