Skip to content

Commit 61cbb50

Browse files
authored
SIP-117 Add OVM exchanging functionality (Synthetixio#1086)
1 parent fee69fa commit 61cbb50

20 files changed

+21766
-15494
lines changed

.circleci/config.yml

+2-1
Original file line numberDiff line numberDiff line change
@@ -82,12 +82,13 @@ jobs:
8282
- attach_workspace:
8383
at: .
8484
- run: node publish build --test-helpers
85+
- run: npx hardhat compile --force
8586
- run:
8687
command: npx hardhat node
8788
background: true
8889
- cmd-wait-for-rpc
8990
- run: node publish deploy --network local --fresh-deploy --yes --use-ovm --ignore-safety-checks --ignore-custom-parameters --deployment-path ./publish/deployed/local-ovm
90-
- run: npm run test:prod -- --use-ovm --patch-fresh-deployment --deployment-path ./publish/deployed/local-ovm
91+
- run: npm run test:prod -- --no-compile --use-ovm --patch-fresh-deployment --deployment-path ./publish/deployed/local-ovm
9192
job-prod-diff-tests-local:
9293
working_directory: ~/repo
9394
docker:

.circleci/src/jobs/job-prod-diff-tests-local-ovm.yml

+5-2
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@ steps:
44
- checkout
55
- attach_workspace:
66
at: .
7-
# Compile
7+
# Note: Both compilations below target the evm intentionally
8+
# Compile - required for deploy
89
- run: node publish build --test-helpers
10+
# Compile again - required for tests
11+
- run: npx hardhat compile --force
912
# Start local chain
1013
- run:
1114
command: npx hardhat node
@@ -14,4 +17,4 @@ steps:
1417
# Deploy
1518
- run: node publish deploy --network local --fresh-deploy --yes --use-ovm --ignore-safety-checks --ignore-custom-parameters --deployment-path ./publish/deployed/local-ovm
1619
# Run production tests
17-
- run: npm run test:prod -- --use-ovm --patch-fresh-deployment --deployment-path ./publish/deployed/local-ovm
20+
- run: npm run test:prod -- --no-compile --use-ovm --patch-fresh-deployment --deployment-path ./publish/deployed/local-ovm

contracts/BaseDebtCache.sol

+278
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,278 @@
1+
pragma solidity ^0.5.16;
2+
3+
// Inheritance
4+
import "./Owned.sol";
5+
import "./MixinResolver.sol";
6+
import "./MixinSystemSettings.sol";
7+
import "./interfaces/IDebtCache.sol";
8+
9+
// Libraries
10+
import "./SafeDecimalMath.sol";
11+
12+
// Internal references
13+
import "./interfaces/IIssuer.sol";
14+
import "./interfaces/IExchanger.sol";
15+
import "./interfaces/IExchangeRates.sol";
16+
import "./interfaces/ISystemStatus.sol";
17+
import "./interfaces/IEtherCollateral.sol";
18+
import "./interfaces/IEtherCollateralsUSD.sol";
19+
import "./interfaces/IERC20.sol";
20+
import "./interfaces/ICollateralManager.sol";
21+
22+
23+
// https://docs.synthetix.io/contracts/source/contracts/debtcache
24+
contract BaseDebtCache is Owned, MixinSystemSettings, IDebtCache {
25+
using SafeMath for uint;
26+
using SafeDecimalMath for uint;
27+
28+
uint internal _cachedDebt;
29+
mapping(bytes32 => uint) internal _cachedSynthDebt;
30+
uint internal _cacheTimestamp;
31+
bool internal _cacheInvalid = true;
32+
33+
/* ========== ENCODED NAMES ========== */
34+
35+
bytes32 internal constant sUSD = "sUSD";
36+
bytes32 internal constant sETH = "sETH";
37+
38+
/* ========== ADDRESS RESOLVER CONFIGURATION ========== */
39+
40+
bytes32 private constant CONTRACT_ISSUER = "Issuer";
41+
bytes32 private constant CONTRACT_EXCHANGER = "Exchanger";
42+
bytes32 private constant CONTRACT_EXRATES = "ExchangeRates";
43+
bytes32 private constant CONTRACT_SYSTEMSTATUS = "SystemStatus";
44+
bytes32 private constant CONTRACT_ETHERCOLLATERAL = "EtherCollateral";
45+
bytes32 private constant CONTRACT_ETHERCOLLATERAL_SUSD = "EtherCollateralsUSD";
46+
bytes32 private constant CONTRACT_COLLATERALMANAGER = "CollateralManager";
47+
48+
constructor(address _owner, address _resolver) public Owned(_owner) MixinSystemSettings(_resolver) {}
49+
50+
/* ========== VIEWS ========== */
51+
52+
function resolverAddressesRequired() public view returns (bytes32[] memory addresses) {
53+
bytes32[] memory existingAddresses = MixinSystemSettings.resolverAddressesRequired();
54+
bytes32[] memory newAddresses = new bytes32[](7);
55+
newAddresses[0] = CONTRACT_ISSUER;
56+
newAddresses[1] = CONTRACT_EXCHANGER;
57+
newAddresses[2] = CONTRACT_EXRATES;
58+
newAddresses[3] = CONTRACT_SYSTEMSTATUS;
59+
newAddresses[4] = CONTRACT_ETHERCOLLATERAL;
60+
newAddresses[5] = CONTRACT_ETHERCOLLATERAL_SUSD;
61+
newAddresses[6] = CONTRACT_COLLATERALMANAGER;
62+
addresses = combineArrays(existingAddresses, newAddresses);
63+
}
64+
65+
function issuer() internal view returns (IIssuer) {
66+
return IIssuer(requireAndGetAddress(CONTRACT_ISSUER));
67+
}
68+
69+
function exchanger() internal view returns (IExchanger) {
70+
return IExchanger(requireAndGetAddress(CONTRACT_EXCHANGER));
71+
}
72+
73+
function exchangeRates() internal view returns (IExchangeRates) {
74+
return IExchangeRates(requireAndGetAddress(CONTRACT_EXRATES));
75+
}
76+
77+
function systemStatus() internal view returns (ISystemStatus) {
78+
return ISystemStatus(requireAndGetAddress(CONTRACT_SYSTEMSTATUS));
79+
}
80+
81+
function etherCollateral() internal view returns (IEtherCollateral) {
82+
return IEtherCollateral(requireAndGetAddress(CONTRACT_ETHERCOLLATERAL));
83+
}
84+
85+
function etherCollateralsUSD() internal view returns (IEtherCollateralsUSD) {
86+
return IEtherCollateralsUSD(requireAndGetAddress(CONTRACT_ETHERCOLLATERAL_SUSD));
87+
}
88+
89+
function collateralManager() internal view returns (ICollateralManager) {
90+
return ICollateralManager(requireAndGetAddress(CONTRACT_COLLATERALMANAGER));
91+
}
92+
93+
function debtSnapshotStaleTime() external view returns (uint) {
94+
return getDebtSnapshotStaleTime();
95+
}
96+
97+
function cachedDebt() external view returns (uint) {
98+
return _cachedDebt;
99+
}
100+
101+
function cachedSynthDebt(bytes32 currencyKey) external view returns (uint) {
102+
return _cachedSynthDebt[currencyKey];
103+
}
104+
105+
function cacheTimestamp() external view returns (uint) {
106+
return _cacheTimestamp;
107+
}
108+
109+
function cacheInvalid() external view returns (bool) {
110+
return _cacheInvalid;
111+
}
112+
113+
function _cacheStale(uint timestamp) internal view returns (bool) {
114+
// Note a 0 timestamp means that the cache is uninitialised.
115+
// We'll keep the check explicitly in case the stale time is
116+
// ever set to something higher than the current unix time (e.g. to turn off staleness).
117+
return getDebtSnapshotStaleTime() < block.timestamp - timestamp || timestamp == 0;
118+
}
119+
120+
function cacheStale() external view returns (bool) {
121+
return _cacheStale(_cacheTimestamp);
122+
}
123+
124+
function _issuedSynthValues(bytes32[] memory currencyKeys, uint[] memory rates) internal view returns (uint[] memory) {
125+
uint numValues = currencyKeys.length;
126+
uint[] memory values = new uint[](numValues);
127+
ISynth[] memory synths = issuer().getSynths(currencyKeys);
128+
129+
for (uint i = 0; i < numValues; i++) {
130+
bytes32 key = currencyKeys[i];
131+
address synthAddress = address(synths[i]);
132+
require(synthAddress != address(0), "Synth does not exist");
133+
uint supply = IERC20(synthAddress).totalSupply();
134+
135+
if (collateralManager().isSynthManaged(key)) {
136+
uint collateralIssued = collateralManager().long(key);
137+
138+
// this is an edge case --
139+
// if a synth other than sUSD is only issued by non SNX collateral
140+
// the long value will exceed the supply if there was a minting fee,
141+
// so we check explicitly and 0 it out to prevent
142+
// a safesub overflow.
143+
144+
if (collateralIssued > supply) {
145+
supply = 0;
146+
} else {
147+
supply = supply.sub(collateralIssued);
148+
}
149+
}
150+
151+
bool isSUSD = key == sUSD;
152+
if (isSUSD || key == sETH) {
153+
IEtherCollateral etherCollateralContract = isSUSD
154+
? IEtherCollateral(address(etherCollateralsUSD()))
155+
: etherCollateral();
156+
uint etherCollateralSupply = etherCollateralContract.totalIssuedSynths();
157+
supply = supply.sub(etherCollateralSupply);
158+
}
159+
160+
values[i] = supply.multiplyDecimalRound(rates[i]);
161+
}
162+
return values;
163+
}
164+
165+
function _currentSynthDebts(bytes32[] memory currencyKeys)
166+
internal
167+
view
168+
returns (uint[] memory snxIssuedDebts, bool anyRateIsInvalid)
169+
{
170+
(uint[] memory rates, bool isInvalid) = exchangeRates().ratesAndInvalidForCurrencies(currencyKeys);
171+
return (_issuedSynthValues(currencyKeys, rates), isInvalid);
172+
}
173+
174+
function currentSynthDebts(bytes32[] calldata currencyKeys)
175+
external
176+
view
177+
returns (uint[] memory debtValues, bool anyRateIsInvalid)
178+
{
179+
return _currentSynthDebts(currencyKeys);
180+
}
181+
182+
function _cachedSynthDebts(bytes32[] memory currencyKeys) internal view returns (uint[] memory) {
183+
uint numKeys = currencyKeys.length;
184+
uint[] memory debts = new uint[](numKeys);
185+
for (uint i = 0; i < numKeys; i++) {
186+
debts[i] = _cachedSynthDebt[currencyKeys[i]];
187+
}
188+
return debts;
189+
}
190+
191+
function cachedSynthDebts(bytes32[] calldata currencyKeys) external view returns (uint[] memory snxIssuedDebts) {
192+
return _cachedSynthDebts(currencyKeys);
193+
}
194+
195+
function _currentDebt() internal view returns (uint debt, bool anyRateIsInvalid) {
196+
(uint[] memory values, bool isInvalid) = _currentSynthDebts(issuer().availableCurrencyKeys());
197+
uint numValues = values.length;
198+
uint total;
199+
for (uint i; i < numValues; i++) {
200+
total = total.add(values[i]);
201+
}
202+
203+
// subtract the USD value of all shorts.
204+
(uint susdValue, bool shortInvalid) = collateralManager().totalShort();
205+
206+
total = total.sub(susdValue);
207+
208+
isInvalid = isInvalid || shortInvalid;
209+
210+
return (total, isInvalid);
211+
}
212+
213+
function currentDebt() external view returns (uint debt, bool anyRateIsInvalid) {
214+
return _currentDebt();
215+
}
216+
217+
function cacheInfo()
218+
external
219+
view
220+
returns (
221+
uint debt,
222+
uint timestamp,
223+
bool isInvalid,
224+
bool isStale
225+
)
226+
{
227+
uint time = _cacheTimestamp;
228+
return (_cachedDebt, time, _cacheInvalid, _cacheStale(time));
229+
}
230+
231+
/* ========== MUTATIVE FUNCTIONS ========== */
232+
233+
// Stub out all mutative functions as no-ops;
234+
// since they do nothing, there are no restrictions
235+
236+
function updateCachedSynthDebts(bytes32[] calldata currencyKeys) external {}
237+
238+
function updateCachedSynthDebtWithRate(bytes32 currencyKey, uint currencyRate) external {}
239+
240+
function updateCachedSynthDebtsWithRates(bytes32[] calldata currencyKeys, uint[] calldata currencyRates) external {}
241+
242+
function updateDebtCacheValidity(bool currentlyInvalid) external {}
243+
244+
function purgeCachedSynthDebt(bytes32 currencyKey) external {}
245+
246+
function takeDebtSnapshot() external {}
247+
248+
/* ========== MODIFIERS ========== */
249+
250+
function _requireSystemActiveIfNotOwner() internal view {
251+
if (msg.sender != owner) {
252+
systemStatus().requireSystemActive();
253+
}
254+
}
255+
256+
modifier requireSystemActiveIfNotOwner() {
257+
_requireSystemActiveIfNotOwner();
258+
_;
259+
}
260+
261+
function _onlyIssuer() internal view {
262+
require(msg.sender == address(issuer()), "Sender is not Issuer");
263+
}
264+
265+
modifier onlyIssuer() {
266+
_onlyIssuer();
267+
_;
268+
}
269+
270+
function _onlyIssuerOrExchanger() internal view {
271+
require(msg.sender == address(issuer()) || msg.sender == address(exchanger()), "Sender is not Issuer or Exchanger");
272+
}
273+
274+
modifier onlyIssuerOrExchanger() {
275+
_onlyIssuerOrExchanger();
276+
_;
277+
}
278+
}

0 commit comments

Comments
 (0)