文件 1 的 1:Exchanger.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 {
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 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 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);
}
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 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);
}
}
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;
}
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 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 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;
}
interface IExchangeState {
struct ExchangeEntry {
bytes32 src;
uint amount;
bytes32 dest;
uint amountReceived;
uint exchangeFeeRate;
uint timestamp;
uint roundIdForSrc;
uint roundIdForDest;
}
function getLengthOfEntries(address account, bytes32 currencyKey) external view returns (uint);
function getEntryAt(
address account,
bytes32 currencyKey,
uint index
)
external
view
returns (
bytes32 src,
uint amount,
bytes32 dest,
uint amountReceived,
uint exchangeFeeRate,
uint timestamp,
uint roundIdForSrc,
uint roundIdForDest
);
function getMaxTimestamp(address account, bytes32 currencyKey) external view returns (uint);
function appendExchangeEntry(
address account,
bytes32 src,
uint amount,
bytes32 dest,
uint amountReceived,
uint exchangeFeeRate,
uint timestamp,
uint roundIdForSrc,
uint roundIdForDest
) external;
function removeEntries(address account, bytes32 currencyKey) 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 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 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 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 ITradingRewards {
function getAvailableRewards() external view returns (uint);
function getUnassignedRewards() external view returns (uint);
function getRewardsToken() external view returns (address);
function getPeriodController() external view returns (address);
function getCurrentPeriod() external view returns (uint);
function getPeriodIsClaimable(uint periodID) external view returns (bool);
function getPeriodIsFinalized(uint periodID) external view returns (bool);
function getPeriodRecordedFees(uint periodID) external view returns (uint);
function getPeriodTotalRewards(uint periodID) external view returns (uint);
function getPeriodAvailableRewards(uint periodID) external view returns (uint);
function getUnaccountedFeesForAccountForPeriod(address account, uint periodID) external view returns (uint);
function getAvailableRewardsForAccountForPeriod(address account, uint periodID) external view returns (uint);
function getAvailableRewardsForAccountForPeriods(address account, uint[] calldata periodIDs)
external
view
returns (uint totalRewards);
function claimRewardsForPeriod(uint periodID) external;
function claimRewardsForPeriods(uint[] calldata periodIDs) external;
function recordExchangeFeeForAccount(uint usdFeeAmount, address account) external;
function closeCurrentPeriodWithRewards(uint rewards) external;
function recoverEther(address payable recoverAddress) external;
function recoverTokens(address tokenAddress, address recoverAddress) external;
function recoverUnassignedRewardTokens(address recoverAddress) external;
function recoverAssignedRewardTokensAndDestroyPeriod(address recoverAddress, uint periodID) external;
function setPeriodController(address newPeriodController) external;
}
interface ISynthetixInternal {
function emitExchangeTracking(
bytes32 trackingCode,
bytes32 toCurrencyKey,
uint256 toAmount
) external;
function emitSynthExchange(
address account,
bytes32 fromCurrencyKey,
uint fromAmount,
bytes32 toCurrencyKey,
uint toAmount,
address toAddress
) external;
function emitExchangeReclaim(
address account,
bytes32 currencyKey,
uint amount
) external;
function emitExchangeRebate(
address account,
bytes32 currencyKey,
uint amount
) external;
}
contract Exchanger is Owned, MixinResolver, MixinSystemSettings, IExchanger {
using SafeMath for uint;
using SafeDecimalMath for uint;
struct ExchangeEntrySettlement {
bytes32 src;
uint amount;
bytes32 dest;
uint reclaim;
uint rebate;
uint srcRoundIdAtPeriodEnd;
uint destRoundIdAtPeriodEnd;
uint timestamp;
}
bytes32 private constant sUSD = "sUSD";
uint public constant CIRCUIT_BREAKER_SUSPENSION_REASON = 65;
mapping(bytes32 => uint) public lastExchangeRate;
bytes32 private constant CONTRACT_SYSTEMSTATUS = "SystemStatus";
bytes32 private constant CONTRACT_EXCHANGESTATE = "ExchangeState";
bytes32 private constant CONTRACT_EXRATES = "ExchangeRates";
bytes32 private constant CONTRACT_SYNTHETIX = "Synthetix";
bytes32 private constant CONTRACT_FEEPOOL = "FeePool";
bytes32 private constant CONTRACT_TRADING_REWARDS = "TradingRewards";
bytes32 private constant CONTRACT_DELEGATEAPPROVALS = "DelegateApprovals";
bytes32 private constant CONTRACT_ISSUER = "Issuer";
bytes32[24] private addressesToCache = [
CONTRACT_SYSTEMSTATUS,
CONTRACT_EXCHANGESTATE,
CONTRACT_EXRATES,
CONTRACT_SYNTHETIX,
CONTRACT_FEEPOOL,
CONTRACT_TRADING_REWARDS,
CONTRACT_DELEGATEAPPROVALS,
CONTRACT_ISSUER
];
constructor(address _owner, address _resolver)
public
Owned(_owner)
MixinResolver(_resolver, addressesToCache)
MixinSystemSettings()
{}
function systemStatus() internal view returns (ISystemStatus) {
return ISystemStatus(requireAndGetAddress(CONTRACT_SYSTEMSTATUS, "Missing SystemStatus address"));
}
function exchangeState() internal view returns (IExchangeState) {
return IExchangeState(requireAndGetAddress(CONTRACT_EXCHANGESTATE, "Missing ExchangeState address"));
}
function exchangeRates() internal view returns (IExchangeRates) {
return IExchangeRates(requireAndGetAddress(CONTRACT_EXRATES, "Missing ExchangeRates address"));
}
function synthetix() internal view returns (ISynthetix) {
return ISynthetix(requireAndGetAddress(CONTRACT_SYNTHETIX, "Missing Synthetix address"));
}
function feePool() internal view returns (IFeePool) {
return IFeePool(requireAndGetAddress(CONTRACT_FEEPOOL, "Missing FeePool address"));
}
function tradingRewards() internal view returns (ITradingRewards) {
return ITradingRewards(requireAndGetAddress(CONTRACT_TRADING_REWARDS, "Missing TradingRewards address"));
}
function delegateApprovals() internal view returns (IDelegateApprovals) {
return IDelegateApprovals(requireAndGetAddress(CONTRACT_DELEGATEAPPROVALS, "Missing DelegateApprovals address"));
}
function issuer() internal view returns (IIssuer) {
return IIssuer(requireAndGetAddress(CONTRACT_ISSUER, "Missing Issuer address"));
}
function maxSecsLeftInWaitingPeriod(address account, bytes32 currencyKey) public view returns (uint) {
return secsLeftInWaitingPeriodForExchange(exchangeState().getMaxTimestamp(account, currencyKey));
}
function waitingPeriodSecs() external view returns (uint) {
return getWaitingPeriodSecs();
}
function tradingRewardsEnabled() external view returns (bool) {
return getTradingRewardsEnabled();
}
function priceDeviationThresholdFactor() external view returns (uint) {
return getPriceDeviationThresholdFactor();
}
function settlementOwing(address account, bytes32 currencyKey)
public
view
returns (
uint reclaimAmount,
uint rebateAmount,
uint numEntries
)
{
(reclaimAmount, rebateAmount, numEntries, ) = _settlementOwing(account, currencyKey);
}
function _settlementOwing(address account, bytes32 currencyKey)
internal
view
returns (
uint reclaimAmount,
uint rebateAmount,
uint numEntries,
ExchangeEntrySettlement[] memory
)
{
numEntries = exchangeState().getLengthOfEntries(account, currencyKey);
ExchangeEntrySettlement[] memory settlements = new ExchangeEntrySettlement[](numEntries);
for (uint i = 0; i < numEntries; i++) {
uint reclaim;
uint rebate;
IExchangeState.ExchangeEntry memory exchangeEntry = _getExchangeEntry(account, currencyKey, i);
(uint srcRoundIdAtPeriodEnd, uint destRoundIdAtPeriodEnd) = getRoundIdsAtPeriodEnd(exchangeEntry);
uint destinationAmount = exchangeRates().effectiveValueAtRound(
exchangeEntry.src,
exchangeEntry.amount,
exchangeEntry.dest,
srcRoundIdAtPeriodEnd,
destRoundIdAtPeriodEnd
);
uint amountShouldHaveReceived = _getAmountReceivedForExchange(destinationAmount, exchangeEntry.exchangeFeeRate);
if (!_isDeviationAboveThreshold(exchangeEntry.amountReceived, amountShouldHaveReceived)) {
if (exchangeEntry.amountReceived > amountShouldHaveReceived) {
reclaim = exchangeEntry.amountReceived.sub(amountShouldHaveReceived);
reclaimAmount = reclaimAmount.add(reclaim);
} else if (amountShouldHaveReceived > exchangeEntry.amountReceived) {
rebate = amountShouldHaveReceived.sub(exchangeEntry.amountReceived);
rebateAmount = rebateAmount.add(rebate);
}
}
settlements[i] = ExchangeEntrySettlement({
src: exchangeEntry.src,
amount: exchangeEntry.amount,
dest: exchangeEntry.dest,
reclaim: reclaim,
rebate: rebate,
srcRoundIdAtPeriodEnd: srcRoundIdAtPeriodEnd,
destRoundIdAtPeriodEnd: destRoundIdAtPeriodEnd,
timestamp: exchangeEntry.timestamp
});
}
return (reclaimAmount, rebateAmount, numEntries, settlements);
}
function _getExchangeEntry(
address account,
bytes32 currencyKey,
uint index
) internal view returns (IExchangeState.ExchangeEntry memory) {
(
bytes32 src,
uint amount,
bytes32 dest,
uint amountReceived,
uint exchangeFeeRate,
uint timestamp,
uint roundIdForSrc,
uint roundIdForDest
) = exchangeState().getEntryAt(account, currencyKey, index);
return
IExchangeState.ExchangeEntry({
src: src,
amount: amount,
dest: dest,
amountReceived: amountReceived,
exchangeFeeRate: exchangeFeeRate,
timestamp: timestamp,
roundIdForSrc: roundIdForSrc,
roundIdForDest: roundIdForDest
});
}
function hasWaitingPeriodOrSettlementOwing(address account, bytes32 currencyKey) external view returns (bool) {
if (maxSecsLeftInWaitingPeriod(account, currencyKey) != 0) {
return true;
}
(uint reclaimAmount, , , ) = _settlementOwing(account, currencyKey);
return reclaimAmount > 0;
}
function calculateAmountAfterSettlement(
address from,
bytes32 currencyKey,
uint amount,
uint refunded
) public view returns (uint amountAfterSettlement) {
amountAfterSettlement = amount;
uint balanceOfSourceAfterSettlement = IERC20(address(issuer().synths(currencyKey))).balanceOf(from);
if (amountAfterSettlement > balanceOfSourceAfterSettlement) {
amountAfterSettlement = balanceOfSourceAfterSettlement;
}
if (refunded > 0) {
amountAfterSettlement = amountAfterSettlement.add(refunded);
}
}
function isSynthRateInvalid(bytes32 currencyKey) external view returns (bool) {
return _isSynthRateInvalid(currencyKey, exchangeRates().rateForCurrency(currencyKey));
}
function exchange(
address from,
bytes32 sourceCurrencyKey,
uint sourceAmount,
bytes32 destinationCurrencyKey,
address destinationAddress
) external onlySynthetixorSynth returns (uint amountReceived) {
uint fee;
(amountReceived, fee) = _exchange(from, sourceCurrencyKey, sourceAmount, destinationCurrencyKey, destinationAddress);
_processTradingRewards(fee, destinationAddress);
}
function exchangeOnBehalf(
address exchangeForAddress,
address from,
bytes32 sourceCurrencyKey,
uint sourceAmount,
bytes32 destinationCurrencyKey
) external onlySynthetixorSynth returns (uint amountReceived) {
require(delegateApprovals().canExchangeFor(exchangeForAddress, from), "Not approved to act on behalf");
uint fee;
(amountReceived, fee) = _exchange(
exchangeForAddress,
sourceCurrencyKey,
sourceAmount,
destinationCurrencyKey,
exchangeForAddress
);
_processTradingRewards(fee, exchangeForAddress);
}
function exchangeWithTracking(
address from,
bytes32 sourceCurrencyKey,
uint sourceAmount,
bytes32 destinationCurrencyKey,
address destinationAddress,
address originator,
bytes32 trackingCode
) external onlySynthetixorSynth returns (uint amountReceived) {
uint fee;
(amountReceived, fee) = _exchange(from, sourceCurrencyKey, sourceAmount, destinationCurrencyKey, destinationAddress);
_processTradingRewards(fee, originator);
_emitTrackingEvent(trackingCode, destinationCurrencyKey, amountReceived);
}
function exchangeOnBehalfWithTracking(
address exchangeForAddress,
address from,
bytes32 sourceCurrencyKey,
uint sourceAmount,
bytes32 destinationCurrencyKey,
address originator,
bytes32 trackingCode
) external onlySynthetixorSynth returns (uint amountReceived) {
require(delegateApprovals().canExchangeFor(exchangeForAddress, from), "Not approved to act on behalf");
uint fee;
(amountReceived, fee) = _exchange(
exchangeForAddress,
sourceCurrencyKey,
sourceAmount,
destinationCurrencyKey,
exchangeForAddress
);
_processTradingRewards(fee, originator);
_emitTrackingEvent(trackingCode, destinationCurrencyKey, amountReceived);
}
function _emitTrackingEvent(
bytes32 trackingCode,
bytes32 toCurrencyKey,
uint256 toAmount
) internal {
ISynthetixInternal(address(synthetix())).emitExchangeTracking(trackingCode, toCurrencyKey, toAmount);
}
function _processTradingRewards(uint fee, address originator) internal {
if (fee > 0 && originator != address(0) && getTradingRewardsEnabled()) {
tradingRewards().recordExchangeFeeForAccount(fee, originator);
}
}
function _exchange(
address from,
bytes32 sourceCurrencyKey,
uint sourceAmount,
bytes32 destinationCurrencyKey,
address destinationAddress
) internal returns (uint amountReceived, uint fee) {
_ensureCanExchange(sourceCurrencyKey, sourceAmount, destinationCurrencyKey);
(, uint refunded, uint numEntriesSettled) = _internalSettle(from, sourceCurrencyKey);
uint sourceAmountAfterSettlement = sourceAmount;
if (numEntriesSettled > 0) {
sourceAmountAfterSettlement = calculateAmountAfterSettlement(from, sourceCurrencyKey, sourceAmount, refunded);
if (sourceAmountAfterSettlement == 0) {
return (0, 0);
}
}
uint exchangeFeeRate;
uint sourceRate;
uint destinationRate;
(amountReceived, fee, exchangeFeeRate, sourceRate, destinationRate) = _getAmountsForExchangeMinusFees(
sourceAmountAfterSettlement,
sourceCurrencyKey,
destinationCurrencyKey
);
if (_isSynthRateInvalid(sourceCurrencyKey, sourceRate)) {
systemStatus().suspendSynth(sourceCurrencyKey, CIRCUIT_BREAKER_SUSPENSION_REASON);
return (0, 0);
} else {
lastExchangeRate[sourceCurrencyKey] = sourceRate;
}
if (_isSynthRateInvalid(destinationCurrencyKey, destinationRate)) {
systemStatus().suspendSynth(destinationCurrencyKey, CIRCUIT_BREAKER_SUSPENSION_REASON);
return (0, 0);
} else {
lastExchangeRate[destinationCurrencyKey] = destinationRate;
}
issuer().synths(sourceCurrencyKey).burn(from, sourceAmountAfterSettlement);
issuer().synths(destinationCurrencyKey).issue(destinationAddress, amountReceived);
if (fee > 0) {
fee = exchangeRates().effectiveValue(destinationCurrencyKey, fee, sUSD);
issuer().synths(sUSD).issue(feePool().FEE_ADDRESS(), fee);
feePool().recordFeePaid(fee);
}
ISynthetixInternal(address(synthetix())).emitSynthExchange(
from,
sourceCurrencyKey,
sourceAmountAfterSettlement,
destinationCurrencyKey,
amountReceived,
destinationAddress
);
appendExchange(
destinationAddress,
sourceCurrencyKey,
sourceAmountAfterSettlement,
destinationCurrencyKey,
amountReceived,
exchangeFeeRate
);
}
function settle(address from, bytes32 currencyKey)
external
returns (
uint reclaimed,
uint refunded,
uint numEntriesSettled
)
{
systemStatus().requireSynthActive(currencyKey);
return _internalSettle(from, currencyKey);
}
function suspendSynthWithInvalidRate(bytes32 currencyKey) external {
systemStatus().requireSystemActive();
require(issuer().synths(currencyKey) != ISynth(0), "No such synth");
require(_isSynthRateInvalid(currencyKey, exchangeRates().rateForCurrency(currencyKey)), "Synth price is valid");
systemStatus().suspendSynth(currencyKey, CIRCUIT_BREAKER_SUSPENSION_REASON);
}
function setLastExchangeRateForSynth(bytes32 currencyKey, uint rate) external onlyExchangeRates {
require(rate > 0, "Rate must be above 0");
lastExchangeRate[currencyKey] = rate;
}
function _ensureCanExchange(
bytes32 sourceCurrencyKey,
uint sourceAmount,
bytes32 destinationCurrencyKey
) internal view {
require(sourceCurrencyKey != destinationCurrencyKey, "Can't be same synth");
require(sourceAmount > 0, "Zero amount");
bytes32[] memory synthKeys = new bytes32[](2);
synthKeys[0] = sourceCurrencyKey;
synthKeys[1] = destinationCurrencyKey;
require(!exchangeRates().anyRateIsInvalid(synthKeys), "Src/dest rate invalid or not found");
}
function _isSynthRateInvalid(bytes32 currencyKey, uint currentRate) internal view returns (bool) {
if (currentRate == 0) {
return true;
}
uint lastRateFromExchange = lastExchangeRate[currencyKey];
if (lastRateFromExchange > 0) {
return _isDeviationAboveThreshold(lastRateFromExchange, currentRate);
}
(uint[] memory rates, ) = exchangeRates().ratesAndUpdatedTimeForCurrencyLastNRounds(currencyKey, 4);
for (uint i = 1; i < rates.length; i++) {
if (rates[i] > 0 && _isDeviationAboveThreshold(rates[i], currentRate)) {
return true;
}
}
return false;
}
function _isDeviationAboveThreshold(uint base, uint comparison) internal view returns (bool) {
if (base == 0 || comparison == 0) {
return true;
}
uint factor;
if (comparison > base) {
factor = comparison.divideDecimal(base);
} else {
factor = base.divideDecimal(comparison);
}
return factor >= getPriceDeviationThresholdFactor();
}
function _internalSettle(address from, bytes32 currencyKey)
internal
returns (
uint reclaimed,
uint refunded,
uint numEntriesSettled
)
{
require(maxSecsLeftInWaitingPeriod(from, currencyKey) == 0, "Cannot settle during waiting period");
(
uint reclaimAmount,
uint rebateAmount,
uint entries,
ExchangeEntrySettlement[] memory settlements
) = _settlementOwing(from, currencyKey);
if (reclaimAmount > rebateAmount) {
reclaimed = reclaimAmount.sub(rebateAmount);
reclaim(from, currencyKey, reclaimed);
} else if (rebateAmount > reclaimAmount) {
refunded = rebateAmount.sub(reclaimAmount);
refund(from, currencyKey, refunded);
}
for (uint i = 0; i < settlements.length; i++) {
emit ExchangeEntrySettled(
from,
settlements[i].src,
settlements[i].amount,
settlements[i].dest,
settlements[i].reclaim,
settlements[i].rebate,
settlements[i].srcRoundIdAtPeriodEnd,
settlements[i].destRoundIdAtPeriodEnd,
settlements[i].timestamp
);
}
numEntriesSettled = entries;
exchangeState().removeEntries(from, currencyKey);
}
function reclaim(
address from,
bytes32 currencyKey,
uint amount
) internal {
issuer().synths(currencyKey).burn(from, amount);
ISynthetixInternal(address(synthetix())).emitExchangeReclaim(from, currencyKey, amount);
}
function refund(
address from,
bytes32 currencyKey,
uint amount
) internal {
issuer().synths(currencyKey).issue(from, amount);
ISynthetixInternal(address(synthetix())).emitExchangeRebate(from, currencyKey, amount);
}
function secsLeftInWaitingPeriodForExchange(uint timestamp) internal view returns (uint) {
uint _waitingPeriodSecs = getWaitingPeriodSecs();
if (timestamp == 0 || now >= timestamp.add(_waitingPeriodSecs)) {
return 0;
}
return timestamp.add(_waitingPeriodSecs).sub(now);
}
function feeRateForExchange(bytes32 sourceCurrencyKey, bytes32 destinationCurrencyKey)
external
view
returns (uint exchangeFeeRate)
{
exchangeFeeRate = _feeRateForExchange(sourceCurrencyKey, destinationCurrencyKey);
}
function _feeRateForExchange(
bytes32,
bytes32 destinationCurrencyKey
) internal view returns (uint exchangeFeeRate) {
return getExchangeFeeRate(destinationCurrencyKey);
}
function getAmountsForExchange(
uint sourceAmount,
bytes32 sourceCurrencyKey,
bytes32 destinationCurrencyKey
)
external
view
returns (
uint amountReceived,
uint fee,
uint exchangeFeeRate
)
{
(amountReceived, fee, exchangeFeeRate, , ) = _getAmountsForExchangeMinusFees(
sourceAmount,
sourceCurrencyKey,
destinationCurrencyKey
);
}
function _getAmountsForExchangeMinusFees(
uint sourceAmount,
bytes32 sourceCurrencyKey,
bytes32 destinationCurrencyKey
)
internal
view
returns (
uint amountReceived,
uint fee,
uint exchangeFeeRate,
uint sourceRate,
uint destinationRate
)
{
uint destinationAmount;
(destinationAmount, sourceRate, destinationRate) = exchangeRates().effectiveValueAndRates(
sourceCurrencyKey,
sourceAmount,
destinationCurrencyKey
);
exchangeFeeRate = _feeRateForExchange(sourceCurrencyKey, destinationCurrencyKey);
amountReceived = _getAmountReceivedForExchange(destinationAmount, exchangeFeeRate);
fee = destinationAmount.sub(amountReceived);
}
function _getAmountReceivedForExchange(uint destinationAmount, uint exchangeFeeRate)
internal
pure
returns (uint amountReceived)
{
amountReceived = destinationAmount.multiplyDecimal(SafeDecimalMath.unit().sub(exchangeFeeRate));
}
function appendExchange(
address account,
bytes32 src,
uint amount,
bytes32 dest,
uint amountReceived,
uint exchangeFeeRate
) internal {
IExchangeRates exRates = exchangeRates();
uint roundIdForSrc = exRates.getCurrentRoundId(src);
uint roundIdForDest = exRates.getCurrentRoundId(dest);
exchangeState().appendExchangeEntry(
account,
src,
amount,
dest,
amountReceived,
exchangeFeeRate,
now,
roundIdForSrc,
roundIdForDest
);
emit ExchangeEntryAppended(
account,
src,
amount,
dest,
amountReceived,
exchangeFeeRate,
roundIdForSrc,
roundIdForDest
);
}
function getRoundIdsAtPeriodEnd(IExchangeState.ExchangeEntry memory exchangeEntry)
internal
view
returns (uint srcRoundIdAtPeriodEnd, uint destRoundIdAtPeriodEnd)
{
IExchangeRates exRates = exchangeRates();
uint _waitingPeriodSecs = getWaitingPeriodSecs();
srcRoundIdAtPeriodEnd = exRates.getLastRoundIdBeforeElapsedSecs(
exchangeEntry.src,
exchangeEntry.roundIdForSrc,
exchangeEntry.timestamp,
_waitingPeriodSecs
);
destRoundIdAtPeriodEnd = exRates.getLastRoundIdBeforeElapsedSecs(
exchangeEntry.dest,
exchangeEntry.roundIdForDest,
exchangeEntry.timestamp,
_waitingPeriodSecs
);
}
modifier onlySynthetixorSynth() {
ISynthetix _synthetix = synthetix();
require(
msg.sender == address(_synthetix) || _synthetix.synthsByAddress(msg.sender) != bytes32(0),
"Exchanger: Only synthetix or a synth contract can perform this action"
);
_;
}
modifier onlyExchangeRates() {
IExchangeRates _exchangeRates = exchangeRates();
require(msg.sender == address(_exchangeRates), "Restricted to ExchangeRates");
_;
}
event ExchangeEntryAppended(
address indexed account,
bytes32 src,
uint256 amount,
bytes32 dest,
uint256 amountReceived,
uint256 exchangeFeeRate,
uint256 roundIdForSrc,
uint256 roundIdForDest
);
event ExchangeEntrySettled(
address indexed from,
bytes32 src,
uint256 amount,
bytes32 dest,
uint256 reclaim,
uint256 rebate,
uint256 srcRoundIdAtPeriodEnd,
uint256 destRoundIdAtPeriodEnd,
uint256 exchangeTimestamp
);
}