Skip to content

Commit

Permalink
SIP-165 Debt Orcale (#1694)
Browse files Browse the repository at this point in the history
Impl for 165

From a code perspective, this entails:

    FeePool and Issuer are changed to use the chainlink oracle instead of DebtCache and SynthetixDebtShare directly
    FeePool now closes its fee period across networks (using optimism relay call) to allow for synchronized sharing of close parameters between networks
    For testing and initial deployment, a dummy oracles SingleNetworkAggregatorDebtRatio and SingleNetworkAggregatorIssuedSynths are utilized to retrieve debt values for this network, meaning most unit tests can work exactly the same as before.

Notes:

    The SC has indicated that inflation should be divided evenly between networks based on amount of debt shares on each network, so this has been implemented. Also, fees will remain on the network they originate from for the time being
    Dual Integration test was added to verify fee pool closure
    Tests were removed from DebtCache because the functionality is no longer used within the system, but the actual code from solidity was not removed because there is no need to include DebtCache in an update. Doing so would require more migration complexity and it would be better if we could avoid that, so no changes have been made to DebtCache for the time being.

The release process for this SIP is 2 steps:

    First, we will release as usual with the included SingleNetworkAggregators, which will preserve current functionality while enabling for us to start reading from an oracle interface for debt info
    Second, we will use the pdao to change the AddressResolver setting for the two aggregators to be the chainlink provided ones, which will effectively complete the debt synthethsis and enable synth fungibility
  • Loading branch information
dbeal-eth authored Mar 10, 2022
1 parent 17a48ae commit a3bc598
Show file tree
Hide file tree
Showing 41 changed files with 2,911 additions and 556 deletions.
73 changes: 73 additions & 0 deletions contracts/BaseSingleNetworkAggregator.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
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;

emit SetOverrideTimestamp(timestamp);
}

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
);

event SetOverrideTimestamp(uint timestamp);
}
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
74 changes: 71 additions & 3 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 @@ -27,6 +29,7 @@ import "./interfaces/ICollateralManager.sol";
import "./interfaces/IEtherWrapper.sol";
import "./interfaces/IFuturesMarketManager.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 @@ -45,6 +48,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 @@ -76,6 +83,12 @@ contract FeePool is Owned, Proxyable, LimitedSetup, MixinSystemSettings, IFeePoo
bytes32 private constant CONTRACT_FUTURES_MARKET_MANAGER = "FuturesMarketManager";
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 @@ -93,7 +106,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[](12);
bytes32[] memory newAddresses = new bytes32[](14);
newAddresses[0] = CONTRACT_SYSTEMSTATUS;
newAddresses[1] = CONTRACT_SYNTHETIXDEBTSHARE;
newAddresses[2] = CONTRACT_FEEPOOLETERNALSTORAGE;
Expand All @@ -105,7 +118,9 @@ 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_FUTURES_MARKET_MANAGER;
newAddresses[11] = CONTRACT_EXT_AGGREGATOR_ISSUED_SYNTHS;
newAddresses[12] = CONTRACT_EXT_AGGREGATOR_DEBT_RATIO;
newAddresses[13] = CONTRACT_FUTURES_MARKET_MANAGER;
addresses = combineArrays(existingAddresses, newAddresses);
}

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

function allNetworksSnxBackedDebt() public view returns (uint256 debt, uint256 updatedAt) {
(, int256 rawData, , uint timestamp, ) = AggregatorV2V3Interface(requireAndGetAddress(CONTRACT_EXT_AGGREGATOR_ISSUED_SYNTHS))
.latestRoundData();

debt = uint(rawData);
updatedAt = timestamp;
}

function allNetworksDebtSharesSupply() public view returns (uint256 sharesSupply, uint256 updatedAt) {
(, int256 rawIssuedSynths, , uint issuedSynthsUpdatedAt, ) = AggregatorV2V3Interface(requireAndGetAddress(CONTRACT_EXT_AGGREGATOR_ISSUED_SYNTHS))
.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 @@ -223,9 +258,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);
}

function closeSecondary(uint allNetworksSnxBackedDebt, uint allNetworksDebtSharesSupply) external onlyRelayer {
_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;
_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 @@ -347,7 +405,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 @@ -651,6 +711,14 @@ contract FeePool is Owned, Proxyable, LimitedSetup, MixinSystemSettings, IFeePoo
_;
}

modifier onlyRelayer {
require(
msg.sender == address(this) ||
msg.sender == resolver.getAddress(CONTRACT_SYNTHETIX_BRIDGE_TO_BASE)
, "Only valid relayer can call");
_;
}

modifier notFeeAddress(address account) {
require(account != FEE_ADDRESS, "Fee address not allowed");
_;
Expand Down
Loading

0 comments on commit a3bc598

Please sign in to comment.