-
Notifications
You must be signed in to change notification settings - Fork 20
/
DerivedPriceFeedFlat.sol
149 lines (123 loc) · 4.09 KB
/
DerivedPriceFeedFlat.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
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
// Sources flattened with hardhat v2.14.1 https://hardhat.org
// File @chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol@v0.6.1
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface AggregatorV3Interface {
function decimals() external view returns (uint8);
function description() external view returns (string memory);
function version() external view returns (uint256);
function getRoundData(uint80 _roundId)
external
view
returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
);
function latestRoundData()
external
view
returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
);
}
// File contracts/token/oracles/DerivedPriceFeed.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
error MismatchInBaseAndQuoteDecimals();
error InvalidPriceFromRound();
error LatestRoundIncomplete();
error PriceFeedStale();
error OracleAddressCannotBeZero();
contract DerivedPriceFeed {
// price is native-per-dollar
AggregatorV3Interface internal nativeOracle;
// price is tokens-per-dollar
AggregatorV3Interface internal tokenOracle;
string internal DESCRIPTION;
constructor(
address _nativeOracleAddress,
address _tokenOracleAddress,
string memory _description
) {
if (_nativeOracleAddress == address(0))
revert OracleAddressCannotBeZero();
if (_tokenOracleAddress == address(0))
revert OracleAddressCannotBeZero();
nativeOracle = AggregatorV3Interface(_nativeOracleAddress);
tokenOracle = AggregatorV3Interface(_tokenOracleAddress);
// If either of the base or quote price feeds have mismatch in decimal then it could be a problem, so throw!
uint8 decimals1 = nativeOracle.decimals();
uint8 decimals2 = tokenOracle.decimals();
if (decimals1 != decimals2) revert MismatchInBaseAndQuoteDecimals();
DESCRIPTION = _description;
}
function decimals() public view returns (uint8) {
return 18;
}
function description() public view returns (string memory) {
return DESCRIPTION;
}
function validateRound(
uint80 roundId,
int256 price,
uint256 updatedAt,
uint80 answeredInRound,
uint256 staleFeedThreshold
) internal view {
if (price <= 0) revert InvalidPriceFromRound();
// 2 days old price is considered stale since the price is updated every 24 hours
if (updatedAt < block.timestamp - staleFeedThreshold)
revert PriceFeedStale();
if (updatedAt == 0) revert LatestRoundIncomplete();
if (answeredInRound < roundId) revert PriceFeedStale();
}
function getThePrice() public view returns (int) {
/**
* Returns the latest price of price feed 1
*/
(
uint80 roundID1,
int256 price1,
,
uint256 updatedAt1,
uint80 answeredInRound1
) = nativeOracle.latestRoundData();
// By default 2 days old price is considered stale BUT it may vary per price feed
// comapred to stable coin feeds this may have different heartbeat
validateRound(
roundID1,
price1,
updatedAt1,
answeredInRound1,
60 * 60 * 24 * 2
);
/**
* Returns the latest price of price feed 2
*/
(
uint80 roundID2,
int256 price2,
,
uint256 updatedAt2,
uint80 answeredInRound2
) = tokenOracle.latestRoundData();
// By default 2 days old price is considered stale BUT it may vary per price feed
validateRound(
roundID2,
price2,
updatedAt2,
answeredInRound2,
60 * 60 * 24 * 2
);
// Always using decimals 18 for derived price feeds
int token_native = (price2 * (10 ** 18)) / price1;
return token_native;
}
}