文件 1 的 1:Issuer.sol
pragma solidity ^0.5.16;
contract Owned {
address public owner;
address public nominatedOwner;
constructor(address _owner) public {
require(_owner != address(0), "Owner address cannot be 0");
owner = _owner;
emit OwnerChanged(address(0), _owner);
}
function nominateNewOwner(address _owner) external onlyOwner {
nominatedOwner = _owner;
emit OwnerNominated(_owner);
}
function acceptOwnership() external {
require(msg.sender == nominatedOwner, "You must be nominated before you can accept ownership");
emit OwnerChanged(owner, nominatedOwner);
owner = nominatedOwner;
nominatedOwner = address(0);
}
modifier onlyOwner {
_onlyOwner();
_;
}
function _onlyOwner() private view {
require(msg.sender == owner, "Only the contract owner may perform this action");
}
event OwnerNominated(address newOwner);
event OwnerChanged(address oldOwner, address newOwner);
}
interface IAddressResolver {
function getAddress(bytes32 name) external view returns (address);
function getSynth(bytes32 key) external view returns (address);
function requireAndGetAddress(bytes32 name, string calldata reason) external view returns (address);
}
interface ISynth {
function currencyKey() external view returns (bytes32);
function transferableSynths(address account) external view returns (uint);
function transferAndSettle(address to, uint value) external returns (bool);
function transferFromAndSettle(
address from,
address to,
uint value
) external returns (bool);
function burn(address account, uint amount) external;
function issue(address account, uint amount) external;
}
interface IIssuer {
function anySynthOrSNXRateIsInvalid() external view returns (bool anyRateInvalid);
function availableCurrencyKeys() external view returns (bytes32[] memory);
function availableSynthCount() external view returns (uint);
function availableSynths(uint index) external view returns (ISynth);
function canBurnSynths(address account) external view returns (bool);
function collateral(address account) external view returns (uint);
function collateralisationRatio(address issuer) external view returns (uint);
function collateralisationRatioAndAnyRatesInvalid(address _issuer)
external
view
returns (uint cratio, bool anyRateIsInvalid);
function debtBalanceOf(address issuer, bytes32 currencyKey) external view returns (uint debtBalance);
function issuanceRatio() external view returns (uint);
function debtSnapshotStaleTime() external view returns (uint);
function lastIssueEvent(address account) external view returns (uint);
function maxIssuableSynths(address issuer) external view returns (uint maxIssuable);
function minimumStakeTime() external view returns (uint);
function remainingIssuableSynths(address issuer)
external
view
returns (
uint maxIssuable,
uint alreadyIssued,
uint totalSystemDebt
);
function synths(bytes32 currencyKey) external view returns (ISynth);
function synthsByAddress(address synthAddress) external view returns (bytes32);
function totalIssuedSynths(bytes32 currencyKey, bool excludeEtherCollateral) external view returns (uint);
function currentSNXIssuedDebtForCurrencies(bytes32[] calldata currencyKeys)
external
view
returns (uint[] memory snxIssuedDebts, bool anyRateIsInvalid);
function cachedSNXIssuedDebtForCurrencies(bytes32[] calldata currencyKeys)
external
view
returns (uint[] memory snxIssuedDebts);
function currentSNXIssuedDebt() external view returns (uint snxIssuedDebt, bool anyRateIsInvalid);
function cachedSNXIssuedDebtInfo()
external
view
returns (
uint cachedDebt,
uint timestamp,
bool isInvalid
);
function debtCacheIsStale() external view returns (bool);
function transferableSynthetixAndAnyRateIsInvalid(address account, uint balance)
external
view
returns (uint transferable, bool anyRateIsInvalid);
function issueSynths(address from, uint amount) external;
function issueSynthsOnBehalf(
address issueFor,
address from,
uint amount
) external;
function issueMaxSynths(address from) external;
function issueMaxSynthsOnBehalf(address issueFor, address from) external;
function burnSynths(address from, uint amount) external;
function burnSynthsOnBehalf(
address burnForAddress,
address from,
uint amount
) external;
function burnSynthsToTarget(address from) external;
function burnSynthsToTargetOnBehalf(address burnForAddress, address from) external;
function liquidateDelinquentAccount(
address account,
uint susdAmount,
address liquidator
) external returns (uint totalRedeemed, uint amountToLiquidate);
function cacheSNXIssuedDebt() external;
function updateSNXIssuedDebtForCurrencies(bytes32[] calldata currencyKeys) external;
}
contract AddressResolver is Owned, IAddressResolver {
mapping(bytes32 => address) public repository;
constructor(address _owner) public Owned(_owner) {}
function importAddresses(bytes32[] calldata names, address[] calldata destinations) external onlyOwner {
require(names.length == destinations.length, "Input lengths must match");
for (uint i = 0; i < names.length; i++) {
repository[names[i]] = destinations[i];
}
}
function getAddress(bytes32 name) external view returns (address) {
return repository[name];
}
function requireAndGetAddress(bytes32 name, string calldata reason) external view returns (address) {
address _foundAddress = repository[name];
require(_foundAddress != address(0), reason);
return _foundAddress;
}
function getSynth(bytes32 key) external view returns (address) {
IIssuer issuer = IIssuer(repository["Issuer"]);
require(address(issuer) != address(0), "Cannot find Issuer address");
return address(issuer.synths(key));
}
}
contract MixinResolver is Owned {
AddressResolver public resolver;
mapping(bytes32 => address) private addressCache;
bytes32[] public resolverAddressesRequired;
uint public constant MAX_ADDRESSES_FROM_RESOLVER = 24;
constructor(address _resolver, bytes32[MAX_ADDRESSES_FROM_RESOLVER] memory _addressesToCache) internal {
require(owner != address(0), "Owner must be set");
for (uint i = 0; i < _addressesToCache.length; i++) {
if (_addressesToCache[i] != bytes32(0)) {
resolverAddressesRequired.push(_addressesToCache[i]);
} else {
break;
}
}
resolver = AddressResolver(_resolver);
}
function setResolverAndSyncCache(AddressResolver _resolver) external onlyOwner {
resolver = _resolver;
for (uint i = 0; i < resolverAddressesRequired.length; i++) {
bytes32 name = resolverAddressesRequired[i];
addressCache[name] = resolver.requireAndGetAddress(name, "Resolver missing target");
}
}
function requireAndGetAddress(bytes32 name, string memory reason) internal view returns (address) {
address _foundAddress = addressCache[name];
require(_foundAddress != address(0), reason);
return _foundAddress;
}
function isResolverCached(AddressResolver _resolver) external view returns (bool) {
if (resolver != _resolver) {
return false;
}
for (uint i = 0; i < resolverAddressesRequired.length; i++) {
bytes32 name = resolverAddressesRequired[i];
if (resolver.getAddress(name) != addressCache[name] || addressCache[name] == address(0)) {
return false;
}
}
return true;
}
function getResolverAddressesRequired()
external
view
returns (bytes32[MAX_ADDRESSES_FROM_RESOLVER] memory addressesRequired)
{
for (uint i = 0; i < resolverAddressesRequired.length; i++) {
addressesRequired[i] = resolverAddressesRequired[i];
}
}
function appendToAddressCache(bytes32 name) internal {
resolverAddressesRequired.push(name);
require(resolverAddressesRequired.length < MAX_ADDRESSES_FROM_RESOLVER, "Max resolver cache size met");
addressCache[name] = resolver.getAddress(name);
}
}
interface IFlexibleStorage {
function getUIntValue(bytes32 contractName, bytes32 record) external view returns (uint);
function getUIntValues(bytes32 contractName, bytes32[] calldata records) external view returns (uint[] memory);
function getIntValue(bytes32 contractName, bytes32 record) external view returns (int);
function getIntValues(bytes32 contractName, bytes32[] calldata records) external view returns (int[] memory);
function getAddressValue(bytes32 contractName, bytes32 record) external view returns (address);
function getAddressValues(bytes32 contractName, bytes32[] calldata records) external view returns (address[] memory);
function getBoolValue(bytes32 contractName, bytes32 record) external view returns (bool);
function getBoolValues(bytes32 contractName, bytes32[] calldata records) external view returns (bool[] memory);
function getBytes32Value(bytes32 contractName, bytes32 record) external view returns (bytes32);
function getBytes32Values(bytes32 contractName, bytes32[] calldata records) external view returns (bytes32[] memory);
function deleteUIntValue(bytes32 contractName, bytes32 record) external;
function deleteIntValue(bytes32 contractName, bytes32 record) external;
function deleteAddressValue(bytes32 contractName, bytes32 record) external;
function deleteBoolValue(bytes32 contractName, bytes32 record) external;
function deleteBytes32Value(bytes32 contractName, bytes32 record) external;
function setUIntValue(
bytes32 contractName,
bytes32 record,
uint value
) external;
function setUIntValues(
bytes32 contractName,
bytes32[] calldata records,
uint[] calldata values
) external;
function setIntValue(
bytes32 contractName,
bytes32 record,
int value
) external;
function setIntValues(
bytes32 contractName,
bytes32[] calldata records,
int[] calldata values
) external;
function setAddressValue(
bytes32 contractName,
bytes32 record,
address value
) external;
function setAddressValues(
bytes32 contractName,
bytes32[] calldata records,
address[] calldata values
) external;
function setBoolValue(
bytes32 contractName,
bytes32 record,
bool value
) external;
function setBoolValues(
bytes32 contractName,
bytes32[] calldata records,
bool[] calldata values
) external;
function setBytes32Value(
bytes32 contractName,
bytes32 record,
bytes32 value
) external;
function setBytes32Values(
bytes32 contractName,
bytes32[] calldata records,
bytes32[] calldata values
) external;
}
contract MixinSystemSettings is MixinResolver {
bytes32 internal constant SETTING_CONTRACT_NAME = "SystemSettings";
bytes32 internal constant SETTING_WAITING_PERIOD_SECS = "waitingPeriodSecs";
bytes32 internal constant SETTING_PRICE_DEVIATION_THRESHOLD_FACTOR = "priceDeviationThresholdFactor";
bytes32 internal constant SETTING_ISSUANCE_RATIO = "issuanceRatio";
bytes32 internal constant SETTING_FEE_PERIOD_DURATION = "feePeriodDuration";
bytes32 internal constant SETTING_TARGET_THRESHOLD = "targetThreshold";
bytes32 internal constant SETTING_LIQUIDATION_DELAY = "liquidationDelay";
bytes32 internal constant SETTING_LIQUIDATION_RATIO = "liquidationRatio";
bytes32 internal constant SETTING_LIQUIDATION_PENALTY = "liquidationPenalty";
bytes32 internal constant SETTING_RATE_STALE_PERIOD = "rateStalePeriod";
bytes32 internal constant SETTING_EXCHANGE_FEE_RATE = "exchangeFeeRate";
bytes32 internal constant SETTING_MINIMUM_STAKE_TIME = "minimumStakeTime";
bytes32 internal constant SETTING_AGGREGATOR_WARNING_FLAGS = "aggregatorWarningFlags";
bytes32 internal constant SETTING_TRADING_REWARDS_ENABLED = "tradingRewardsEnabled";
bytes32 internal constant SETTING_DEBT_SNAPSHOT_STALE_TIME = "debtSnapshotStaleTime";
bytes32 private constant CONTRACT_FLEXIBLESTORAGE = "FlexibleStorage";
constructor() internal {
appendToAddressCache(CONTRACT_FLEXIBLESTORAGE);
}
function flexibleStorage() internal view returns (IFlexibleStorage) {
return IFlexibleStorage(requireAndGetAddress(CONTRACT_FLEXIBLESTORAGE, "Missing FlexibleStorage address"));
}
function getTradingRewardsEnabled() internal view returns (bool) {
return flexibleStorage().getBoolValue(SETTING_CONTRACT_NAME, SETTING_TRADING_REWARDS_ENABLED);
}
function getWaitingPeriodSecs() internal view returns (uint) {
return flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_WAITING_PERIOD_SECS);
}
function getPriceDeviationThresholdFactor() internal view returns (uint) {
return flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_PRICE_DEVIATION_THRESHOLD_FACTOR);
}
function getIssuanceRatio() internal view returns (uint) {
return flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_ISSUANCE_RATIO);
}
function getFeePeriodDuration() internal view returns (uint) {
return flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_FEE_PERIOD_DURATION);
}
function getTargetThreshold() internal view returns (uint) {
return flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_TARGET_THRESHOLD);
}
function getLiquidationDelay() internal view returns (uint) {
return flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_LIQUIDATION_DELAY);
}
function getLiquidationRatio() internal view returns (uint) {
return flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_LIQUIDATION_RATIO);
}
function getLiquidationPenalty() internal view returns (uint) {
return flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_LIQUIDATION_PENALTY);
}
function getRateStalePeriod() internal view returns (uint) {
return flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_RATE_STALE_PERIOD);
}
function getExchangeFeeRate(bytes32 currencyKey) internal view returns (uint) {
return
flexibleStorage().getUIntValue(
SETTING_CONTRACT_NAME,
keccak256(abi.encodePacked(SETTING_EXCHANGE_FEE_RATE, currencyKey))
);
}
function getMinimumStakeTime() internal view returns (uint) {
return flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_MINIMUM_STAKE_TIME);
}
function getAggregatorWarningFlags() internal view returns (address) {
return flexibleStorage().getAddressValue(SETTING_CONTRACT_NAME, SETTING_AGGREGATOR_WARNING_FLAGS);
}
function getDebtSnapshotStaleTime() internal view returns (uint) {
return flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_DEBT_SNAPSHOT_STALE_TIME);
}
}
library SafeMath {
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a, "SafeMath: subtraction overflow");
uint256 c = a - b;
return c;
}
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0, "SafeMath: division by zero");
uint256 c = a / b;
return c;
}
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b != 0, "SafeMath: modulo by zero");
return a % b;
}
}
library SafeDecimalMath {
using SafeMath for uint;
uint8 public constant decimals = 18;
uint8 public constant highPrecisionDecimals = 27;
uint public constant UNIT = 10**uint(decimals);
uint public constant PRECISE_UNIT = 10**uint(highPrecisionDecimals);
uint private constant UNIT_TO_HIGH_PRECISION_CONVERSION_FACTOR = 10**uint(highPrecisionDecimals - decimals);
function unit() external pure returns (uint) {
return UNIT;
}
function preciseUnit() external pure returns (uint) {
return PRECISE_UNIT;
}
function multiplyDecimal(uint x, uint y) internal pure returns (uint) {
return x.mul(y) / UNIT;
}
function _multiplyDecimalRound(
uint x,
uint y,
uint precisionUnit
) private pure returns (uint) {
uint quotientTimesTen = x.mul(y) / (precisionUnit / 10);
if (quotientTimesTen % 10 >= 5) {
quotientTimesTen += 10;
}
return quotientTimesTen / 10;
}
function multiplyDecimalRoundPrecise(uint x, uint y) internal pure returns (uint) {
return _multiplyDecimalRound(x, y, PRECISE_UNIT);
}
function multiplyDecimalRound(uint x, uint y) internal pure returns (uint) {
return _multiplyDecimalRound(x, y, UNIT);
}
function divideDecimal(uint x, uint y) internal pure returns (uint) {
return x.mul(UNIT).div(y);
}
function _divideDecimalRound(
uint x,
uint y,
uint precisionUnit
) private pure returns (uint) {
uint resultTimesTen = x.mul(precisionUnit * 10).div(y);
if (resultTimesTen % 10 >= 5) {
resultTimesTen += 10;
}
return resultTimesTen / 10;
}
function divideDecimalRound(uint x, uint y) internal pure returns (uint) {
return _divideDecimalRound(x, y, UNIT);
}
function divideDecimalRoundPrecise(uint x, uint y) internal pure returns (uint) {
return _divideDecimalRound(x, y, PRECISE_UNIT);
}
function decimalToPreciseDecimal(uint i) internal pure returns (uint) {
return i.mul(UNIT_TO_HIGH_PRECISION_CONVERSION_FACTOR);
}
function preciseDecimalToDecimal(uint i) internal pure returns (uint) {
uint quotientTimesTen = i / (UNIT_TO_HIGH_PRECISION_CONVERSION_FACTOR / 10);
if (quotientTimesTen % 10 >= 5) {
quotientTimesTen += 10;
}
return quotientTimesTen / 10;
}
}
interface ISynthetix {
function anySynthOrSNXRateIsInvalid() external view returns (bool anyRateInvalid);
function availableCurrencyKeys() external view returns (bytes32[] memory);
function availableSynthCount() external view returns (uint);
function availableSynths(uint index) external view returns (ISynth);
function collateral(address account) external view returns (uint);
function collateralisationRatio(address issuer) external view returns (uint);
function debtBalanceOf(address issuer, bytes32 currencyKey) external view returns (uint);
function isWaitingPeriod(bytes32 currencyKey) external view returns (bool);
function maxIssuableSynths(address issuer) external view returns (uint maxIssuable);
function remainingIssuableSynths(address issuer)
external
view
returns (
uint maxIssuable,
uint alreadyIssued,
uint totalSystemDebt
);
function synths(bytes32 currencyKey) external view returns (ISynth);
function synthsByAddress(address synthAddress) external view returns (bytes32);
function totalIssuedSynths(bytes32 currencyKey) external view returns (uint);
function totalIssuedSynthsExcludeEtherCollateral(bytes32 currencyKey) external view returns (uint);
function transferableSynthetix(address account) external view returns (uint transferable);
function burnSynths(uint amount) external;
function burnSynthsOnBehalf(address burnForAddress, uint amount) external;
function burnSynthsToTarget() external;
function burnSynthsToTargetOnBehalf(address burnForAddress) external;
function exchange(
bytes32 sourceCurrencyKey,
uint sourceAmount,
bytes32 destinationCurrencyKey
) external returns (uint amountReceived);
function exchangeOnBehalf(
address exchangeForAddress,
bytes32 sourceCurrencyKey,
uint sourceAmount,
bytes32 destinationCurrencyKey
) external returns (uint amountReceived);
function exchangeWithTracking(
bytes32 sourceCurrencyKey,
uint sourceAmount,
bytes32 destinationCurrencyKey,
address originator,
bytes32 trackingCode
) external returns (uint amountReceived);
function exchangeOnBehalfWithTracking(
address exchangeForAddress,
bytes32 sourceCurrencyKey,
uint sourceAmount,
bytes32 destinationCurrencyKey,
address originator,
bytes32 trackingCode
) external returns (uint amountReceived);
function issueMaxSynths() external;
function issueMaxSynthsOnBehalf(address issueForAddress) external;
function issueSynths(uint amount) external;
function issueSynthsOnBehalf(address issueForAddress, uint amount) external;
function mint() external returns (bool);
function settle(bytes32 currencyKey)
external
returns (
uint reclaimed,
uint refunded,
uint numEntries
);
function liquidateDelinquentAccount(address account, uint susdAmount) external returns (bool);
}
interface IFeePool {
function FEE_ADDRESS() external view returns (address);
function feesAvailable(address account) external view returns (uint, uint);
function feePeriodDuration() external view returns (uint);
function isFeesClaimable(address account) external view returns (bool);
function targetThreshold() external view returns (uint);
function totalFeesAvailable() external view returns (uint);
function totalRewardsAvailable() external view returns (uint);
function claimFees() external returns (bool);
function claimOnBehalf(address claimingForAddress) external returns (bool);
function closeCurrentFeePeriod() external;
function appendAccountIssuanceRecord(
address account,
uint lockedAmount,
uint debtEntryIndex
) external;
function recordFeePaid(uint sUSDAmount) external;
function setRewardsToDistribute(uint amount) external;
}
interface ISynthetixState {
function debtLedger(uint index) external view returns (uint);
function issuanceData(address account) external view returns (uint initialDebtOwnership, uint debtEntryIndex);
function debtLedgerLength() external view returns (uint);
function hasIssued(address account) external view returns (bool);
function lastDebtLedgerEntry() external view returns (uint);
function incrementTotalIssuerCount() external;
function decrementTotalIssuerCount() external;
function setCurrentIssuanceData(address account, uint initialDebtOwnership) external;
function appendDebtLedgerValue(uint value) external;
function clearIssuanceData(address account) external;
}
interface IExchanger {
function calculateAmountAfterSettlement(
address from,
bytes32 currencyKey,
uint amount,
uint refunded
) external view returns (uint amountAfterSettlement);
function isSynthRateInvalid(bytes32 currencyKey) external view returns (bool);
function maxSecsLeftInWaitingPeriod(address account, bytes32 currencyKey) external view returns (uint);
function settlementOwing(address account, bytes32 currencyKey)
external
view
returns (
uint reclaimAmount,
uint rebateAmount,
uint numEntries
);
function hasWaitingPeriodOrSettlementOwing(address account, bytes32 currencyKey) external view returns (bool);
function feeRateForExchange(bytes32 sourceCurrencyKey, bytes32 destinationCurrencyKey)
external
view
returns (uint exchangeFeeRate);
function getAmountsForExchange(
uint sourceAmount,
bytes32 sourceCurrencyKey,
bytes32 destinationCurrencyKey
)
external
view
returns (
uint amountReceived,
uint fee,
uint exchangeFeeRate
);
function priceDeviationThresholdFactor() external view returns (uint);
function waitingPeriodSecs() external view returns (uint);
function exchange(
address from,
bytes32 sourceCurrencyKey,
uint sourceAmount,
bytes32 destinationCurrencyKey,
address destinationAddress
) external returns (uint amountReceived);
function exchangeOnBehalf(
address exchangeForAddress,
address from,
bytes32 sourceCurrencyKey,
uint sourceAmount,
bytes32 destinationCurrencyKey
) external returns (uint amountReceived);
function exchangeWithTracking(
address from,
bytes32 sourceCurrencyKey,
uint sourceAmount,
bytes32 destinationCurrencyKey,
address destinationAddress,
address originator,
bytes32 trackingCode
) external returns (uint amountReceived);
function exchangeOnBehalfWithTracking(
address exchangeForAddress,
address from,
bytes32 sourceCurrencyKey,
uint sourceAmount,
bytes32 destinationCurrencyKey,
address originator,
bytes32 trackingCode
) external returns (uint amountReceived);
function settle(address from, bytes32 currencyKey)
external
returns (
uint reclaimed,
uint refunded,
uint numEntries
);
function setLastExchangeRateForSynth(bytes32 currencyKey, uint rate) external;
function suspendSynthWithInvalidRate(bytes32 currencyKey) external;
}
interface IDelegateApprovals {
function canBurnFor(address authoriser, address delegate) external view returns (bool);
function canIssueFor(address authoriser, address delegate) external view returns (bool);
function canClaimFor(address authoriser, address delegate) external view returns (bool);
function canExchangeFor(address authoriser, address delegate) external view returns (bool);
function approveAllDelegatePowers(address delegate) external;
function removeAllDelegatePowers(address delegate) external;
function approveBurnOnBehalf(address delegate) external;
function removeBurnOnBehalf(address delegate) external;
function approveIssueOnBehalf(address delegate) external;
function removeIssueOnBehalf(address delegate) external;
function approveClaimOnBehalf(address delegate) external;
function removeClaimOnBehalf(address delegate) external;
function approveExchangeOnBehalf(address delegate) external;
function removeExchangeOnBehalf(address delegate) external;
}
interface IExchangeRates {
struct RateAndUpdatedTime {
uint216 rate;
uint40 time;
}
struct InversePricing {
uint entryPoint;
uint upperLimit;
uint lowerLimit;
bool frozenAtUpperLimit;
bool frozenAtLowerLimit;
}
function aggregators(bytes32 currencyKey) external view returns (address);
function aggregatorWarningFlags() external view returns (address);
function anyRateIsInvalid(bytes32[] calldata currencyKeys) external view returns (bool);
function canFreezeRate(bytes32 currencyKey) external view returns (bool);
function currentRoundForRate(bytes32 currencyKey) external view returns (uint);
function currenciesUsingAggregator(address aggregator) external view returns (bytes32[] memory);
function effectiveValue(
bytes32 sourceCurrencyKey,
uint sourceAmount,
bytes32 destinationCurrencyKey
) external view returns (uint value);
function effectiveValueAndRates(
bytes32 sourceCurrencyKey,
uint sourceAmount,
bytes32 destinationCurrencyKey
)
external
view
returns (
uint value,
uint sourceRate,
uint destinationRate
);
function effectiveValueAtRound(
bytes32 sourceCurrencyKey,
uint sourceAmount,
bytes32 destinationCurrencyKey,
uint roundIdForSrc,
uint roundIdForDest
) external view returns (uint value);
function getCurrentRoundId(bytes32 currencyKey) external view returns (uint);
function getLastRoundIdBeforeElapsedSecs(
bytes32 currencyKey,
uint startingRoundId,
uint startingTimestamp,
uint timediff
) external view returns (uint);
function inversePricing(bytes32 currencyKey)
external
view
returns (
uint entryPoint,
uint upperLimit,
uint lowerLimit,
bool frozenAtUpperLimit,
bool frozenAtLowerLimit
);
function lastRateUpdateTimes(bytes32 currencyKey) external view returns (uint256);
function oracle() external view returns (address);
function rateAndTimestampAtRound(bytes32 currencyKey, uint roundId) external view returns (uint rate, uint time);
function rateAndUpdatedTime(bytes32 currencyKey) external view returns (uint rate, uint time);
function rateAndInvalid(bytes32 currencyKey) external view returns (uint rate, bool isInvalid);
function rateForCurrency(bytes32 currencyKey) external view returns (uint);
function rateIsFlagged(bytes32 currencyKey) external view returns (bool);
function rateIsFrozen(bytes32 currencyKey) external view returns (bool);
function rateIsInvalid(bytes32 currencyKey) external view returns (bool);
function rateIsStale(bytes32 currencyKey) external view returns (bool);
function rateStalePeriod() external view returns (uint);
function ratesAndUpdatedTimeForCurrencyLastNRounds(bytes32 currencyKey, uint numRounds)
external
view
returns (uint[] memory rates, uint[] memory times);
function ratesAndInvalidForCurrencies(bytes32[] calldata currencyKeys)
external
view
returns (uint[] memory rates, bool anyRateInvalid);
function ratesForCurrencies(bytes32[] calldata currencyKeys) external view returns (uint[] memory);
function freezeRate(bytes32 currencyKey) external;
}
interface IEtherCollateral {
function totalIssuedSynths() external view returns (uint256);
function totalLoansCreated() external view returns (uint256);
function totalOpenLoanCount() external view returns (uint256);
function openLoan() external payable returns (uint256 loanID);
function closeLoan(uint256 loanID) external;
function liquidateUnclosedLoan(address _loanCreatorsAddress, uint256 _loanID) external;
}
interface IEtherCollateralsUSD {
function totalIssuedSynths() external view returns (uint256);
function totalLoansCreated() external view returns (uint256);
function totalOpenLoanCount() external view returns (uint256);
function openLoan(uint256 _loanAmount) external payable returns (uint256 loanID);
function closeLoan(uint256 loanID) external;
function liquidateUnclosedLoan(address _loanCreatorsAddress, uint256 _loanID) external;
function depositCollateral(address account, uint256 loanID) external payable;
function withdrawCollateral(uint256 loanID, uint256 withdrawAmount) external;
function repayLoan(
address _loanCreatorsAddress,
uint256 _loanID,
uint256 _repayAmount
) external;
}
interface IRewardEscrow {
function balanceOf(address account) external view returns (uint);
function numVestingEntries(address account) external view returns (uint);
function totalEscrowedAccountBalance(address account) external view returns (uint);
function totalVestedAccountBalance(address account) external view returns (uint);
function appendVestingEntry(address account, uint quantity) external;
function vest() external;
}
interface IHasBalance {
function balanceOf(address account) external view returns (uint);
}
interface IERC20 {
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
function totalSupply() external view returns (uint);
function balanceOf(address owner) external view returns (uint);
function allowance(address owner, address spender) external view returns (uint);
function transfer(address to, uint value) external returns (bool);
function approve(address spender, uint value) external returns (bool);
function transferFrom(
address from,
address to,
uint value
) external returns (bool);
event Transfer(address indexed from, address indexed to, uint value);
event Approval(address indexed owner, address indexed spender, uint value);
}
interface ILiquidations {
function isOpenForLiquidation(address account) external view returns (bool);
function getLiquidationDeadlineForAccount(address account) external view returns (uint);
function isLiquidationDeadlinePassed(address account) external view returns (bool);
function liquidationDelay() external view returns (uint);
function liquidationRatio() external view returns (uint);
function liquidationPenalty() external view returns (uint);
function calculateAmountToFixCollateral(uint debtBalance, uint collateral) external view returns (uint);
function flagAccountForLiquidation(address account) external;
function removeAccountInLiquidation(address account) external;
function checkAndRemoveAccountInLiquidation(address account) external;
}
interface ISystemStatus {
struct Status {
bool canSuspend;
bool canResume;
}
struct Suspension {
bool suspended;
uint248 reason;
}
function accessControl(bytes32 section, address account) external view returns (bool canSuspend, bool canResume);
function requireSystemActive() external view;
function requireIssuanceActive() external view;
function requireExchangeActive() external view;
function requireSynthActive(bytes32 currencyKey) external view;
function requireSynthsActive(bytes32 sourceCurrencyKey, bytes32 destinationCurrencyKey) external view;
function synthSuspension(bytes32 currencyKey) external view returns (bool suspended, uint248 reason);
function suspendSynth(bytes32 currencyKey, uint256 reason) external;
function updateAccessControl(
bytes32 section,
address account,
bool canSuspend,
bool canResume
) external;
}
contract Issuer is Owned, MixinResolver, MixinSystemSettings, IIssuer {
using SafeMath for uint;
using SafeDecimalMath for uint;
ISynth[] public availableSynths;
mapping(bytes32 => ISynth) public synths;
mapping(address => bytes32) public synthsByAddress;
bytes32 internal constant sUSD = "sUSD";
bytes32 internal constant sETH = "sETH";
bytes32 internal constant SNX = "SNX";
bytes32 public constant CONTRACT_NAME = "Issuer";
bytes32 internal constant LAST_ISSUE_EVENT = "lastIssueEvent";
bytes32 internal constant CACHED_SNX_ISSUED_DEBT = "cachedSNXIssuedDebt";
bytes32 internal constant CACHED_SNX_ISSUED_DEBT_TIMESTAMP = "cachedSNXIssuedDebtTimestamp";
bytes32 internal constant CACHED_SNX_ISSUED_DEBT_INVALID = "cachedSNXIssuedDebtInvalid";
bytes32 private constant CONTRACT_SYNTHETIX = "Synthetix";
bytes32 private constant CONTRACT_EXCHANGER = "Exchanger";
bytes32 private constant CONTRACT_EXRATES = "ExchangeRates";
bytes32 private constant CONTRACT_SYNTHETIXSTATE = "SynthetixState";
bytes32 private constant CONTRACT_FEEPOOL = "FeePool";
bytes32 private constant CONTRACT_DELEGATEAPPROVALS = "DelegateApprovals";
bytes32 private constant CONTRACT_ETHERCOLLATERAL = "EtherCollateral";
bytes32 private constant CONTRACT_ETHERCOLLATERAL_SUSD = "EtherCollateralsUSD";
bytes32 private constant CONTRACT_REWARDESCROW = "RewardEscrow";
bytes32 private constant CONTRACT_SYNTHETIXESCROW = "SynthetixEscrow";
bytes32 private constant CONTRACT_LIQUIDATIONS = "Liquidations";
bytes32 private constant CONTRACT_FLEXIBLESTORAGE = "FlexibleStorage";
bytes32 private constant CONTRACT_SYSTEMSTATUS = "SystemStatus";
bytes32[24] private addressesToCache = [
CONTRACT_SYNTHETIX,
CONTRACT_EXCHANGER,
CONTRACT_EXRATES,
CONTRACT_SYNTHETIXSTATE,
CONTRACT_FEEPOOL,
CONTRACT_DELEGATEAPPROVALS,
CONTRACT_ETHERCOLLATERAL,
CONTRACT_ETHERCOLLATERAL_SUSD,
CONTRACT_REWARDESCROW,
CONTRACT_SYNTHETIXESCROW,
CONTRACT_LIQUIDATIONS,
CONTRACT_FLEXIBLESTORAGE,
CONTRACT_SYSTEMSTATUS
];
constructor(address _owner, address _resolver)
public
Owned(_owner)
MixinResolver(_resolver, addressesToCache)
MixinSystemSettings()
{}
function synthetix() internal view returns (ISynthetix) {
return ISynthetix(requireAndGetAddress(CONTRACT_SYNTHETIX, "Missing Synthetix address"));
}
function exchanger() internal view returns (IExchanger) {
return IExchanger(requireAndGetAddress(CONTRACT_EXCHANGER, "Missing Exchanger address"));
}
function exchangeRates() internal view returns (IExchangeRates) {
return IExchangeRates(requireAndGetAddress(CONTRACT_EXRATES, "Missing ExchangeRates address"));
}
function synthetixState() internal view returns (ISynthetixState) {
return ISynthetixState(requireAndGetAddress(CONTRACT_SYNTHETIXSTATE, "Missing SynthetixState address"));
}
function feePool() internal view returns (IFeePool) {
return IFeePool(requireAndGetAddress(CONTRACT_FEEPOOL, "Missing FeePool address"));
}
function liquidations() internal view returns (ILiquidations) {
return ILiquidations(requireAndGetAddress(CONTRACT_LIQUIDATIONS, "Missing Liquidations address"));
}
function systemStatus() internal view returns (ISystemStatus) {
return ISystemStatus(requireAndGetAddress(CONTRACT_SYSTEMSTATUS, "Missing SystemStatus address"));
}
function delegateApprovals() internal view returns (IDelegateApprovals) {
return IDelegateApprovals(requireAndGetAddress(CONTRACT_DELEGATEAPPROVALS, "Missing DelegateApprovals address"));
}
function etherCollateral() internal view returns (IEtherCollateral) {
return IEtherCollateral(requireAndGetAddress(CONTRACT_ETHERCOLLATERAL, "Missing EtherCollateral address"));
}
function etherCollateralsUSD() internal view returns (IEtherCollateralsUSD) {
return
IEtherCollateralsUSD(requireAndGetAddress(CONTRACT_ETHERCOLLATERAL_SUSD, "Missing EtherCollateralsUSD address"));
}
function rewardEscrow() internal view returns (IRewardEscrow) {
return IRewardEscrow(requireAndGetAddress(CONTRACT_REWARDESCROW, "Missing RewardEscrow address"));
}
function synthetixEscrow() internal view returns (IHasBalance) {
return IHasBalance(requireAndGetAddress(CONTRACT_SYNTHETIXESCROW, "Missing SynthetixEscrow address"));
}
function issuanceRatio() external view returns (uint) {
return getIssuanceRatio();
}
function debtSnapshotStaleTime() external view returns (uint) {
return getDebtSnapshotStaleTime();
}
function _availableCurrencyKeysWithOptionalSNX(bool withSNX) internal view returns (bytes32[] memory) {
bytes32[] memory currencyKeys = new bytes32[](availableSynths.length + (withSNX ? 1 : 0));
for (uint i = 0; i < availableSynths.length; i++) {
currencyKeys[i] = synthsByAddress[address(availableSynths[i])];
}
if (withSNX) {
currencyKeys[availableSynths.length] = SNX;
}
return currencyKeys;
}
function _issuedSynthValues(bytes32[] memory currencyKeys, uint[] memory rates) internal view returns (uint[] memory) {
uint numValues = currencyKeys.length;
uint[] memory values = new uint[](numValues);
for (uint i = 0; i < numValues; i++) {
bytes32 key = currencyKeys[i];
uint supply = IERC20(address(synths[key])).totalSupply();
bool isSUSD = key == sUSD;
if (isSUSD || key == sETH) {
IEtherCollateral etherCollateralContract = isSUSD
? IEtherCollateral(address(etherCollateralsUSD()))
: etherCollateral();
uint etherCollateralSupply = etherCollateralContract.totalIssuedSynths();
supply = supply.sub(etherCollateralSupply);
}
values[i] = supply.multiplyDecimalRound(rates[i]);
}
return values;
}
function _cachedSNXIssuedDebtAndTimestamp(IFlexibleStorage store) internal view returns (uint debt, uint timestamp) {
bytes32[] memory keys = new bytes32[](2);
keys[0] = CACHED_SNX_ISSUED_DEBT;
keys[1] = CACHED_SNX_ISSUED_DEBT_TIMESTAMP;
uint[] memory values = store.getUIntValues(CONTRACT_NAME, keys);
return (values[0], values[1]);
}
function _cacheIsInvalid(IFlexibleStorage store) internal view returns (bool) {
return store.getBoolValue(CONTRACT_NAME, CACHED_SNX_ISSUED_DEBT_INVALID);
}
function _totalIssuedSynths(bytes32 currencyKey, bool excludeEtherCollateral)
internal
view
returns (uint totalIssued, bool anyRateIsInvalid)
{
IFlexibleStorage store = flexibleStorage();
(uint debt, uint timestamp) = _cachedSNXIssuedDebtAndTimestamp(store);
bool isStale = getDebtSnapshotStaleTime() < block.timestamp - timestamp;
anyRateIsInvalid = isStale || _cacheIsInvalid(store) || timestamp == 0;
IExchangeRates exRates = exchangeRates();
if (!excludeEtherCollateral) {
debt = debt.add(etherCollateralsUSD().totalIssuedSynths());
(uint ethRate, bool ethRateInvalid) = exRates.rateAndInvalid(sETH);
uint ethIssuedDebt = etherCollateral().totalIssuedSynths().multiplyDecimalRound(ethRate);
debt = debt.add(ethIssuedDebt);
anyRateIsInvalid = anyRateIsInvalid || ethRateInvalid;
}
if (currencyKey == sUSD) {
return (debt, anyRateIsInvalid);
}
(uint currencyRate, bool currencyRateInvalid) = exRates.rateAndInvalid(currencyKey);
return (debt.divideDecimalRound(currencyRate), anyRateIsInvalid || currencyRateInvalid);
}
function _debtBalanceOfAndTotalDebt(address _issuer, bytes32 currencyKey)
internal
view
returns (
uint debtBalance,
uint totalSystemValue,
bool anyRateIsInvalid
)
{
ISynthetixState state = synthetixState();
(uint initialDebtOwnership, uint debtEntryIndex) = state.issuanceData(_issuer);
(totalSystemValue, anyRateIsInvalid) = _totalIssuedSynths(currencyKey, true);
if (initialDebtOwnership == 0) return (0, totalSystemValue, anyRateIsInvalid);
uint currentDebtOwnership = state
.lastDebtLedgerEntry()
.divideDecimalRoundPrecise(state.debtLedger(debtEntryIndex))
.multiplyDecimalRoundPrecise(initialDebtOwnership);
uint highPrecisionBalance = totalSystemValue.decimalToPreciseDecimal().multiplyDecimalRoundPrecise(
currentDebtOwnership
);
debtBalance = highPrecisionBalance.preciseDecimalToDecimal();
}
function _canBurnSynths(address account) internal view returns (bool) {
return now >= _lastIssueEvent(account).add(getMinimumStakeTime());
}
function _lastIssueEvent(address account) internal view returns (uint) {
return flexibleStorage().getUIntValue(CONTRACT_NAME, keccak256(abi.encodePacked(LAST_ISSUE_EVENT, account)));
}
function _remainingIssuableSynths(address _issuer)
internal
view
returns (
uint maxIssuable,
uint alreadyIssued,
uint totalSystemDebt,
bool anyRateIsInvalid
)
{
(alreadyIssued, totalSystemDebt, anyRateIsInvalid) = _debtBalanceOfAndTotalDebt(_issuer, sUSD);
(uint issuable, bool isInvalid) = _maxIssuableSynths(_issuer);
maxIssuable = issuable;
anyRateIsInvalid = anyRateIsInvalid || isInvalid;
if (alreadyIssued >= maxIssuable) {
maxIssuable = 0;
} else {
maxIssuable = maxIssuable.sub(alreadyIssued);
}
}
function _snxToUSD(uint amount, uint snxRate) internal pure returns (uint) {
return amount.multiplyDecimalRound(snxRate);
}
function _usdToSnx(uint amount, uint snxRate) internal pure returns (uint) {
return amount.divideDecimalRound(snxRate);
}
function _maxIssuableSynths(address _issuer) internal view returns (uint, bool) {
(uint snxRate, bool isInvalid) = exchangeRates().rateAndInvalid(SNX);
uint destinationValue = _snxToUSD(_collateral(_issuer), snxRate);
return (destinationValue.multiplyDecimal(getIssuanceRatio()), isInvalid);
}
function _collateralisationRatio(address _issuer) internal view returns (uint, bool) {
uint totalOwnedSynthetix = _collateral(_issuer);
(uint debtBalance, , bool anyRateIsInvalid) = _debtBalanceOfAndTotalDebt(_issuer, SNX);
if (totalOwnedSynthetix == 0) return (0, anyRateIsInvalid);
return (debtBalance.divideDecimalRound(totalOwnedSynthetix), anyRateIsInvalid);
}
function _collateral(address account) internal view returns (uint) {
uint balance = IERC20(address(synthetix())).balanceOf(account);
if (address(synthetixEscrow()) != address(0)) {
balance = balance.add(synthetixEscrow().balanceOf(account));
}
if (address(rewardEscrow()) != address(0)) {
balance = balance.add(rewardEscrow().balanceOf(account));
}
return balance;
}
function minimumStakeTime() external view returns (uint) {
return getMinimumStakeTime();
}
function canBurnSynths(address account) external view returns (bool) {
return _canBurnSynths(account);
}
function availableCurrencyKeys() external view returns (bytes32[] memory) {
return _availableCurrencyKeysWithOptionalSNX(false);
}
function availableSynthCount() external view returns (uint) {
return availableSynths.length;
}
function anySynthOrSNXRateIsInvalid() external view returns (bool anyRateInvalid) {
(, anyRateInvalid) = exchangeRates().ratesAndInvalidForCurrencies(_availableCurrencyKeysWithOptionalSNX(true));
}
function totalIssuedSynths(bytes32 currencyKey, bool excludeEtherCollateral) external view returns (uint totalIssued) {
(totalIssued, ) = _totalIssuedSynths(currencyKey, excludeEtherCollateral);
}
function lastIssueEvent(address account) external view returns (uint) {
return _lastIssueEvent(account);
}
function collateralisationRatio(address _issuer) external view returns (uint cratio) {
(cratio, ) = _collateralisationRatio(_issuer);
}
function collateralisationRatioAndAnyRatesInvalid(address _issuer)
external
view
returns (uint cratio, bool anyRateIsInvalid)
{
return _collateralisationRatio(_issuer);
}
function collateral(address account) external view returns (uint) {
return _collateral(account);
}
function debtBalanceOf(address _issuer, bytes32 currencyKey) external view returns (uint debtBalance) {
ISynthetixState state = synthetixState();
(uint initialDebtOwnership, ) = state.issuanceData(_issuer);
if (initialDebtOwnership == 0) return 0;
(debtBalance, , ) = _debtBalanceOfAndTotalDebt(_issuer, currencyKey);
}
function remainingIssuableSynths(address _issuer)
external
view
returns (
uint maxIssuable,
uint alreadyIssued,
uint totalSystemDebt
)
{
(maxIssuable, alreadyIssued, totalSystemDebt, ) = _remainingIssuableSynths(_issuer);
}
function maxIssuableSynths(address _issuer) external view returns (uint) {
(uint maxIssuable, ) = _maxIssuableSynths(_issuer);
return maxIssuable;
}
function transferableSynthetixAndAnyRateIsInvalid(address account, uint balance)
external
view
returns (uint transferable, bool anyRateIsInvalid)
{
uint debtBalance;
(debtBalance, , anyRateIsInvalid) = _debtBalanceOfAndTotalDebt(account, SNX);
uint lockedSynthetixValue = debtBalance.divideDecimalRound(getIssuanceRatio());
if (lockedSynthetixValue >= balance) {
transferable = 0;
} else {
transferable = balance.sub(lockedSynthetixValue);
}
}
function currentSNXIssuedDebtForCurrencies(bytes32[] memory currencyKeys)
public
view
returns (uint[] memory snxIssuedDebts, bool anyRateIsInvalid)
{
(uint[] memory rates, bool isInvalid) = exchangeRates().ratesAndInvalidForCurrencies(currencyKeys);
return (_issuedSynthValues(currencyKeys, rates), isInvalid);
}
function cachedSNXIssuedDebtForCurrencies(bytes32[] calldata currencyKeys)
external
view
returns (uint[] memory snxIssuedDebts)
{
return flexibleStorage().getUIntValues(CONTRACT_NAME, currencyKeys);
}
function currentSNXIssuedDebt() external view returns (uint snxIssuedDebt, bool anyRateIsInvalid) {
(uint[] memory values, bool isInvalid) = currentSNXIssuedDebtForCurrencies(
_availableCurrencyKeysWithOptionalSNX(false)
);
uint numValues = values.length;
uint total;
for (uint i; i < numValues; i++) {
total = total.add(values[i]);
}
return (total, isInvalid);
}
function cachedSNXIssuedDebtInfo()
external
view
returns (
uint cachedDebt,
uint timestamp,
bool isInvalid
)
{
IFlexibleStorage store = flexibleStorage();
(uint debt, uint time) = _cachedSNXIssuedDebtAndTimestamp(store);
return (debt, time, _cacheIsInvalid(store));
}
function debtCacheIsStale() external view returns (bool) {
return
getDebtSnapshotStaleTime() <
block.timestamp - flexibleStorage().getUIntValue(CONTRACT_NAME, CACHED_SNX_ISSUED_DEBT_TIMESTAMP);
}
function _requireSynthDoesNotExist(bytes32 currencyKey) internal view {
require(synths[currencyKey] == ISynth(0), "Synth exists");
}
function purgeDebtCacheForSynth(bytes32 currencyKey) external onlyOwner {
_requireSynthDoesNotExist(currencyKey);
flexibleStorage().setUIntValue(CONTRACT_NAME, currencyKey, 0);
}
function addSynth(ISynth synth) external onlyOwner {
bytes32 currencyKey = synth.currencyKey();
_requireSynthDoesNotExist(currencyKey);
require(synthsByAddress[address(synth)] == bytes32(0), "Synth address already exists");
_changeDebtCacheValidityIfNeeded(flexibleStorage(), true);
availableSynths.push(synth);
synths[currencyKey] = synth;
synthsByAddress[address(synth)] = currencyKey;
emit SynthAdded(currencyKey, address(synth));
}
function removeSynth(bytes32 currencyKey) external onlyOwner {
address synthToRemove = address(synths[currencyKey]);
require(synthToRemove != address(0), "Synth does not exist");
require(IERC20(synthToRemove).totalSupply() == 0, "Synth supply exists");
require(currencyKey != sUSD, "Cannot remove synth");
_updateSNXIssuedDebtForSynth(currencyKey, 0);
_changeDebtCacheValidityIfNeeded(flexibleStorage(), true);
for (uint i = 0; i < availableSynths.length; i++) {
if (address(availableSynths[i]) == synthToRemove) {
delete availableSynths[i];
availableSynths[i] = availableSynths[availableSynths.length - 1];
availableSynths.length--;
break;
}
}
delete synthsByAddress[synthToRemove];
delete synths[currencyKey];
emit SynthRemoved(currencyKey, synthToRemove);
}
function issueSynths(address from, uint amount) external onlySynthetix {
_issueSynths(from, amount, false);
}
function issueMaxSynths(address from) external onlySynthetix {
_issueSynths(from, 0, true);
}
function issueSynthsOnBehalf(
address issueForAddress,
address from,
uint amount
) external onlySynthetix {
_requireCanIssueOnBehalf(issueForAddress, from);
_issueSynths(issueForAddress, amount, false);
}
function issueMaxSynthsOnBehalf(address issueForAddress, address from) external onlySynthetix {
_requireCanIssueOnBehalf(issueForAddress, from);
_issueSynths(issueForAddress, 0, true);
}
function burnSynths(address from, uint amount) external onlySynthetix {
_voluntaryBurnSynths(from, amount, false);
}
function burnSynthsOnBehalf(
address burnForAddress,
address from,
uint amount
) external onlySynthetix {
_requireCanBurnOnBehalf(burnForAddress, from);
_voluntaryBurnSynths(burnForAddress, amount, false);
}
function burnSynthsToTarget(address from) external onlySynthetix {
_voluntaryBurnSynths(from, 0, true);
}
function burnSynthsToTargetOnBehalf(address burnForAddress, address from) external onlySynthetix {
_requireCanBurnOnBehalf(burnForAddress, from);
_voluntaryBurnSynths(burnForAddress, 0, true);
}
function liquidateDelinquentAccount(
address account,
uint susdAmount,
address liquidator
) external onlySynthetix returns (uint totalRedeemed, uint amountToLiquidate) {
require(!exchanger().hasWaitingPeriodOrSettlementOwing(liquidator, sUSD), "sUSD needs to be settled");
require(liquidations().isOpenForLiquidation(account), "Account not open for liquidation");
require(IERC20(address(synths[sUSD])).balanceOf(liquidator) >= susdAmount, "Not enough sUSD");
uint liquidationPenalty = liquidations().liquidationPenalty();
(uint debtBalance, uint totalDebtIssued, bool anyRateIsInvalid) = _debtBalanceOfAndTotalDebt(account, sUSD);
(uint snxRate, bool snxRateInvalid) = exchangeRates().rateAndInvalid(SNX);
_requireRatesNotInvalid(anyRateIsInvalid || snxRateInvalid);
uint collateralForAccount = _collateral(account);
uint amountToFixRatio = liquidations().calculateAmountToFixCollateral(
debtBalance,
_snxToUSD(collateralForAccount, snxRate)
);
amountToLiquidate = amountToFixRatio < susdAmount ? amountToFixRatio : susdAmount;
uint snxRedeemed = _usdToSnx(amountToLiquidate, snxRate);
totalRedeemed = snxRedeemed.multiplyDecimal(SafeDecimalMath.unit().add(liquidationPenalty));
if (totalRedeemed > collateralForAccount) {
totalRedeemed = collateralForAccount;
amountToLiquidate = _snxToUSD(
collateralForAccount.divideDecimal(SafeDecimalMath.unit().add(liquidationPenalty)),
snxRate
);
}
_burnSynths(account, liquidator, amountToLiquidate, debtBalance, totalDebtIssued);
if (amountToLiquidate == amountToFixRatio) {
liquidations().removeAccountInLiquidation(account);
}
}
function cacheSNXIssuedDebt() external requireSystemActiveIfNotOwner {
bytes32[] memory currencyKeys = _availableCurrencyKeysWithOptionalSNX(false);
(uint[] memory values, bool isInvalid) = currentSNXIssuedDebtForCurrencies(currencyKeys);
uint numValues = values.length;
uint snxCollateralDebt;
for (uint i; i < numValues; i++) {
snxCollateralDebt = snxCollateralDebt.add(values[i]);
}
bytes32[] memory debtKeys = new bytes32[](2);
debtKeys[0] = CACHED_SNX_ISSUED_DEBT;
debtKeys[1] = CACHED_SNX_ISSUED_DEBT_TIMESTAMP;
uint[] memory debtValues = new uint[](2);
debtValues[0] = snxCollateralDebt;
debtValues[1] = block.timestamp;
IFlexibleStorage store = flexibleStorage();
store.setUIntValues(CONTRACT_NAME, currencyKeys, values);
store.setUIntValues(CONTRACT_NAME, debtKeys, debtValues);
emit DebtCacheUpdated(snxCollateralDebt);
emit DebtCacheSynchronised(block.timestamp);
_changeDebtCacheValidityIfNeeded(store, isInvalid);
}
function updateSNXIssuedDebtForCurrencies(bytes32[] calldata currencyKeys) external requireSystemActiveIfNotOwner {
(uint[] memory rates, bool anyRateInvalid) = exchangeRates().ratesAndInvalidForCurrencies(currencyKeys);
_updateSNXIssuedDebtForCurrencies(currencyKeys, rates, anyRateInvalid);
}
function updateSNXIssuedDebtOnExchange(bytes32[2] calldata currencyKeys, uint[2] calldata currencyRates) external {
require(msg.sender == address(exchanger()), "Sender is not Exchanger");
bool includesSUSD = currencyKeys[0] == sUSD || currencyKeys[1] == sUSD;
uint numKeys = includesSUSD ? 2 : 3;
bytes32[] memory keys = new bytes32[](numKeys);
keys[0] = currencyKeys[0];
keys[1] = currencyKeys[1];
uint[] memory rates = new uint[](numKeys);
rates[0] = currencyRates[0];
rates[1] = currencyRates[1];
if (!includesSUSD) {
keys[2] = sUSD;
rates[2] = SafeDecimalMath.unit();
}
_updateSNXIssuedDebtForCurrencies(keys, rates, false);
}
function _requireRatesNotInvalid(bool anyRateIsInvalid) internal pure {
require(!anyRateIsInvalid, "A synth or SNX rate is invalid");
}
function _requireCanIssueOnBehalf(address issueForAddress, address from) internal view {
require(delegateApprovals().canIssueFor(issueForAddress, from), "Not approved to act on behalf");
}
function _requireCanBurnOnBehalf(address burnForAddress, address from) internal view {
require(delegateApprovals().canBurnFor(burnForAddress, from), "Not approved to act on behalf");
}
function _issueSynths(
address from,
uint amount,
bool issueMax
) internal {
(uint maxIssuable, uint existingDebt, uint totalSystemDebt, bool anyRateIsInvalid) = _remainingIssuableSynths(from);
_requireRatesNotInvalid(anyRateIsInvalid);
if (!issueMax) {
require(amount <= maxIssuable, "Amount too large");
} else {
amount = maxIssuable;
}
_addToDebtRegister(from, amount, existingDebt, totalSystemDebt);
_setLastIssueEvent(from);
synths[sUSD].issue(from, amount);
_updateSNXIssuedDebtForSynth(sUSD, SafeDecimalMath.unit());
_appendAccountIssuanceRecord(from);
}
function _burnSynths(
address debtAccount,
address burnAccount,
uint amount,
uint existingDebt,
uint totalDebtIssued
) internal returns (uint amountBurnt) {
amountBurnt = existingDebt < amount ? existingDebt : amount;
_removeFromDebtRegister(debtAccount, amountBurnt, existingDebt, totalDebtIssued);
synths[sUSD].burn(burnAccount, amountBurnt);
_updateSNXIssuedDebtForSynth(sUSD, SafeDecimalMath.unit());
_appendAccountIssuanceRecord(debtAccount);
}
function _voluntaryBurnSynths(
address from,
uint amount,
bool burnToTarget
) internal {
if (!burnToTarget) {
require(_canBurnSynths(from), "Minimum stake time not reached");
(, uint refunded, uint numEntriesSettled) = exchanger().settle(from, sUSD);
if (numEntriesSettled > 0) {
amount = exchanger().calculateAmountAfterSettlement(from, sUSD, amount, refunded);
}
}
(uint existingDebt, uint totalSystemValue, bool anyRateIsInvalid) = _debtBalanceOfAndTotalDebt(from, sUSD);
(uint maxIssuableSynthsForAccount, bool snxRateInvalid) = _maxIssuableSynths(from);
_requireRatesNotInvalid(anyRateIsInvalid || snxRateInvalid);
require(existingDebt > 0, "No debt to forgive");
if (burnToTarget) {
amount = existingDebt.sub(maxIssuableSynthsForAccount);
}
uint amountBurnt = _burnSynths(from, from, amount, existingDebt, totalSystemValue);
if (existingDebt.sub(amountBurnt) <= maxIssuableSynthsForAccount) {
liquidations().removeAccountInLiquidation(from);
}
}
function _setLastIssueEvent(address account) internal {
flexibleStorage().setUIntValue(
CONTRACT_NAME,
keccak256(abi.encodePacked(LAST_ISSUE_EVENT, account)),
block.timestamp
);
}
function _appendAccountIssuanceRecord(address from) internal {
uint initialDebtOwnership;
uint debtEntryIndex;
(initialDebtOwnership, debtEntryIndex) = synthetixState().issuanceData(from);
feePool().appendAccountIssuanceRecord(from, initialDebtOwnership, debtEntryIndex);
}
function _addToDebtRegister(
address from,
uint amount,
uint existingDebt,
uint totalDebtIssued
) internal {
ISynthetixState state = synthetixState();
uint newTotalDebtIssued = amount.add(totalDebtIssued);
uint debtPercentage = amount.divideDecimalRoundPrecise(newTotalDebtIssued);
uint delta = SafeDecimalMath.preciseUnit().sub(debtPercentage);
if (existingDebt > 0) {
debtPercentage = amount.add(existingDebt).divideDecimalRoundPrecise(newTotalDebtIssued);
} else {
state.incrementTotalIssuerCount();
}
state.setCurrentIssuanceData(from, debtPercentage);
if (state.debtLedgerLength() > 0) {
state.appendDebtLedgerValue(state.lastDebtLedgerEntry().multiplyDecimalRoundPrecise(delta));
} else {
state.appendDebtLedgerValue(SafeDecimalMath.preciseUnit());
}
}
function _removeFromDebtRegister(
address from,
uint debtToRemove,
uint existingDebt,
uint totalDebtIssued
) internal {
ISynthetixState state = synthetixState();
uint newTotalDebtIssued = totalDebtIssued.sub(debtToRemove);
uint delta = 0;
if (newTotalDebtIssued > 0) {
uint debtPercentage = debtToRemove.divideDecimalRoundPrecise(newTotalDebtIssued);
delta = SafeDecimalMath.preciseUnit().add(debtPercentage);
}
if (debtToRemove == existingDebt) {
state.setCurrentIssuanceData(from, 0);
state.decrementTotalIssuerCount();
} else {
uint newDebt = existingDebt.sub(debtToRemove);
uint newDebtPercentage = newDebt.divideDecimalRoundPrecise(newTotalDebtIssued);
state.setCurrentIssuanceData(from, newDebtPercentage);
}
state.appendDebtLedgerValue(state.lastDebtLedgerEntry().multiplyDecimalRoundPrecise(delta));
}
function _changeDebtCacheValidityIfNeeded(IFlexibleStorage store, bool currentlyInvalid) internal {
bool cacheInvalid = _cacheIsInvalid(store);
if (cacheInvalid != currentlyInvalid) {
store.setBoolValue(CONTRACT_NAME, CACHED_SNX_ISSUED_DEBT_INVALID, currentlyInvalid);
emit DebtCacheValidityChanged(currentlyInvalid);
}
}
function _updateSNXIssuedDebtForCurrencies(
bytes32[] memory currencyKeys,
uint[] memory currentRates,
bool anyRateIsInvalid
) internal {
uint numKeys = currencyKeys.length;
require(numKeys == currentRates.length, "Input array lengths differ");
IFlexibleStorage store = flexibleStorage();
uint[] memory cachedValues = store.getUIntValues(CONTRACT_NAME, currencyKeys);
uint[] memory currentValues = _issuedSynthValues(currencyKeys, currentRates);
store.setUIntValues(CONTRACT_NAME, currencyKeys, currentValues);
uint cachedSum;
uint currentSum;
for (uint i = 0; i < numKeys; i++) {
cachedSum = cachedSum.add(cachedValues[i]);
currentSum = currentSum.add(currentValues[i]);
}
if (cachedSum != currentSum) {
uint debt = store.getUIntValue(CONTRACT_NAME, CACHED_SNX_ISSUED_DEBT);
require(cachedSum <= debt, "Cached synth sum exceeds total debt");
debt = debt.sub(cachedSum).add(currentSum);
store.setUIntValue(CONTRACT_NAME, CACHED_SNX_ISSUED_DEBT, debt);
emit DebtCacheUpdated(debt);
}
if (anyRateIsInvalid) {
_changeDebtCacheValidityIfNeeded(store, anyRateIsInvalid);
}
}
function _updateSNXIssuedDebtForSynth(bytes32 currencyKey, uint currencyRate) internal {
bytes32[] memory synthKeyArray = new bytes32[](1);
synthKeyArray[0] = currencyKey;
uint[] memory synthRateArray = new uint[](1);
synthRateArray[0] = currencyRate;
_updateSNXIssuedDebtForCurrencies(synthKeyArray, synthRateArray, false);
}
function _onlySynthetix() internal view {
require(msg.sender == address(synthetix()), "Issuer: Only the synthetix contract can perform this action");
}
modifier onlySynthetix() {
_onlySynthetix();
_;
}
function _requireSystemActiveIfNotOwner() internal view {
if (msg.sender != owner) {
systemStatus().requireSystemActive();
}
}
modifier requireSystemActiveIfNotOwner() {
_requireSystemActiveIfNotOwner();
_;
}
event SynthAdded(bytes32 currencyKey, address synth);
event SynthRemoved(bytes32 currencyKey, address synth);
event DebtCacheUpdated(uint cachedDebt);
event DebtCacheSynchronised(uint timestamp);
event DebtCacheValidityChanged(bool indexed isInvalid);
}