Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SIP-165 Debt Orcale #1694

Merged
merged 68 commits into from
Mar 10, 2022
Merged
Show file tree
Hide file tree
Changes from 53 commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
6522f12
add synthetixdebtshare
dbeal-eth Nov 11, 2021
510823f
lots of implementation
dbeal-eth Nov 13, 2021
8ed2fff
tests work
dbeal-eth Nov 15, 2021
834ec9d
add synthetix debt share tests
dbeal-eth Nov 16, 2021
bff2fb6
fix unit test failures
dbeal-eth Nov 16, 2021
9043de1
remove comments and np
dbeal-eth Nov 16, 2021
ae5c111
fix lints
dbeal-eth Nov 16, 2021
d532380
initial period is now 1
dbeal-eth Nov 16, 2021
3b53405
add a few tests for coverage
dbeal-eth Nov 16, 2021
5076ab1
use `hardhat-interact` instead of builtin
dbeal-eth Nov 28, 2021
e351a65
fix module resolution
dbeal-eth Nov 29, 2021
f1280ab
fix package lock again
dbeal-eth Nov 29, 2021
fea5c91
fix provider url issues and network id for kvoan
dbeal-eth Nov 29, 2021
10ea7bd
add synthetixdebtshare
dbeal-eth Nov 11, 2021
7cf745b
lots of implementation
dbeal-eth Nov 13, 2021
113ada3
tests work
dbeal-eth Nov 15, 2021
f267e6e
add synthetix debt share tests
dbeal-eth Nov 16, 2021
667fcc8
fix unit test failures
dbeal-eth Nov 16, 2021
2265bf6
remove comments and np
dbeal-eth Nov 16, 2021
fb1530a
fix lints
dbeal-eth Nov 16, 2021
bdf3e69
initial period is now 1
dbeal-eth Nov 16, 2021
87cd0da
add a few tests for coverage
dbeal-eth Nov 16, 2021
8171cb3
now its not necessary to work around load-contracts functionality wit…
dbeal-eth Dec 2, 2021
1deac59
Merge branch 'sip-185-debt-shares' of https://github.com/Synthetixio/…
dbeal-eth Dec 12, 2021
e961d51
oops
dbeal-eth Dec 12, 2021
09c501e
add migration script start
dbeal-eth Dec 12, 2021
6d380b8
add oracle pseudo
dbeal-eth Dec 8, 2021
00cb270
add oracle code
dbeal-eth Jan 3, 2022
f2cc452
test fixes
dbeal-eth Jan 5, 2022
dc6aefa
add missing
dbeal-eth Jan 5, 2022
3f7ffe7
fixes
dbeal-eth Jan 5, 2022
122e37e
unit tests fixes
dbeal-eth Jan 6, 2022
0107563
fixes for tests
dbeal-eth Jan 7, 2022
a2c42d3
more unit tests for bridge functions
dbeal-eth Jan 7, 2022
1ea3e68
fee pool unit tests
dbeal-eth Jan 9, 2022
eb4fad0
Merge remote-tracking branch 'origin/develop' into sip-165-oracles
dbeal-eth Feb 8, 2022
5afa691
small fixes after looking at the diff
dbeal-eth Feb 8, 2022
68269c4
fix tests
dbeal-eth Feb 8, 2022
e287b37
minor fixes for tests
dbeal-eth Feb 8, 2022
cb5a604
move staking test to dual since optimism realy doesn't have propertie…
dbeal-eth Feb 8, 2022
6b3f157
move staking test to dual since optimism realy doesn't have propertie…
dbeal-eth Feb 8, 2022
a5f47ba
Merge branch 'sip-165-oracles' of https://github.com/Synthetixio/synt…
dbeal-eth Feb 8, 2022
ed6f858
Merge remote-tracking branch 'origin/develop' into sip-165-oracles
dbeal-eth Feb 9, 2022
4d5e5fe
test fixes
dbeal-eth Feb 9, 2022
b5b81ef
fix lints
dbeal-eth Feb 9, 2022
79c33f7
updates for using 2 oracles
dbeal-eth Feb 11, 2022
c3bed7d
fix quirk with showsize option on compile
dbeal-eth Feb 11, 2022
45d94e4
file names must correspond with exported contracts
dbeal-eth Feb 11, 2022
5229444
remove hardhat console import
dbeal-eth Feb 11, 2022
1676f1f
fix integration
dbeal-eth Feb 11, 2022
4180c14
add a couple more units
dbeal-eth Feb 12, 2022
9bb44cf
lints
dbeal-eth Feb 12, 2022
05005cf
fix lint 2
dbeal-eth Feb 12, 2022
86b41d8
fix wrong title
dbeal-eth Mar 3, 2022
db35a57
remove empty test case
dbeal-eth Mar 3, 2022
657443e
Merge remote-tracking branch 'origin/develop' into sip-165-oracles
dbeal-eth Mar 3, 2022
e9450a5
add emitted event
dbeal-eth Mar 3, 2022
f74803d
update releases json
dbeal-eth Mar 3, 2022
20bb800
Merge remote-tracking branch 'origin/develop' into sip-165-oracles
dbeal-eth Mar 9, 2022
a0eba7e
remove debt cache tests which are no long relevant to functionality o…
dbeal-eth Mar 9, 2022
e45b6d9
add minor tests for DebtCache, and add a circuit breaker in issuer
dbeal-eth Mar 9, 2022
edcce22
improve coverage
dbeal-eth Mar 9, 2022
c375a93
fixes from arthur comments
dbeal-eth Mar 10, 2022
ad3d288
Merge branch 'develop' into sip-165-oracles
dbeal-eth Mar 10, 2022
e64d771
turn off heartbeat
dbeal-eth Mar 10, 2022
27293b5
Merge branch 'sip-165-oracles' of https://github.com/Synthetixio/synt…
dbeal-eth Mar 10, 2022
f81aae7
add needed `setLastDebtRatio()` function
dbeal-eth Mar 10, 2022
770b3c1
fix lint
dbeal-eth Mar 10, 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
69 changes: 69 additions & 0 deletions contracts/BaseSingleNetworkAggregator.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
pragma solidity ^0.5.16;

//import "@chainlink/contracts-0.0.10/src/v0.5/interfaces/AggregatorV2V3Interface.sol";

import "./AddressResolver.sol";
import "./interfaces/IDebtCache.sol";
import "./interfaces/ISynthetixDebtShare.sol";
import "./interfaces/AggregatorV2V3Interface.sol";

import "./SafeDecimalMath.sol";

// aggregator which reports the data from the system itself
// useful for testing
contract BaseSingleNetworkAggregator is Owned, AggregatorV2V3Interface {
using SafeDecimalMath for uint;

AddressResolver public resolver;

uint public overrideTimestamp;

constructor(AddressResolver _resolver) public Owned(msg.sender) {
resolver = _resolver;
}

function setOverrideTimestamp(uint timestamp) public onlyOwner {
overrideTimestamp = timestamp;
dbeal-eth marked this conversation as resolved.
Show resolved Hide resolved
}

function latestRoundData()
external
view
returns (
uint80,
int256,
uint256,
uint256,
uint80
)
{
return getRoundData(uint80(latestRound()));
}

function latestRound() public view returns (uint256) {
return 1;
}

function decimals() external view returns (uint8) {
return 0;
}

function getAnswer(uint256 _roundId) external view returns (int256 answer) {
(,answer,,,) = getRoundData(uint80(_roundId));
}

function getTimestamp(uint256 _roundId) external view returns (uint256 timestamp) {
(,,timestamp,,) = getRoundData(uint80(_roundId));
}

function getRoundData(uint80)
public
view
returns (
uint80,
int256,
uint256,
uint256,
uint80
);
}
9 changes: 8 additions & 1 deletion contracts/BaseSynthetixBridge.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@ import "./interfaces/IBaseSynthetixBridge.sol";
// Internal references
import "./interfaces/ISynthetix.sol";
import "./interfaces/IRewardEscrowV2.sol";
import "./interfaces/IFeePool.sol";
import "@eth-optimism/contracts/iOVM/bridge/messaging/iAbs_BaseCrossDomainMessenger.sol";

contract BaseSynthetixBridge is Owned, MixinSystemSettings, IBaseSynthetixBridge {
/* ========== ADDRESS RESOLVER CONFIGURATION ========== */
bytes32 private constant CONTRACT_EXT_MESSENGER = "ext:Messenger";
bytes32 internal constant CONTRACT_SYNTHETIX = "Synthetix";
bytes32 private constant CONTRACT_REWARDESCROW = "RewardEscrowV2";
bytes32 private constant CONTRACT_FEEPOOL = "FeePool";

bool public initiationActive;

Expand All @@ -40,6 +42,10 @@ contract BaseSynthetixBridge is Owned, MixinSystemSettings, IBaseSynthetixBridge
return IRewardEscrowV2(requireAndGetAddress(CONTRACT_REWARDESCROW));
}

function feePool() internal view returns (IFeePool) {
return IFeePool(requireAndGetAddress(CONTRACT_FEEPOOL));
}

function initiatingActive() internal view {
require(initiationActive, "Initiation deactivated");
}
Expand All @@ -48,10 +54,11 @@ contract BaseSynthetixBridge is Owned, MixinSystemSettings, IBaseSynthetixBridge

function resolverAddressesRequired() public view returns (bytes32[] memory addresses) {
bytes32[] memory existingAddresses = MixinSystemSettings.resolverAddressesRequired();
bytes32[] memory newAddresses = new bytes32[](3);
bytes32[] memory newAddresses = new bytes32[](4);
newAddresses[0] = CONTRACT_EXT_MESSENGER;
newAddresses[1] = CONTRACT_SYNTHETIX;
newAddresses[2] = CONTRACT_REWARDESCROW;
newAddresses[3] = CONTRACT_FEEPOOL;
addresses = combineArrays(existingAddresses, newAddresses);
}

Expand Down
78 changes: 76 additions & 2 deletions contracts/FeePool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import "./interfaces/IFeePool.sol";
// Libraries
import "./SafeDecimalMath.sol";

import "@chainlink/contracts-0.0.10/src/v0.5/interfaces/AggregatorV2V3Interface.sol";

// Internal references
import "./interfaces/IERC20.sol";
import "./interfaces/ISynth.sol";
Expand All @@ -26,6 +28,7 @@ import "./interfaces/IRewardsDistribution.sol";
import "./interfaces/ICollateralManager.sol";
import "./interfaces/IEtherWrapper.sol";
import "./interfaces/IWrapperFactory.sol";
import "./interfaces/ISynthetixBridgeToOptimism.sol";

// https://docs.synthetix.io/contracts/source/contracts/feepool
contract FeePool is Owned, Proxyable, LimitedSetup, MixinSystemSettings, IFeePool {
Expand All @@ -44,6 +47,10 @@ contract FeePool is Owned, Proxyable, LimitedSetup, MixinSystemSettings, IFeePoo
struct FeePeriod {
uint64 feePeriodId;
uint64 startTime;

uint allNetworksSnxBackedDebt;
uint allNetworksDebtSharesSupply;

uint feesToDistribute;
uint feesClaimed;
uint rewardsToDistribute;
Expand Down Expand Up @@ -74,6 +81,12 @@ contract FeePool is Owned, Proxyable, LimitedSetup, MixinSystemSettings, IFeePoo
bytes32 private constant CONTRACT_ETHER_WRAPPER = "EtherWrapper";
bytes32 private constant CONTRACT_WRAPPER_FACTORY = "WrapperFactory";

bytes32 private constant CONTRACT_SYNTHETIX_BRIDGE_TO_OPTIMISM = "SynthetixBridgeToOptimism";
bytes32 private constant CONTRACT_SYNTHETIX_BRIDGE_TO_BASE = "SynthetixBridgeToBase";

bytes32 private constant CONTRACT_EXT_AGGREGATOR_ISSUED_SYNTHS = "ext:AggregatorIssuedSynths";
bytes32 private constant CONTRACT_EXT_AGGREGATOR_DEBT_RATIO = "ext:AggregatorDebtRatio";

/* ========== ETERNAL STORAGE CONSTANTS ========== */

bytes32 private constant LAST_FEE_WITHDRAWAL = "last_fee_withdrawal";
Expand All @@ -91,7 +104,7 @@ contract FeePool is Owned, Proxyable, LimitedSetup, MixinSystemSettings, IFeePoo
/* ========== VIEWS ========== */
function resolverAddressesRequired() public view returns (bytes32[] memory addresses) {
bytes32[] memory existingAddresses = MixinSystemSettings.resolverAddressesRequired();
bytes32[] memory newAddresses = new bytes32[](11);
bytes32[] memory newAddresses = new bytes32[](13);
newAddresses[0] = CONTRACT_SYSTEMSTATUS;
newAddresses[1] = CONTRACT_SYNTHETIXDEBTSHARE;
newAddresses[2] = CONTRACT_FEEPOOLETERNALSTORAGE;
Expand All @@ -103,6 +116,8 @@ contract FeePool is Owned, Proxyable, LimitedSetup, MixinSystemSettings, IFeePoo
newAddresses[8] = CONTRACT_COLLATERALMANAGER;
newAddresses[9] = CONTRACT_WRAPPER_FACTORY;
newAddresses[10] = CONTRACT_ETHER_WRAPPER;
newAddresses[11] = CONTRACT_EXT_AGGREGATOR_ISSUED_SYNTHS;
newAddresses[12] = CONTRACT_EXT_AGGREGATOR_DEBT_RATIO;
addresses = combineArrays(existingAddresses, newAddresses);
}

Expand Down Expand Up @@ -162,6 +177,26 @@ contract FeePool is Owned, Proxyable, LimitedSetup, MixinSystemSettings, IFeePoo
return getTargetThreshold();
}

function allNetworksSnxBackedDebt() internal view returns (uint256 debt, uint256 updatedAt) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[np] these internal functions should have an underscore prefix for consistency

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you know why internal functions have underscores btw? I still have yet to figure this out...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's just a naming convention for functions and variables that are supposed to be internal or private to the contract.
Sometimes it's also used to avoid shadowing variables.

Technically it isn't needed (unless you got a linter like solint), but it does improve readability when scanning through contracts imo.

Like without reading all the keywords I can quickly say: "OK, this function is internal so it's not called by anyone else."

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would it be useful to have this as public or is this info available through some other view?

(, int256 rawData, , uint timestamp, ) = AggregatorV2V3Interface(requireAndGetAddress(CONTRACT_EXT_AGGREGATOR_ISSUED_SYNTHS))
.latestRoundData();

debt = uint(rawData);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same question about oracle output validation

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

figured out how to do it, will be impl before merge today

updatedAt = timestamp;
}

function allNetworksDebtSharesSupply() internal view returns (uint256 sharesSupply, uint256 updatedAt) {
dbeal-eth marked this conversation as resolved.
Show resolved Hide resolved
(, int256 rawIssuedSynths, , uint issuedSynthsUpdatedAt, ) = AggregatorV2V3Interface(requireAndGetAddress(CONTRACT_EXT_AGGREGATOR_ISSUED_SYNTHS))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should there be some validation on the values returned from these oracles? what if the oracles experience some issues / are fed obviously bad data?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will be impl

.latestRoundData();

(, int256 rawRatio, , uint ratioUpdatedAt, ) = AggregatorV2V3Interface(requireAndGetAddress(CONTRACT_EXT_AGGREGATOR_DEBT_RATIO))
.latestRoundData();

uint debt = uint(rawIssuedSynths);
sharesSupply = rawRatio == 0 ? 0 : debt.divideDecimalRoundPrecise(uint(rawRatio));
updatedAt = issuedSynthsUpdatedAt < ratioUpdatedAt ? issuedSynthsUpdatedAt : ratioUpdatedAt;
}

function recentFeePeriods(uint index)
external
view
Expand Down Expand Up @@ -216,9 +251,32 @@ contract FeePool is Owned, Proxyable, LimitedSetup, MixinSystemSettings, IFeePoo
require(getFeePeriodDuration() > 0, "Fee Period Duration not set");
require(_recentFeePeriodsStorage(0).startTime <= (now - getFeePeriodDuration()), "Too early to close fee period");

// get current oracle values
(uint snxBackedDebt, ) = allNetworksSnxBackedDebt();
(uint debtSharesSupply, ) = allNetworksDebtSharesSupply();

// close on this chain
_closeSecondary(snxBackedDebt, debtSharesSupply);

// inform other chain of the chosen values
ISynthetixBridgeToOptimism(resolver.requireAndGetAddress(CONTRACT_SYNTHETIX_BRIDGE_TO_OPTIMISM, "Missing contract: SynthetixBridgeToOptimism")).closeFeePeriod(snxBackedDebt, debtSharesSupply);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: refactor address to intermediate variable for readability?

}

function closeSecondary(uint allNetworksSnxBackedDebt, uint allNetworksDebtSharesSupply) external onlyRelayer {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit(s):

  • closeSecondary name is not very informative, probably can be better, at this point in reading (having not read its implementation yet) I don't even have a guess what it's about :)
  • slither complains about shadowing and I'm a bit bothered by it as well

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the onlyRelayer is only used once I think, and it looks like an an important and comlplex part of access control:

  • would it be better to explicitely do the checks here instead of a single use modifier?
  • could you please add some documentation about the invocations pattern that is expected to be supported, and not?

_closeSecondary(allNetworksSnxBackedDebt, allNetworksDebtSharesSupply);
}

/**
* @notice Close the current fee period and start a new one.
*/
function _closeSecondary(uint allNetworksSnxBackedDebt, uint allNetworksDebtSharesSupply) internal {
etherWrapper().distributeFees();
wrapperFactory().distributeFees();

// before closing the current fee period, set the recorded snxBackedDebt and debtSharesSupply
_recentFeePeriodsStorage(0).allNetworksDebtSharesSupply = allNetworksDebtSharesSupply;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this be 0 or FEE_PERIOD_LENGTH - 2 like below? I know they evaluate to the same thing with current parameters, but what the correct number should be if the FEE_PERIOD_LENGTH is different, since the one that actually is being closed is the FEE_PERIOD_LENGTH - 2 one?

If they should evaluate to the same number, maybe better to just reuse the storage periodClosing from below to update the values.

_recentFeePeriodsStorage(0).allNetworksSnxBackedDebt = allNetworksSnxBackedDebt;

// Note: when FEE_PERIOD_LENGTH = 2, periodClosing is the current period & periodToRollover is the last open claimable period
FeePeriod storage periodClosing = _recentFeePeriodsStorage(FEE_PERIOD_LENGTH - 2);
FeePeriod storage periodToRollover = _recentFeePeriodsStorage(FEE_PERIOD_LENGTH - 1);
Expand Down Expand Up @@ -340,7 +398,9 @@ contract FeePool is Owned, Proxyable, LimitedSetup, MixinSystemSettings, IFeePoo
feesToDistribute: feesToDistribute,
feesClaimed: feesClaimed,
rewardsToDistribute: rewardsToDistribute,
rewardsClaimed: rewardsClaimed
rewardsClaimed: rewardsClaimed,
allNetworksSnxBackedDebt: 0,
allNetworksDebtSharesSupply: 0
});

// make sure recording is aware of the actual period id
Expand Down Expand Up @@ -645,6 +705,20 @@ contract FeePool is Owned, Proxyable, LimitedSetup, MixinSystemSettings, IFeePoo
_;
}

modifier onlyIssuer {
bool isIssuer = msg.sender == address(issuer());
require(isIssuer, "Issuer only");
_;
}

modifier onlyRelayer {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: onlySelfOrBridge

require(
msg.sender == address(this) ||
Copy link
Contributor

@artdgn artdgn Mar 2, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this check is not being hit as in this contract the _closeSecondary is directly called in closeCurrentFeePeriod so I guess this check can be removed

msg.sender == resolver.getAddress(CONTRACT_SYNTHETIX_BRIDGE_TO_BASE)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: might be a good place to add some comments about the invocation path

, "Only valid relayer can call");
_;
}

modifier notFeeAddress(address account) {
require(account != FEE_ADDRESS, "Fee address not allowed");
_;
Expand Down
41 changes: 33 additions & 8 deletions contracts/Issuer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,12 @@ import "./interfaces/IExchangeRates.sol";
import "./interfaces/IHasBalance.sol";
import "./interfaces/IERC20.sol";
import "./interfaces/ILiquidations.sol";
import "./interfaces/ICollateralManager.sol";
import "./interfaces/IRewardEscrowV2.sol";
import "./interfaces/ISynthRedeemer.sol";
import "./Proxyable.sol";

import "@chainlink/contracts-0.0.10/src/v0.5/interfaces/AggregatorV2V3Interface.sol";

interface IProxy {
function target() external view returns (address);
}
Expand Down Expand Up @@ -88,12 +89,15 @@ contract Issuer is Owned, MixinSystemSettings, IIssuer {
bytes32 private constant CONTRACT_DEBTCACHE = "DebtCache";
bytes32 private constant CONTRACT_SYNTHREDEEMER = "SynthRedeemer";

bytes32 private constant CONTRACT_EXT_AGGREGATOR_ISSUED_SYNTHS = "ext:AggregatorIssuedSynths";
bytes32 private constant CONTRACT_EXT_AGGREGATOR_DEBT_RATIO = "ext:AggregatorDebtRatio";

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[](11);
bytes32[] memory newAddresses = new bytes32[](13);
newAddresses[0] = CONTRACT_SYNTHETIX;
newAddresses[1] = CONTRACT_EXCHANGER;
newAddresses[2] = CONTRACT_EXRATES;
Expand All @@ -105,6 +109,8 @@ contract Issuer is Owned, MixinSystemSettings, IIssuer {
newAddresses[8] = CONTRACT_LIQUIDATIONS;
newAddresses[9] = CONTRACT_DEBTCACHE;
newAddresses[10] = CONTRACT_SYNTHREDEEMER;
newAddresses[11] = CONTRACT_EXT_AGGREGATOR_ISSUED_SYNTHS;
newAddresses[12] = CONTRACT_EXT_AGGREGATOR_DEBT_RATIO;
return combineArrays(existingAddresses, newAddresses);
}

Expand Down Expand Up @@ -152,6 +158,20 @@ contract Issuer is Owned, MixinSystemSettings, IIssuer {
return ISynthRedeemer(requireAndGetAddress(CONTRACT_SYNTHREDEEMER));
}

function allNetworksDebtInfo() public view returns (uint256 debt, uint256 sharesSupply, bool isStale) {

(, int256 rawIssuedSynths, , uint issuedSynthsUpdatedAt, ) = AggregatorV2V3Interface(requireAndGetAddress(CONTRACT_EXT_AGGREGATOR_ISSUED_SYNTHS))
.latestRoundData();

(, int256 rawRatio, , uint ratioUpdatedAt, ) = AggregatorV2V3Interface(requireAndGetAddress(CONTRACT_EXT_AGGREGATOR_DEBT_RATIO))
.latestRoundData();

debt = uint(rawIssuedSynths);
sharesSupply = rawRatio == 0 ? 0 : debt.divideDecimalRoundPrecise(uint(rawRatio));
isStale = block.timestamp - getRateStalePeriod() > issuedSynthsUpdatedAt ||
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should there be some isInvalid value (or something that ORs both) that does some validation on the oracle values (some expected minimal size, or some expected ratio deviation from previous known values)?

block.timestamp - getRateStalePeriod() > ratioUpdatedAt;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this check that issuedSynthsUpdatedAt and ratioUpdatedAt are not in future relative to block.timestamp? in case something bad happens to optimism injected timestamps?

}

function issuanceRatio() external view returns (uint) {
return getIssuanceRatio();
}
Expand Down Expand Up @@ -216,14 +236,19 @@ contract Issuer is Owned, MixinSystemSettings, IIssuer {
{

// What's the total value of the system excluding ETH backed synths in their requested currency?
(totalSystemValue, anyRateIsInvalid) = _totalIssuedSynths(currencyKey, true);
(uint snxBackedAmount, uint debtSharesAmount, bool debtInfoStale) = allNetworksDebtInfo();

// If it's zero, they haven't issued, and they have no debt.
// Note: it's more gas intensive to put this check here rather than before _totalIssuedSynths
// if they have 0 SNX, but it's a necessary trade-off
if (debtShareBalance == 0) return (0, totalSystemValue, anyRateIsInvalid);
if (debtShareBalance == 0) {
return (0, snxBackedAmount, debtInfoStale);
}

// existing functionality requires for us to convert into the exchange rate specified by `currencyKey`
(uint currencyRate, bool currencyRateInvalid) = exchangeRates().rateAndInvalid(currencyKey);

debtBalance = _debtSharesToIssuedSynth(debtShareBalance, snxBackedAmount, debtSharesAmount).divideDecimalRound(currencyRate);
totalSystemValue = snxBackedAmount;

debtBalance = _debtSharesToIssuedSynth(debtShareBalance, totalSystemValue, synthetixDebtShare().totalSupply());
anyRateIsInvalid = currencyRateInvalid || debtInfoStale;
}

function _canBurnSynths(address account) internal view returns (bool) {
Expand Down
5 changes: 4 additions & 1 deletion contracts/MixinSystemSettings.sol
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ contract MixinSystemSettings is MixinResolver {
bytes32 internal constant SETTING_CROSS_DOMAIN_ESCROW_GAS_LIMIT = "crossDomainEscrowGasLimit";
bytes32 internal constant SETTING_CROSS_DOMAIN_REWARD_GAS_LIMIT = "crossDomainRewardGasLimit";
bytes32 internal constant SETTING_CROSS_DOMAIN_WITHDRAWAL_GAS_LIMIT = "crossDomainWithdrawalGasLimit";
bytes32 internal constant SETTING_CROSS_DOMAIN_FEE_PERIOD_CLOSE_GAS_LIMIT = "crossDomainCloseGasLimit";
bytes32 internal constant SETTING_CROSS_DOMAIN_RELAY_GAS_LIMIT = "crossDomainRelayGasLimit";
bytes32 internal constant SETTING_ETHER_WRAPPER_MAX_ETH = "etherWrapperMaxETH";
bytes32 internal constant SETTING_ETHER_WRAPPER_MINT_FEE_RATE = "etherWrapperMintFeeRate";
Expand All @@ -53,7 +54,7 @@ contract MixinSystemSettings is MixinResolver {

bytes32 internal constant CONTRACT_FLEXIBLESTORAGE = "FlexibleStorage";

enum CrossDomainMessageGasLimits {Deposit, Escrow, Reward, Withdrawal, Relay}
enum CrossDomainMessageGasLimits {Deposit, Escrow, Reward, Withdrawal, CloseFeePeriod, Relay}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: probably safer to append values to enums, although I don't think it matters here


struct DynamicFeeConfig {
uint threshold;
Expand Down Expand Up @@ -84,6 +85,8 @@ contract MixinSystemSettings is MixinResolver {
return SETTING_CROSS_DOMAIN_WITHDRAWAL_GAS_LIMIT;
} else if (gasLimitType == CrossDomainMessageGasLimits.Relay) {
return SETTING_CROSS_DOMAIN_RELAY_GAS_LIMIT;
} else if (gasLimitType == CrossDomainMessageGasLimits.CloseFeePeriod) {
return SETTING_CROSS_DOMAIN_FEE_PERIOD_CLOSE_GAS_LIMIT;
} else {
revert("Unknown gas limit type");
}
Expand Down
Loading