Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
c53070d
fix slither crashing in CI and local (#1519)
artdgn Sep 23, 2021
333b07b
Adding gnosis safe multisend functionality to owner (#1513)
Sep 24, 2021
bbbb0c3
SIP-135 Cleanup (#1522)
barrasso Sep 24, 2021
98e46fa
Prepping for the Sargas (2.50) release to OVM (#1526)
Sep 25, 2021
9e26043
Fixing settle for ethers (#1509)
Sep 27, 2021
91ac866
Reduce gas usage for opening loans (#1529)
barrasso Sep 28, 2021
aad6caf
Print deploy param object value (#1534)
barrasso Sep 28, 2021
5e48e5a
add slither code check github integration (#1523)
artdgn Sep 30, 2021
645a49c
Refactor exceedsDebtLimit check (#1539)
barrasso Sep 30, 2021
744265a
Removing unneeded kovan contracts (#1538)
Oct 1, 2021
07c85e5
ci: lavamoat integration (#1517)
drptbl Oct 3, 2021
f84eca5
CI: add codeql (#1544)
drptbl Oct 4, 2021
9a8384a
Adds block tag parameter to interact task (#1528) (#1536)
Oct 4, 2021
294f325
ci: slither github actions improvements (#1543)
drptbl Oct 4, 2021
963f266
Removing integrationProxy from Proxyable (#1521)
Oct 5, 2021
eb88a85
Fix prepare deploy (#1548)
barrasso Oct 6, 2021
28b6f95
Document debt cache (#1533)
liamzebedee Oct 7, 2021
b36a057
Updating packages for audits and temporarily disabling the audit chec…
jjgonecrypto Oct 7, 2021
fded644
ci: fail audit on critical severity (#1557)
drptbl Oct 12, 2021
0f9fcc0
ci: update docker containers to node@14.18 (#1558)
drptbl Oct 12, 2021
ce6d79d
Merge branch 'master' into develop
jjgonecrypto Oct 12, 2021
476c5a4
During deployment, ensure deprecated synths from Wezen have cache upd…
jjgonecrypto Oct 14, 2021
5f8c732
Update configure loans interactionDelay (#1568)
barrasso Oct 14, 2021
f1fcfcf
update deployer to support EIP-1559 (#1504)
dbeal-eth Oct 15, 2021
963d78d
SIP-187 fix partial synth updates and debt cache updates (#1551)
jacko125 Oct 19, 2021
1172632
remove gasPrice overrides from simulate-deploy (#1573)
dbeal-eth Oct 19, 2021
1f0c027
Merge branch 'master' into develop
jjgonecrypto Oct 20, 2021
795f1c7
SIP-182 Wrapper Factory (#1489)
dbeal-eth Oct 26, 2021
9d6ac2e
SIP-182 Audit feedback - allowance in constructor (#1584)
barrasso Oct 27, 2021
0743256
Merge branch 'master' into develop
jjgonecrypto Oct 28, 2021
94987e3
Fix param spam during deployment (#1587)
barrasso Oct 29, 2021
defb035
add expalantion about releases.json in readme (#1569)
artdgn Nov 2, 2021
1c126e1
ci: use node 16 (LTS) (#1588)
drptbl Nov 2, 2021
913d0b3
Removing inverse synths (#1592)
jjgonecrypto Nov 3, 2021
963c7a5
Ensuring SCCP-139 feeds are persisted to future ExchangeRates contrac…
jjgonecrypto Nov 3, 2021
76d43ce
Skip shorts integration tests when cannot open loans (#1597)
jjgonecrypto Nov 10, 2021
073cb12
Add default private key for local-ovm deploys (#1455)
liamzebedee Nov 15, 2021
f2172c3
SIP-120: TWAP Exchange Function (#1127)
sohkai Nov 15, 2021
509688a
Adding 6 more potential releases
jjgonecrypto Nov 15, 2021
472d20e
Merge branch 'master' into develop
jjgonecrypto Nov 17, 2021
dac3532
Fixing owner script to not submit multiple accept for dupe contracts
jjgonecrypto Nov 17, 2021
2bd3ceb
Upgrading to OVM 2.0 (or the destruction of useless code) (#1598)
jjgonecrypto Nov 18, 2021
682a2ef
Minor fixes for release history
jjgonecrypto Nov 18, 2021
9f38d1a
SIP-167 Introduces an L2 governance bridge (#1402)
eternauta1337 Nov 23, 2021
5c9b643
Removing mistake
jjgonecrypto Nov 23, 2021
fe6a61a
Merge branch 'master' into develop
jjgonecrypto Nov 23, 2021
6a5b7b2
Fixes from menkalinan (#1608)
jjgonecrypto Nov 24, 2021
ffc3d61
Update OVM bytecode (#1613)
barrasso Dec 1, 2021
69fb372
Move debt snapshot to beforeEach block (#1616)
barrasso Dec 1, 2021
1f6dd63
Fix duplicate fee reporting on WrapperFactory (#1617)
dbeal-eth Dec 3, 2021
e1cdc23
Merge branch 'master' into develop
jjgonecrypto Dec 3, 2021
437f62f
SIP-194 Fix Liquidations on L2 (#1621)
jjgonecrypto Dec 7, 2021
92fe9e7
Fix broken unit test (#1622)
jjgonecrypto Dec 8, 2021
af2813c
Ensuring job-compile size check matches build command (#1628)
jjgonecrypto Dec 9, 2021
b898b4e
Adding exchange gas usage output
jjgonecrypto Dec 13, 2021
f734d9a
Adding gas output to synth issue, burn and claim in int tests
jjgonecrypto Dec 13, 2021
b93863a
SIP-188: Add sETHBTC synth (#1618)
barrasso Dec 13, 2021
828c24e
SIP-195: L2 CollateralEth Loans (#1632)
barrasso Dec 15, 2021
4513b16
Merge branch 'master' into develop
jjgonecrypto Dec 15, 2021
4c981ba
Sip 196 remove internal oracle (#1636)
artdgn Dec 22, 2021
61eca39
add liquidation tests in integration (#1625)
dbeal-eth Dec 30, 2021
a88b2ee
use new `hardhat-interact` package instead of builtin (#1612)
dbeal-eth Jan 10, 2022
d34061c
fix liquidations fork test fail (#1646)
dbeal-eth Jan 11, 2022
54a9c52
SIP-193 Reduce size for SystemSettings (#1627)
leckylao Jan 13, 2022
2d56244
Update configuration of loan and system settings (#1637)
barrasso Jan 13, 2022
ab736f0
SIP-200: Fix FeePool Rewards Distribution (#1650)
barrasso Jan 14, 2022
88e7be2
Update releases.json
barrasso Jan 14, 2022
5a5f6f6
migration script helper allows deployment and staging of migration sc…
dbeal-eth Jan 14, 2022
665ee2a
Merge remote-tracking branch 'origin/develop' into peacock-release-v2.57
barrasso Jan 14, 2022
26c2379
Add kovan deployment artifacts and migration contracts. Add other min…
barrasso Jan 15, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
213 changes: 54 additions & 159 deletions contracts/ExchangeRates.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,8 @@ contract ExchangeRates is Owned, MixinSystemSettings, IExchangeRates {
using SafeDecimalMath for uint;

bytes32 public constant CONTRACT_NAME = "ExchangeRates";

// Exchange rates and update times stored by currency code, e.g. 'SNX', or 'sUSD'
mapping(bytes32 => mapping(uint => RateAndUpdatedTime)) private _rates;

// The address of the oracle which pushes rate updates to this contract
address public oracle;
//slither-disable-next-line naming-convention
bytes32 internal constant sUSD = "sUSD";

// Decentralized oracle networks that feed into pricing aggregators
mapping(bytes32 => AggregatorV2V3Interface) public aggregators;
Expand All @@ -37,58 +33,12 @@ contract ExchangeRates is Owned, MixinSystemSettings, IExchangeRates {
// List of aggregator keys for convenient iteration
bytes32[] public aggregatorKeys;

// Do not allow the oracle to submit times any further forward into the future than this constant.
uint private constant ORACLE_FUTURE_LIMIT = 10 minutes;

mapping(bytes32 => uint) public currentRoundForRate;

//
// ========== CONSTRUCTOR ==========

constructor(
address _owner,
address _oracle,
address _resolver,
bytes32[] memory _currencyKeys,
uint[] memory _newRates
) public Owned(_owner) MixinSystemSettings(_resolver) {
require(_currencyKeys.length == _newRates.length, "Currency key length and rate length must match.");

oracle = _oracle;

// The sUSD rate is always 1 and is never stale.
_setRate("sUSD", SafeDecimalMath.unit(), now);

internalUpdateRates(_currencyKeys, _newRates, now);
}

/* ========== SETTERS ========== */

function setOracle(address _oracle) external onlyOwner {
oracle = _oracle;
emit OracleUpdated(oracle);
}
constructor(address _owner, address _resolver) public Owned(_owner) MixinSystemSettings(_resolver) {}

/* ========== MUTATIVE FUNCTIONS ========== */

function updateRates(
bytes32[] calldata currencyKeys,
uint[] calldata newRates,
uint timeSent
) external onlyOracle returns (bool) {
return internalUpdateRates(currencyKeys, newRates, timeSent);
}

function deleteRate(bytes32 currencyKey) external onlyOracle {
require(_getRate(currencyKey) > 0, "Rate is zero");

delete _rates[currencyKey][currentRoundForRate[currencyKey]];

currentRoundForRate[currencyKey]--;

emit RateDeleted(currencyKey);
}

function addAggregator(bytes32 currencyKey, address aggregatorAddress) external onlyOwner {
AggregatorV2V3Interface aggregator = AggregatorV2V3Interface(aggregatorAddress);
// This check tries to make sure that a valid aggregator is being added.
Expand Down Expand Up @@ -288,7 +238,7 @@ contract ExchangeRates is Owned, MixinSystemSettings, IExchangeRates {
function rateAndInvalid(bytes32 currencyKey) external view returns (uint rate, bool isInvalid) {
RateAndUpdatedTime memory rateAndTime = _getRateAndUpdatedTime(currencyKey);

if (currencyKey == "sUSD") {
if (currencyKey == sUSD) {
return (rateAndTime.rate, false);
}
return (
Expand All @@ -314,7 +264,7 @@ contract ExchangeRates is Owned, MixinSystemSettings, IExchangeRates {
// do one lookup of the rate & time to minimize gas
RateAndUpdatedTime memory rateEntry = _getRateAndUpdatedTime(currencyKeys[i]);
rates[i] = rateEntry.rate;
if (!anyRateInvalid && currencyKeys[i] != "sUSD") {
if (!anyRateInvalid && currencyKeys[i] != sUSD) {
anyRateInvalid = flagList[i] || _rateIsStaleWithTime(_rateStalePeriod, rateEntry.time);
}
}
Expand Down Expand Up @@ -372,52 +322,6 @@ contract ExchangeRates is Owned, MixinSystemSettings, IExchangeRates {
}
}

function _setRate(
bytes32 currencyKey,
uint256 rate,
uint256 time
) internal {
// Note: this will effectively start the rounds at 1, which matches Chainlink's Agggregators
currentRoundForRate[currencyKey]++;

_rates[currencyKey][currentRoundForRate[currencyKey]] = RateAndUpdatedTime({
rate: uint216(rate),
time: uint40(time)
});
}

function internalUpdateRates(
bytes32[] memory currencyKeys,
uint[] memory newRates,
uint timeSent
) internal returns (bool) {
require(currencyKeys.length == newRates.length, "Currency key array length must match rates array length.");
require(timeSent < (now + ORACLE_FUTURE_LIMIT), "Time is too far into the future");

// Loop through each key and perform update.
for (uint i = 0; i < currencyKeys.length; i++) {
bytes32 currencyKey = currencyKeys[i];

// Should not set any rate to zero ever, as no asset will ever be
// truely worthless and still valid. In this scenario, we should
// delete the rate and remove it from the system.
require(newRates[i] != 0, "Zero is not a valid rate, please call deleteRate instead.");
require(currencyKey != "sUSD", "Rate of sUSD cannot be updated, it's always UNIT.");

// We should only update the rate if it's at least the same age as the last rate we've got.
if (timeSent < _getUpdatedTime(currencyKey)) {
continue;
}

// Ok, go ahead with the update.
_setRate(currencyKey, newRates[i], timeSent);
}

emit RatesUpdated(currencyKeys, newRates);

return true;
}

function removeFromArray(bytes32 entry, bytes32[] storage array) internal returns (bool) {
for (uint i = 0; i < array.length; i++) {
if (array[i] == entry) {
Expand Down Expand Up @@ -447,60 +351,64 @@ contract ExchangeRates is Owned, MixinSystemSettings, IExchangeRates {
}

function _getRateAndUpdatedTime(bytes32 currencyKey) internal view returns (RateAndUpdatedTime memory) {
AggregatorV2V3Interface aggregator = aggregators[currencyKey];

if (aggregator != AggregatorV2V3Interface(0)) {
// this view from the aggregator is the most gas efficient but it can throw when there's no data,
// so let's call it low-level to suppress any reverts
bytes memory payload = abi.encodeWithSignature("latestRoundData()");
// solhint-disable avoid-low-level-calls
(bool success, bytes memory returnData) = address(aggregator).staticcall(payload);

if (success) {
(, int256 answer, , uint256 updatedAt, ) =
abi.decode(returnData, (uint80, int256, uint256, uint256, uint80));
return
RateAndUpdatedTime({
rate: uint216(_formatAggregatorAnswer(currencyKey, answer)),
time: uint40(updatedAt)
});
}
// sUSD rate is 1.0
if (currencyKey == sUSD) {
return RateAndUpdatedTime({rate: uint216(SafeDecimalMath.unit()), time: 0});
} else {
uint roundId = currentRoundForRate[currencyKey];
RateAndUpdatedTime memory entry = _rates[currencyKey][roundId];

return RateAndUpdatedTime({rate: uint216(entry.rate), time: entry.time});
AggregatorV2V3Interface aggregator = aggregators[currencyKey];
if (aggregator != AggregatorV2V3Interface(0)) {
// this view from the aggregator is the most gas efficient but it can throw when there's no data,
// so let's call it low-level to suppress any reverts
bytes memory payload = abi.encodeWithSignature("latestRoundData()");
// solhint-disable avoid-low-level-calls
// slither-disable-next-line low-level-calls
(bool success, bytes memory returnData) = address(aggregator).staticcall(payload);

if (success) {
(, int256 answer, , uint256 updatedAt, ) =
abi.decode(returnData, (uint80, int256, uint256, uint256, uint80));
return
RateAndUpdatedTime({
rate: uint216(_formatAggregatorAnswer(currencyKey, answer)),
time: uint40(updatedAt)
});
} // else return defaults, to avoid reverting in views
} // else return defaults, to avoid reverting in views
}
}

function _getCurrentRoundId(bytes32 currencyKey) internal view returns (uint) {
if (currencyKey == sUSD) {
return 0; // no roundIds for sUSD
}
AggregatorV2V3Interface aggregator = aggregators[currencyKey];

if (aggregator != AggregatorV2V3Interface(0)) {
return aggregator.latestRound();
} else {
return currentRoundForRate[currencyKey];
}
} // else return defaults, to avoid reverting in views
}

function _getRateAndTimestampAtRound(bytes32 currencyKey, uint roundId) internal view returns (uint rate, uint time) {
AggregatorV2V3Interface aggregator = aggregators[currencyKey];

if (aggregator != AggregatorV2V3Interface(0)) {
// this view from the aggregator is the most gas efficient but it can throw when there's no data,
// so let's call it low-level to suppress any reverts
bytes memory payload = abi.encodeWithSignature("getRoundData(uint80)", roundId);
// solhint-disable avoid-low-level-calls
(bool success, bytes memory returnData) = address(aggregator).staticcall(payload);

if (success) {
(, int256 answer, , uint256 updatedAt, ) =
abi.decode(returnData, (uint80, int256, uint256, uint256, uint80));
return (_formatAggregatorAnswer(currencyKey, answer), updatedAt);
}
// short circuit sUSD
if (currencyKey == sUSD) {
// sUSD has no rounds, and 0 time is preferrable for "volatility" heuristics
// which are used in atomic swaps and fee reclamation
return (SafeDecimalMath.unit(), 0);
} else {
RateAndUpdatedTime memory update = _rates[currencyKey][roundId];
return (update.rate, update.time);
AggregatorV2V3Interface aggregator = aggregators[currencyKey];

if (aggregator != AggregatorV2V3Interface(0)) {
// this view from the aggregator is the most gas efficient but it can throw when there's no data,
// so let's call it low-level to suppress any reverts
bytes memory payload = abi.encodeWithSignature("getRoundData(uint80)", roundId);
// solhint-disable avoid-low-level-calls
(bool success, bytes memory returnData) = address(aggregator).staticcall(payload);

if (success) {
(, int256 answer, , uint256 updatedAt, ) =
abi.decode(returnData, (uint80, int256, uint256, uint256, uint80));
return (_formatAggregatorAnswer(currencyKey, answer), updatedAt);
} // else return defaults, to avoid reverting in views
} // else return defaults, to avoid reverting in views
}
}

Expand Down Expand Up @@ -542,7 +450,7 @@ contract ExchangeRates is Owned, MixinSystemSettings, IExchangeRates {

function _rateIsStale(bytes32 currencyKey, uint _rateStalePeriod) internal view returns (bool) {
// sUSD is a special case and is never stale (check before an SLOAD of getRateAndUpdatedTime)
if (currencyKey == "sUSD") return false;
if (currencyKey == sUSD) return false;

return _rateIsStaleWithTime(_rateStalePeriod, _getUpdatedTime(currencyKey));
}
Expand All @@ -553,7 +461,7 @@ contract ExchangeRates is Owned, MixinSystemSettings, IExchangeRates {

function _rateIsFlagged(bytes32 currencyKey, FlagsInterface flags) internal view returns (bool) {
// sUSD is a special case and is never invalid
if (currencyKey == "sUSD") return false;
if (currencyKey == sUSD) return false;
address aggregator = address(aggregators[currencyKey]);
// when no aggregator or when the flags haven't been setup
if (aggregator == address(0) || flags == FlagsInterface(0)) {
Expand All @@ -563,25 +471,12 @@ contract ExchangeRates is Owned, MixinSystemSettings, IExchangeRates {
}

function _notImplemented() internal pure {
// slither-disable-next-line dead-code
revert("Cannot be run on this layer");
}

/* ========== MODIFIERS ========== */

modifier onlyOracle {
_onlyOracle();
_;
}

function _onlyOracle() internal view {
require(msg.sender == oracle, "Only the oracle can perform this action");
}

/* ========== EVENTS ========== */

event OracleUpdated(address newOracle);
event RatesUpdated(bytes32[] currencyKeys, uint[] newRates);
event RateDeleted(bytes32 currencyKey);
event AggregatorAdded(bytes32 currencyKey, address aggregator);
event AggregatorRemoved(bytes32 currencyKey, address aggregator);
}
8 changes: 1 addition & 7 deletions contracts/ExchangeRatesWithDexPricing.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,7 @@ contract ExchangeRatesWithDexPricing is ExchangeRates {

bytes32 internal constant SETTING_DEX_PRICE_AGGREGATOR = "dexPriceAggregator";

constructor(
address _owner,
address _oracle,
address _resolver,
bytes32[] memory _currencyKeys,
uint[] memory _newRates
) public ExchangeRates(_owner, _oracle, _resolver, _currencyKeys, _newRates) {}
constructor(address _owner, address _resolver) public ExchangeRates(_owner, _resolver) {}

/* ========== SETTERS ========== */

Expand Down
5 changes: 2 additions & 3 deletions contracts/FeePool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -242,9 +242,8 @@ contract FeePool is Owned, Proxyable, LimitedSetup, MixinSystemSettings, IFeePoo
/**
* @notice The RewardsDistribution contract informs us how many SNX rewards are sent to RewardEscrow to be claimed.
*/
function setRewardsToDistribute(uint amount) external {
address rewardsAuthority = address(rewardsDistribution());
require(messageSender == rewardsAuthority || msg.sender == rewardsAuthority, "Caller is not rewardsAuthority");
function setRewardsToDistribute(uint amount) external optionalProxy {
require(messageSender == address(rewardsDistribution()), "RewardsDistribution only");
// Add the amount of SNX rewards to distribute on top of any rolling unclaimed amount
_recentFeePeriodsStorage(0).rewardsToDistribute = _recentFeePeriodsStorage(0).rewardsToDistribute.add(amount);
}
Expand Down
19 changes: 1 addition & 18 deletions contracts/MixinSystemSettings.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import "./interfaces/IFlexibleStorage.sol";

// https://docs.synthetix.io/contracts/source/contracts/mixinsystemsettings
contract MixinSystemSettings is MixinResolver {
// must match the one defined SystemSettingsLib, defined in both places due to sol v0.5 limitations
bytes32 internal constant SETTING_CONTRACT_NAME = "SystemSettings";

bytes32 internal constant SETTING_WAITING_PERIOD_SECS = "waitingPeriodSecs";
Expand Down Expand Up @@ -34,8 +35,6 @@ contract MixinSystemSettings is MixinResolver {
bytes32 internal constant SETTING_WRAPPER_MAX_TOKEN_AMOUNT = "wrapperMaxTokens";
bytes32 internal constant SETTING_WRAPPER_MINT_FEE_RATE = "wrapperMintFeeRate";
bytes32 internal constant SETTING_WRAPPER_BURN_FEE_RATE = "wrapperBurnFeeRate";
bytes32 internal constant SETTING_MIN_CRATIO = "minCratio";
bytes32 internal constant SETTING_NEW_COLLATERAL_MANAGER = "newCollateralManager";
bytes32 internal constant SETTING_INTERACTION_DELAY = "interactionDelay";
bytes32 internal constant SETTING_COLLAPSE_FEE_RATE = "collapseFeeRate";
bytes32 internal constant SETTING_ATOMIC_MAX_VOLUME_PER_BLOCK = "atomicMaxVolumePerBlock";
Expand Down Expand Up @@ -180,22 +179,6 @@ contract MixinSystemSettings is MixinResolver {
);
}

function getMinCratio(address collateral) internal view returns (uint) {
return
flexibleStorage().getUIntValue(
SETTING_CONTRACT_NAME,
keccak256(abi.encodePacked(SETTING_MIN_CRATIO, collateral))
);
}

function getNewCollateralManager(address collateral) internal view returns (address) {
return
flexibleStorage().getAddressValue(
SETTING_CONTRACT_NAME,
keccak256(abi.encodePacked(SETTING_NEW_COLLATERAL_MANAGER, collateral))
);
}

function getInteractionDelay(address collateral) internal view returns (uint) {
return
flexibleStorage().getUIntValue(
Expand Down
Loading