-
-
Notifications
You must be signed in to change notification settings - Fork 6
/
FluxPriceFeedFactory.sol
186 lines (165 loc) · 6.3 KB
/
FluxPriceFeedFactory.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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.12;
import "@openzeppelin/contracts/utils/Strings.sol";
import "./interface/IERC2362.sol";
import "./FluxPriceFeed.sol";
/**
* @title Flux first-party price feed factory
* @author fluxprotocol.org
*/
contract FluxPriceFeedFactory is IERC2362 {
// roles
bytes32 public constant VALIDATOR_ROLE = keccak256("VALIDATOR_ROLE");
// mapping of id to FluxPriceFeed
mapping(bytes32 => FluxPriceFeed) public fluxPriceFeeds;
/**
* @notice indicates that a new oracle was created
* @param id hash of the price pair of the deployed oracle
* @param oracle address of the deployed oracle
*/
event FluxPriceFeedCreated(bytes32 indexed id, address indexed oracle);
/**
* @notice to log error messages
* @param message the logged message
*/
event Log(string message);
/**
* @notice transmit submits an answer to a price feed or creates a new one if it does not exist
* @param _pricePairs array of price pairs strings (e.g. ETH/USD)
* @param _decimals array of decimals for associated price pairs (e.g. 3)
* @param _answers array of prices for associated price pairs
* @param _provider optional address of the provider, if different from msg.sender
*/
function transmit(
string[] calldata _pricePairs,
uint8[] calldata _decimals,
int192[] calldata _answers,
address _provider
) external {
require(
(_pricePairs.length == _decimals.length) && (_pricePairs.length == _answers.length),
"Transmitted arrays must be equal"
);
// if no provider is provided, use the msg.sender
address provider = (_provider == address(0)) ? msg.sender : _provider;
// Iterate through each transmitted price pair
for (uint256 i = 0; i < _pricePairs.length; i++) {
string memory str = string(
abi.encodePacked("Price-", _pricePairs[i], "-", Strings.toString(_decimals[i]), "-", provider)
);
bytes32 id = keccak256(bytes(str));
// deploy a new oracle if there's none previously deployed and this is the original provider
if (address(fluxPriceFeeds[id]) == address(0x0) && msg.sender == provider) {
_deployOracle(id, _pricePairs[i], _decimals[i]);
}
require(address(fluxPriceFeeds[id]) != address(0x0), "Provider doesn't exist");
// if this is not the original provider, make sure the caller has the VALIDATOR_ROLE on the oracle
if (msg.sender != provider) {
require(fluxPriceFeeds[id].hasRole(VALIDATOR_ROLE, msg.sender), "Only validators can transmit");
}
// try transmitting values to the oracle
/* solhint-disable-next-line no-empty-blocks */
try fluxPriceFeeds[id].transmit(_answers[i]) {
// transmission is successful, nothing to do
} catch Error(string memory reason) {
// catch failing revert() and require()
emit Log(reason);
}
}
}
/**
* @notice internal function to create a new FluxPriceFeed
* @dev only a validator should be able to call this function
*/
function _deployOracle(
bytes32 _id,
string calldata _pricePair,
uint8 _decimals
) internal {
// deploy the new contract and store it in the mapping
FluxPriceFeed newPriceFeed = new FluxPriceFeed(address(this), _decimals, _pricePair);
fluxPriceFeeds[_id] = newPriceFeed;
// grant the provider DEFAULT_ADMIN_ROLE and VALIDATOR_ROLE on the new FluxPriceFeed
newPriceFeed.grantRole(0x00, msg.sender);
newPriceFeed.grantRole(VALIDATOR_ROLE, msg.sender);
emit FluxPriceFeedCreated(_id, address(newPriceFeed));
}
/**
* @notice answer from the most recent report of a certain price pair from factory
* @param _id hash of the price pair string to query
*/
function valueFor(bytes32 _id)
external
view
override
returns (
int256,
uint256,
uint256
)
{
// if oracle exists then fetch values
if (address(fluxPriceFeeds[_id]) != address(0x0)) {
// fetch the price feed contract and read its latest answer and timestamp
try fluxPriceFeeds[_id].latestRoundData() returns (
uint80,
int256 answer,
uint256,
uint256 updatedAt,
uint80
) {
return (answer, updatedAt, 200);
} catch {
// catch failing revert() and require()
return (0, 0, 404);
}
// else return not found
} else {
return (0, 0, 404);
}
}
/**
* @notice returns address of a price feed id
* @param _id hash of the price pair string to query
*/
function addressOfPricePairId(bytes32 _id) external view returns (address) {
return address(fluxPriceFeeds[_id]);
}
/**
* @notice returns the hash of a price pair
* @param _pricePair ETH/USD
* @param _decimals decimal of the price pair
* @param _provider original provider of the price pair
*/
function getId(
string calldata _pricePair,
uint8 _decimals,
address _provider
) external pure returns (bytes32) {
string memory str = string(
abi.encodePacked("Price-", _pricePair, "-", Strings.toString(_decimals), "-", _provider)
);
bytes32 id = keccak256(bytes(str));
return id;
}
/**
* @notice returns address of a price feed id
* @param _pricePair ETH/USD
* @param _decimals decimal of the price pair
* @param _provider original provider of the price pair
*/
function addressOfPricePair(
string calldata _pricePair,
uint8 _decimals,
address _provider
) external view returns (address) {
bytes32 id = this.getId(_pricePair, _decimals, _provider);
return address(fluxPriceFeeds[id]);
}
/**
* @notice returns factory's type and version
*/
function typeAndVersion() external pure virtual returns (string memory) {
return "FluxPriceFeedFactory 2.0.0";
}
}