文件 1 的 1:ExchangerWithFeeRecAlternatives.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 allNetworksDebtInfo()
external
view
returns (
uint256 debt,
uint256 sharesSupply,
bool isStale
);
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 getSynths(bytes32[] calldata currencyKeys) external view returns (ISynth[] memory);
function synthsByAddress(address synthAddress) external view returns (bytes32);
function totalIssuedSynths(bytes32 currencyKey, bool excludeOtherCollateral) external view returns (uint);
function transferableSynthetixAndAnyRateIsInvalid(address account, uint balance)
external
view
returns (uint transferable, bool anyRateIsInvalid);
function liquidationAmounts(address account, bool isSelfLiquidation)
external
view
returns (
uint totalRedeemed,
uint debtToRemove,
uint escrowToLiquidate,
uint initialDebtBalance
);
function addSynths(ISynth[] calldata synthsToAdd) external;
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 burnForRedemption(
address deprecatedSynthProxy,
address account,
uint balance
) external;
function setCurrentPeriodId(uint128 periodId) external;
function liquidateAccount(address account, bool isSelfLiquidation)
external
returns (
uint totalRedeemed,
uint debtRemoved,
uint escrowToLiquidate
);
function issueSynthsWithoutDebt(
bytes32 currencyKey,
address to,
uint amount
) external returns (bool rateInvalid);
function burnSynthsWithoutDebt(
bytes32 currencyKey,
address to,
uint amount
) external returns (bool rateInvalid);
}
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++) {
bytes32 name = names[i];
address destination = destinations[i];
repository[name] = destination;
emit AddressImported(name, destination);
}
}
function rebuildCaches(MixinResolver[] calldata destinations) external {
for (uint i = 0; i < destinations.length; i++) {
destinations[i].rebuildCache();
}
}
function areAddressesImported(bytes32[] calldata names, address[] calldata destinations) external view returns (bool) {
for (uint i = 0; i < names.length; i++) {
if (repository[names[i]] != destinations[i]) {
return false;
}
}
return true;
}
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));
}
event AddressImported(bytes32 name, address destination);
}
contract MixinResolver {
AddressResolver public resolver;
mapping(bytes32 => address) private addressCache;
constructor(address _resolver) internal {
resolver = AddressResolver(_resolver);
}
function combineArrays(bytes32[] memory first, bytes32[] memory second)
internal
pure
returns (bytes32[] memory combination)
{
combination = new bytes32[](first.length + second.length);
for (uint i = 0; i < first.length; i++) {
combination[i] = first[i];
}
for (uint j = 0; j < second.length; j++) {
combination[first.length + j] = second[j];
}
}
function resolverAddressesRequired() public view returns (bytes32[] memory addresses) {}
function rebuildCache() public {
bytes32[] memory requiredAddresses = resolverAddressesRequired();
for (uint i = 0; i < requiredAddresses.length; i++) {
bytes32 name = requiredAddresses[i];
address destination =
resolver.requireAndGetAddress(name, string(abi.encodePacked("Resolver missing target: ", name)));
addressCache[name] = destination;
emit CacheUpdated(name, destination);
}
}
function isResolverCached() external view returns (bool) {
bytes32[] memory requiredAddresses = resolverAddressesRequired();
for (uint i = 0; i < requiredAddresses.length; i++) {
bytes32 name = requiredAddresses[i];
if (resolver.getAddress(name) != addressCache[name] || addressCache[name] == address(0)) {
return false;
}
}
return true;
}
function requireAndGetAddress(bytes32 name) internal view returns (address) {
address _foundAddress = addressCache[name];
require(_foundAddress != address(0), string(abi.encodePacked("Missing address: ", name)));
return _foundAddress;
}
event CacheUpdated(bytes32 name, address destination);
}
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_ESCROW_DURATION = "liquidationEscrowDuration";
bytes32 internal constant SETTING_LIQUIDATION_PENALTY = "liquidationPenalty";
bytes32 internal constant SETTING_SNX_LIQUIDATION_PENALTY = "snxLiquidationPenalty";
bytes32 internal constant SETTING_SELF_LIQUIDATION_PENALTY = "selfLiquidationPenalty";
bytes32 internal constant SETTING_FLAG_REWARD = "flagReward";
bytes32 internal constant SETTING_LIQUIDATE_REWARD = "liquidateReward";
bytes32 internal constant SETTING_RATE_STALE_PERIOD = "rateStalePeriod";
bytes32 internal constant SETTING_EXCHANGE_FEE_RATE = "exchangeFeeRate";
bytes32 internal constant SETTING_EXCHANGE_DYNAMIC_FEE_THRESHOLD = "exchangeDynamicFeeThreshold";
bytes32 internal constant SETTING_EXCHANGE_DYNAMIC_FEE_WEIGHT_DECAY = "exchangeDynamicFeeWeightDecay";
bytes32 internal constant SETTING_EXCHANGE_DYNAMIC_FEE_ROUNDS = "exchangeDynamicFeeRounds";
bytes32 internal constant SETTING_EXCHANGE_MAX_DYNAMIC_FEE = "exchangeMaxDynamicFee";
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 internal constant SETTING_CROSS_DOMAIN_DEPOSIT_GAS_LIMIT = "crossDomainDepositGasLimit";
bytes32 internal constant SETTING_CROSS_DOMAIN_ESCROW_GAS_LIMIT = "crossDomainEscrowGasLimit";
bytes32 internal constant SETTING_CROSS_DOMAIN_REWARD_GAS_LIMIT = "crossDomainRewardGasLimit";
bytes32 internal constant SETTING_CROSS_DOMAIN_WITHDRAWAL_GAS_LIMIT = "crossDomainWithdrawalGasLimit";
bytes32 internal constant SETTING_CROSS_DOMAIN_FEE_PERIOD_CLOSE_GAS_LIMIT = "crossDomainCloseGasLimit";
bytes32 internal constant SETTING_CROSS_DOMAIN_RELAY_GAS_LIMIT = "crossDomainRelayGasLimit";
bytes32 internal constant SETTING_ETHER_WRAPPER_MAX_ETH = "etherWrapperMaxETH";
bytes32 internal constant SETTING_ETHER_WRAPPER_MINT_FEE_RATE = "etherWrapperMintFeeRate";
bytes32 internal constant SETTING_ETHER_WRAPPER_BURN_FEE_RATE = "etherWrapperBurnFeeRate";
bytes32 internal constant SETTING_WRAPPER_MAX_TOKEN_AMOUNT = "wrapperMaxTokens";
bytes32 internal constant SETTING_WRAPPER_MINT_FEE_RATE = "wrapperMintFeeRate";
bytes32 internal constant SETTING_WRAPPER_BURN_FEE_RATE = "wrapperBurnFeeRate";
bytes32 internal constant SETTING_INTERACTION_DELAY = "interactionDelay";
bytes32 internal constant SETTING_COLLAPSE_FEE_RATE = "collapseFeeRate";
bytes32 internal constant SETTING_ATOMIC_MAX_VOLUME_PER_BLOCK = "atomicMaxVolumePerBlock";
bytes32 internal constant SETTING_ATOMIC_TWAP_WINDOW = "atomicTwapWindow";
bytes32 internal constant SETTING_ATOMIC_EQUIVALENT_FOR_DEX_PRICING = "atomicEquivalentForDexPricing";
bytes32 internal constant SETTING_ATOMIC_EXCHANGE_FEE_RATE = "atomicExchangeFeeRate";
bytes32 internal constant SETTING_ATOMIC_VOLATILITY_CONSIDERATION_WINDOW = "atomicVolConsiderationWindow";
bytes32 internal constant SETTING_ATOMIC_VOLATILITY_UPDATE_THRESHOLD = "atomicVolUpdateThreshold";
bytes32 internal constant SETTING_PURE_CHAINLINK_PRICE_FOR_ATOMIC_SWAPS_ENABLED = "pureChainlinkForAtomicsEnabled";
bytes32 internal constant SETTING_CROSS_SYNTH_TRANSFER_ENABLED = "crossChainSynthTransferEnabled";
bytes32 internal constant CONTRACT_FLEXIBLESTORAGE = "FlexibleStorage";
enum CrossDomainMessageGasLimits {Deposit, Escrow, Reward, Withdrawal, CloseFeePeriod, Relay}
struct DynamicFeeConfig {
uint threshold;
uint weightDecay;
uint rounds;
uint maxFee;
}
constructor(address _resolver) internal MixinResolver(_resolver) {}
function resolverAddressesRequired() public view returns (bytes32[] memory addresses) {
addresses = new bytes32[](1);
addresses[0] = CONTRACT_FLEXIBLESTORAGE;
}
function flexibleStorage() internal view returns (IFlexibleStorage) {
return IFlexibleStorage(requireAndGetAddress(CONTRACT_FLEXIBLESTORAGE));
}
function _getGasLimitSetting(CrossDomainMessageGasLimits gasLimitType) internal pure returns (bytes32) {
if (gasLimitType == CrossDomainMessageGasLimits.Deposit) {
return SETTING_CROSS_DOMAIN_DEPOSIT_GAS_LIMIT;
} else if (gasLimitType == CrossDomainMessageGasLimits.Escrow) {
return SETTING_CROSS_DOMAIN_ESCROW_GAS_LIMIT;
} else if (gasLimitType == CrossDomainMessageGasLimits.Reward) {
return SETTING_CROSS_DOMAIN_REWARD_GAS_LIMIT;
} else if (gasLimitType == CrossDomainMessageGasLimits.Withdrawal) {
return SETTING_CROSS_DOMAIN_WITHDRAWAL_GAS_LIMIT;
} else if (gasLimitType == CrossDomainMessageGasLimits.Relay) {
return SETTING_CROSS_DOMAIN_RELAY_GAS_LIMIT;
} else if (gasLimitType == CrossDomainMessageGasLimits.CloseFeePeriod) {
return SETTING_CROSS_DOMAIN_FEE_PERIOD_CLOSE_GAS_LIMIT;
} else {
revert("Unknown gas limit type");
}
}
function getCrossDomainMessageGasLimit(CrossDomainMessageGasLimits gasLimitType) internal view returns (uint) {
return flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, _getGasLimitSetting(gasLimitType));
}
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 getLiquidationEscrowDuration() internal view returns (uint) {
return flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_LIQUIDATION_ESCROW_DURATION);
}
function getLiquidationPenalty() internal view returns (uint) {
return flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_LIQUIDATION_PENALTY);
}
function getSnxLiquidationPenalty() internal view returns (uint) {
return flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_SNX_LIQUIDATION_PENALTY);
}
function getSelfLiquidationPenalty() internal view returns (uint) {
return flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_SELF_LIQUIDATION_PENALTY);
}
function getFlagReward() internal view returns (uint) {
return flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_FLAG_REWARD);
}
function getLiquidateReward() internal view returns (uint) {
return flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_LIQUIDATE_REWARD);
}
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 getExchangeDynamicFeeConfig() internal view returns (DynamicFeeConfig memory) {
bytes32[] memory keys = new bytes32[](4);
keys[0] = SETTING_EXCHANGE_DYNAMIC_FEE_THRESHOLD;
keys[1] = SETTING_EXCHANGE_DYNAMIC_FEE_WEIGHT_DECAY;
keys[2] = SETTING_EXCHANGE_DYNAMIC_FEE_ROUNDS;
keys[3] = SETTING_EXCHANGE_MAX_DYNAMIC_FEE;
uint[] memory values = flexibleStorage().getUIntValues(SETTING_CONTRACT_NAME, keys);
return DynamicFeeConfig({threshold: values[0], weightDecay: values[1], rounds: values[2], maxFee: values[3]});
}
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);
}
function getEtherWrapperMaxETH() internal view returns (uint) {
return flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_ETHER_WRAPPER_MAX_ETH);
}
function getEtherWrapperMintFeeRate() internal view returns (uint) {
return flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_ETHER_WRAPPER_MINT_FEE_RATE);
}
function getEtherWrapperBurnFeeRate() internal view returns (uint) {
return flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_ETHER_WRAPPER_BURN_FEE_RATE);
}
function getWrapperMaxTokenAmount(address wrapper) internal view returns (uint) {
return
flexibleStorage().getUIntValue(
SETTING_CONTRACT_NAME,
keccak256(abi.encodePacked(SETTING_WRAPPER_MAX_TOKEN_AMOUNT, wrapper))
);
}
function getWrapperMintFeeRate(address wrapper) internal view returns (int) {
return
flexibleStorage().getIntValue(
SETTING_CONTRACT_NAME,
keccak256(abi.encodePacked(SETTING_WRAPPER_MINT_FEE_RATE, wrapper))
);
}
function getWrapperBurnFeeRate(address wrapper) internal view returns (int) {
return
flexibleStorage().getIntValue(
SETTING_CONTRACT_NAME,
keccak256(abi.encodePacked(SETTING_WRAPPER_BURN_FEE_RATE, wrapper))
);
}
function getInteractionDelay(address collateral) internal view returns (uint) {
return
flexibleStorage().getUIntValue(
SETTING_CONTRACT_NAME,
keccak256(abi.encodePacked(SETTING_INTERACTION_DELAY, collateral))
);
}
function getCollapseFeeRate(address collateral) internal view returns (uint) {
return
flexibleStorage().getUIntValue(
SETTING_CONTRACT_NAME,
keccak256(abi.encodePacked(SETTING_COLLAPSE_FEE_RATE, collateral))
);
}
function getAtomicMaxVolumePerBlock() internal view returns (uint) {
return flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_ATOMIC_MAX_VOLUME_PER_BLOCK);
}
function getAtomicTwapWindow() internal view returns (uint) {
return flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_ATOMIC_TWAP_WINDOW);
}
function getAtomicEquivalentForDexPricing(bytes32 currencyKey) internal view returns (address) {
return
flexibleStorage().getAddressValue(
SETTING_CONTRACT_NAME,
keccak256(abi.encodePacked(SETTING_ATOMIC_EQUIVALENT_FOR_DEX_PRICING, currencyKey))
);
}
function getAtomicExchangeFeeRate(bytes32 currencyKey) internal view returns (uint) {
return
flexibleStorage().getUIntValue(
SETTING_CONTRACT_NAME,
keccak256(abi.encodePacked(SETTING_ATOMIC_EXCHANGE_FEE_RATE, currencyKey))
);
}
function getAtomicVolatilityConsiderationWindow(bytes32 currencyKey) internal view returns (uint) {
return
flexibleStorage().getUIntValue(
SETTING_CONTRACT_NAME,
keccak256(abi.encodePacked(SETTING_ATOMIC_VOLATILITY_CONSIDERATION_WINDOW, currencyKey))
);
}
function getAtomicVolatilityUpdateThreshold(bytes32 currencyKey) internal view returns (uint) {
return
flexibleStorage().getUIntValue(
SETTING_CONTRACT_NAME,
keccak256(abi.encodePacked(SETTING_ATOMIC_VOLATILITY_UPDATE_THRESHOLD, currencyKey))
);
}
function getPureChainlinkPriceForAtomicSwapsEnabled(bytes32 currencyKey) internal view returns (bool) {
return
flexibleStorage().getBoolValue(
SETTING_CONTRACT_NAME,
keccak256(abi.encodePacked(SETTING_PURE_CHAINLINK_PRICE_FOR_ATOMIC_SWAPS_ENABLED, currencyKey))
);
}
function getCrossChainSynthTransferEnabled(bytes32 currencyKey) internal view returns (uint) {
return
flexibleStorage().getUIntValue(
SETTING_CONTRACT_NAME,
keccak256(abi.encodePacked(SETTING_CROSS_SYNTH_TRANSFER_ENABLED, currencyKey))
);
}
function getExchangeMaxDynamicFee() internal view returns (uint) {
return flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_EXCHANGE_MAX_DYNAMIC_FEE);
}
function getExchangeDynamicFeeRounds() internal view returns (uint) {
return flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_EXCHANGE_DYNAMIC_FEE_ROUNDS);
}
function getExchangeDynamicFeeThreshold() internal view returns (uint) {
return flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_EXCHANGE_DYNAMIC_FEE_THRESHOLD);
}
function getExchangeDynamicFeeWeightDecay() internal view returns (uint) {
return flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_EXCHANGE_DYNAMIC_FEE_WEIGHT_DECAY);
}
}
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;
}
function floorsub(uint a, uint b) internal pure returns (uint) {
return b >= a ? 0 : a - b;
}
function signedAbs(int x) internal pure returns (int) {
return x < 0 ? -x : x;
}
function abs(int x) internal pure returns (uint) {
return uint(signedAbs(x));
}
}
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 systemSuspended() external view returns (bool);
function requireIssuanceActive() external view;
function requireExchangeActive() external view;
function requireFuturesActive() external view;
function requireFuturesMarketActive(bytes32 marketKey) external view;
function requireExchangeBetweenSynthsAllowed(bytes32 sourceCurrencyKey, bytes32 destinationCurrencyKey) external view;
function requireSynthActive(bytes32 currencyKey) external view;
function synthSuspended(bytes32 currencyKey) external view returns (bool);
function requireSynthsActive(bytes32 sourceCurrencyKey, bytes32 destinationCurrencyKey) external view;
function systemSuspension() external view returns (bool suspended, uint248 reason);
function issuanceSuspension() external view returns (bool suspended, uint248 reason);
function exchangeSuspension() external view returns (bool suspended, uint248 reason);
function futuresSuspension() external view returns (bool suspended, uint248 reason);
function synthExchangeSuspension(bytes32 currencyKey) external view returns (bool suspended, uint248 reason);
function synthSuspension(bytes32 currencyKey) external view returns (bool suspended, uint248 reason);
function futuresMarketSuspension(bytes32 marketKey) external view returns (bool suspended, uint248 reason);
function getSynthExchangeSuspensions(bytes32[] calldata synths)
external
view
returns (bool[] memory exchangeSuspensions, uint256[] memory reasons);
function getSynthSuspensions(bytes32[] calldata synths)
external
view
returns (bool[] memory suspensions, uint256[] memory reasons);
function getFuturesMarketSuspensions(bytes32[] calldata marketKeys)
external
view
returns (bool[] memory suspensions, uint256[] memory reasons);
function suspendIssuance(uint256 reason) external;
function suspendSynth(bytes32 currencyKey, uint256 reason) external;
function suspendFuturesMarket(bytes32 marketKey, uint256 reason) external;
function updateAccessControl(
bytes32 section,
address account,
bool canSuspend,
bool canResume
) external;
}
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 ICircuitBreaker {
function isInvalid(address oracleAddress, uint value) external view returns (bool);
function priceDeviationThresholdFactor() external view returns (uint);
function isDeviationAboveThreshold(uint base, uint comparison) external view returns (bool);
function lastValue(address oracleAddress) external view returns (uint);
function circuitBroken(address oracleAddress) external view returns (bool);
function resetLastValue(address[] calldata oracleAddresses, uint[] calldata values) external;
function probeCircuitBreaker(address oracleAddress, uint value) external returns (bool circuitBroken);
}
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 closeSecondary(uint snxBackedDebt, uint debtShareSupply) 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 recoverTokens(address tokenAddress, address recoverAddress) external;
function recoverUnassignedRewardTokens(address recoverAddress) external;
function recoverAssignedRewardTokensAndDestroyPeriod(address recoverAddress, uint periodID) external;
function setPeriodController(address newPeriodController) external;
}
interface IVirtualSynth {
function balanceOfUnderlying(address account) external view returns (uint);
function rate() external view returns (uint);
function readyToSettle() external view returns (bool);
function secsLeftInWaitingPeriod() external view returns (uint);
function settled() external view returns (bool);
function synth() external view returns (ISynth);
function settle(address account) external;
}
pragma experimental ABIEncoderV2;
interface IExchanger {
struct ExchangeEntrySettlement {
bytes32 src;
uint amount;
bytes32 dest;
uint reclaim;
uint rebate;
uint srcRoundIdAtPeriodEnd;
uint destRoundIdAtPeriodEnd;
uint timestamp;
}
struct ExchangeEntry {
uint sourceRate;
uint destinationRate;
uint destinationAmount;
uint exchangeFeeRate;
uint exchangeDynamicFeeRate;
uint roundIdForSrc;
uint roundIdForDest;
uint sourceAmountAfterSettlement;
}
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);
function dynamicFeeRateForExchange(bytes32 sourceCurrencyKey, bytes32 destinationCurrencyKey)
external
view
returns (uint feeRate, bool tooVolatile);
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 lastExchangeRate(bytes32 currencyKey) external view returns (uint);
function exchange(
address exchangeForAddress,
address from,
bytes32 sourceCurrencyKey,
uint sourceAmount,
bytes32 destinationCurrencyKey,
address destinationAddress,
bool virtualSynth,
address rewardAddress,
bytes32 trackingCode
) external returns (uint amountReceived, IVirtualSynth vSynth);
function exchangeAtomically(
address from,
bytes32 sourceCurrencyKey,
uint sourceAmount,
bytes32 destinationCurrencyKey,
address destinationAddress,
bytes32 trackingCode,
uint minAmount
) external returns (uint amountReceived);
function settle(address from, bytes32 currencyKey)
external
returns (
uint reclaimed,
uint refunded,
uint numEntries
);
}
interface ISynthetixInternal {
function emitExchangeTracking(
bytes32 trackingCode,
bytes32 toCurrencyKey,
uint256 toAmount,
uint256 fee
) external;
function emitSynthExchange(
address account,
bytes32 fromCurrencyKey,
uint fromAmount,
bytes32 toCurrencyKey,
uint toAmount,
address toAddress
) external;
function emitAtomicSynthExchange(
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;
}
interface IExchangerInternalDebtCache {
function updateCachedSynthDebtsWithRates(bytes32[] calldata currencyKeys, uint[] calldata currencyRates) external;
function updateCachedSynthDebts(bytes32[] calldata currencyKeys) external;
}
interface IDirectIntegrationManager {
struct ParameterIntegrationSettings {
bytes32 currencyKey;
address dexPriceAggregator;
address atomicEquivalentForDexPricing;
uint atomicExchangeFeeRate;
uint atomicTwapWindow;
uint atomicMaxVolumePerBlock;
uint atomicVolatilityConsiderationWindow;
uint atomicVolatilityUpdateThreshold;
uint exchangeFeeRate;
uint exchangeMaxDynamicFee;
uint exchangeDynamicFeeRounds;
uint exchangeDynamicFeeThreshold;
uint exchangeDynamicFeeWeightDecay;
}
function getExchangeParameters(address integration, bytes32 key)
external
view
returns (ParameterIntegrationSettings memory settings);
function setExchangeParameters(
address integration,
bytes32[] calldata currencyKeys,
ParameterIntegrationSettings calldata params
) external;
}
interface IExchangeRates {
struct RateAndUpdatedTime {
uint216 rate;
uint40 time;
}
function aggregators(bytes32 currencyKey) external view returns (address);
function aggregatorWarningFlags() external view returns (address);
function anyRateIsInvalid(bytes32[] calldata currencyKeys) external view returns (bool);
function anyRateIsInvalidAtRound(bytes32[] calldata currencyKeys, uint[] calldata roundIds) external view returns (bool);
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 effectiveValueAndRatesAtRound(
bytes32 sourceCurrencyKey,
uint sourceAmount,
bytes32 destinationCurrencyKey,
uint roundIdForSrc,
uint roundIdForDest
)
external
view
returns (
uint value,
uint sourceRate,
uint destinationRate
);
function effectiveAtomicValueAndRates(
bytes32 sourceCurrencyKey,
uint sourceAmount,
bytes32 destinationCurrencyKey
)
external
view
returns (
uint value,
uint systemValue,
uint systemSourceRate,
uint systemDestinationRate
);
function effectiveAtomicValueAndRates(
IDirectIntegrationManager.ParameterIntegrationSettings calldata sourceSettings,
uint sourceAmount,
IDirectIntegrationManager.ParameterIntegrationSettings calldata destinationSettings,
IDirectIntegrationManager.ParameterIntegrationSettings calldata usdSettings
)
external
view
returns (
uint value,
uint systemValue,
uint systemSourceRate,
uint systemDestinationRate
);
function getCurrentRoundId(bytes32 currencyKey) external view returns (uint);
function getLastRoundIdBeforeElapsedSecs(
bytes32 currencyKey,
uint startingRoundId,
uint startingTimestamp,
uint timediff
) external view returns (uint);
function lastRateUpdateTimes(bytes32 currencyKey) external view returns (uint256);
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 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,
uint roundId
) 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 synthTooVolatileForAtomicExchange(bytes32 currencyKey) external view returns (bool);
function synthTooVolatileForAtomicExchange(IDirectIntegrationManager.ParameterIntegrationSettings calldata settings)
external
view
returns (bool);
function rateWithSafetyChecks(bytes32 currencyKey)
external
returns (
uint rate,
bool broken,
bool invalid
);
}
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 IDebtCache {
function cachedDebt() external view returns (uint);
function cachedSynthDebt(bytes32 currencyKey) external view returns (uint);
function cacheTimestamp() external view returns (uint);
function cacheInvalid() external view returns (bool);
function cacheStale() external view returns (bool);
function isInitialized() external view returns (bool);
function currentSynthDebts(bytes32[] calldata currencyKeys)
external
view
returns (
uint[] memory debtValues,
uint futuresDebt,
uint excludedDebt,
bool anyRateIsInvalid
);
function cachedSynthDebts(bytes32[] calldata currencyKeys) external view returns (uint[] memory debtValues);
function totalNonSnxBackedDebt() external view returns (uint excludedDebt, bool isInvalid);
function currentDebt() external view returns (uint debt, bool anyRateIsInvalid);
function cacheInfo()
external
view
returns (
uint debt,
uint timestamp,
bool isInvalid,
bool isStale
);
function excludedIssuedDebts(bytes32[] calldata currencyKeys) external view returns (uint[] memory excludedDebts);
function updateCachedSynthDebts(bytes32[] calldata currencyKeys) external;
function updateCachedSynthDebtWithRate(bytes32 currencyKey, uint currencyRate) external;
function updateCachedSynthDebtsWithRates(bytes32[] calldata currencyKeys, uint[] calldata currencyRates) external;
function updateDebtCacheValidity(bool currentlyInvalid) external;
function purgeCachedSynthDebt(bytes32 currencyKey) external;
function takeDebtSnapshot() external;
function recordExcludedDebtChange(bytes32 currencyKey, int256 delta) external;
function updateCachedsUSDDebt(int amount) external;
function importExcludedIssuedDebts(IDebtCache prevDebtCache, IIssuer prevIssuer) 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 totalIssuedSynthsExcludeOtherCollateral(bytes32 currencyKey) external view returns (uint);
function transferableSynthetix(address account) external view returns (uint transferable);
function getFirstNonZeroEscrowIndex(address account) external view returns (uint);
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 rewardAddress,
bytes32 trackingCode
) external returns (uint amountReceived);
function exchangeWithTrackingForInitiator(
bytes32 sourceCurrencyKey,
uint sourceAmount,
bytes32 destinationCurrencyKey,
address rewardAddress,
bytes32 trackingCode
) external returns (uint amountReceived);
function exchangeOnBehalfWithTracking(
address exchangeForAddress,
bytes32 sourceCurrencyKey,
uint sourceAmount,
bytes32 destinationCurrencyKey,
address rewardAddress,
bytes32 trackingCode
) external returns (uint amountReceived);
function exchangeWithVirtual(
bytes32 sourceCurrencyKey,
uint sourceAmount,
bytes32 destinationCurrencyKey,
bytes32 trackingCode
) external returns (uint amountReceived, IVirtualSynth vSynth);
function exchangeAtomically(
bytes32 sourceCurrencyKey,
uint sourceAmount,
bytes32 destinationCurrencyKey,
bytes32 trackingCode,
uint minAmount
) 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) external returns (bool);
function liquidateDelinquentAccountEscrowIndex(address account, uint escrowStartIndex) external returns (bool);
function liquidateSelf() external returns (bool);
function mintSecondary(address account, uint amount) external;
function mintSecondaryRewards(uint amount) external;
function burnSecondary(address account, uint amount) external;
}
library ExchangeSettlementLib {
using SafeMath for uint256;
using SafeDecimalMath for uint256;
struct ResolvedAddresses {
IExchangeState exchangeState;
IExchangeRates exchangeRates;
ICircuitBreaker circuitBreaker;
IExchangerInternalDebtCache debtCache;
IIssuer issuer;
ISynthetix synthetix;
}
bytes32 internal constant sUSD = "sUSD";
function internalSettle(
ResolvedAddresses calldata resolvedAddresses,
address from,
bytes32 currencyKey,
bool updateCache,
uint waitingPeriod
)
external
returns (
uint reclaimed,
uint refunded,
uint numEntriesSettled
)
{
require(
maxSecsLeftInWaitingPeriod(resolvedAddresses.exchangeState, from, currencyKey, waitingPeriod) == 0,
"Cannot settle during waiting period"
);
(uint reclaimAmount, uint rebateAmount, uint entries, IExchanger.ExchangeEntrySettlement[] memory settlements) =
_settlementOwing(resolvedAddresses, from, currencyKey, waitingPeriod);
if (reclaimAmount > rebateAmount) {
reclaimed = reclaimAmount.sub(rebateAmount);
_reclaim(resolvedAddresses, from, currencyKey, reclaimed);
} else if (rebateAmount > reclaimAmount) {
refunded = rebateAmount.sub(reclaimAmount);
_refund(resolvedAddresses, from, currencyKey, refunded);
}
if (updateCache && (reclaimed > 0 || refunded > 0)) {
bytes32[] memory key = new bytes32[](1);
key[0] = currencyKey;
resolvedAddresses.debtCache.updateCachedSynthDebts(key);
}
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;
resolvedAddresses.exchangeState.removeEntries(from, currencyKey);
}
function maxSecsLeftInWaitingPeriod(
IExchangeState exchangeState,
address account,
bytes32 currencyKey,
uint waitingPeriod
) public view returns (uint) {
return _secsLeftInWaitingPeriodForExchange(exchangeState.getMaxTimestamp(account, currencyKey), waitingPeriod);
}
function _secsLeftInWaitingPeriodForExchange(uint timestamp, uint waitingPeriod) internal view returns (uint) {
if (timestamp == 0 || now >= timestamp.add(waitingPeriod)) {
return 0;
}
return timestamp.add(waitingPeriod).sub(now);
}
function _reclaim(
ResolvedAddresses memory resolvedAddresses,
address from,
bytes32 currencyKey,
uint amount
) internal {
resolvedAddresses.issuer.synths(currencyKey).burn(from, amount);
ISynthetixInternal(address(resolvedAddresses.synthetix)).emitExchangeReclaim(from, currencyKey, amount);
}
function _refund(
ResolvedAddresses memory resolvedAddresses,
address from,
bytes32 currencyKey,
uint amount
) internal {
resolvedAddresses.issuer.synths(currencyKey).issue(from, amount);
ISynthetixInternal(address(resolvedAddresses.synthetix)).emitExchangeRebate(from, currencyKey, amount);
}
function hasWaitingPeriodOrSettlementOwing(
ResolvedAddresses calldata resolvedAddresses,
address account,
bytes32 currencyKey,
uint waitingPeriod
) external view returns (bool) {
if (maxSecsLeftInWaitingPeriod(resolvedAddresses.exchangeState, account, currencyKey, waitingPeriod) != 0) {
return true;
}
(uint reclaimAmount, , , ) = _settlementOwing(resolvedAddresses, account, currencyKey, waitingPeriod);
return reclaimAmount > 0;
}
function settlementOwing(
ResolvedAddresses calldata resolvedAddresses,
address account,
bytes32 currencyKey,
uint waitingPeriod
)
external
view
returns (
uint reclaimAmount,
uint rebateAmount,
uint numEntries,
IExchanger.ExchangeEntrySettlement[] memory
)
{
return _settlementOwing(resolvedAddresses, account, currencyKey, waitingPeriod);
}
function _settlementOwing(
ResolvedAddresses memory resolvedAddresses,
address account,
bytes32 currencyKey,
uint waitingPeriod
)
internal
view
returns (
uint reclaimAmount,
uint rebateAmount,
uint numEntries,
IExchanger.ExchangeEntrySettlement[] memory
)
{
numEntries = resolvedAddresses.exchangeState.getLengthOfEntries(account, currencyKey);
IExchanger.ExchangeEntrySettlement[] memory settlements = new IExchanger.ExchangeEntrySettlement[](numEntries);
for (uint i = 0; i < numEntries; i++) {
IExchangeState.ExchangeEntry memory exchangeEntry =
_getExchangeEntry(resolvedAddresses.exchangeState, account, currencyKey, i);
(uint srcRoundIdAtPeriodEnd, uint destRoundIdAtPeriodEnd) =
_getRoundIdsAtPeriodEnd(resolvedAddresses.exchangeRates, exchangeEntry, waitingPeriod);
uint amountShouldHaveReceived;
{
(uint destinationAmount, , ) =
resolvedAddresses.exchangeRates.effectiveValueAndRatesAtRound(
exchangeEntry.src,
exchangeEntry.amount,
exchangeEntry.dest,
srcRoundIdAtPeriodEnd,
destRoundIdAtPeriodEnd
);
amountShouldHaveReceived = _deductFeesFromAmount(destinationAmount, exchangeEntry.exchangeFeeRate);
}
bool sip65condition =
resolvedAddresses.circuitBreaker.isDeviationAboveThreshold(
exchangeEntry.amountReceived,
amountShouldHaveReceived
);
uint reclaim;
uint rebate;
if (!sip65condition) {
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] = IExchanger.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(
IExchangeState exchangeState,
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 _getRoundIdsAtPeriodEnd(
IExchangeRates exRates,
IExchangeState.ExchangeEntry memory exchangeEntry,
uint waitingPeriod
) internal view returns (uint srcRoundIdAtPeriodEnd, uint destRoundIdAtPeriodEnd) {
srcRoundIdAtPeriodEnd = exRates.getLastRoundIdBeforeElapsedSecs(
exchangeEntry.src,
exchangeEntry.roundIdForSrc,
exchangeEntry.timestamp,
waitingPeriod
);
destRoundIdAtPeriodEnd = exRates.getLastRoundIdBeforeElapsedSecs(
exchangeEntry.dest,
exchangeEntry.roundIdForDest,
exchangeEntry.timestamp,
waitingPeriod
);
}
function _deductFeesFromAmount(uint destinationAmount, uint exchangeFeeRate)
internal
pure
returns (uint amountReceived)
{
amountReceived = destinationAmount.multiplyDecimal(SafeDecimalMath.unit().sub(exchangeFeeRate));
}
function appendExchange(
ResolvedAddresses calldata resolvedAddresses,
address account,
bytes32 src,
uint amount,
bytes32 dest,
uint amountReceived,
uint exchangeFeeRate
) external {
uint roundIdForSrc = resolvedAddresses.exchangeRates.getCurrentRoundId(src);
uint roundIdForDest = resolvedAddresses.exchangeRates.getCurrentRoundId(dest);
resolvedAddresses.exchangeState.appendExchangeEntry(
account,
src,
amount,
dest,
amountReceived,
exchangeFeeRate,
now,
roundIdForSrc,
roundIdForDest
);
emit ExchangeEntryAppended(
account,
src,
amount,
dest,
amountReceived,
exchangeFeeRate,
roundIdForSrc,
roundIdForDest
);
}
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
);
}
contract Proxy is Owned {
Proxyable public target;
constructor(address _owner) public Owned(_owner) {}
function setTarget(Proxyable _target) external onlyOwner {
target = _target;
emit TargetUpdated(_target);
}
function _emit(
bytes calldata callData,
uint numTopics,
bytes32 topic1,
bytes32 topic2,
bytes32 topic3,
bytes32 topic4
) external onlyTarget {
uint size = callData.length;
bytes memory _callData = callData;
assembly {
switch numTopics
case 0 {
log0(add(_callData, 32), size)
}
case 1 {
log1(add(_callData, 32), size, topic1)
}
case 2 {
log2(add(_callData, 32), size, topic1, topic2)
}
case 3 {
log3(add(_callData, 32), size, topic1, topic2, topic3)
}
case 4 {
log4(add(_callData, 32), size, topic1, topic2, topic3, topic4)
}
}
}
function() external payable {
target.setMessageSender(msg.sender);
assembly {
let free_ptr := mload(0x40)
calldatacopy(free_ptr, 0, calldatasize)
let result := call(gas, sload(target_slot), callvalue, free_ptr, calldatasize, 0, 0)
returndatacopy(free_ptr, 0, returndatasize)
if iszero(result) {
revert(free_ptr, returndatasize)
}
return(free_ptr, returndatasize)
}
}
modifier onlyTarget {
require(Proxyable(msg.sender) == target, "Must be proxy target");
_;
}
event TargetUpdated(Proxyable newTarget);
}
contract Proxyable is Owned {
Proxy public proxy;
address public messageSender;
constructor(address payable _proxy) internal {
require(owner != address(0), "Owner must be set");
proxy = Proxy(_proxy);
emit ProxyUpdated(_proxy);
}
function setProxy(address payable _proxy) external onlyOwner {
proxy = Proxy(_proxy);
emit ProxyUpdated(_proxy);
}
function setMessageSender(address sender) external onlyProxy {
messageSender = sender;
}
modifier onlyProxy {
_onlyProxy();
_;
}
function _onlyProxy() private view {
require(Proxy(msg.sender) == proxy, "Only the proxy can call");
}
modifier optionalProxy {
_optionalProxy();
_;
}
function _optionalProxy() private {
if (Proxy(msg.sender) != proxy && messageSender != msg.sender) {
messageSender = msg.sender;
}
}
modifier optionalProxy_onlyOwner {
_optionalProxy_onlyOwner();
_;
}
function _optionalProxy_onlyOwner() private {
if (Proxy(msg.sender) != proxy && messageSender != msg.sender) {
messageSender = msg.sender;
}
require(messageSender == owner, "Owner only function");
}
event ProxyUpdated(address proxyAddress);
}
contract Exchanger is Owned, MixinSystemSettings, IExchanger {
using SafeMath for uint;
using SafeDecimalMath for uint;
bytes32 public constant CONTRACT_NAME = "Exchanger";
bytes32 internal constant sUSD = "sUSD";
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 private constant CONTRACT_DEBTCACHE = "DebtCache";
bytes32 private constant CONTRACT_CIRCUIT_BREAKER = "CircuitBreaker";
bytes32 private constant CONTRACT_DIRECT_INTEGRATION_MANAGER = "DirectIntegrationManager";
constructor(address _owner, address _resolver) public Owned(_owner) MixinSystemSettings(_resolver) {}
function resolverAddressesRequired() public view returns (bytes32[] memory addresses) {
bytes32[] memory existingAddresses = MixinSystemSettings.resolverAddressesRequired();
bytes32[] memory newAddresses = new bytes32[](11);
newAddresses[0] = CONTRACT_SYSTEMSTATUS;
newAddresses[1] = CONTRACT_EXCHANGESTATE;
newAddresses[2] = CONTRACT_EXRATES;
newAddresses[3] = CONTRACT_SYNTHETIX;
newAddresses[4] = CONTRACT_FEEPOOL;
newAddresses[5] = CONTRACT_TRADING_REWARDS;
newAddresses[6] = CONTRACT_DELEGATEAPPROVALS;
newAddresses[7] = CONTRACT_ISSUER;
newAddresses[8] = CONTRACT_DEBTCACHE;
newAddresses[9] = CONTRACT_CIRCUIT_BREAKER;
newAddresses[10] = CONTRACT_DIRECT_INTEGRATION_MANAGER;
addresses = combineArrays(existingAddresses, newAddresses);
}
function systemStatus() internal view returns (ISystemStatus) {
return ISystemStatus(requireAndGetAddress(CONTRACT_SYSTEMSTATUS));
}
function exchangeState() internal view returns (IExchangeState) {
return IExchangeState(requireAndGetAddress(CONTRACT_EXCHANGESTATE));
}
function exchangeRates() internal view returns (IExchangeRates) {
return IExchangeRates(requireAndGetAddress(CONTRACT_EXRATES));
}
function circuitBreaker() internal view returns (ICircuitBreaker) {
return ICircuitBreaker(requireAndGetAddress(CONTRACT_CIRCUIT_BREAKER));
}
function synthetix() internal view returns (ISynthetix) {
return ISynthetix(requireAndGetAddress(CONTRACT_SYNTHETIX));
}
function feePool() internal view returns (IFeePool) {
return IFeePool(requireAndGetAddress(CONTRACT_FEEPOOL));
}
function tradingRewards() internal view returns (ITradingRewards) {
return ITradingRewards(requireAndGetAddress(CONTRACT_TRADING_REWARDS));
}
function delegateApprovals() internal view returns (IDelegateApprovals) {
return IDelegateApprovals(requireAndGetAddress(CONTRACT_DELEGATEAPPROVALS));
}
function issuer() internal view returns (IIssuer) {
return IIssuer(requireAndGetAddress(CONTRACT_ISSUER));
}
function debtCache() internal view returns (IExchangerInternalDebtCache) {
return IExchangerInternalDebtCache(requireAndGetAddress(CONTRACT_DEBTCACHE));
}
function directIntegrationManager() internal view returns (IDirectIntegrationManager) {
return IDirectIntegrationManager(requireAndGetAddress(CONTRACT_DIRECT_INTEGRATION_MANAGER));
}
function resolvedAddresses() internal view returns (ExchangeSettlementLib.ResolvedAddresses memory) {
return
ExchangeSettlementLib.ResolvedAddresses(
exchangeState(),
exchangeRates(),
circuitBreaker(),
debtCache(),
issuer(),
synthetix()
);
}
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 lastExchangeRate(bytes32 currencyKey) external view returns (uint) {
return circuitBreaker().lastValue(address(exchangeRates().aggregators(currencyKey)));
}
function settlementOwing(address account, bytes32 currencyKey)
public
view
returns (
uint reclaimAmount,
uint rebateAmount,
uint numEntries
)
{
(reclaimAmount, rebateAmount, numEntries, ) = ExchangeSettlementLib.settlementOwing(
resolvedAddresses(),
account,
currencyKey,
getWaitingPeriodSecs()
);
}
function hasWaitingPeriodOrSettlementOwing(address account, bytes32 currencyKey) external view returns (bool) {
return
ExchangeSettlementLib.hasWaitingPeriodOrSettlementOwing(
resolvedAddresses(),
account,
currencyKey,
getWaitingPeriodSecs()
);
}
function maxSecsLeftInWaitingPeriod(address account, bytes32 currencyKey) public view returns (uint) {
return
ExchangeSettlementLib._secsLeftInWaitingPeriodForExchange(
exchangeState().getMaxTimestamp(account, currencyKey),
getWaitingPeriodSecs()
);
}
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) {
(, bool invalid) = exchangeRates().rateAndInvalid(currencyKey);
return invalid;
}
function exchange(
address exchangeForAddress,
address from,
bytes32 sourceCurrencyKey,
uint sourceAmount,
bytes32 destinationCurrencyKey,
address destinationAddress,
bool virtualSynth,
address rewardAddress,
bytes32 trackingCode
) external onlySynthetixorSynth returns (uint amountReceived, IVirtualSynth vSynth) {
uint fee;
if (from != exchangeForAddress) {
require(delegateApprovals().canExchangeFor(exchangeForAddress, from), "Not approved to act on behalf");
}
IDirectIntegrationManager.ParameterIntegrationSettings memory sourceSettings =
_exchangeSettings(from, sourceCurrencyKey);
IDirectIntegrationManager.ParameterIntegrationSettings memory destinationSettings =
_exchangeSettings(from, destinationCurrencyKey);
(amountReceived, fee, vSynth) = _exchange(
exchangeForAddress,
sourceSettings,
sourceAmount,
destinationSettings,
destinationAddress,
virtualSynth
);
_processTradingRewards(fee, rewardAddress);
if (trackingCode != bytes32(0)) {
_emitTrackingEvent(trackingCode, destinationCurrencyKey, amountReceived, fee);
}
}
function exchangeAtomically(
address,
bytes32,
uint,
bytes32,
address,
bytes32,
uint
) external returns (uint) {
_notImplemented();
}
function _emitTrackingEvent(
bytes32 trackingCode,
bytes32 toCurrencyKey,
uint256 toAmount,
uint256 fee
) internal {
ISynthetixInternal(address(synthetix())).emitExchangeTracking(trackingCode, toCurrencyKey, toAmount, fee);
}
function _processTradingRewards(uint fee, address rewardAddress) internal {
if (fee > 0 && rewardAddress != address(0) && getTradingRewardsEnabled()) {
tradingRewards().recordExchangeFeeForAccount(fee, rewardAddress);
}
}
function _updateSNXIssuedDebtOnExchange(bytes32[2] memory currencyKeys, uint[2] memory currencyRates) internal {
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();
}
debtCache().updateCachedSynthDebtsWithRates(keys, rates);
}
function _settleAndCalcSourceAmountRemaining(
uint sourceAmount,
address from,
bytes32 sourceCurrencyKey
) internal returns (uint sourceAmountAfterSettlement) {
(, uint refunded, uint numEntriesSettled) =
ExchangeSettlementLib.internalSettle(
resolvedAddresses(),
from,
sourceCurrencyKey,
false,
getWaitingPeriodSecs()
);
sourceAmountAfterSettlement = sourceAmount;
if (numEntriesSettled > 0) {
sourceAmountAfterSettlement = calculateAmountAfterSettlement(from, sourceCurrencyKey, sourceAmount, refunded);
}
}
function _exchange(
address from,
IDirectIntegrationManager.ParameterIntegrationSettings memory sourceSettings,
uint sourceAmount,
IDirectIntegrationManager.ParameterIntegrationSettings memory destinationSettings,
address destinationAddress,
bool virtualSynth
)
internal
returns (
uint amountReceived,
uint fee,
IVirtualSynth vSynth
)
{
if (!_ensureCanExchange(sourceSettings.currencyKey, destinationSettings.currencyKey, sourceAmount)) {
return (0, 0, IVirtualSynth(0));
}
IExchanger.ExchangeEntry memory entry;
ExchangeSettlementLib.ResolvedAddresses memory addrs = resolvedAddresses();
entry.roundIdForSrc = addrs.exchangeRates.getCurrentRoundId(sourceSettings.currencyKey);
entry.roundIdForDest = addrs.exchangeRates.getCurrentRoundId(destinationSettings.currencyKey);
entry.sourceAmountAfterSettlement = _settleAndCalcSourceAmountRemaining(
sourceAmount,
from,
sourceSettings.currencyKey
);
if (entry.sourceAmountAfterSettlement == 0) {
return (0, 0, IVirtualSynth(0));
}
(entry.destinationAmount, entry.sourceRate, entry.destinationRate) = addrs
.exchangeRates
.effectiveValueAndRatesAtRound(
sourceSettings.currencyKey,
entry.sourceAmountAfterSettlement,
destinationSettings.currencyKey,
entry.roundIdForSrc,
entry.roundIdForDest
);
_ensureCanExchangeAtRound(
sourceSettings.currencyKey,
destinationSettings.currencyKey,
entry.roundIdForSrc,
entry.roundIdForDest
);
bool tooVolatile;
(entry.exchangeFeeRate, tooVolatile) = _feeRateForExchangeAtRounds(
sourceSettings,
destinationSettings,
entry.roundIdForSrc,
entry.roundIdForDest
);
if (tooVolatile) {
return (0, 0, IVirtualSynth(0));
}
amountReceived = ExchangeSettlementLib._deductFeesFromAmount(entry.destinationAmount, entry.exchangeFeeRate);
fee = entry.destinationAmount.sub(amountReceived);
vSynth = _convert(
sourceSettings.currencyKey,
from,
entry.sourceAmountAfterSettlement,
destinationSettings.currencyKey,
amountReceived,
destinationAddress,
virtualSynth
);
if (vSynth != IVirtualSynth(0)) {
destinationAddress = address(vSynth);
}
if (fee > 0) {
fee = addrs.exchangeRates.effectiveValue(destinationSettings.currencyKey, fee, sUSD);
issuer().synths(sUSD).issue(feePool().FEE_ADDRESS(), fee);
feePool().recordFeePaid(fee);
}
_updateSNXIssuedDebtOnExchange(
[sourceSettings.currencyKey, destinationSettings.currencyKey],
[entry.sourceRate, entry.destinationRate]
);
ISynthetixInternal(address(synthetix())).emitSynthExchange(
from,
sourceSettings.currencyKey,
entry.sourceAmountAfterSettlement,
destinationSettings.currencyKey,
amountReceived,
destinationAddress
);
if (getWaitingPeriodSecs() > 0) {
ExchangeSettlementLib.appendExchange(
addrs,
destinationAddress,
sourceSettings.currencyKey,
entry.sourceAmountAfterSettlement,
destinationSettings.currencyKey,
amountReceived,
entry.exchangeFeeRate
);
}
}
function _convert(
bytes32 sourceCurrencyKey,
address from,
uint sourceAmountAfterSettlement,
bytes32 destinationCurrencyKey,
uint amountReceived,
address recipient,
bool virtualSynth
) internal returns (IVirtualSynth vSynth) {
issuer().synths(sourceCurrencyKey).burn(from, sourceAmountAfterSettlement);
ISynth dest = issuer().synths(destinationCurrencyKey);
if (virtualSynth) {
Proxyable synth = Proxyable(address(dest));
vSynth = _createVirtualSynth(IERC20(address(synth.proxy())), recipient, amountReceived, destinationCurrencyKey);
dest.issue(address(vSynth), amountReceived);
} else {
dest.issue(recipient, amountReceived);
}
}
function _createVirtualSynth(
IERC20,
address,
uint,
bytes32
) internal returns (IVirtualSynth) {
_notImplemented();
}
function settle(address from, bytes32 currencyKey)
external
returns (
uint reclaimed,
uint refunded,
uint numEntriesSettled
)
{
systemStatus().requireSynthActive(currencyKey);
return ExchangeSettlementLib.internalSettle(resolvedAddresses(), from, currencyKey, true, getWaitingPeriodSecs());
}
function _exchangeSettings(address from, bytes32 currencyKey)
internal
view
returns (IDirectIntegrationManager.ParameterIntegrationSettings memory settings)
{
settings = directIntegrationManager().getExchangeParameters(from, currencyKey);
}
function _ensureCanExchange(
bytes32 sourceCurrencyKey,
bytes32 destinationCurrencyKey,
uint sourceAmount
) internal returns (bool) {
require(sourceCurrencyKey != destinationCurrencyKey, "Can't be same synth");
require(sourceAmount > 0, "Zero amount");
(, bool srcBroken, bool srcStaleOrInvalid) =
sourceCurrencyKey != sUSD ? exchangeRates().rateWithSafetyChecks(sourceCurrencyKey) : (0, false, false);
(, bool dstBroken, bool dstStaleOrInvalid) =
destinationCurrencyKey != sUSD
? exchangeRates().rateWithSafetyChecks(destinationCurrencyKey)
: (0, false, false);
require(!srcStaleOrInvalid, "src rate stale or flagged");
require(!dstStaleOrInvalid, "dest rate stale or flagged");
return !srcBroken && !dstBroken;
}
function _ensureCanExchangeAtRound(
bytes32 sourceCurrencyKey,
bytes32 destinationCurrencyKey,
uint roundIdForSrc,
uint roundIdForDest
) internal view {
require(sourceCurrencyKey != destinationCurrencyKey, "Can't be same synth");
bytes32[] memory synthKeys = new bytes32[](2);
synthKeys[0] = sourceCurrencyKey;
synthKeys[1] = destinationCurrencyKey;
uint[] memory roundIds = new uint[](2);
roundIds[0] = roundIdForSrc;
roundIds[1] = roundIdForDest;
require(!exchangeRates().anyRateIsInvalidAtRound(synthKeys, roundIds), "src/dest rate stale or flagged");
}
function feeRateForExchange(bytes32 sourceCurrencyKey, bytes32 destinationCurrencyKey) external view returns (uint) {
IDirectIntegrationManager.ParameterIntegrationSettings memory sourceSettings =
_exchangeSettings(msg.sender, sourceCurrencyKey);
IDirectIntegrationManager.ParameterIntegrationSettings memory destinationSettings =
_exchangeSettings(msg.sender, destinationCurrencyKey);
(uint feeRate, bool tooVolatile) = _feeRateForExchange(sourceSettings, destinationSettings);
require(!tooVolatile, "too volatile");
return feeRate;
}
function dynamicFeeRateForExchange(bytes32 sourceCurrencyKey, bytes32 destinationCurrencyKey)
external
view
returns (uint feeRate, bool tooVolatile)
{
IDirectIntegrationManager.ParameterIntegrationSettings memory sourceSettings =
_exchangeSettings(msg.sender, sourceCurrencyKey);
IDirectIntegrationManager.ParameterIntegrationSettings memory destinationSettings =
_exchangeSettings(msg.sender, destinationCurrencyKey);
return _dynamicFeeRateForExchange(sourceSettings, destinationSettings);
}
function _feeRateForExchange(
IDirectIntegrationManager.ParameterIntegrationSettings memory sourceSettings,
IDirectIntegrationManager.ParameterIntegrationSettings memory destinationSettings
) internal view returns (uint feeRate, bool tooVolatile) {
uint baseRate = sourceSettings.exchangeFeeRate.add(destinationSettings.exchangeFeeRate);
uint dynamicFee;
(dynamicFee, tooVolatile) = _dynamicFeeRateForExchange(sourceSettings, destinationSettings);
return (baseRate.add(dynamicFee), tooVolatile);
}
function _feeRateForExchangeAtRounds(
IDirectIntegrationManager.ParameterIntegrationSettings memory sourceSettings,
IDirectIntegrationManager.ParameterIntegrationSettings memory destinationSettings,
uint roundIdForSrc,
uint roundIdForDest
) internal view returns (uint feeRate, bool tooVolatile) {
uint baseRate = sourceSettings.exchangeFeeRate.add(destinationSettings.exchangeFeeRate);
uint dynamicFee;
(dynamicFee, tooVolatile) = _dynamicFeeRateForExchangeAtRounds(
sourceSettings,
destinationSettings,
roundIdForSrc,
roundIdForDest
);
return (baseRate.add(dynamicFee), tooVolatile);
}
function _dynamicFeeRateForExchange(
IDirectIntegrationManager.ParameterIntegrationSettings memory sourceSettings,
IDirectIntegrationManager.ParameterIntegrationSettings memory destinationSettings
) internal view returns (uint dynamicFee, bool tooVolatile) {
(uint dynamicFeeDst, bool dstVolatile) = _dynamicFeeRateForCurrency(destinationSettings);
(uint dynamicFeeSrc, bool srcVolatile) = _dynamicFeeRateForCurrency(sourceSettings);
dynamicFee = dynamicFeeDst.add(dynamicFeeSrc);
bool overMax = dynamicFee > sourceSettings.exchangeMaxDynamicFee;
dynamicFee = overMax ? sourceSettings.exchangeMaxDynamicFee : dynamicFee;
return (dynamicFee, overMax || dstVolatile || srcVolatile);
}
function _dynamicFeeRateForExchangeAtRounds(
IDirectIntegrationManager.ParameterIntegrationSettings memory sourceSettings,
IDirectIntegrationManager.ParameterIntegrationSettings memory destinationSettings,
uint roundIdForSrc,
uint roundIdForDest
) internal view returns (uint dynamicFee, bool tooVolatile) {
(uint dynamicFeeDst, bool dstVolatile) = _dynamicFeeRateForCurrencyRound(destinationSettings, roundIdForDest);
(uint dynamicFeeSrc, bool srcVolatile) = _dynamicFeeRateForCurrencyRound(sourceSettings, roundIdForSrc);
dynamicFee = dynamicFeeDst.add(dynamicFeeSrc);
bool overMax = dynamicFee > sourceSettings.exchangeMaxDynamicFee;
dynamicFee = overMax ? sourceSettings.exchangeMaxDynamicFee : dynamicFee;
return (dynamicFee, overMax || dstVolatile || srcVolatile);
}
function _dynamicFeeRateForCurrency(IDirectIntegrationManager.ParameterIntegrationSettings memory settings)
internal
view
returns (uint dynamicFee, bool tooVolatile)
{
if (settings.currencyKey == sUSD || settings.exchangeDynamicFeeRounds <= 1) {
return (0, false);
}
uint roundId = exchangeRates().getCurrentRoundId(settings.currencyKey);
return _dynamicFeeRateForCurrencyRound(settings, roundId);
}
function _dynamicFeeRateForCurrencyRound(
IDirectIntegrationManager.ParameterIntegrationSettings memory settings,
uint roundId
) internal view returns (uint dynamicFee, bool tooVolatile) {
if (settings.currencyKey == sUSD || settings.exchangeDynamicFeeRounds <= 1) {
return (0, false);
}
uint[] memory prices;
(prices, ) = exchangeRates().ratesAndUpdatedTimeForCurrencyLastNRounds(
settings.currencyKey,
settings.exchangeDynamicFeeRounds,
roundId
);
dynamicFee = _dynamicFeeCalculation(
prices,
settings.exchangeDynamicFeeThreshold,
settings.exchangeDynamicFeeWeightDecay
);
bool overMax = dynamicFee > settings.exchangeMaxDynamicFee;
dynamicFee = overMax ? settings.exchangeMaxDynamicFee : dynamicFee;
return (dynamicFee, overMax);
}
function _dynamicFeeCalculation(
uint[] memory prices,
uint threshold,
uint weightDecay
) internal pure returns (uint) {
if (prices.length == 0) {
return 0;
}
uint dynamicFee = 0;
for (uint i = prices.length - 1; i > 0; i--) {
dynamicFee = dynamicFee.multiplyDecimal(weightDecay);
uint deviation = _thresholdedAbsDeviationRatio(prices[i - 1], prices[i], threshold);
dynamicFee = dynamicFee.add(deviation);
}
return dynamicFee;
}
function _thresholdedAbsDeviationRatio(
uint price,
uint previousPrice,
uint threshold
) internal pure returns (uint) {
if (previousPrice == 0) {
return 0;
}
uint absDelta = price > previousPrice ? price - previousPrice : previousPrice - price;
uint deviationRatio = absDelta.divideDecimal(previousPrice);
return deviationRatio > threshold ? deviationRatio - threshold : 0;
}
function getAmountsForExchange(
uint sourceAmount,
bytes32 sourceCurrencyKey,
bytes32 destinationCurrencyKey
)
external
view
returns (
uint amountReceived,
uint fee,
uint exchangeFeeRate
)
{
IDirectIntegrationManager.ParameterIntegrationSettings memory sourceSettings =
_exchangeSettings(msg.sender, sourceCurrencyKey);
IDirectIntegrationManager.ParameterIntegrationSettings memory destinationSettings =
_exchangeSettings(msg.sender, destinationCurrencyKey);
require(sourceCurrencyKey == sUSD || !exchangeRates().rateIsInvalid(sourceCurrencyKey), "src synth rate invalid");
require(
destinationCurrencyKey == sUSD || !exchangeRates().rateIsInvalid(destinationCurrencyKey),
"dest synth rate invalid"
);
systemStatus().requireSynthActive(sourceCurrencyKey);
systemStatus().requireSynthActive(destinationCurrencyKey);
bool tooVolatile;
(exchangeFeeRate, tooVolatile) = _feeRateForExchange(sourceSettings, destinationSettings);
require(!tooVolatile, "exchange rates too volatile");
(uint destinationAmount, , ) =
exchangeRates().effectiveValueAndRates(sourceCurrencyKey, sourceAmount, destinationCurrencyKey);
amountReceived = ExchangeSettlementLib._deductFeesFromAmount(destinationAmount, exchangeFeeRate);
fee = destinationAmount.sub(amountReceived);
}
function _notImplemented() internal pure {
revert("Cannot be run on this layer");
}
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"
);
_;
}
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
);
}
contract MinimalProxyFactory {
function _cloneAsMinimalProxy(address _base, string memory _revertMsg) internal returns (address clone) {
bytes memory createData = _generateMinimalProxyCreateData(_base);
assembly {
clone := create(
0,
add(createData, 0x20),
55
)
}
require(clone != address(0), _revertMsg);
}
function _generateMinimalProxyCreateData(address _base) internal pure returns (bytes memory) {
return
abi.encodePacked(
bytes10(0x3d602d80600a3d3981f3),
bytes10(0x363d3d373d3d3d363d73),
_base,
bytes15(0x5af43d82803e903d91602b57fd5bf3)
);
}
}
interface IVirtualSynthInternal {
function initialize(
IERC20 _synth,
IAddressResolver _resolver,
address _recipient,
uint _amount,
bytes32 _currencyKey
) external;
}
contract ExchangerWithFeeRecAlternatives is MinimalProxyFactory, Exchanger {
bytes32 public constant CONTRACT_NAME = "ExchangerWithFeeRecAlternatives";
using SafeMath for uint;
struct ExchangeVolumeAtPeriod {
uint64 time;
uint192 volume;
}
ExchangeVolumeAtPeriod public lastAtomicVolume;
constructor(address _owner, address _resolver) public MinimalProxyFactory() Exchanger(_owner, _resolver) {}
bytes32 private constant CONTRACT_VIRTUALSYNTH_MASTERCOPY = "VirtualSynthMastercopy";
function resolverAddressesRequired() public view returns (bytes32[] memory addresses) {
bytes32[] memory existingAddresses = Exchanger.resolverAddressesRequired();
bytes32[] memory newAddresses = new bytes32[](1);
newAddresses[0] = CONTRACT_VIRTUALSYNTH_MASTERCOPY;
addresses = combineArrays(existingAddresses, newAddresses);
}
function atomicMaxVolumePerBlock() external view returns (uint) {
return getAtomicMaxVolumePerBlock();
}
function feeRateForAtomicExchange(bytes32 sourceCurrencyKey, bytes32 destinationCurrencyKey)
external
view
returns (uint exchangeFeeRate)
{
IDirectIntegrationManager.ParameterIntegrationSettings memory sourceSettings =
_exchangeSettings(msg.sender, sourceCurrencyKey);
IDirectIntegrationManager.ParameterIntegrationSettings memory destinationSettings =
_exchangeSettings(msg.sender, destinationCurrencyKey);
exchangeFeeRate = _feeRateForAtomicExchange(sourceSettings, destinationSettings);
}
function getAmountsForAtomicExchange(
uint sourceAmount,
bytes32 sourceCurrencyKey,
bytes32 destinationCurrencyKey
)
external
view
returns (
uint amountReceived,
uint fee,
uint exchangeFeeRate
)
{
IDirectIntegrationManager.ParameterIntegrationSettings memory sourceSettings =
_exchangeSettings(msg.sender, sourceCurrencyKey);
IDirectIntegrationManager.ParameterIntegrationSettings memory destinationSettings =
_exchangeSettings(msg.sender, destinationCurrencyKey);
IDirectIntegrationManager.ParameterIntegrationSettings memory usdSettings = _exchangeSettings(msg.sender, sUSD);
(amountReceived, fee, exchangeFeeRate, , , ) = _getAmountsForAtomicExchangeMinusFees(
sourceAmount,
sourceSettings,
destinationSettings,
usdSettings
);
}
function exchangeAtomically(
address from,
bytes32 sourceCurrencyKey,
uint sourceAmount,
bytes32 destinationCurrencyKey,
address destinationAddress,
bytes32 trackingCode,
uint minAmount
) external onlySynthetixorSynth returns (uint amountReceived) {
uint fee;
(amountReceived, fee) = _exchangeAtomically(
from,
sourceCurrencyKey,
sourceAmount,
destinationCurrencyKey,
destinationAddress
);
require(amountReceived >= minAmount, "The amount received is below the minimum amount specified.");
_processTradingRewards(fee, destinationAddress);
if (trackingCode != bytes32(0)) {
_emitTrackingEvent(trackingCode, destinationCurrencyKey, amountReceived, fee);
}
}
function _virtualSynthMastercopy() internal view returns (address) {
return requireAndGetAddress(CONTRACT_VIRTUALSYNTH_MASTERCOPY);
}
function _createVirtualSynth(
IERC20 synth,
address recipient,
uint amount,
bytes32 currencyKey
) internal returns (IVirtualSynth) {
require(currencyKey[0] != 0x69, "Cannot virtualize this synth");
IVirtualSynthInternal vSynth =
IVirtualSynthInternal(_cloneAsMinimalProxy(_virtualSynthMastercopy(), "Could not create new vSynth"));
vSynth.initialize(synth, resolver, recipient, amount, currencyKey);
emit VirtualSynthCreated(address(synth), recipient, address(vSynth), currencyKey, amount);
return IVirtualSynth(address(vSynth));
}
function _exchangeAtomically(
address from,
bytes32 sourceCurrencyKey,
uint sourceAmount,
bytes32 destinationCurrencyKey,
address destinationAddress
) internal returns (uint amountReceived, uint fee) {
uint sourceAmountAfterSettlement;
uint exchangeFeeRate;
uint systemSourceRate;
uint systemDestinationRate;
{
IDirectIntegrationManager.ParameterIntegrationSettings memory sourceSettings =
_exchangeSettings(from, sourceCurrencyKey);
IDirectIntegrationManager.ParameterIntegrationSettings memory destinationSettings =
_exchangeSettings(from, destinationCurrencyKey);
if (!_ensureCanExchange(sourceCurrencyKey, destinationCurrencyKey, sourceAmount)) {
return (0, 0);
}
require(!exchangeRates().synthTooVolatileForAtomicExchange(sourceSettings), "Src synth too volatile");
require(!exchangeRates().synthTooVolatileForAtomicExchange(destinationSettings), "Dest synth too volatile");
sourceAmountAfterSettlement = _settleAndCalcSourceAmountRemaining(sourceAmount, from, sourceCurrencyKey);
if (sourceAmountAfterSettlement == 0) {
return (0, 0);
}
IDirectIntegrationManager.ParameterIntegrationSettings memory usdSettings = _exchangeSettings(from, sUSD);
uint systemConvertedAmount;
(
amountReceived,
fee,
exchangeFeeRate,
systemConvertedAmount,
systemSourceRate,
systemDestinationRate
) = _getAmountsForAtomicExchangeMinusFees(
sourceAmountAfterSettlement,
sourceSettings,
destinationSettings,
usdSettings
);
require(
!circuitBreaker().isDeviationAboveThreshold(systemConvertedAmount, amountReceived.add(fee)),
"Atomic rate deviates too much"
);
uint sourceSusdValue;
if (sourceCurrencyKey == sUSD) {
sourceSusdValue = sourceAmountAfterSettlement;
} else if (destinationCurrencyKey == sUSD) {
sourceSusdValue = systemConvertedAmount;
} else {
(uint amountReceivedInUSD, uint sUsdFee, , , , ) =
_getAmountsForAtomicExchangeMinusFees(
sourceAmountAfterSettlement,
sourceSettings,
usdSettings,
usdSettings
);
sourceSusdValue = amountReceivedInUSD.add(sUsdFee);
}
_checkAndUpdateAtomicVolume(sourceSettings, sourceSusdValue);
}
_convert(
sourceCurrencyKey,
from,
sourceAmountAfterSettlement,
destinationCurrencyKey,
amountReceived,
destinationAddress,
false
);
if (fee > 0) {
fee = exchangeRates().effectiveValue(destinationCurrencyKey, fee, sUSD);
issuer().synths(sUSD).issue(feePool().FEE_ADDRESS(), fee);
feePool().recordFeePaid(fee);
}
_updateSNXIssuedDebtOnExchange(
[sourceCurrencyKey, destinationCurrencyKey],
[systemSourceRate, systemDestinationRate]
);
ISynthetixInternal(address(synthetix())).emitSynthExchange(
from,
sourceCurrencyKey,
sourceAmountAfterSettlement,
destinationCurrencyKey,
amountReceived,
destinationAddress
);
ISynthetixInternal(address(synthetix())).emitAtomicSynthExchange(
from,
sourceCurrencyKey,
sourceAmountAfterSettlement,
destinationCurrencyKey,
amountReceived,
destinationAddress
);
}
function _checkAndUpdateAtomicVolume(
IDirectIntegrationManager.ParameterIntegrationSettings memory settings,
uint sourceSusdValue
) internal {
uint currentVolume =
uint(lastAtomicVolume.time) == block.timestamp
? uint(lastAtomicVolume.volume).add(sourceSusdValue)
: sourceSusdValue;
require(currentVolume <= settings.atomicMaxVolumePerBlock, "Surpassed volume limit");
lastAtomicVolume.time = uint64(block.timestamp);
lastAtomicVolume.volume = uint192(currentVolume);
}
function _feeRateForAtomicExchange(
IDirectIntegrationManager.ParameterIntegrationSettings memory sourceSettings,
IDirectIntegrationManager.ParameterIntegrationSettings memory destinationSettings
) internal view returns (uint) {
uint baseRate = sourceSettings.atomicExchangeFeeRate.add(destinationSettings.atomicExchangeFeeRate);
if (baseRate == 0) {
baseRate = sourceSettings.exchangeFeeRate.add(destinationSettings.exchangeFeeRate);
}
return baseRate;
}
function _getAmountsForAtomicExchangeMinusFees(
uint sourceAmount,
IDirectIntegrationManager.ParameterIntegrationSettings memory sourceSettings,
IDirectIntegrationManager.ParameterIntegrationSettings memory destinationSettings,
IDirectIntegrationManager.ParameterIntegrationSettings memory usdSettings
)
internal
view
returns (
uint amountReceived,
uint fee,
uint exchangeFeeRate,
uint systemConvertedAmount,
uint systemSourceRate,
uint systemDestinationRate
)
{
uint destinationAmount;
(destinationAmount, systemConvertedAmount, systemSourceRate, systemDestinationRate) = exchangeRates()
.effectiveAtomicValueAndRates(sourceSettings, sourceAmount, destinationSettings, usdSettings);
exchangeFeeRate = _feeRateForAtomicExchange(sourceSettings, destinationSettings);
amountReceived = ExchangeSettlementLib._deductFeesFromAmount(destinationAmount, exchangeFeeRate);
fee = destinationAmount.sub(amountReceived);
}
event VirtualSynthCreated(
address indexed synth,
address indexed recipient,
address vSynth,
bytes32 currencyKey,
uint amount
);
}