forked from Synthetixio/synthetix
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathBaseDebtCache.sol
384 lines (306 loc) · 14.5 KB
/
BaseDebtCache.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
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
pragma solidity ^0.5.16;
// Inheritance
import "./Owned.sol";
import "./MixinResolver.sol";
import "./MixinSystemSettings.sol";
import "./interfaces/IDebtCache.sol";
// Libraries
import "./SafeDecimalMath.sol";
// Internal references
import "./interfaces/IIssuer.sol";
import "./interfaces/IExchanger.sol";
import "./interfaces/IExchangeRates.sol";
import "./interfaces/ISystemStatus.sol";
import "./interfaces/IERC20.sol";
import "./interfaces/ICollateralManager.sol";
import "./interfaces/IEtherWrapper.sol";
import "./interfaces/IWrapperFactory.sol";
import "./interfaces/IFuturesMarketManager.sol";
// https://docs.synthetix.io/contracts/source/contracts/debtcache
contract BaseDebtCache is Owned, MixinSystemSettings, IDebtCache {
using SafeMath for uint;
using SafeDecimalMath for uint;
uint internal _cachedDebt;
mapping(bytes32 => uint) internal _cachedSynthDebt;
mapping(bytes32 => uint) internal _excludedIssuedDebt;
uint internal _cacheTimestamp;
bool internal _cacheInvalid = true;
// flag to ensure importing excluded debt is invoked only once
bool public isInitialized = false; // public to avoid needing an event
/* ========== ENCODED NAMES ========== */
bytes32 internal constant sUSD = "sUSD";
bytes32 internal constant sETH = "sETH";
/* ========== ADDRESS RESOLVER CONFIGURATION ========== */
bytes32 private constant CONTRACT_ISSUER = "Issuer";
bytes32 private constant CONTRACT_EXCHANGER = "Exchanger";
bytes32 private constant CONTRACT_EXRATES = "ExchangeRates";
bytes32 private constant CONTRACT_SYSTEMSTATUS = "SystemStatus";
bytes32 private constant CONTRACT_COLLATERALMANAGER = "CollateralManager";
bytes32 private constant CONTRACT_ETHER_WRAPPER = "EtherWrapper";
bytes32 private constant CONTRACT_FUTURESMARKETMANAGER = "FuturesMarketManager";
bytes32 private constant CONTRACT_WRAPPER_FACTORY = "WrapperFactory";
constructor(address _owner, address _resolver) public Owned(_owner) MixinSystemSettings(_resolver) {}
/* ========== VIEWS ========== */
function resolverAddressesRequired() public view returns (bytes32[] memory addresses) {
bytes32[] memory existingAddresses = MixinSystemSettings.resolverAddressesRequired();
bytes32[] memory newAddresses = new bytes32[](8);
newAddresses[0] = CONTRACT_ISSUER;
newAddresses[1] = CONTRACT_EXCHANGER;
newAddresses[2] = CONTRACT_EXRATES;
newAddresses[3] = CONTRACT_SYSTEMSTATUS;
newAddresses[4] = CONTRACT_COLLATERALMANAGER;
newAddresses[5] = CONTRACT_WRAPPER_FACTORY;
newAddresses[6] = CONTRACT_ETHER_WRAPPER;
newAddresses[7] = CONTRACT_FUTURESMARKETMANAGER;
addresses = combineArrays(existingAddresses, newAddresses);
}
function issuer() internal view returns (IIssuer) {
return IIssuer(requireAndGetAddress(CONTRACT_ISSUER));
}
function exchanger() internal view returns (IExchanger) {
return IExchanger(requireAndGetAddress(CONTRACT_EXCHANGER));
}
function exchangeRates() internal view returns (IExchangeRates) {
return IExchangeRates(requireAndGetAddress(CONTRACT_EXRATES));
}
function systemStatus() internal view returns (ISystemStatus) {
return ISystemStatus(requireAndGetAddress(CONTRACT_SYSTEMSTATUS));
}
function collateralManager() internal view returns (ICollateralManager) {
return ICollateralManager(requireAndGetAddress(CONTRACT_COLLATERALMANAGER));
}
function etherWrapper() internal view returns (IEtherWrapper) {
return IEtherWrapper(requireAndGetAddress(CONTRACT_ETHER_WRAPPER));
}
function futuresMarketManager() internal view returns (IFuturesMarketManager) {
return IFuturesMarketManager(requireAndGetAddress(CONTRACT_FUTURESMARKETMANAGER));
}
function wrapperFactory() internal view returns (IWrapperFactory) {
return IWrapperFactory(requireAndGetAddress(CONTRACT_WRAPPER_FACTORY));
}
function debtSnapshotStaleTime() external view returns (uint) {
return getDebtSnapshotStaleTime();
}
function cachedDebt() external view returns (uint) {
return _cachedDebt;
}
function cachedSynthDebt(bytes32 currencyKey) external view returns (uint) {
return _cachedSynthDebt[currencyKey];
}
function cacheTimestamp() external view returns (uint) {
return _cacheTimestamp;
}
function cacheInvalid() external view returns (bool) {
return _cacheInvalid;
}
function _cacheStale(uint timestamp) internal view returns (bool) {
// Note a 0 timestamp means that the cache is uninitialised.
// We'll keep the check explicitly in case the stale time is
// ever set to something higher than the current unix time (e.g. to turn off staleness).
return getDebtSnapshotStaleTime() < block.timestamp - timestamp || timestamp == 0;
}
function cacheStale() external view returns (bool) {
return _cacheStale(_cacheTimestamp);
}
function _issuedSynthValues(bytes32[] memory currencyKeys, uint[] memory rates)
internal
view
returns (uint[] memory values)
{
uint numValues = currencyKeys.length;
values = new uint[](numValues);
ISynth[] memory synths = issuer().getSynths(currencyKeys);
for (uint i = 0; i < numValues; i++) {
address synthAddress = address(synths[i]);
require(synthAddress != address(0), "Synth does not exist");
uint supply = IERC20(synthAddress).totalSupply();
values[i] = supply.multiplyDecimalRound(rates[i]);
}
return (values);
}
function _currentSynthDebts(bytes32[] memory currencyKeys)
internal
view
returns (
uint[] memory snxIssuedDebts,
uint _futuresDebt,
uint _excludedDebt,
bool anyRateIsInvalid
)
{
(uint[] memory rates, bool isInvalid) = exchangeRates().ratesAndInvalidForCurrencies(currencyKeys);
uint[] memory values = _issuedSynthValues(currencyKeys, rates);
(uint excludedDebt, bool isAnyNonSnxDebtRateInvalid) = _totalNonSnxBackedDebt(currencyKeys, rates, isInvalid);
(uint futuresDebt, bool futuresDebtIsInvalid) = futuresMarketManager().totalDebt();
return (values, futuresDebt, excludedDebt, isInvalid || futuresDebtIsInvalid || isAnyNonSnxDebtRateInvalid);
}
function currentSynthDebts(bytes32[] calldata currencyKeys)
external
view
returns (
uint[] memory debtValues,
uint futuresDebt,
uint excludedDebt,
bool anyRateIsInvalid
)
{
return _currentSynthDebts(currencyKeys);
}
function _cachedSynthDebts(bytes32[] memory currencyKeys) internal view returns (uint[] memory) {
uint numKeys = currencyKeys.length;
uint[] memory debts = new uint[](numKeys);
for (uint i = 0; i < numKeys; i++) {
debts[i] = _cachedSynthDebt[currencyKeys[i]];
}
return debts;
}
function cachedSynthDebts(bytes32[] calldata currencyKeys) external view returns (uint[] memory snxIssuedDebts) {
return _cachedSynthDebts(currencyKeys);
}
function _excludedIssuedDebts(bytes32[] memory currencyKeys) internal view returns (uint[] memory) {
uint numKeys = currencyKeys.length;
uint[] memory debts = new uint[](numKeys);
for (uint i = 0; i < numKeys; i++) {
debts[i] = _excludedIssuedDebt[currencyKeys[i]];
}
return debts;
}
function excludedIssuedDebts(bytes32[] calldata currencyKeys) external view returns (uint[] memory excludedDebts) {
return _excludedIssuedDebts(currencyKeys);
}
/// used when migrating to new DebtCache instance in order to import the excluded debt records
/// If this method is not run after upgrading the contract, the debt will be
/// incorrect w.r.t to wrapper factory assets until the values are imported from
/// previous instance of the contract
/// Also, in addition to this method it's possible to use recordExcludedDebtChange since
/// it's accessible to owner in case additional adjustments are required
function importExcludedIssuedDebts(IDebtCache prevDebtCache, IIssuer prevIssuer) external onlyOwner {
// this can only be run once so that recorded debt deltas aren't accidentally
// lost or double counted
require(!isInitialized, "already initialized");
isInitialized = true;
// get the currency keys from **previous** issuer, in case current issuer
// doesn't have all the synths at this point
// warning: if a synth won't be added to the current issuer before the next upgrade of this contract,
// its entry will be lost (because it won't be in the prevIssuer for next time).
// if for some reason this is a problem, it should be possible to use recordExcludedDebtChange() to amend
bytes32[] memory keys = prevIssuer.availableCurrencyKeys();
require(keys.length > 0, "previous Issuer has no synths");
// query for previous debt records
uint[] memory debts = prevDebtCache.excludedIssuedDebts(keys);
// store the values
for (uint i = 0; i < keys.length; i++) {
if (debts[i] > 0) {
// adding the values instead of overwriting in case some deltas were recorded in this
// contract already (e.g. if the upgrade was not atomic)
_excludedIssuedDebt[keys[i]] = _excludedIssuedDebt[keys[i]].add(debts[i]);
}
}
}
// Returns the total sUSD debt backed by non-SNX collateral.
function totalNonSnxBackedDebt() external view returns (uint excludedDebt, bool isInvalid) {
bytes32[] memory currencyKeys = issuer().availableCurrencyKeys();
(uint[] memory rates, bool ratesAreInvalid) = exchangeRates().ratesAndInvalidForCurrencies(currencyKeys);
return _totalNonSnxBackedDebt(currencyKeys, rates, ratesAreInvalid);
}
function _totalNonSnxBackedDebt(
bytes32[] memory currencyKeys,
uint[] memory rates,
bool ratesAreInvalid
) internal view returns (uint excludedDebt, bool isInvalid) {
// Calculate excluded debt.
// 1. MultiCollateral long debt + short debt.
(uint longValue, bool anyTotalLongRateIsInvalid) = collateralManager().totalLong();
(uint shortValue, bool anyTotalShortRateIsInvalid) = collateralManager().totalShort();
isInvalid = ratesAreInvalid || anyTotalLongRateIsInvalid || anyTotalShortRateIsInvalid;
excludedDebt = longValue.add(shortValue);
// 2. EtherWrapper.
// Subtract sETH and sUSD issued by EtherWrapper.
excludedDebt = excludedDebt.add(etherWrapper().totalIssuedSynths());
// 3. WrapperFactory.
// Get the debt issued by the Wrappers.
for (uint i = 0; i < currencyKeys.length; i++) {
excludedDebt = excludedDebt.add(_excludedIssuedDebt[currencyKeys[i]].multiplyDecimalRound(rates[i]));
}
return (excludedDebt, isInvalid);
}
function _currentDebt() internal view returns (uint debt, bool anyRateIsInvalid) {
bytes32[] memory currencyKeys = issuer().availableCurrencyKeys();
(uint[] memory rates, bool isInvalid) = exchangeRates().ratesAndInvalidForCurrencies(currencyKeys);
// Sum all issued synth values based on their supply.
uint[] memory values = _issuedSynthValues(currencyKeys, rates);
(uint excludedDebt, bool isAnyNonSnxDebtRateInvalid) = _totalNonSnxBackedDebt(currencyKeys, rates, isInvalid);
uint numValues = values.length;
uint total;
for (uint i; i < numValues; i++) {
total = total.add(values[i]);
}
// Add in the debt accounted for by futures
(uint futuresDebt, bool futuresDebtIsInvalid) = futuresMarketManager().totalDebt();
total = total.add(futuresDebt);
// Ensure that if the excluded non-SNX debt exceeds SNX-backed debt, no overflow occurs
total = total < excludedDebt ? 0 : total.sub(excludedDebt);
return (total, isInvalid || futuresDebtIsInvalid || isAnyNonSnxDebtRateInvalid);
}
function currentDebt() external view returns (uint debt, bool anyRateIsInvalid) {
return _currentDebt();
}
function cacheInfo()
external
view
returns (
uint debt,
uint timestamp,
bool isInvalid,
bool isStale
)
{
uint time = _cacheTimestamp;
return (_cachedDebt, time, _cacheInvalid, _cacheStale(time));
}
/* ========== MUTATIVE FUNCTIONS ========== */
// Stub out all mutative functions as no-ops;
// since they do nothing, there are no restrictions
function updateCachedSynthDebts(bytes32[] calldata currencyKeys) external {}
function updateCachedSynthDebtWithRate(bytes32 currencyKey, uint currencyRate) external {}
function updateCachedSynthDebtsWithRates(bytes32[] calldata currencyKeys, uint[] calldata currencyRates) external {}
function updateDebtCacheValidity(bool currentlyInvalid) external {}
function purgeCachedSynthDebt(bytes32 currencyKey) external {}
function takeDebtSnapshot() external {}
function recordExcludedDebtChange(bytes32 currencyKey, int256 delta) external {}
function updateCachedsUSDDebt(int amount) external {}
/* ========== MODIFIERS ========== */
function _requireSystemActiveIfNotOwner() internal view {
if (msg.sender != owner) {
systemStatus().requireSystemActive();
}
}
modifier requireSystemActiveIfNotOwner() {
_requireSystemActiveIfNotOwner();
_;
}
function _onlyIssuer() internal view {
require(msg.sender == address(issuer()), "Sender is not Issuer");
}
modifier onlyIssuer() {
_onlyIssuer();
_;
}
function _onlyIssuerOrExchanger() internal view {
require(msg.sender == address(issuer()) || msg.sender == address(exchanger()), "Sender is not Issuer or Exchanger");
}
modifier onlyIssuerOrExchanger() {
_onlyIssuerOrExchanger();
_;
}
function _onlyDebtIssuer() internal view {
bool isWrapper = wrapperFactory().isWrapper(msg.sender);
// owner included for debugging and fixing in emergency situation
bool isOwner = msg.sender == owner;
require(isOwner || isWrapper, "Only debt issuers may call this");
}
modifier onlyDebtIssuer() {
_onlyDebtIssuer();
_;
}
}