-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathStdReferenceBasic.sol
154 lines (138 loc) · 6.37 KB
/
StdReferenceBasic.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
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.13;
import {StdReferenceBase} from "./StdReferenceBase.sol";
import {AccessControl} from "./AccessControl.sol";
/// @title BandChain StdReferenceBasic
/// @author Band Protocol Team
contract StdReferenceBasic is AccessControl, StdReferenceBase {
struct RefData {
uint64 rate; // USD-rate, multiplied by 1e9.
uint64 resolveTime; // UNIX epoch when data is last resolved.
uint64 requestID; // BandChain request identifier for this data.
}
/// Mapping from token symbol to ref data
mapping(string => RefData) public refs;
bytes32 public constant RELAYER_ROLE = keccak256("RELAYER_ROLE");
bytes32 public constant WHITELISTED_ROLE = keccak256("WHITELISTED_ROLE");
constructor() {
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
_grantRole(RELAYER_ROLE, msg.sender);
}
function transferContractOwnership(address newOwner) external onlyRole(DEFAULT_ADMIN_ROLE) {
require(newOwner != address(0), "INVALID_ADDRESS");
_revokeRole(DEFAULT_ADMIN_ROLE, msg.sender);
_grantRole(DEFAULT_ADMIN_ROLE, newOwner);
}
/**
* @dev Grants `RELAYER_ROLE` to `accounts`.
*
* If each `account` had not been already granted `RELAYER_ROLE`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``RELAYER_ROLE``'s admin role.
*/
function grantRelayers(address[] calldata accounts) external onlyRole(getRoleAdmin(RELAYER_ROLE)) {
for (uint256 idx = 0; idx < accounts.length; idx++) {
_grantRole(RELAYER_ROLE, accounts[idx]);
}
}
/**
* @dev Revokes `RELAYER_ROLE` from `accounts`.
*
* If each `account` had already granted `RELAYER_ROLE`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must have ``RELAYER_ROLE``'s admin role.
*/
function revokeRelayers(address[] calldata accounts) external onlyRole(getRoleAdmin(RELAYER_ROLE)) {
for (uint256 idx = 0; idx < accounts.length; idx++) {
_revokeRole(RELAYER_ROLE, accounts[idx]);
}
}
/// @notice Relay and save a set of price data to the contract only if resolveTime is newer
/// @dev All of the lists must be of equal length
/// @param symbols A list of symbols whose data is being relayed in this function call
/// @param rates A list of the rates associated with each symbol
/// @param resolveTime A timestamp of when the rate data was retrieved
/// @param requestID A BandChain request ID in which the rate data was retrieved
function relay(
string[] calldata symbols,
uint64[] calldata rates,
uint64 resolveTime,
uint64 requestID
) external {
require(hasRole(RELAYER_ROLE, msg.sender), "NOTARELAYER");
require(rates.length == symbols.length, "BADRATESLENGTH");
for (uint256 idx = 0; idx < symbols.length; idx++) {
RefData storage ref = refs[symbols[idx]];
if (resolveTime > ref.resolveTime) {
refs[symbols[idx]] = RefData({rate: rates[idx], resolveTime: resolveTime, requestID: requestID});
}
}
}
/// @notice Relay and save a set of price data to the contract
/// @dev All of the lists must be of equal length
/// @param symbols A list of symbols whose data is being relayed in this function call
/// @param rates A list of the rates associated with each symbol
/// @param resolveTime A timestamp of when the rate data was retrieved
/// @param requestID A BandChain request ID in which the rate data was retrieved
function forceRelay(
string[] calldata symbols,
uint64[] calldata rates,
uint64 resolveTime,
uint64 requestID
) external {
require(hasRole(RELAYER_ROLE, msg.sender), "NOTARELAYER");
require(rates.length == symbols.length, "BADRATESLENGTH");
for (uint256 idx = 0; idx < symbols.length; idx++) {
refs[symbols[idx]] = RefData({rate: rates[idx], resolveTime: resolveTime, requestID: requestID});
}
}
modifier onlyWhitelisted() {
require(hasRole(WHITELISTED_ROLE, msg.sender), "NOTWHITELISTED");
_;
}
// Add addresses to the whitelist
function addToWhitelist(address[] calldata addresses) external onlyRole(DEFAULT_ADMIN_ROLE) {
for (uint256 idx = 0; idx < addresses.length; idx++) {
grantRole(WHITELISTED_ROLE, addresses[idx]);
}
}
// Remove addresses from the whitelist
function removeFromWhitelist(address[] calldata addresses) external onlyRole(DEFAULT_ADMIN_ROLE) {
for (uint256 idx = 0; idx < addresses.length; idx++) {
revokeRole(WHITELISTED_ROLE, addresses[idx]);
}
}
/// @notice Returns the price data for the given base/quote pair. Revert if not available.
/// @param base the base symbol of the token pair to query
/// @param quote the quote symbol of the token pair to query
function getReferenceData(string memory base, string memory quote) public view override onlyWhitelisted returns (ReferenceData memory) {
(uint256 baseRate, uint256 baseLastUpdate) = _getRefData(base);
(uint256 quoteRate, uint256 quoteLastUpdate) = _getRefData(quote);
return ReferenceData({rate: (baseRate * 1e18) / quoteRate, lastUpdatedBase: baseLastUpdate, lastUpdatedQuote: quoteLastUpdate});
}
function getReferenceDataBulk(string[] memory _bases, string[] memory _quotes) public view override onlyWhitelisted returns (ReferenceData[] memory) {
require(_bases.length == _quotes.length, "BAD_INPUT_LENGTH");
uint256 len = _bases.length;
ReferenceData[] memory results = new ReferenceData[](len);
for (uint256 idx = 0; idx < len; idx++) {
results[idx] = getReferenceData(_bases[idx], _quotes[idx]);
}
return results;
}
/// @notice Get the price data of a token
/// @param symbol the symbol of the token whose price to query
function _getRefData(string memory symbol) internal view returns (uint256 rate, uint256 lastUpdate) {
if (keccak256(bytes(symbol)) == keccak256(bytes("USD"))) {
return (1e9, block.timestamp);
}
RefData storage refData = refs[symbol];
require(refData.resolveTime > 0, "REFDATANOTAVAILABLE");
return (uint256(refData.rate), uint256(refData.resolveTime));
}
}