/
ExchangeRateRegistry.sol
217 lines (181 loc) · 6.83 KB
/
ExchangeRateRegistry.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
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
// SPDX-License-Identifier: BUSL-1.1
/*
██████╗░██╗░░░░░░█████╗░░█████╗░███╗░░░███╗
██╔══██╗██║░░░░░██╔══██╗██╔══██╗████╗░████║
██████╦╝██║░░░░░██║░░██║██║░░██║██╔████╔██║
██╔══██╗██║░░░░░██║░░██║██║░░██║██║╚██╔╝██║
██████╦╝███████╗╚█████╔╝╚█████╔╝██║░╚═╝░██║
╚═════╝░╚══════╝░╚════╝░░╚════╝░╚═╝░░░░░╚═╝
*/
pragma solidity 0.8.19;
import "openzeppelin-contracts/contracts/access/Ownable.sol";
import "openzeppelin-contracts/contracts/utils/structs/EnumerableSet.sol";
import "../interfaces/IBPSFeed.sol";
import "../interfaces/IBloomPool.sol";
/**
* @title ExchangeRateRegistry
* @notice Manage tokens and exchange rates
* @dev This contract stores:
* 1. Address of all TBYs
* 2. Exchange rate of each TBY
* 3. If TBY is active or not
*/
contract ExchangeRateRegistry is Ownable {
using EnumerableSet for EnumerableSet.AddressSet;
uint256 public constant INITIAL_FEED_RATE = 1e4;
uint256 public constant BASE_RATE = 1e18;
uint256 public constant ONE_YEAR = 360 days;
uint256 public constant SCALER = 1e14;
struct TokenInfo {
bool registered;
bool active;
address pool;
uint256 createdAt;
}
/**
* @notice Mapping of token to TokenInfo
*/
mapping(address => TokenInfo) public tokenInfos;
/**
* @dev Group of active tokens
*/
EnumerableSet.AddressSet internal _activeTokens;
/**
* @dev Group of inactive tokens
*/
EnumerableSet.AddressSet internal _inactiveTokens;
/**
* @dev Indicates that the contract has been _initialized
*/
bool internal _initialized;
/**
* @notice Emitted when token is registered
* @param token The token address to register
* @param pool The pool associated with the token
* @param createdAt Timestamp of the token creation
*/
event TokenRegistered(
address indexed token,
address pool,
uint256 createdAt
);
/**
* @notice Emitted when token is activated
* @param token The token address
*/
event TokenActivated(address token);
/**
* @notice Emitted when token is inactivated
* @param token The token address
*/
event TokenInactivated(address token);
/**
* @dev Function to initialize the contract
* @dev Can an only be called once by the deployer of the contract
* @dev The caller is responsible for ensuring that both the new owner and the token contract are configured correctly
* @param newOwner The address of the new owner of the exchange rate updater contract, can either be an EOA or a contract
*/
function initialize(address newOwner) external onlyOwner {
require(
!_initialized,
"ExchangeRateRegistry: contract is already initialized"
);
require(
newOwner != address(0),
"ExchangeRateRegistry: owner is the zero address"
);
transferOwnership(newOwner);
_initialized = true;
}
/**
* @notice Register new token to the registry
* @param token The token address to register
* @param pool The pool associated with the token
*/
function registerToken(
address token,
address pool
) external onlyOwner {
IBloomPool poolContract = IBloomPool(pool);
uint256 createdAt = poolContract.COMMIT_PHASE_END();
TokenInfo storage info = tokenInfos[token];
require(
!info.registered,
"ExchangeRateRegistry: token already registered"
);
info.registered = true;
info.active = true;
info.pool = pool;
info.createdAt = createdAt;
_activeTokens.add(token);
emit TokenRegistered(token, pool, createdAt);
}
/**
* @notice Activate the token
* @param token The token address to activate
*/
function activateToken(address token) external onlyOwner {
TokenInfo storage info = tokenInfos[token];
require(info.registered, "ExchangeRateRegistry: token not registered");
require(!info.active, "ExchangeRateRegistry: token is active");
info.active = true;
_activeTokens.add(token);
_inactiveTokens.remove(token);
emit TokenActivated(token);
}
/**
* @notice Inactivate the token
* @param token The token address to inactivate
*/
function inactivateToken(address token) external onlyOwner {
TokenInfo storage info = tokenInfos[token];
require(info.active, "ExchangeRateRegistry: token is inactive");
info.active = false;
_activeTokens.remove(token);
_inactiveTokens.add(token);
emit TokenInactivated(token);
}
/**
* @notice Return list of active tokens
*/
function getActiveTokens() external view returns (address[] memory) {
return _activeTokens.values();
}
/**
* @notice Return list of inactive tokens
*/
function getInactiveTokens() external view returns (address[] memory) {
return _inactiveTokens.values();
}
/**
* @notice Return true if the registry has been initialized
*/
function isRegistryInitialized() external view returns (bool) {
return _initialized;
}
/**
* @notice Returns the current exchange rate of the given token
* @param token The token address
* @return The current exchange rate of the given token
*/
function getExchangeRate(address token) external view returns (uint256) {
return _getExchangeRate(token);
}
function _getExchangeRate(address token) internal view returns (uint256) {
TokenInfo storage info = tokenInfos[token];
require(info.registered, "ExchangeRateRegistry: token not registered");
IBloomPool pool = IBloomPool(info.pool);
uint256 lenderFee = pool.LENDER_RETURN_FEE();
uint256 duration = pool.POOL_PHASE_DURATION();
IBPSFeed bpsFeed = IBPSFeed(pool.LENDER_RETURN_BPS_FEED());
uint256 rate = (bpsFeed.getWeightedRate() - INITIAL_FEED_RATE) * SCALER;
uint256 timeElapsed = block.timestamp - info.createdAt;
if (timeElapsed > duration) {
timeElapsed = duration;
}
uint256 adjustedLenderFee = (lenderFee * SCALER);
uint256 delta = ((rate * (BASE_RATE - adjustedLenderFee) / 1e18) * timeElapsed) /
ONE_YEAR;
return BASE_RATE + delta;
}
}