Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Source Code Verified (Exact Match)
Contract Name:
Migration_Saiph
Compiler Version
v0.5.16+commit.9c3226ce
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity)
/** *Submitted for verification at Etherscan.io on 2022-06-09 */ /* ____ __ __ __ _ / __/__ __ ___ / /_ / / ___ / /_ (_)__ __ _\ \ / // // _ \/ __// _ \/ -_)/ __// / \ \ / /___/ \_, //_//_/\__//_//_/\__/ \__//_/ /_\_\ /___/ * Synthetix: migrations/Migration_Saiph.sol * * Latest source (may be newer): https://github.com/Synthetixio/synthetix/blob/master/contracts/migrations/Migration_Saiph.sol * Docs: https://docs.synthetix.io/contracts/migrations/Migration_Saiph * * Contract Dependencies: * - BaseMigration * - ExternStateToken * - IAddressResolver * - IERC20 * - IIssuer * - IRewardEscrow * - IRewardsDistribution * - ISynth * - ISystemStatus * - LegacyOwned * - MixinResolver * - MixinSystemSettings * - Owned * - Proxy * - Proxyable * - State * - Synth * Libraries: * - SafeCast * - SafeDecimalMath * - SafeMath * - VestingEntries * * MIT License * =========== * * Copyright (c) 2022 Synthetix * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE */ pragma solidity ^0.5.16; // https://docs.synthetix.io/contracts/source/contracts/owned 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); } // https://docs.synthetix.io/contracts/source/interfaces/iaddressresolver 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); } // https://docs.synthetix.io/contracts/source/interfaces/isynth interface ISynth { // Views function currencyKey() external view returns (bytes32); function transferableSynths(address account) external view returns (uint); // Mutative functions function transferAndSettle(address to, uint value) external returns (bool); function transferFromAndSettle( address from, address to, uint value ) external returns (bool); // Restricted: used internally to Synthetix function burn(address account, uint amount) external; function issue(address account, uint amount) external; } // https://docs.synthetix.io/contracts/source/interfaces/iissuer interface IIssuer { // Views 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); // Restricted: used internally to Synthetix 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 amountToLiquidate); function issueSynthsWithoutDebt( bytes32 currencyKey, address to, uint amount ) external returns (bool rateInvalid); function burnSynthsWithoutDebt( bytes32 currencyKey, address to, uint amount ) external returns (bool rateInvalid); } // Internal references // https://docs.synthetix.io/contracts/source/contracts/mixinresolver contract MixinResolver { AddressResolver public resolver; mapping(bytes32 => address) private addressCache; constructor(address _resolver) internal { resolver = AddressResolver(_resolver); } /* ========== INTERNAL FUNCTIONS ========== */ 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]; } } /* ========== PUBLIC FUNCTIONS ========== */ // Note: this function is public not external in order for it to be overridden and invoked via super in subclasses function resolverAddressesRequired() public view returns (bytes32[] memory addresses) {} function rebuildCache() public { bytes32[] memory requiredAddresses = resolverAddressesRequired(); // The resolver must call this function whenver it updates its state for (uint i = 0; i < requiredAddresses.length; i++) { bytes32 name = requiredAddresses[i]; // Note: can only be invoked once the resolver has all the targets needed added address destination = resolver.requireAndGetAddress(name, string(abi.encodePacked("Resolver missing target: ", name))); addressCache[name] = destination; emit CacheUpdated(name, destination); } } /* ========== VIEWS ========== */ function isResolverCached() external view returns (bool) { bytes32[] memory requiredAddresses = resolverAddressesRequired(); for (uint i = 0; i < requiredAddresses.length; i++) { bytes32 name = requiredAddresses[i]; // false if our cache is invalid or if the resolver doesn't have the required address if (resolver.getAddress(name) != addressCache[name] || addressCache[name] == address(0)) { return false; } } return true; } /* ========== INTERNAL FUNCTIONS ========== */ function requireAndGetAddress(bytes32 name) internal view returns (address) { address _foundAddress = addressCache[name]; require(_foundAddress != address(0), string(abi.encodePacked("Missing address: ", name))); return _foundAddress; } /* ========== EVENTS ========== */ event CacheUpdated(bytes32 name, address destination); } // Inheritance // Internal references // https://docs.synthetix.io/contracts/source/contracts/addressresolver contract AddressResolver is Owned, IAddressResolver { mapping(bytes32 => address) public repository; constructor(address _owner) public Owned(_owner) {} /* ========== RESTRICTED FUNCTIONS ========== */ 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); } } /* ========= PUBLIC FUNCTIONS ========== */ function rebuildCaches(MixinResolver[] calldata destinations) external { for (uint i = 0; i < destinations.length; i++) { destinations[i].rebuildCache(); } } /* ========== VIEWS ========== */ 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)); } /* ========== EVENTS ========== */ event AddressImported(bytes32 name, address destination); } contract BaseMigration is Owned { constructor(address _owner) internal Owned(_owner) {} // safety value to return ownership (anyone can invoke) function returnOwnership(address forContract) public { bytes memory payload = abi.encodeWithSignature("nominateNewOwner(address)", owner); // solhint-disable avoid-low-level-calls (bool success, ) = forContract.call(payload); if (!success) { // then try legacy way bytes memory legacyPayload = abi.encodeWithSignature("nominateOwner(address)", owner); // solhint-disable avoid-low-level-calls (bool legacySuccess, ) = forContract.call(legacyPayload); require(legacySuccess, "Legacy nomination failed"); } } } // https://docs.synthetix.io/contracts/source/interfaces/iflexiblestorage interface IFlexibleStorage { // Views 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); // Mutative functions 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; } // Internal references // https://docs.synthetix.io/contracts/source/contracts/mixinsystemsettings contract MixinSystemSettings is MixinResolver { // must match the one defined SystemSettingsLib, defined in both places due to sol v0.5 limitations 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_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"; /* ========== Exchange Fees Related ========== */ 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"; /* ========== End Exchange Fees Related ========== */ 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) { // lookup on flexible storage directly for gas savings (rather than via SystemSettings) return flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_ISSUANCE_RATIO); } function getFeePeriodDuration() internal view returns (uint) { // lookup on flexible storage directly for gas savings (rather than via SystemSettings) return flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_FEE_PERIOD_DURATION); } function getTargetThreshold() internal view returns (uint) { // lookup on flexible storage directly for gas savings (rather than via SystemSettings) 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 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); } /* ========== Exchange Related Fees ========== */ function getExchangeFeeRate(bytes32 currencyKey) internal view returns (uint) { return flexibleStorage().getUIntValue( SETTING_CONTRACT_NAME, keccak256(abi.encodePacked(SETTING_EXCHANGE_FEE_RATE, currencyKey)) ); } /// @notice Get exchange dynamic fee related keys /// @return threshold, weight decay, rounds, and max fee 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]}); } /* ========== End Exchange Related Fees ========== */ 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)) ); } } // SPDX-License-Identifier: MIT /** * @dev Wrappers over Solidity's uintXX casting operators with added overflow * checks. * * Downcasting from uint256 in Solidity does not revert on overflow. This can * easily result in undesired exploitation or bugs, since developers usually * assume that overflows raise errors. `SafeCast` restores this intuition by * reverting the transaction when such an operation overflows. * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. * * Can be combined with {SafeMath} to extend it to smaller types, by performing * all math on `uint256` and then downcasting. */ library SafeCast { /** * @dev Returns the downcasted uint128 from uint256, reverting on * overflow (when the input is greater than largest uint128). * * Counterpart to Solidity's `uint128` operator. * * Requirements: * * - input must fit into 128 bits */ function toUint128(uint256 value) internal pure returns (uint128) { require(value < 2**128, "SafeCast: value doesn't fit in 128 bits"); return uint128(value); } /** * @dev Returns the downcasted uint64 from uint256, reverting on * overflow (when the input is greater than largest uint64). * * Counterpart to Solidity's `uint64` operator. * * Requirements: * * - input must fit into 64 bits */ function toUint64(uint256 value) internal pure returns (uint64) { require(value < 2**64, "SafeCast: value doesn't fit in 64 bits"); return uint64(value); } /** * @dev Returns the downcasted uint32 from uint256, reverting on * overflow (when the input is greater than largest uint32). * * Counterpart to Solidity's `uint32` operator. * * Requirements: * * - input must fit into 32 bits */ function toUint32(uint256 value) internal pure returns (uint32) { require(value < 2**32, "SafeCast: value doesn't fit in 32 bits"); return uint32(value); } /** * @dev Returns the downcasted uint16 from uint256, reverting on * overflow (when the input is greater than largest uint16). * * Counterpart to Solidity's `uint16` operator. * * Requirements: * * - input must fit into 16 bits */ function toUint16(uint256 value) internal pure returns (uint16) { require(value < 2**16, "SafeCast: value doesn't fit in 16 bits"); return uint16(value); } /** * @dev Returns the downcasted uint8 from uint256, reverting on * overflow (when the input is greater than largest uint8). * * Counterpart to Solidity's `uint8` operator. * * Requirements: * * - input must fit into 8 bits. */ function toUint8(uint256 value) internal pure returns (uint8) { require(value < 2**8, "SafeCast: value doesn't fit in 8 bits"); return uint8(value); } /** * @dev Converts a signed int256 into an unsigned uint256. * * Requirements: * * - input must be greater than or equal to 0. */ function toUint256(int256 value) internal pure returns (uint256) { require(value >= 0, "SafeCast: value must be positive"); return uint256(value); } /** * @dev Converts an unsigned uint256 into a signed int256. * * Requirements: * * - input must be less than or equal to maxInt256. */ function toInt256(uint256 value) internal pure returns (int256) { require(value < 2**255, "SafeCast: value doesn't fit in an int256"); return int256(value); } } /** * @dev Wrappers over Solidity's arithmetic operations with added overflow * checks. * * Arithmetic operations in Solidity wrap on overflow. This can easily result * in bugs, because programmers usually assume that an overflow raises an * error, which is the standard behavior in high level programming languages. * `SafeMath` restores this intuition by reverting the transaction when an * operation overflows. * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. */ library SafeMath { /** * @dev Returns the addition of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * - Addition cannot overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, "SafeMath: addition overflow"); return c; } /** * @dev Returns the subtraction of two unsigned integers, reverting on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { require(b <= a, "SafeMath: subtraction overflow"); uint256 c = a - b; return c; } /** * @dev Returns the multiplication of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * - Multiplication cannot overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 if (a == 0) { return 0; } uint256 c = a * b; require(c / a == b, "SafeMath: multiplication overflow"); return c; } /** * @dev Returns the integer division of two unsigned integers. Reverts on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { // Solidity only automatically asserts when dividing by 0 require(b > 0, "SafeMath: division by zero"); uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return c; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * Reverts when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { require(b != 0, "SafeMath: modulo by zero"); return a % b; } } // Libraries // https://docs.synthetix.io/contracts/source/libraries/safedecimalmath library SafeDecimalMath { using SafeMath for uint; /* Number of decimal places in the representations. */ uint8 public constant decimals = 18; uint8 public constant highPrecisionDecimals = 27; /* The number representing 1.0. */ uint public constant UNIT = 10**uint(decimals); /* The number representing 1.0 for higher fidelity numbers. */ uint public constant PRECISE_UNIT = 10**uint(highPrecisionDecimals); uint private constant UNIT_TO_HIGH_PRECISION_CONVERSION_FACTOR = 10**uint(highPrecisionDecimals - decimals); /** * @return Provides an interface to UNIT. */ function unit() external pure returns (uint) { return UNIT; } /** * @return Provides an interface to PRECISE_UNIT. */ function preciseUnit() external pure returns (uint) { return PRECISE_UNIT; } /** * @return The result of multiplying x and y, interpreting the operands as fixed-point * decimals. * * @dev A unit factor is divided out after the product of x and y is evaluated, * so that product must be less than 2**256. As this is an integer division, * the internal division always rounds down. This helps save on gas. Rounding * is more expensive on gas. */ function multiplyDecimal(uint x, uint y) internal pure returns (uint) { /* Divide by UNIT to remove the extra factor introduced by the product. */ return x.mul(y) / UNIT; } /** * @return The result of safely multiplying x and y, interpreting the operands * as fixed-point decimals of the specified precision unit. * * @dev The operands should be in the form of a the specified unit factor which will be * divided out after the product of x and y is evaluated, so that product must be * less than 2**256. * * Unlike multiplyDecimal, this function rounds the result to the nearest increment. * Rounding is useful when you need to retain fidelity for small decimal numbers * (eg. small fractions or percentages). */ function _multiplyDecimalRound( uint x, uint y, uint precisionUnit ) private pure returns (uint) { /* Divide by UNIT to remove the extra factor introduced by the product. */ uint quotientTimesTen = x.mul(y) / (precisionUnit / 10); if (quotientTimesTen % 10 >= 5) { quotientTimesTen += 10; } return quotientTimesTen / 10; } /** * @return The result of safely multiplying x and y, interpreting the operands * as fixed-point decimals of a precise unit. * * @dev The operands should be in the precise unit factor which will be * divided out after the product of x and y is evaluated, so that product must be * less than 2**256. * * Unlike multiplyDecimal, this function rounds the result to the nearest increment. * Rounding is useful when you need to retain fidelity for small decimal numbers * (eg. small fractions or percentages). */ function multiplyDecimalRoundPrecise(uint x, uint y) internal pure returns (uint) { return _multiplyDecimalRound(x, y, PRECISE_UNIT); } /** * @return The result of safely multiplying x and y, interpreting the operands * as fixed-point decimals of a standard unit. * * @dev The operands should be in the standard unit factor which will be * divided out after the product of x and y is evaluated, so that product must be * less than 2**256. * * Unlike multiplyDecimal, this function rounds the result to the nearest increment. * Rounding is useful when you need to retain fidelity for small decimal numbers * (eg. small fractions or percentages). */ function multiplyDecimalRound(uint x, uint y) internal pure returns (uint) { return _multiplyDecimalRound(x, y, UNIT); } /** * @return The result of safely dividing x and y. The return value is a high * precision decimal. * * @dev y is divided after the product of x and the standard precision unit * is evaluated, so the product of x and UNIT must be less than 2**256. As * this is an integer division, the result is always rounded down. * This helps save on gas. Rounding is more expensive on gas. */ function divideDecimal(uint x, uint y) internal pure returns (uint) { /* Reintroduce the UNIT factor that will be divided out by y. */ return x.mul(UNIT).div(y); } /** * @return The result of safely dividing x and y. The return value is as a rounded * decimal in the precision unit specified in the parameter. * * @dev y is divided after the product of x and the specified precision unit * is evaluated, so the product of x and the specified precision unit must * be less than 2**256. The result is rounded to the nearest increment. */ 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; } /** * @return The result of safely dividing x and y. The return value is as a rounded * standard precision decimal. * * @dev y is divided after the product of x and the standard precision unit * is evaluated, so the product of x and the standard precision unit must * be less than 2**256. The result is rounded to the nearest increment. */ function divideDecimalRound(uint x, uint y) internal pure returns (uint) { return _divideDecimalRound(x, y, UNIT); } /** * @return The result of safely dividing x and y. The return value is as a rounded * high precision decimal. * * @dev y is divided after the product of x and the high precision unit * is evaluated, so the product of x and the high precision unit must * be less than 2**256. The result is rounded to the nearest increment. */ function divideDecimalRoundPrecise(uint x, uint y) internal pure returns (uint) { return _divideDecimalRound(x, y, PRECISE_UNIT); } /** * @dev Convert a standard decimal representation to a high precision one. */ function decimalToPreciseDecimal(uint i) internal pure returns (uint) { return i.mul(UNIT_TO_HIGH_PRECISION_CONVERSION_FACTOR); } /** * @dev Convert a high precision decimal to a standard decimal representation. */ 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; } // Computes `a - b`, setting the value to 0 if b > a. function floorsub(uint a, uint b) internal pure returns (uint) { return b >= a ? 0 : a - b; } /* ---------- Utilities ---------- */ /* * Absolute value of the input, returned as a signed number. */ function signedAbs(int x) internal pure returns (int) { return x < 0 ? -x : x; } /* * Absolute value of the input, returned as an unsigned number. */ function abs(int x) internal pure returns (uint) { return uint(signedAbs(x)); } } interface IVirtualSynth { // Views 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); // Mutative functions function settle(address account) external; } // https://docs.synthetix.io/contracts/source/interfaces/isynthetix interface ISynthetix { // Views 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); // Mutative Functions 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 ); // Liquidations function liquidateDelinquentAccount(address account) external returns (bool); function liquidateSelf() external returns (bool); // Restricted Functions function mintSecondary(address account, uint amount) external; function mintSecondaryRewards(uint amount) external; function burnSecondary(address account, uint amount) external; } // https://docs.synthetix.io/contracts/source/interfaces/ifeepool interface IFeePool { // Views // solhint-disable-next-line func-name-mixedcase 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); // Mutative Functions 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; } // https://docs.synthetix.io/contracts/source/interfaces/isynthetixdebtshare interface ISynthetixDebtShare { // Views function currentPeriodId() external view returns (uint128); function allowance(address account, address spender) external view returns (uint); function balanceOf(address account) external view returns (uint); function balanceOfOnPeriod(address account, uint periodId) external view returns (uint); function totalSupply() external view returns (uint); function sharePercent(address account) external view returns (uint); function sharePercentOnPeriod(address account, uint periodId) external view returns (uint); // Mutative functions function takeSnapshot(uint128 id) external; function mintShare(address account, uint256 amount) external; function burnShare(address account, uint256 amount) external; function approve(address, uint256) external pure returns (bool); function transfer(address to, uint256 amount) external pure returns (bool); function transferFrom( address from, address to, uint256 amount ) external returns (bool); function addAuthorizedBroker(address target) external; function removeAuthorizedBroker(address target) external; function addAuthorizedToSnapshot(address target) external; function removeAuthorizedToSnapshot(address target) external; } // https://docs.synthetix.io/contracts/source/interfaces/iexchanger 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; } // Views 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); // Mutative functions 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 ); function suspendSynthWithInvalidRate(bytes32 currencyKey) external; } // https://docs.synthetix.io/contracts/source/interfaces/idelegateapprovals interface IDelegateApprovals { // Views 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); // Mutative 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; } // https://docs.synthetix.io/contracts/source/interfaces/iexchangerates interface IExchangeRates { // Structs struct RateAndUpdatedTime { uint216 rate; uint40 time; } // Views 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 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); } // https://docs.synthetix.io/contracts/source/interfaces/ihasbalance interface IHasBalance { // Views function balanceOf(address account) external view returns (uint); } // https://docs.synthetix.io/contracts/source/interfaces/ierc20 interface IERC20 { // ERC20 Optional Views function name() external view returns (string memory); function symbol() external view returns (string memory); function decimals() external view returns (uint8); // Views function totalSupply() external view returns (uint); function balanceOf(address owner) external view returns (uint); function allowance(address owner, address spender) external view returns (uint); // Mutative functions 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); // Events event Transfer(address indexed from, address indexed to, uint value); event Approval(address indexed owner, address indexed spender, uint value); } interface ILiquidator { // Views function issuanceRatio() external view returns (uint); function liquidationDelay() external view returns (uint); function liquidationRatio() external view returns (uint); function liquidationEscrowDuration() external view returns (uint); function liquidationPenalty() external view returns (uint); function selfLiquidationPenalty() external view returns (uint); function liquidateReward() external view returns (uint); function flagReward() external view returns (uint); function liquidationCollateralRatio() external view returns (uint); function getLiquidationDeadlineForAccount(address account) external view returns (uint); function getLiquidationCallerForAccount(address account) external view returns (address); function isLiquidationOpen(address account, bool isSelfLiquidation) external view returns (bool); function isLiquidationDeadlinePassed(address account) external view returns (bool); function calculateAmountToFixCollateral( uint debtBalance, uint collateral, uint penalty ) external view returns (uint); // Mutative Functions function flagAccountForLiquidation(address account) external; // Restricted: used internally to Synthetix contracts function removeAccountInLiquidation(address account) external; function checkAndRemoveAccountInLiquidation(address account) external; } interface ILiquidatorRewards { // Views function earned(address account) external view returns (uint256); // Mutative function getReward(address account) external; function notifyRewardAmount(uint256 reward) external; function updateEntry(address account) external; } interface ICollateralManager { // Manager information function hasCollateral(address collateral) external view returns (bool); function isSynthManaged(bytes32 currencyKey) external view returns (bool); // State information function long(bytes32 synth) external view returns (uint amount); function short(bytes32 synth) external view returns (uint amount); function totalLong() external view returns (uint susdValue, bool anyRateIsInvalid); function totalShort() external view returns (uint susdValue, bool anyRateIsInvalid); function getBorrowRate() external view returns (uint borrowRate, bool anyRateIsInvalid); function getShortRate(bytes32 synth) external view returns (uint shortRate, bool rateIsInvalid); function getRatesAndTime(uint index) external view returns ( uint entryRate, uint lastRate, uint lastUpdated, uint newIndex ); function getShortRatesAndTime(bytes32 currency, uint index) external view returns ( uint entryRate, uint lastRate, uint lastUpdated, uint newIndex ); function exceedsDebtLimit(uint amount, bytes32 currency) external view returns (bool canIssue, bool anyRateIsInvalid); function areSynthsAndCurrenciesSet(bytes32[] calldata requiredSynthNamesInResolver, bytes32[] calldata synthKeys) external view returns (bool); function areShortableSynthsSet(bytes32[] calldata requiredSynthNamesInResolver, bytes32[] calldata synthKeys) external view returns (bool); // Loans function getNewLoanId() external returns (uint id); // Manager mutative function addCollaterals(address[] calldata collaterals) external; function removeCollaterals(address[] calldata collaterals) external; function addSynths(bytes32[] calldata synthNamesInResolver, bytes32[] calldata synthKeys) external; function removeSynths(bytes32[] calldata synths, bytes32[] calldata synthKeys) external; function addShortableSynths(bytes32[] calldata requiredSynthNamesInResolver, bytes32[] calldata synthKeys) external; function removeShortableSynths(bytes32[] calldata synths) external; // State mutative function incrementLongs(bytes32 synth, uint amount) external; function decrementLongs(bytes32 synth, uint amount) external; function incrementShorts(bytes32 synth, uint amount) external; function decrementShorts(bytes32 synth, uint amount) external; function accrueInterest( uint interestIndex, bytes32 currency, bool isShort ) external returns (uint difference, uint index); function updateBorrowRatesCollateral(uint rate) external; function updateShortRatesCollateral(bytes32 currency, uint rate) external; } pragma experimental ABIEncoderV2; library VestingEntries { struct VestingEntry { uint64 endTime; uint256 escrowAmount; } struct VestingEntryWithID { uint64 endTime; uint256 escrowAmount; uint256 entryID; } } interface IRewardEscrowV2 { // Views function balanceOf(address account) external view returns (uint); function numVestingEntries(address account) external view returns (uint); function totalEscrowedAccountBalance(address account) external view returns (uint); function totalVestedAccountBalance(address account) external view returns (uint); function getVestingQuantity(address account, uint256[] calldata entryIDs) external view returns (uint); function getVestingSchedules( address account, uint256 index, uint256 pageSize ) external view returns (VestingEntries.VestingEntryWithID[] memory); function getAccountVestingEntryIDs( address account, uint256 index, uint256 pageSize ) external view returns (uint256[] memory); function getVestingEntryClaimable(address account, uint256 entryID) external view returns (uint); function getVestingEntry(address account, uint256 entryID) external view returns (uint64, uint256); // Mutative functions function vest(uint256[] calldata entryIDs) external; function createEscrowEntry( address beneficiary, uint256 deposit, uint256 duration ) external; function appendVestingEntry( address account, uint256 quantity, uint256 duration ) external; function migrateVestingSchedule(address _addressToMigrate) external; function migrateAccountEscrowBalances( address[] calldata accounts, uint256[] calldata escrowBalances, uint256[] calldata vestedBalances ) external; // Account Merging function startMergingWindow() external; function mergeAccount(address accountToMerge, uint256[] calldata entryIDs) external; function nominateAccountToMerge(address account) external; function accountMergingIsOpen() external view returns (bool); // L2 Migration function importVestingEntries( address account, uint256 escrowedAmount, VestingEntries.VestingEntry[] calldata vestingEntries ) external; // Return amount of SNX transfered to SynthetixBridgeToOptimism deposit contract function burnForMigration(address account, uint256[] calldata entryIDs) external returns (uint256 escrowedAccountBalance, VestingEntries.VestingEntry[] memory vestingEntries); } interface ISynthRedeemer { // Rate of redemption - 0 for none function redemptions(address synthProxy) external view returns (uint redeemRate); // sUSD balance of deprecated token holder function balanceOf(IERC20 synthProxy, address account) external view returns (uint balanceOfInsUSD); // Full sUSD supply of token function totalSupply(IERC20 synthProxy) external view returns (uint totalSupplyInsUSD); function redeem(IERC20 synthProxy) external; function redeemAll(IERC20[] calldata synthProxies) external; function redeemPartial(IERC20 synthProxy, uint amountOfSynth) external; // Restricted to Issuer function deprecate(IERC20 synthProxy, uint rateToRedeem) external; } // https://docs.synthetix.io/contracts/source/interfaces/isystemstatus interface ISystemStatus { struct Status { bool canSuspend; bool canResume; } struct Suspension { bool suspended; // reason is an integer code, // 0 => no reason, 1 => upgrading, 2+ => defined by system usage uint248 reason; } // Views 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); // Restricted functions 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; } // Inheritance // Internal references // https://docs.synthetix.io/contracts/source/contracts/proxy 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 { /* The first 32 bytes of callData contain its length (as specified by the abi). * Length is assumed to be a uint256 and therefore maximum of 32 bytes * in length. It is also leftpadded to be a multiple of 32 bytes. * This means moving call_data across 32 bytes guarantees we correctly access * the data itself. */ 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) } } } // solhint-disable no-complex-fallback function() external payable { // Mutable call setting Proxyable.messageSender as this is using call not delegatecall target.setMessageSender(msg.sender); assembly { let free_ptr := mload(0x40) calldatacopy(free_ptr, 0, calldatasize) /* We must explicitly forward ether to the underlying contract as well. */ 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); } // Inheritance // Internal references // https://docs.synthetix.io/contracts/source/contracts/proxyable contract Proxyable is Owned { // This contract should be treated like an abstract contract /* The proxy this contract exists behind. */ Proxy public proxy; /* The caller of the proxy, passed through to this contract. * Note that every function using this member must apply the onlyProxy or * optionalProxy modifiers, otherwise their invocations can use stale values. */ address public messageSender; constructor(address payable _proxy) internal { // This contract is abstract, and thus cannot be instantiated directly 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(); _; } // solhint-disable-next-line func-name-mixedcase 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); } interface AggregatorInterface { function latestAnswer() external view returns (int256); function latestTimestamp() external view returns (uint256); function latestRound() external view returns (uint256); function getAnswer(uint256 roundId) external view returns (int256); function getTimestamp(uint256 roundId) external view returns (uint256); event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 timestamp); event NewRound(uint256 indexed roundId, address indexed startedBy, uint256 startedAt); } interface AggregatorV3Interface { function decimals() external view returns (uint8); function description() external view returns (string memory); function version() external view returns (uint256); // getRoundData and latestRoundData should both raise "No data present" // if they do not have data to report, instead of returning unset values // which could be misinterpreted as actual reported values. function getRoundData(uint80 _roundId) external view returns ( uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound ); function latestRoundData() external view returns ( uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound ); } /** * @title The V2 & V3 Aggregator Interface * @notice Solidity V0.5 does not allow interfaces to inherit from other * interfaces so this contract is a combination of v0.5 AggregatorInterface.sol * and v0.5 AggregatorV3Interface.sol. */ interface AggregatorV2V3Interface { // // V2 Interface: // function latestAnswer() external view returns (int256); function latestTimestamp() external view returns (uint256); function latestRound() external view returns (uint256); function getAnswer(uint256 roundId) external view returns (int256); function getTimestamp(uint256 roundId) external view returns (uint256); event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 timestamp); event NewRound(uint256 indexed roundId, address indexed startedBy, uint256 startedAt); // // V3 Interface: // function decimals() external view returns (uint8); function description() external view returns (string memory); function version() external view returns (uint256); // getRoundData and latestRoundData should both raise "No data present" // if they do not have data to report, instead of returning unset values // which could be misinterpreted as actual reported values. function getRoundData(uint80 _roundId) external view returns ( uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound ); function latestRoundData() external view returns ( uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound ); } // Inheritance // Libraries // Internal references interface IProxy { function target() external view returns (address); } interface IIssuerInternalDebtCache { function updateCachedSynthDebtWithRate(bytes32 currencyKey, uint currencyRate) external; function updateCachedSynthDebtsWithRates(bytes32[] calldata currencyKeys, uint[] calldata currencyRates) external; function updateDebtCacheValidity(bool currentlyInvalid) external; function totalNonSnxBackedDebt() external view returns (uint excludedDebt, bool isInvalid); function cacheInfo() external view returns ( uint cachedDebt, uint timestamp, bool isInvalid, bool isStale ); function updateCachedsUSDDebt(int amount) external; } // https://docs.synthetix.io/contracts/source/contracts/issuer contract Issuer is Owned, MixinSystemSettings, IIssuer { using SafeMath for uint; using SafeDecimalMath for uint; bytes32 public constant CONTRACT_NAME = "Issuer"; // SIP-165: Circuit breaker for Debt Synthesis uint public constant CIRCUIT_BREAKER_SUSPENSION_REASON = 165; // Available Synths which can be used with the system ISynth[] public availableSynths; mapping(bytes32 => ISynth) public synths; mapping(address => bytes32) public synthsByAddress; uint public lastDebtRatio; /* ========== ENCODED NAMES ========== */ bytes32 internal constant sUSD = "sUSD"; bytes32 internal constant sETH = "sETH"; bytes32 internal constant SNX = "SNX"; // Flexible storage names bytes32 internal constant LAST_ISSUE_EVENT = "lastIssueEvent"; /* ========== ADDRESS RESOLVER CONFIGURATION ========== */ bytes32 private constant CONTRACT_SYNTHETIX = "Synthetix"; bytes32 private constant CONTRACT_EXCHANGER = "Exchanger"; bytes32 private constant CONTRACT_EXRATES = "ExchangeRates"; bytes32 private constant CONTRACT_SYNTHETIXDEBTSHARE = "SynthetixDebtShare"; bytes32 private constant CONTRACT_FEEPOOL = "FeePool"; bytes32 private constant CONTRACT_DELEGATEAPPROVALS = "DelegateApprovals"; bytes32 private constant CONTRACT_REWARDESCROW_V2 = "RewardEscrowV2"; bytes32 private constant CONTRACT_SYNTHETIXESCROW = "SynthetixEscrow"; bytes32 private constant CONTRACT_LIQUIDATOR = "Liquidator"; bytes32 private constant CONTRACT_LIQUIDATOR_REWARDS = "LiquidatorRewards"; bytes32 private constant CONTRACT_DEBTCACHE = "DebtCache"; bytes32 private constant CONTRACT_SYNTHREDEEMER = "SynthRedeemer"; bytes32 private constant CONTRACT_SYSTEMSTATUS = "SystemStatus"; bytes32 private constant CONTRACT_SYNTHETIXBRIDGETOOPTIMISM = "SynthetixBridgeToOptimism"; bytes32 private constant CONTRACT_SYNTHETIXBRIDGETOBASE = "SynthetixBridgeToBase"; bytes32 private constant CONTRACT_EXT_AGGREGATOR_ISSUED_SYNTHS = "ext:AggregatorIssuedSynths"; bytes32 private constant CONTRACT_EXT_AGGREGATOR_DEBT_RATIO = "ext:AggregatorDebtRatio"; constructor(address _owner, address _resolver) public Owned(_owner) MixinSystemSettings(_resolver) {} /* ========== VIEWS ========== */ function resolverAddressesRequired() public view returns (bytes32[] memory addresses) { bytes32[] memory existingAddresses = MixinSystemSettings.resolverAddressesRequired(); bytes32[] memory newAddresses = new bytes32[](15); newAddresses[0] = CONTRACT_SYNTHETIX; newAddresses[1] = CONTRACT_EXCHANGER; newAddresses[2] = CONTRACT_EXRATES; newAddresses[3] = CONTRACT_SYNTHETIXDEBTSHARE; newAddresses[4] = CONTRACT_FEEPOOL; newAddresses[5] = CONTRACT_DELEGATEAPPROVALS; newAddresses[6] = CONTRACT_REWARDESCROW_V2; newAddresses[7] = CONTRACT_SYNTHETIXESCROW; newAddresses[8] = CONTRACT_LIQUIDATOR; newAddresses[9] = CONTRACT_LIQUIDATOR_REWARDS; newAddresses[10] = CONTRACT_DEBTCACHE; newAddresses[11] = CONTRACT_SYNTHREDEEMER; newAddresses[12] = CONTRACT_SYSTEMSTATUS; newAddresses[13] = CONTRACT_EXT_AGGREGATOR_ISSUED_SYNTHS; newAddresses[14] = CONTRACT_EXT_AGGREGATOR_DEBT_RATIO; return combineArrays(existingAddresses, newAddresses); } function synthetix() internal view returns (ISynthetix) { return ISynthetix(requireAndGetAddress(CONTRACT_SYNTHETIX)); } function exchanger() internal view returns (IExchanger) { return IExchanger(requireAndGetAddress(CONTRACT_EXCHANGER)); } function exchangeRates() internal view returns (IExchangeRates) { return IExchangeRates(requireAndGetAddress(CONTRACT_EXRATES)); } function synthetixDebtShare() internal view returns (ISynthetixDebtShare) { return ISynthetixDebtShare(requireAndGetAddress(CONTRACT_SYNTHETIXDEBTSHARE)); } function feePool() internal view returns (IFeePool) { return IFeePool(requireAndGetAddress(CONTRACT_FEEPOOL)); } function liquidator() internal view returns (ILiquidator) { return ILiquidator(requireAndGetAddress(CONTRACT_LIQUIDATOR)); } function liquidatorRewards() internal view returns (ILiquidatorRewards) { return ILiquidatorRewards(requireAndGetAddress(CONTRACT_LIQUIDATOR_REWARDS)); } function delegateApprovals() internal view returns (IDelegateApprovals) { return IDelegateApprovals(requireAndGetAddress(CONTRACT_DELEGATEAPPROVALS)); } function rewardEscrowV2() internal view returns (IRewardEscrowV2) { return IRewardEscrowV2(requireAndGetAddress(CONTRACT_REWARDESCROW_V2)); } function synthetixEscrow() internal view returns (IHasBalance) { return IHasBalance(requireAndGetAddress(CONTRACT_SYNTHETIXESCROW)); } function debtCache() internal view returns (IIssuerInternalDebtCache) { return IIssuerInternalDebtCache(requireAndGetAddress(CONTRACT_DEBTCACHE)); } function synthRedeemer() internal view returns (ISynthRedeemer) { return ISynthRedeemer(requireAndGetAddress(CONTRACT_SYNTHREDEEMER)); } function systemStatus() internal view returns (ISystemStatus) { return ISystemStatus(requireAndGetAddress(CONTRACT_SYSTEMSTATUS)); } function allNetworksDebtInfo() public view returns ( uint256 debt, uint256 sharesSupply, bool isStale ) { (, int256 rawIssuedSynths, , uint issuedSynthsUpdatedAt, ) = AggregatorV2V3Interface(requireAndGetAddress(CONTRACT_EXT_AGGREGATOR_ISSUED_SYNTHS)).latestRoundData(); (, int256 rawRatio, , uint ratioUpdatedAt, ) = AggregatorV2V3Interface(requireAndGetAddress(CONTRACT_EXT_AGGREGATOR_DEBT_RATIO)).latestRoundData(); debt = uint(rawIssuedSynths); sharesSupply = rawRatio == 0 ? 0 : debt.divideDecimalRoundPrecise(uint(rawRatio)); isStale = block.timestamp - getRateStalePeriod() > issuedSynthsUpdatedAt || block.timestamp - getRateStalePeriod() > ratioUpdatedAt; } function issuanceRatio() external view returns (uint) { return getIssuanceRatio(); } function _sharesForDebt(uint debtAmount) internal view returns (uint) { (, int256 rawRatio, , , ) = AggregatorV2V3Interface(requireAndGetAddress(CONTRACT_EXT_AGGREGATOR_DEBT_RATIO)).latestRoundData(); return rawRatio == 0 ? 0 : debtAmount.divideDecimalRoundPrecise(uint(rawRatio)); } function _debtForShares(uint sharesAmount) internal view returns (uint) { (, int256 rawRatio, , , ) = AggregatorV2V3Interface(requireAndGetAddress(CONTRACT_EXT_AGGREGATOR_DEBT_RATIO)).latestRoundData(); return sharesAmount.multiplyDecimalRoundPrecise(uint(rawRatio)); } function _availableCurrencyKeysWithOptionalSNX(bool withSNX) internal view returns (bytes32[] memory) { bytes32[] memory currencyKeys = new bytes32[](availableSynths.length + (withSNX ? 1 : 0)); for (uint i = 0; i < availableSynths.length; i++) { currencyKeys[i] = synthsByAddress[address(availableSynths[i])]; } if (withSNX) { currencyKeys[availableSynths.length] = SNX; } return currencyKeys; } // Returns the total value of the debt pool in currency specified by `currencyKey`. // To return only the SNX-backed debt, set `excludeCollateral` to true. function _totalIssuedSynths(bytes32 currencyKey, bool excludeCollateral) internal view returns (uint totalIssued, bool anyRateIsInvalid) { (uint debt, , bool cacheIsInvalid, bool cacheIsStale) = debtCache().cacheInfo(); anyRateIsInvalid = cacheIsInvalid || cacheIsStale; IExchangeRates exRates = exchangeRates(); // Add total issued synths from non snx collateral back into the total if not excluded if (!excludeCollateral) { (uint nonSnxDebt, bool invalid) = debtCache().totalNonSnxBackedDebt(); debt = debt.add(nonSnxDebt); anyRateIsInvalid = anyRateIsInvalid || invalid; } if (currencyKey == sUSD) { return (debt, anyRateIsInvalid); } (uint currencyRate, bool currencyRateInvalid) = exRates.rateAndInvalid(currencyKey); return (debt.divideDecimalRound(currencyRate), anyRateIsInvalid || currencyRateInvalid); } function _debtBalanceOfAndTotalDebt(uint debtShareBalance, bytes32 currencyKey) internal view returns ( uint debtBalance, uint totalSystemValue, bool anyRateIsInvalid ) { // What's the total value of the system excluding ETH backed synths in their requested currency? (uint snxBackedAmount, , bool debtInfoStale) = allNetworksDebtInfo(); if (debtShareBalance == 0) { return (0, snxBackedAmount, debtInfoStale); } // existing functionality requires for us to convert into the exchange rate specified by `currencyKey` (uint currencyRate, bool currencyRateInvalid) = exchangeRates().rateAndInvalid(currencyKey); debtBalance = _debtForShares(debtShareBalance).divideDecimalRound(currencyRate); totalSystemValue = snxBackedAmount; anyRateIsInvalid = currencyRateInvalid || debtInfoStale; } function _canBurnSynths(address account) internal view returns (bool) { return now >= _lastIssueEvent(account).add(getMinimumStakeTime()); } function _lastIssueEvent(address account) internal view returns (uint) { // Get the timestamp of the last issue this account made return flexibleStorage().getUIntValue(CONTRACT_NAME, keccak256(abi.encodePacked(LAST_ISSUE_EVENT, account))); } function _remainingIssuableSynths(address _issuer) internal view returns ( uint maxIssuable, uint alreadyIssued, uint totalSystemDebt, bool anyRateIsInvalid ) { (alreadyIssued, totalSystemDebt, anyRateIsInvalid) = _debtBalanceOfAndTotalDebt( synthetixDebtShare().balanceOf(_issuer), sUSD ); (uint issuable, bool isInvalid) = _maxIssuableSynths(_issuer); maxIssuable = issuable; anyRateIsInvalid = anyRateIsInvalid || isInvalid; if (alreadyIssued >= maxIssuable) { maxIssuable = 0; } else { maxIssuable = maxIssuable.sub(alreadyIssued); } } function _snxToUSD(uint amount, uint snxRate) internal pure returns (uint) { return amount.multiplyDecimalRound(snxRate); } function _usdToSnx(uint amount, uint snxRate) internal pure returns (uint) { return amount.divideDecimalRound(snxRate); } function _maxIssuableSynths(address _issuer) internal view returns (uint, bool) { // What is the value of their SNX balance in sUSD (uint snxRate, bool isInvalid) = exchangeRates().rateAndInvalid(SNX); uint destinationValue = _snxToUSD(_collateral(_issuer), snxRate); // They're allowed to issue up to issuanceRatio of that value return (destinationValue.multiplyDecimal(getIssuanceRatio()), isInvalid); } function _collateralisationRatio(address _issuer) internal view returns (uint, bool) { uint totalOwnedSynthetix = _collateral(_issuer); (uint debtBalance, , bool anyRateIsInvalid) = _debtBalanceOfAndTotalDebt(synthetixDebtShare().balanceOf(_issuer), SNX); // it's more gas intensive to put this check here if they have 0 SNX, but it complies with the interface if (totalOwnedSynthetix == 0) return (0, anyRateIsInvalid); return (debtBalance.divideDecimalRound(totalOwnedSynthetix), anyRateIsInvalid); } function _collateral(address account) internal view returns (uint) { uint balance = IERC20(address(synthetix())).balanceOf(account); if (address(synthetixEscrow()) != address(0)) { balance = balance.add(synthetixEscrow().balanceOf(account)); } if (address(rewardEscrowV2()) != address(0)) { balance = balance.add(rewardEscrowV2().balanceOf(account)); } if (address(liquidatorRewards()) != address(0)) { balance = balance.add(liquidatorRewards().earned(account)); } return balance; } function minimumStakeTime() external view returns (uint) { return getMinimumStakeTime(); } function canBurnSynths(address account) external view returns (bool) { return _canBurnSynths(account); } function availableCurrencyKeys() external view returns (bytes32[] memory) { return _availableCurrencyKeysWithOptionalSNX(false); } function availableSynthCount() external view returns (uint) { return availableSynths.length; } function anySynthOrSNXRateIsInvalid() external view returns (bool anyRateInvalid) { (, anyRateInvalid) = exchangeRates().ratesAndInvalidForCurrencies(_availableCurrencyKeysWithOptionalSNX(true)); } function totalIssuedSynths(bytes32 currencyKey, bool excludeOtherCollateral) external view returns (uint totalIssued) { (totalIssued, ) = _totalIssuedSynths(currencyKey, excludeOtherCollateral); } function lastIssueEvent(address account) external view returns (uint) { return _lastIssueEvent(account); } function collateralisationRatio(address _issuer) external view returns (uint cratio) { (cratio, ) = _collateralisationRatio(_issuer); } function collateralisationRatioAndAnyRatesInvalid(address _issuer) external view returns (uint cratio, bool anyRateIsInvalid) { return _collateralisationRatio(_issuer); } function collateral(address account) external view returns (uint) { return _collateral(account); } function debtBalanceOf(address _issuer, bytes32 currencyKey) external view returns (uint debtBalance) { ISynthetixDebtShare sds = synthetixDebtShare(); // What was their initial debt ownership? uint debtShareBalance = sds.balanceOf(_issuer); // If it's zero, they haven't issued, and they have no debt. if (debtShareBalance == 0) return 0; (debtBalance, , ) = _debtBalanceOfAndTotalDebt(debtShareBalance, currencyKey); } function remainingIssuableSynths(address _issuer) external view returns ( uint maxIssuable, uint alreadyIssued, uint totalSystemDebt ) { (maxIssuable, alreadyIssued, totalSystemDebt, ) = _remainingIssuableSynths(_issuer); } function maxIssuableSynths(address _issuer) external view returns (uint) { (uint maxIssuable, ) = _maxIssuableSynths(_issuer); return maxIssuable; } function transferableSynthetixAndAnyRateIsInvalid(address account, uint balance) external view returns (uint transferable, bool anyRateIsInvalid) { // How many SNX do they have, excluding escrow? // Note: We're excluding escrow here because we're interested in their transferable amount // and escrowed SNX are not transferable. // How many of those will be locked by the amount they've issued? // Assuming issuance ratio is 20%, then issuing 20 SNX of value would require // 100 SNX to be locked in their wallet to maintain their collateralisation ratio // The locked synthetix value can exceed their balance. uint debtBalance; (debtBalance, , anyRateIsInvalid) = _debtBalanceOfAndTotalDebt(synthetixDebtShare().balanceOf(account), SNX); uint lockedSynthetixValue = debtBalance.divideDecimalRound(getIssuanceRatio()); // If we exceed the balance, no SNX are transferable, otherwise the difference is. if (lockedSynthetixValue >= balance) { transferable = 0; } else { transferable = balance.sub(lockedSynthetixValue); } } function getSynths(bytes32[] calldata currencyKeys) external view returns (ISynth[] memory) { uint numKeys = currencyKeys.length; ISynth[] memory addresses = new ISynth[](numKeys); for (uint i = 0; i < numKeys; i++) { addresses[i] = synths[currencyKeys[i]]; } return addresses; } /* ========== MUTATIVE FUNCTIONS ========== */ function _addSynth(ISynth synth) internal { bytes32 currencyKey = synth.currencyKey(); require(synths[currencyKey] == ISynth(0), "Synth exists"); require(synthsByAddress[address(synth)] == bytes32(0), "Synth address already exists"); availableSynths.push(synth); synths[currencyKey] = synth; synthsByAddress[address(synth)] = currencyKey; emit SynthAdded(currencyKey, address(synth)); } function addSynth(ISynth synth) external onlyOwner { _addSynth(synth); // Invalidate the cache to force a snapshot to be recomputed. If a synth were to be added // back to the system and it still somehow had cached debt, this would force the value to be // updated. debtCache().updateDebtCacheValidity(true); } function addSynths(ISynth[] calldata synthsToAdd) external onlyOwner { uint numSynths = synthsToAdd.length; for (uint i = 0; i < numSynths; i++) { _addSynth(synthsToAdd[i]); } // Invalidate the cache to force a snapshot to be recomputed. debtCache().updateDebtCacheValidity(true); } function _removeSynth(bytes32 currencyKey) internal { address synthToRemove = address(synths[currencyKey]); require(synthToRemove != address(0), "Synth does not exist"); require(currencyKey != sUSD, "Cannot remove synth"); uint synthSupply = IERC20(synthToRemove).totalSupply(); if (synthSupply > 0) { (uint amountOfsUSD, uint rateToRedeem, ) = exchangeRates().effectiveValueAndRates(currencyKey, synthSupply, "sUSD"); require(rateToRedeem > 0, "Cannot remove synth to redeem without rate"); ISynthRedeemer _synthRedeemer = synthRedeemer(); synths[sUSD].issue(address(_synthRedeemer), amountOfsUSD); // ensure the debt cache is aware of the new sUSD issued debtCache().updateCachedsUSDDebt(SafeCast.toInt256(amountOfsUSD)); _synthRedeemer.deprecate(IERC20(address(Proxyable(address(synthToRemove)).proxy())), rateToRedeem); } // Remove the synth from the availableSynths array. for (uint i = 0; i < availableSynths.length; i++) { if (address(availableSynths[i]) == synthToRemove) { delete availableSynths[i]; // Copy the last synth into the place of the one we just deleted // If there's only one synth, this is synths[0] = synths[0]. // If we're deleting the last one, it's also a NOOP in the same way. availableSynths[i] = availableSynths[availableSynths.length - 1]; // Decrease the size of the array by one. availableSynths.length--; break; } } // And remove it from the synths mapping delete synthsByAddress[synthToRemove]; delete synths[currencyKey]; emit SynthRemoved(currencyKey, synthToRemove); } function removeSynth(bytes32 currencyKey) external onlyOwner { // Remove its contribution from the debt pool snapshot, and // invalidate the cache to force a new snapshot. IIssuerInternalDebtCache cache = debtCache(); cache.updateCachedSynthDebtWithRate(currencyKey, 0); cache.updateDebtCacheValidity(true); _removeSynth(currencyKey); } function removeSynths(bytes32[] calldata currencyKeys) external onlyOwner { uint numKeys = currencyKeys.length; // Remove their contributions from the debt pool snapshot, and // invalidate the cache to force a new snapshot. IIssuerInternalDebtCache cache = debtCache(); uint[] memory zeroRates = new uint[](numKeys); cache.updateCachedSynthDebtsWithRates(currencyKeys, zeroRates); cache.updateDebtCacheValidity(true); for (uint i = 0; i < numKeys; i++) { _removeSynth(currencyKeys[i]); } } function issueSynthsWithoutDebt( bytes32 currencyKey, address to, uint amount ) external onlyTrustedMinters returns (bool rateInvalid) { require(address(synths[currencyKey]) != address(0), "Issuer: synth doesn't exist"); require(amount > 0, "Issuer: cannot issue 0 synths"); // record issue timestamp _setLastIssueEvent(to); // Create their synths synths[currencyKey].issue(to, amount); // Account for the issued debt in the cache (uint rate, bool rateInvalid) = exchangeRates().rateAndInvalid(currencyKey); debtCache().updateCachedsUSDDebt(SafeCast.toInt256(amount.multiplyDecimal(rate))); // returned so that the caller can decide what to do if the rate is invalid return rateInvalid; } function burnSynthsWithoutDebt( bytes32 currencyKey, address from, uint amount ) external onlyTrustedMinters returns (bool rateInvalid) { require(address(synths[currencyKey]) != address(0), "Issuer: synth doesn't exist"); require(amount > 0, "Issuer: cannot issue 0 synths"); exchanger().settle(from, currencyKey); // Burn some synths synths[currencyKey].burn(from, amount); // Account for the burnt debt in the cache. If rate is invalid, the user won't be able to exchange (uint rate, bool rateInvalid) = exchangeRates().rateAndInvalid(currencyKey); debtCache().updateCachedsUSDDebt(-SafeCast.toInt256(amount.multiplyDecimal(rate))); // returned so that the caller can decide what to do if the rate is invalid return rateInvalid; } /** * Function used to migrate balances from the CollateralShort contract * @param short The address of the CollateralShort contract to be upgraded * @param amount The amount of sUSD collateral to be burnt */ function upgradeCollateralShort(address short, uint amount) external onlyOwner { require(short != address(0), "Issuer: invalid address"); require(short == resolver.getAddress("CollateralShortLegacy"), "Issuer: wrong short address"); require(address(synths[sUSD]) != address(0), "Issuer: synth doesn't exist"); require(amount > 0, "Issuer: cannot burn 0 synths"); exchanger().settle(short, sUSD); synths[sUSD].burn(short, amount); } function issueSynths(address from, uint amount) external onlySynthetix { require(amount > 0, "Issuer: cannot issue 0 synths"); _issueSynths(from, amount, false); } function issueMaxSynths(address from) external onlySynthetix { _issueSynths(from, 0, true); } function issueSynthsOnBehalf( address issueForAddress, address from, uint amount ) external onlySynthetix { _requireCanIssueOnBehalf(issueForAddress, from); _issueSynths(issueForAddress, amount, false); } function issueMaxSynthsOnBehalf(address issueForAddress, address from) external onlySynthetix { _requireCanIssueOnBehalf(issueForAddress, from); _issueSynths(issueForAddress, 0, true); } function burnSynths(address from, uint amount) external onlySynthetix { _voluntaryBurnSynths(from, amount, false); } function burnSynthsOnBehalf( address burnForAddress, address from, uint amount ) external onlySynthetix { _requireCanBurnOnBehalf(burnForAddress, from); _voluntaryBurnSynths(burnForAddress, amount, false); } function burnSynthsToTarget(address from) external onlySynthetix { _voluntaryBurnSynths(from, 0, true); } function burnSynthsToTargetOnBehalf(address burnForAddress, address from) external onlySynthetix { _requireCanBurnOnBehalf(burnForAddress, from); _voluntaryBurnSynths(burnForAddress, 0, true); } function burnForRedemption( address deprecatedSynthProxy, address account, uint balance ) external onlySynthRedeemer { ISynth(IProxy(deprecatedSynthProxy).target()).burn(account, balance); } // SIP-148: Upgraded Liquidation Mechanism /// @notice This is where the core internal liquidation logic resides. This function can only be invoked by Synthetix. /// @param account The account to be liquidated /// @param isSelfLiquidation boolean to determine if this is a forced or self-invoked liquidation /// @return uint the total amount of collateral (SNX) to redeem /// @return uint the amount of debt (sUSD) to burn in order to fix the account's c-ratio function liquidateAccount(address account, bool isSelfLiquidation) external onlySynthetix returns (uint totalRedeemed, uint amountToLiquidate) { require(liquidator().isLiquidationOpen(account, isSelfLiquidation), "Not open for liquidation"); // Get the penalty for the liquidation type uint penalty = isSelfLiquidation ? getSelfLiquidationPenalty() : getLiquidationPenalty(); // Get the account's debt balance (uint debtBalance, , bool anyRateIsInvalid) = _debtBalanceOfAndTotalDebt(synthetixDebtShare().balanceOf(account), sUSD); // Get the SNX rate (uint snxRate, bool snxRateInvalid) = exchangeRates().rateAndInvalid(SNX); _requireRatesNotInvalid(anyRateIsInvalid || snxRateInvalid); // Get the total amount of SNX collateral (including escrows and rewards) uint collateralForAccount = _collateral(account); // Calculate the amount of debt to liquidate to fix c-ratio amountToLiquidate = liquidator().calculateAmountToFixCollateral( debtBalance, _snxToUSD(collateralForAccount, snxRate), penalty ); // Get the equivalent amount of SNX for the amount to liquidate // Note: While amountToLiquidate takes the penalty into account, it does not accommodate for the addition of the penalty in terms of SNX. // Therefore, it is correct to add the penalty modification below to the totalRedeemed. totalRedeemed = _usdToSnx(amountToLiquidate, snxRate).multiplyDecimal(SafeDecimalMath.unit().add(penalty)); // The balanceOf here can be considered "transferable" since it's not escrowed, // and it is the only SNX that can potentially be transfered if unstaked. uint transferableBalance = IERC20(address(synthetix())).balanceOf(account); if (totalRedeemed > transferableBalance) { // Liquidate the account's debt based on the liquidation penalty. amountToLiquidate = amountToLiquidate.multiplyDecimal(transferableBalance).divideDecimal(totalRedeemed); // Set totalRedeemed to all transferable collateral. // i.e. the value of the account's staking position relative to balanceOf will be unwound. totalRedeemed = transferableBalance; } // Reduce debt shares by amount to liquidate. _removeFromDebtRegister(account, amountToLiquidate, debtBalance); // Remove liquidation flag liquidator().removeAccountInLiquidation(account); } function setCurrentPeriodId(uint128 periodId) external { require(msg.sender == address(feePool()), "Must be fee pool"); ISynthetixDebtShare sds = synthetixDebtShare(); if (sds.currentPeriodId() < periodId) { sds.takeSnapshot(periodId); } } function setLastDebtRatio(uint256 ratio) external onlyOwner { lastDebtRatio = ratio; } /* ========== INTERNAL FUNCTIONS ========== */ function _requireRatesNotInvalid(bool anyRateIsInvalid) internal pure { require(!anyRateIsInvalid, "A synth or SNX rate is invalid"); } function _requireCanIssueOnBehalf(address issueForAddress, address from) internal view { require(delegateApprovals().canIssueFor(issueForAddress, from), "Not approved to act on behalf"); } function _requireCanBurnOnBehalf(address burnForAddress, address from) internal view { require(delegateApprovals().canBurnFor(burnForAddress, from), "Not approved to act on behalf"); } function _issueSynths( address from, uint amount, bool issueMax ) internal { // check breaker if (!_verifyCircuitBreaker()) { return; } (uint maxIssuable, , , bool anyRateIsInvalid) = _remainingIssuableSynths(from); _requireRatesNotInvalid(anyRateIsInvalid); if (!issueMax) { require(amount <= maxIssuable, "Amount too large"); } else { amount = maxIssuable; } // Keep track of the debt they're about to create _addToDebtRegister(from, amount); // record issue timestamp _setLastIssueEvent(from); // Create their synths synths[sUSD].issue(from, amount); // Account for the issued debt in the cache debtCache().updateCachedsUSDDebt(SafeCast.toInt256(amount)); } function _burnSynths( address debtAccount, address burnAccount, uint amount, uint existingDebt ) internal returns (uint amountBurnt) { // check breaker if (!_verifyCircuitBreaker()) { return 0; } // liquidation requires sUSD to be already settled / not in waiting period // If they're trying to burn more debt than they actually owe, rather than fail the transaction, let's just // clear their debt and leave them be. amountBurnt = existingDebt < amount ? existingDebt : amount; // Remove liquidated debt from the ledger _removeFromDebtRegister(debtAccount, amountBurnt, existingDebt); // synth.burn does a safe subtraction on balance (so it will revert if there are not enough synths). synths[sUSD].burn(burnAccount, amountBurnt); // Account for the burnt debt in the cache. debtCache().updateCachedsUSDDebt(-SafeCast.toInt256(amountBurnt)); } // If burning to target, `amount` is ignored, and the correct quantity of sUSD is burnt to reach the target // c-ratio, allowing fees to be claimed. In this case, pending settlements will be skipped as the user // will still have debt remaining after reaching their target. function _voluntaryBurnSynths( address from, uint amount, bool burnToTarget ) internal { // check breaker if (!_verifyCircuitBreaker()) { return; } if (!burnToTarget) { // If not burning to target, then burning requires that the minimum stake time has elapsed. require(_canBurnSynths(from), "Minimum stake time not reached"); // First settle anything pending into sUSD as burning or issuing impacts the size of the debt pool (, uint refunded, uint numEntriesSettled) = exchanger().settle(from, sUSD); if (numEntriesSettled > 0) { amount = exchanger().calculateAmountAfterSettlement(from, sUSD, amount, refunded); } } (uint existingDebt, , bool anyRateIsInvalid) = _debtBalanceOfAndTotalDebt(synthetixDebtShare().balanceOf(from), sUSD); (uint maxIssuableSynthsForAccount, bool snxRateInvalid) = _maxIssuableSynths(from); _requireRatesNotInvalid(anyRateIsInvalid || snxRateInvalid); require(existingDebt > 0, "No debt to forgive"); if (burnToTarget) { amount = existingDebt.sub(maxIssuableSynthsForAccount); } uint amountBurnt = _burnSynths(from, from, amount, existingDebt); // Check and remove liquidation if existingDebt after burning is <= maxIssuableSynths // Issuance ratio is fixed so should remove any liquidations if (existingDebt.sub(amountBurnt) <= maxIssuableSynthsForAccount) { liquidator().removeAccountInLiquidation(from); } } function _setLastIssueEvent(address account) internal { // Set the timestamp of the last issueSynths flexibleStorage().setUIntValue( CONTRACT_NAME, keccak256(abi.encodePacked(LAST_ISSUE_EVENT, account)), block.timestamp ); } function _addToDebtRegister(address from, uint amount) internal { // important: this has to happen before any updates to user's debt shares liquidatorRewards().updateEntry(from); ISynthetixDebtShare sds = synthetixDebtShare(); // it is possible (eg in tests, system initialized with extra debt) to have issued debt without any shares issued // in which case, the first account to mint gets the debt. yw. uint debtShares = _sharesForDebt(amount); if (debtShares == 0) { sds.mintShare(from, amount); } else { sds.mintShare(from, debtShares); } } function _removeFromDebtRegister( address from, uint debtToRemove, uint existingDebt ) internal { // important: this has to happen before any updates to user's debt shares liquidatorRewards().updateEntry(from); ISynthetixDebtShare sds = synthetixDebtShare(); uint currentDebtShare = sds.balanceOf(from); if (debtToRemove == existingDebt) { sds.burnShare(from, currentDebtShare); } else { uint sharesToRemove = _sharesForDebt(debtToRemove); sds.burnShare(from, sharesToRemove < currentDebtShare ? sharesToRemove : currentDebtShare); } } function _verifyCircuitBreaker() internal returns (bool) { (, int256 rawRatio, , , ) = AggregatorV2V3Interface(requireAndGetAddress(CONTRACT_EXT_AGGREGATOR_DEBT_RATIO)).latestRoundData(); uint deviation = _calculateDeviation(lastDebtRatio, uint(rawRatio)); if (deviation >= getPriceDeviationThresholdFactor()) { systemStatus().suspendIssuance(CIRCUIT_BREAKER_SUSPENSION_REASON); return false; } lastDebtRatio = uint(rawRatio); return true; } function _calculateDeviation(uint last, uint fresh) internal pure returns (uint deviation) { if (last == 0) { deviation = 1; } else if (fresh == 0) { deviation = uint(-1); } else if (last > fresh) { deviation = last.divideDecimal(fresh); } else { deviation = fresh.divideDecimal(last); } } /* ========== MODIFIERS ========== */ modifier onlySynthetix() { require(msg.sender == address(synthetix()), "Issuer: Only the synthetix contract can perform this action"); _; } modifier onlyTrustedMinters() { address bridgeL1 = resolver.getAddress(CONTRACT_SYNTHETIXBRIDGETOOPTIMISM); address bridgeL2 = resolver.getAddress(CONTRACT_SYNTHETIXBRIDGETOBASE); require(msg.sender == bridgeL1 || msg.sender == bridgeL2, "Issuer: only trusted minters"); require(bridgeL1 == address(0) || bridgeL2 == address(0), "Issuer: one minter must be 0x0"); _; } function _onlySynthRedeemer() internal view { require(msg.sender == address(synthRedeemer()), "Issuer: Only the SynthRedeemer contract can perform this action"); } modifier onlySynthRedeemer() { _onlySynthRedeemer(); _; } modifier issuanceActive() { _issuanceActive(); _; } function _issuanceActive() private { systemStatus().requireIssuanceActive(); } modifier synthActive(bytes32 currencyKey) { _synthActive(currencyKey); _; } function _synthActive(bytes32 currencyKey) private { systemStatus().requireSynthActive(currencyKey); } /* ========== EVENTS ========== */ event SynthAdded(bytes32 currencyKey, address synth); event SynthRemoved(bytes32 currencyKey, address synth); } contract LegacyOwned { address public owner; address public nominatedOwner; constructor(address _owner) public { owner = _owner; } function nominateOwner(address _owner) external onlyOwner { nominatedOwner = _owner; emit OwnerNominated(_owner); } function acceptOwnership() external { require(msg.sender == nominatedOwner); emit OwnerChanged(owner, nominatedOwner); owner = nominatedOwner; nominatedOwner = address(0); } modifier onlyOwner { require(msg.sender == owner); _; } event OwnerNominated(address newOwner); event OwnerChanged(address oldOwner, address newOwner); } contract LegacyTokenState is LegacyOwned { // the address of the contract that can modify balances and allowances // this can only be changed by the owner of this contract address public associatedContract; // ERC20 fields. mapping(address => uint) public balanceOf; mapping(address => mapping(address => uint256)) public allowance; constructor(address _owner, address _associatedContract) public LegacyOwned(_owner) { associatedContract = _associatedContract; emit AssociatedContractUpdated(_associatedContract); } /* ========== SETTERS ========== */ // Change the associated contract to a new address function setAssociatedContract(address _associatedContract) external onlyOwner { associatedContract = _associatedContract; emit AssociatedContractUpdated(_associatedContract); } function setAllowance( address tokenOwner, address spender, uint value ) external onlyAssociatedContract { allowance[tokenOwner][spender] = value; } function setBalanceOf(address account, uint value) external onlyAssociatedContract { balanceOf[account] = value; } /* ========== MODIFIERS ========== */ modifier onlyAssociatedContract { require(msg.sender == associatedContract); _; } /* ========== EVENTS ========== */ event AssociatedContractUpdated(address _associatedContract); } // Inheritance // https://docs.synthetix.io/contracts/source/contracts/state contract State is Owned { // the address of the contract that can modify variables // this can only be changed by the owner of this contract address public associatedContract; constructor(address _associatedContract) internal { // This contract is abstract, and thus cannot be instantiated directly require(owner != address(0), "Owner must be set"); associatedContract = _associatedContract; emit AssociatedContractUpdated(_associatedContract); } /* ========== SETTERS ========== */ // Change the associated contract to a new address function setAssociatedContract(address _associatedContract) external onlyOwner { associatedContract = _associatedContract; emit AssociatedContractUpdated(_associatedContract); } /* ========== MODIFIERS ========== */ modifier onlyAssociatedContract { require(msg.sender == associatedContract, "Only the associated contract can perform this action"); _; } /* ========== EVENTS ========== */ event AssociatedContractUpdated(address associatedContract); } // Inheritance // https://docs.synthetix.io/contracts/source/contracts/tokenstate contract TokenState is Owned, State { /* ERC20 fields. */ mapping(address => uint) public balanceOf; mapping(address => mapping(address => uint)) public allowance; constructor(address _owner, address _associatedContract) public Owned(_owner) State(_associatedContract) {} /* ========== SETTERS ========== */ /** * @notice Set ERC20 allowance. * @dev Only the associated contract may call this. * @param tokenOwner The authorising party. * @param spender The authorised party. * @param value The total value the authorised party may spend on the * authorising party's behalf. */ function setAllowance( address tokenOwner, address spender, uint value ) external onlyAssociatedContract { allowance[tokenOwner][spender] = value; } /** * @notice Set the balance in a given account * @dev Only the associated contract may call this. * @param account The account whose value to set. * @param value The new balance of the given account. */ function setBalanceOf(address account, uint value) external onlyAssociatedContract { balanceOf[account] = value; } } // Inheritance // Libraries // Internal references // https://docs.synthetix.io/contracts/source/contracts/externstatetoken contract ExternStateToken is Owned, Proxyable { using SafeMath for uint; using SafeDecimalMath for uint; /* ========== STATE VARIABLES ========== */ /* Stores balances and allowances. */ TokenState public tokenState; /* Other ERC20 fields. */ string public name; string public symbol; uint public totalSupply; uint8 public decimals; constructor( address payable _proxy, TokenState _tokenState, string memory _name, string memory _symbol, uint _totalSupply, uint8 _decimals, address _owner ) public Owned(_owner) Proxyable(_proxy) { tokenState = _tokenState; name = _name; symbol = _symbol; totalSupply = _totalSupply; decimals = _decimals; } /* ========== VIEWS ========== */ /** * @notice Returns the ERC20 allowance of one party to spend on behalf of another. * @param owner The party authorising spending of their funds. * @param spender The party spending tokenOwner's funds. */ function allowance(address owner, address spender) public view returns (uint) { return tokenState.allowance(owner, spender); } /** * @notice Returns the ERC20 token balance of a given account. */ function balanceOf(address account) external view returns (uint) { return tokenState.balanceOf(account); } /* ========== MUTATIVE FUNCTIONS ========== */ /** * @notice Set the address of the TokenState contract. * @dev This can be used to "pause" transfer functionality, by pointing the tokenState at 0x000.. * as balances would be unreachable. */ function setTokenState(TokenState _tokenState) external optionalProxy_onlyOwner { tokenState = _tokenState; emitTokenStateUpdated(address(_tokenState)); } function _internalTransfer( address from, address to, uint value ) internal returns (bool) { /* Disallow transfers to irretrievable-addresses. */ require(to != address(0) && to != address(this) && to != address(proxy), "Cannot transfer to this address"); // Insufficient balance will be handled by the safe subtraction. tokenState.setBalanceOf(from, tokenState.balanceOf(from).sub(value)); tokenState.setBalanceOf(to, tokenState.balanceOf(to).add(value)); // Emit a standard ERC20 transfer event emitTransfer(from, to, value); return true; } /** * @dev Perform an ERC20 token transfer. Designed to be called by transfer functions possessing * the onlyProxy or optionalProxy modifiers. */ function _transferByProxy( address from, address to, uint value ) internal returns (bool) { return _internalTransfer(from, to, value); } /* * @dev Perform an ERC20 token transferFrom. Designed to be called by transferFrom functions * possessing the optionalProxy or optionalProxy modifiers. */ function _transferFromByProxy( address sender, address from, address to, uint value ) internal returns (bool) { /* Insufficient allowance will be handled by the safe subtraction. */ tokenState.setAllowance(from, sender, tokenState.allowance(from, sender).sub(value)); return _internalTransfer(from, to, value); } /** * @notice Approves spender to transfer on the message sender's behalf. */ function approve(address spender, uint value) public optionalProxy returns (bool) { address sender = messageSender; tokenState.setAllowance(sender, spender, value); emitApproval(sender, spender, value); return true; } /* ========== EVENTS ========== */ function addressToBytes32(address input) internal pure returns (bytes32) { return bytes32(uint256(uint160(input))); } event Transfer(address indexed from, address indexed to, uint value); bytes32 internal constant TRANSFER_SIG = keccak256("Transfer(address,address,uint256)"); function emitTransfer( address from, address to, uint value ) internal { proxy._emit(abi.encode(value), 3, TRANSFER_SIG, addressToBytes32(from), addressToBytes32(to), 0); } event Approval(address indexed owner, address indexed spender, uint value); bytes32 internal constant APPROVAL_SIG = keccak256("Approval(address,address,uint256)"); function emitApproval( address owner, address spender, uint value ) internal { proxy._emit(abi.encode(value), 3, APPROVAL_SIG, addressToBytes32(owner), addressToBytes32(spender), 0); } event TokenStateUpdated(address newTokenState); bytes32 internal constant TOKENSTATEUPDATED_SIG = keccak256("TokenStateUpdated(address)"); function emitTokenStateUpdated(address newTokenState) internal { proxy._emit(abi.encode(newTokenState), 1, TOKENSTATEUPDATED_SIG, 0, 0, 0); } } interface IFuturesMarketManager { function markets(uint index, uint pageSize) external view returns (address[] memory); function numMarkets() external view returns (uint); function allMarkets() external view returns (address[] memory); function marketForKey(bytes32 marketKey) external view returns (address); function marketsForKeys(bytes32[] calldata marketKeys) external view returns (address[] memory); function totalDebt() external view returns (uint debt, bool isInvalid); } // Inheritance // Internal references // https://docs.synthetix.io/contracts/source/contracts/synth contract Synth is Owned, IERC20, ExternStateToken, MixinResolver, ISynth { bytes32 public constant CONTRACT_NAME = "Synth"; /* ========== STATE VARIABLES ========== */ // Currency key which identifies this Synth to the Synthetix system bytes32 public currencyKey; uint8 public constant DECIMALS = 18; // Where fees are pooled in sUSD address public constant FEE_ADDRESS = 0xfeEFEEfeefEeFeefEEFEEfEeFeefEEFeeFEEFEeF; /* ========== ADDRESS RESOLVER CONFIGURATION ========== */ bytes32 private constant CONTRACT_SYSTEMSTATUS = "SystemStatus"; bytes32 private constant CONTRACT_EXCHANGER = "Exchanger"; bytes32 private constant CONTRACT_ISSUER = "Issuer"; bytes32 private constant CONTRACT_FEEPOOL = "FeePool"; bytes32 private constant CONTRACT_FUTURESMARKETMANAGER = "FuturesMarketManager"; /* ========== CONSTRUCTOR ========== */ constructor( address payable _proxy, TokenState _tokenState, string memory _tokenName, string memory _tokenSymbol, address _owner, bytes32 _currencyKey, uint _totalSupply, address _resolver ) public ExternStateToken(_proxy, _tokenState, _tokenName, _tokenSymbol, _totalSupply, DECIMALS, _owner) MixinResolver(_resolver) { require(_proxy != address(0), "_proxy cannot be 0"); require(_owner != address(0), "_owner cannot be 0"); currencyKey = _currencyKey; } /* ========== MUTATIVE FUNCTIONS ========== */ function transfer(address to, uint value) public onlyProxyOrInternal returns (bool) { _ensureCanTransfer(messageSender, value); // transfers to FEE_ADDRESS will be exchanged into sUSD and recorded as fee if (to == FEE_ADDRESS) { return _transferToFeeAddress(to, value); } // transfers to 0x address will be burned if (to == address(0)) { return _internalBurn(messageSender, value); } return super._internalTransfer(messageSender, to, value); } function transferAndSettle(address to, uint value) public onlyProxyOrInternal returns (bool) { // Exchanger.settle ensures synth is active (, , uint numEntriesSettled) = exchanger().settle(messageSender, currencyKey); // Save gas instead of calling transferableSynths uint balanceAfter = value; if (numEntriesSettled > 0) { balanceAfter = tokenState.balanceOf(messageSender); } // Reduce the value to transfer if balance is insufficient after reclaimed value = value > balanceAfter ? balanceAfter : value; return super._internalTransfer(messageSender, to, value); } function transferFrom( address from, address to, uint value ) public onlyProxyOrInternal returns (bool) { _ensureCanTransfer(from, value); return _internalTransferFrom(from, to, value); } function transferFromAndSettle( address from, address to, uint value ) public onlyProxyOrInternal returns (bool) { // Exchanger.settle() ensures synth is active (, , uint numEntriesSettled) = exchanger().settle(from, currencyKey); // Save gas instead of calling transferableSynths uint balanceAfter = value; if (numEntriesSettled > 0) { balanceAfter = tokenState.balanceOf(from); } // Reduce the value to transfer if balance is insufficient after reclaimed value = value >= balanceAfter ? balanceAfter : value; return _internalTransferFrom(from, to, value); } /** * @notice _transferToFeeAddress function * non-sUSD synths are exchanged into sUSD via synthInitiatedExchange * notify feePool to record amount as fee paid to feePool */ function _transferToFeeAddress(address to, uint value) internal returns (bool) { uint amountInUSD; // sUSD can be transferred to FEE_ADDRESS directly if (currencyKey == "sUSD") { amountInUSD = value; super._internalTransfer(messageSender, to, value); } else { // else exchange synth into sUSD and send to FEE_ADDRESS (amountInUSD, ) = exchanger().exchange( messageSender, messageSender, currencyKey, value, "sUSD", FEE_ADDRESS, false, address(0), bytes32(0) ); } // Notify feePool to record sUSD to distribute as fees feePool().recordFeePaid(amountInUSD); return true; } function issue(address account, uint amount) external onlyInternalContracts { _internalIssue(account, amount); } function burn(address account, uint amount) external onlyInternalContracts { _internalBurn(account, amount); } function _internalIssue(address account, uint amount) internal { tokenState.setBalanceOf(account, tokenState.balanceOf(account).add(amount)); totalSupply = totalSupply.add(amount); emitTransfer(address(0), account, amount); emitIssued(account, amount); } function _internalBurn(address account, uint amount) internal returns (bool) { tokenState.setBalanceOf(account, tokenState.balanceOf(account).sub(amount)); totalSupply = totalSupply.sub(amount); emitTransfer(account, address(0), amount); emitBurned(account, amount); return true; } // Allow owner to set the total supply on import. function setTotalSupply(uint amount) external optionalProxy_onlyOwner { totalSupply = amount; } /* ========== VIEWS ========== */ // Note: use public visibility so that it can be invoked in a subclass function resolverAddressesRequired() public view returns (bytes32[] memory addresses) { addresses = new bytes32[](5); addresses[0] = CONTRACT_SYSTEMSTATUS; addresses[1] = CONTRACT_EXCHANGER; addresses[2] = CONTRACT_ISSUER; addresses[3] = CONTRACT_FEEPOOL; addresses[4] = CONTRACT_FUTURESMARKETMANAGER; } function systemStatus() internal view returns (ISystemStatus) { return ISystemStatus(requireAndGetAddress(CONTRACT_SYSTEMSTATUS)); } function feePool() internal view returns (IFeePool) { return IFeePool(requireAndGetAddress(CONTRACT_FEEPOOL)); } function exchanger() internal view returns (IExchanger) { return IExchanger(requireAndGetAddress(CONTRACT_EXCHANGER)); } function issuer() internal view returns (IIssuer) { return IIssuer(requireAndGetAddress(CONTRACT_ISSUER)); } function futuresMarketManager() internal view returns (IFuturesMarketManager) { return IFuturesMarketManager(requireAndGetAddress(CONTRACT_FUTURESMARKETMANAGER)); } function _ensureCanTransfer(address from, uint value) internal view { require(exchanger().maxSecsLeftInWaitingPeriod(from, currencyKey) == 0, "Cannot transfer during waiting period"); require(transferableSynths(from) >= value, "Insufficient balance after any settlement owing"); systemStatus().requireSynthActive(currencyKey); } function transferableSynths(address account) public view returns (uint) { (uint reclaimAmount, , ) = exchanger().settlementOwing(account, currencyKey); // Note: ignoring rebate amount here because a settle() is required in order to // allow the transfer to actually work uint balance = tokenState.balanceOf(account); if (reclaimAmount > balance) { return 0; } else { return balance.sub(reclaimAmount); } } /* ========== INTERNAL FUNCTIONS ========== */ function _internalTransferFrom( address from, address to, uint value ) internal returns (bool) { // Skip allowance update in case of infinite allowance if (tokenState.allowance(from, messageSender) != uint(-1)) { // Reduce the allowance by the amount we're transferring. // The safeSub call will handle an insufficient allowance. tokenState.setAllowance(from, messageSender, tokenState.allowance(from, messageSender).sub(value)); } return super._internalTransfer(from, to, value); } /* ========== MODIFIERS ========== */ function _isInternalContract(address account) internal view returns (bool) { return account == address(feePool()) || account == address(exchanger()) || account == address(issuer()) || account == address(futuresMarketManager()); } modifier onlyInternalContracts() { require(_isInternalContract(msg.sender), "Only internal contracts allowed"); _; } modifier onlyProxyOrInternal { _onlyProxyOrInternal(); _; } function _onlyProxyOrInternal() internal { if (msg.sender == address(proxy)) { // allow proxy through, messageSender should be already set correctly return; } else if (_isInternalTransferCaller(msg.sender)) { // optionalProxy behaviour only for the internal legacy contracts messageSender = msg.sender; } else { revert("Only the proxy can call"); } } /// some legacy internal contracts use transfer methods directly on implementation /// which isn't supported due to SIP-238 for other callers function _isInternalTransferCaller(address caller) internal view returns (bool) { // These entries are not required or cached in order to allow them to not exist (==address(0)) // e.g. due to not being available on L2 or at some future point in time. return // ordered to reduce gas for more frequent calls caller == resolver.getAddress("CollateralShort") || // not used frequently caller == resolver.getAddress("SynthRedeemer") || caller == resolver.getAddress("WrapperFactory") || // transfer not used by users // legacy caller == resolver.getAddress("NativeEtherWrapper") || caller == resolver.getAddress("Depot"); } /* ========== EVENTS ========== */ event Issued(address indexed account, uint value); bytes32 private constant ISSUED_SIG = keccak256("Issued(address,uint256)"); function emitIssued(address account, uint value) internal { proxy._emit(abi.encode(value), 2, ISSUED_SIG, addressToBytes32(account), 0, 0); } event Burned(address indexed account, uint value); bytes32 private constant BURNED_SIG = keccak256("Burned(address,uint256)"); function emitBurned(address account, uint value) internal { proxy._emit(abi.encode(value), 2, BURNED_SIG, addressToBytes32(account), 0, 0); } } interface IWETH { // ERC20 Optional Views function name() external view returns (string memory); function symbol() external view returns (string memory); function decimals() external view returns (uint8); // Views function totalSupply() external view returns (uint); function balanceOf(address owner) external view returns (uint); function allowance(address owner, address spender) external view returns (uint); // Mutative functions 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); // WETH-specific functions. function deposit() external payable; function withdraw(uint amount) external; // Events event Transfer(address indexed from, address indexed to, uint value); event Approval(address indexed owner, address indexed spender, uint value); event Deposit(address indexed to, uint amount); event Withdrawal(address indexed to, uint amount); } // https://docs.synthetix.io/contracts/source/interfaces/ietherwrapper contract IEtherWrapper { function mint(uint amount) external; function burn(uint amount) external; function distributeFees() external; function capacity() external view returns (uint); function getReserves() external view returns (uint); function totalIssuedSynths() external view returns (uint); function calculateMintFee(uint amount) public view returns (uint); function calculateBurnFee(uint amount) public view returns (uint); function maxETH() public view returns (uint256); function mintFeeRate() public view returns (uint256); function burnFeeRate() public view returns (uint256); function weth() public view returns (IWETH); } // https://docs.synthetix.io/contracts/source/interfaces/iwrapperfactory interface IWrapperFactory { function isWrapper(address possibleWrapper) external view returns (bool); function createWrapper( IERC20 token, bytes32 currencyKey, bytes32 synthContractName ) external returns (address); function distributeFees() external; } // Inheritance // Internal references // https://docs.synthetix.io/contracts/source/contracts/multicollateralsynth contract MultiCollateralSynth is Synth { bytes32 public constant CONTRACT_NAME = "MultiCollateralSynth"; /* ========== ADDRESS RESOLVER CONFIGURATION ========== */ bytes32 private constant CONTRACT_COLLATERALMANAGER = "CollateralManager"; bytes32 private constant CONTRACT_ETHER_WRAPPER = "EtherWrapper"; bytes32 private constant CONTRACT_WRAPPER_FACTORY = "WrapperFactory"; /* ========== CONSTRUCTOR ========== */ constructor( address payable _proxy, TokenState _tokenState, string memory _tokenName, string memory _tokenSymbol, address _owner, bytes32 _currencyKey, uint _totalSupply, address _resolver ) public Synth(_proxy, _tokenState, _tokenName, _tokenSymbol, _owner, _currencyKey, _totalSupply, _resolver) {} /* ========== VIEWS ======================= */ function collateralManager() internal view returns (ICollateralManager) { return ICollateralManager(requireAndGetAddress(CONTRACT_COLLATERALMANAGER)); } function etherWrapper() internal view returns (IEtherWrapper) { return IEtherWrapper(requireAndGetAddress(CONTRACT_ETHER_WRAPPER)); } function wrapperFactory() internal view returns (IWrapperFactory) { return IWrapperFactory(requireAndGetAddress(CONTRACT_WRAPPER_FACTORY)); } function resolverAddressesRequired() public view returns (bytes32[] memory addresses) { bytes32[] memory existingAddresses = Synth.resolverAddressesRequired(); bytes32[] memory newAddresses = new bytes32[](3); newAddresses[0] = CONTRACT_COLLATERALMANAGER; newAddresses[1] = CONTRACT_ETHER_WRAPPER; newAddresses[2] = CONTRACT_WRAPPER_FACTORY; addresses = combineArrays(existingAddresses, newAddresses); } /* ========== MUTATIVE FUNCTIONS ========== */ /** * @notice Function that allows multi Collateral to issue a certain number of synths from an account. * @param account Account to issue synths to * @param amount Number of synths */ function issue(address account, uint amount) external onlyInternalContracts { super._internalIssue(account, amount); } /** * @notice Function that allows multi Collateral to burn a certain number of synths from an account. * @param account Account to burn synths from * @param amount Number of synths */ function burn(address account, uint amount) external onlyInternalContracts { super._internalBurn(account, amount); } /* ========== MODIFIERS ========== */ // overriding modifier from super to add more internal contracts and checks function _isInternalContract(address account) internal view returns (bool) { return super._isInternalContract(account) || collateralManager().hasCollateral(account) || wrapperFactory().isWrapper(account) || (account == address(etherWrapper())); } } // Inheritance // https://docs.synthetix.io/contracts/source/contracts/proxyerc20 contract ProxyERC20 is Proxy, IERC20 { constructor(address _owner) public Proxy(_owner) {} // ------------- ERC20 Details ------------- // function name() public view returns (string memory) { // Immutable static call from target contract return IERC20(address(target)).name(); } function symbol() public view returns (string memory) { // Immutable static call from target contract return IERC20(address(target)).symbol(); } function decimals() public view returns (uint8) { // Immutable static call from target contract return IERC20(address(target)).decimals(); } // ------------- ERC20 Interface ------------- // /** * @dev Total number of tokens in existence */ function totalSupply() public view returns (uint256) { // Immutable static call from target contract return IERC20(address(target)).totalSupply(); } /** * @dev Gets the balance of the specified address. * @param account The address to query the balance of. * @return An uint256 representing the amount owned by the passed address. */ function balanceOf(address account) public view returns (uint256) { // Immutable static call from target contract return IERC20(address(target)).balanceOf(account); } /** * @dev Function to check the amount of tokens that an owner allowed to a spender. * @param owner address The address which owns the funds. * @param spender address The address which will spend the funds. * @return A uint256 specifying the amount of tokens still available for the spender. */ function allowance(address owner, address spender) public view returns (uint256) { // Immutable static call from target contract return IERC20(address(target)).allowance(owner, spender); } /** * @dev Transfer token for a specified address * @param to The address to transfer to. * @param value The amount to be transferred. */ function transfer(address to, uint256 value) public returns (bool) { // Mutable state call requires the proxy to tell the target who the msg.sender is. target.setMessageSender(msg.sender); // Forward the ERC20 call to the target contract IERC20(address(target)).transfer(to, value); // Event emitting will occur via Synthetix.Proxy._emit() return true; } /** * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender. * Beware that changing an allowance with this method brings the risk that someone may use both the old * and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this * race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * @param spender The address which will spend the funds. * @param value The amount of tokens to be spent. */ function approve(address spender, uint256 value) public returns (bool) { // Mutable state call requires the proxy to tell the target who the msg.sender is. target.setMessageSender(msg.sender); // Forward the ERC20 call to the target contract IERC20(address(target)).approve(spender, value); // Event emitting will occur via Synthetix.Proxy._emit() return true; } /** * @dev Transfer tokens from one address to another * @param from address The address which you want to send tokens from * @param to address The address which you want to transfer to * @param value uint256 the amount of tokens to be transferred */ function transferFrom( address from, address to, uint256 value ) public returns (bool) { // Mutable state call requires the proxy to tell the target who the msg.sender is. target.setMessageSender(msg.sender); // Forward the ERC20 call to the target contract IERC20(address(target)).transferFrom(from, to, value); // Event emitting will occur via Synthetix.Proxy._emit() return true; } } // https://docs.synthetix.io/contracts/source/interfaces/irewardescrow interface IRewardEscrow { // Views function balanceOf(address account) external view returns (uint); function numVestingEntries(address account) external view returns (uint); function totalEscrowedAccountBalance(address account) external view returns (uint); function totalVestedAccountBalance(address account) external view returns (uint); function getVestingScheduleEntry(address account, uint index) external view returns (uint[2] memory); function getNextVestingIndex(address account) external view returns (uint); // Mutative functions function appendVestingEntry(address account, uint quantity) external; function vest() external; } // Inheritance // Libraries // Internal references // https://docs.synthetix.io/contracts/source/contracts/rewardescrow contract RewardEscrow is Owned, IRewardEscrow { using SafeMath for uint; /* The corresponding Synthetix contract. */ ISynthetix public synthetix; IFeePool public feePool; /* Lists of (timestamp, quantity) pairs per account, sorted in ascending time order. * These are the times at which each given quantity of SNX vests. */ mapping(address => uint[2][]) public vestingSchedules; /* An account's total escrowed synthetix balance to save recomputing this for fee extraction purposes. */ mapping(address => uint) public totalEscrowedAccountBalance; /* An account's total vested reward synthetix. */ mapping(address => uint) public totalVestedAccountBalance; /* The total remaining escrowed balance, for verifying the actual synthetix balance of this contract against. */ uint public totalEscrowedBalance; uint internal constant TIME_INDEX = 0; uint internal constant QUANTITY_INDEX = 1; /* Limit vesting entries to disallow unbounded iteration over vesting schedules. * There are 5 years of the supply schedule */ uint public constant MAX_VESTING_ENTRIES = 52 * 5; /* ========== CONSTRUCTOR ========== */ constructor( address _owner, ISynthetix _synthetix, IFeePool _feePool ) public Owned(_owner) { synthetix = _synthetix; feePool = _feePool; } /* ========== SETTERS ========== */ /** * @notice set the synthetix contract address as we need to transfer SNX when the user vests */ function setSynthetix(ISynthetix _synthetix) external onlyOwner { synthetix = _synthetix; emit SynthetixUpdated(address(_synthetix)); } /** * @notice set the FeePool contract as it is the only authority to be able to call * appendVestingEntry with the onlyFeePool modifer */ function setFeePool(IFeePool _feePool) external onlyOwner { feePool = _feePool; emit FeePoolUpdated(address(_feePool)); } /* ========== VIEW FUNCTIONS ========== */ /** * @notice A simple alias to totalEscrowedAccountBalance: provides ERC20 balance integration. */ function balanceOf(address account) public view returns (uint) { return totalEscrowedAccountBalance[account]; } function _numVestingEntries(address account) internal view returns (uint) { return vestingSchedules[account].length; } /** * @notice The number of vesting dates in an account's schedule. */ function numVestingEntries(address account) external view returns (uint) { return vestingSchedules[account].length; } /** * @notice Get a particular schedule entry for an account. * @return A pair of uints: (timestamp, synthetix quantity). */ function getVestingScheduleEntry(address account, uint index) public view returns (uint[2] memory) { return vestingSchedules[account][index]; } /** * @notice Get the time at which a given schedule entry will vest. */ function getVestingTime(address account, uint index) public view returns (uint) { return getVestingScheduleEntry(account, index)[TIME_INDEX]; } /** * @notice Get the quantity of SNX associated with a given schedule entry. */ function getVestingQuantity(address account, uint index) public view returns (uint) { return getVestingScheduleEntry(account, index)[QUANTITY_INDEX]; } /** * @notice Obtain the index of the next schedule entry that will vest for a given user. */ function getNextVestingIndex(address account) public view returns (uint) { uint len = _numVestingEntries(account); for (uint i = 0; i < len; i++) { if (getVestingTime(account, i) != 0) { return i; } } return len; } /** * @notice Obtain the next schedule entry that will vest for a given user. * @return A pair of uints: (timestamp, synthetix quantity). */ function getNextVestingEntry(address account) public view returns (uint[2] memory) { uint index = getNextVestingIndex(account); if (index == _numVestingEntries(account)) { return [uint(0), 0]; } return getVestingScheduleEntry(account, index); } /** * @notice Obtain the time at which the next schedule entry will vest for a given user. */ function getNextVestingTime(address account) external view returns (uint) { return getNextVestingEntry(account)[TIME_INDEX]; } /** * @notice Obtain the quantity which the next schedule entry will vest for a given user. */ function getNextVestingQuantity(address account) external view returns (uint) { return getNextVestingEntry(account)[QUANTITY_INDEX]; } /** * @notice return the full vesting schedule entries vest for a given user. * @dev For DApps to display the vesting schedule for the * inflationary supply over 5 years. Solidity cant return variable length arrays * so this is returning pairs of data. Vesting Time at [0] and quantity at [1] and so on */ function checkAccountSchedule(address account) public view returns (uint[520] memory) { uint[520] memory _result; uint schedules = _numVestingEntries(account); for (uint i = 0; i < schedules; i++) { uint[2] memory pair = getVestingScheduleEntry(account, i); _result[i * 2] = pair[0]; _result[i * 2 + 1] = pair[1]; } return _result; } /* ========== MUTATIVE FUNCTIONS ========== */ function _appendVestingEntry(address account, uint quantity) internal { /* No empty or already-passed vesting entries allowed. */ require(quantity != 0, "Quantity cannot be zero"); /* There must be enough balance in the contract to provide for the vesting entry. */ totalEscrowedBalance = totalEscrowedBalance.add(quantity); require( totalEscrowedBalance <= IERC20(address(synthetix)).balanceOf(address(this)), "Must be enough balance in the contract to provide for the vesting entry" ); /* Disallow arbitrarily long vesting schedules in light of the gas limit. */ uint scheduleLength = vestingSchedules[account].length; require(scheduleLength <= MAX_VESTING_ENTRIES, "Vesting schedule is too long"); /* Escrow the tokens for 1 year. */ uint time = now + 52 weeks; if (scheduleLength == 0) { totalEscrowedAccountBalance[account] = quantity; } else { /* Disallow adding new vested SNX earlier than the last one. * Since entries are only appended, this means that no vesting date can be repeated. */ require( getVestingTime(account, scheduleLength - 1) < time, "Cannot add new vested entries earlier than the last one" ); totalEscrowedAccountBalance[account] = totalEscrowedAccountBalance[account].add(quantity); } vestingSchedules[account].push([time, quantity]); emit VestingEntryCreated(account, now, quantity); } /** * @notice Add a new vesting entry at a given time and quantity to an account's schedule. * @dev A call to this should accompany a previous successful call to synthetix.transfer(rewardEscrow, amount), * to ensure that when the funds are withdrawn, there is enough balance. * Note; although this function could technically be used to produce unbounded * arrays, it's only withinn the 4 year period of the weekly inflation schedule. * @param account The account to append a new vesting entry to. * @param quantity The quantity of SNX that will be escrowed. */ function appendVestingEntry(address account, uint quantity) external onlyFeePool { _appendVestingEntry(account, quantity); } /** * @notice Allow a user to withdraw any SNX in their schedule that have vested. */ function vest() external { uint numEntries = _numVestingEntries(msg.sender); uint total; for (uint i = 0; i < numEntries; i++) { uint time = getVestingTime(msg.sender, i); /* The list is sorted; when we reach the first future time, bail out. */ if (time > now) { break; } uint qty = getVestingQuantity(msg.sender, i); if (qty > 0) { vestingSchedules[msg.sender][i] = [0, 0]; total = total.add(qty); } } if (total != 0) { totalEscrowedBalance = totalEscrowedBalance.sub(total); totalEscrowedAccountBalance[msg.sender] = totalEscrowedAccountBalance[msg.sender].sub(total); totalVestedAccountBalance[msg.sender] = totalVestedAccountBalance[msg.sender].add(total); IERC20(address(synthetix)).transfer(msg.sender, total); emit Vested(msg.sender, now, total); } } /* ========== MODIFIERS ========== */ modifier onlyFeePool() { bool isFeePool = msg.sender == address(feePool); require(isFeePool, "Only the FeePool contracts can perform this action"); _; } /* ========== EVENTS ========== */ event SynthetixUpdated(address newSynthetix); event FeePoolUpdated(address newFeePool); event Vested(address indexed beneficiary, uint time, uint value); event VestingEntryCreated(address indexed beneficiary, uint time, uint value); } // https://docs.synthetix.io/contracts/source/interfaces/irewardsdistribution interface IRewardsDistribution { // Structs struct DistributionData { address destination; uint amount; } // Views function authority() external view returns (address); function distributions(uint index) external view returns (address destination, uint amount); // DistributionData function distributionsLength() external view returns (uint); // Mutative Functions function distributeRewards(uint amount) external returns (bool); } // Inheritance // Libraires // Internal references // https://docs.synthetix.io/contracts/source/contracts/rewardsdistribution contract RewardsDistribution is Owned, IRewardsDistribution { using SafeMath for uint; using SafeDecimalMath for uint; /** * @notice Authorised address able to call distributeRewards */ address public authority; /** * @notice Address of the Synthetix ProxyERC20 */ address public synthetixProxy; /** * @notice Address of the RewardEscrow contract */ address public rewardEscrow; /** * @notice Address of the FeePoolProxy */ address public feePoolProxy; /** * @notice An array of addresses and amounts to send */ DistributionData[] public distributions; /** * @dev _authority maybe the underlying synthetix contract. * Remember to set the authority on a synthetix upgrade */ constructor( address _owner, address _authority, address _synthetixProxy, address _rewardEscrow, address _feePoolProxy ) public Owned(_owner) { authority = _authority; synthetixProxy = _synthetixProxy; rewardEscrow = _rewardEscrow; feePoolProxy = _feePoolProxy; } // ========== EXTERNAL SETTERS ========== function setSynthetixProxy(address _synthetixProxy) external onlyOwner { synthetixProxy = _synthetixProxy; } function setRewardEscrow(address _rewardEscrow) external onlyOwner { rewardEscrow = _rewardEscrow; } function setFeePoolProxy(address _feePoolProxy) external onlyOwner { feePoolProxy = _feePoolProxy; } /** * @notice Set the address of the contract authorised to call distributeRewards() * @param _authority Address of the authorised calling contract. */ function setAuthority(address _authority) external onlyOwner { authority = _authority; } // ========== EXTERNAL FUNCTIONS ========== /** * @notice Adds a Rewards DistributionData struct to the distributions * array. Any entries here will be iterated and rewards distributed to * each address when tokens are sent to this contract and distributeRewards() * is called by the autority. * @param destination An address to send rewards tokens too * @param amount The amount of rewards tokens to send */ function addRewardDistribution(address destination, uint amount) external onlyOwner returns (bool) { require(destination != address(0), "Cant add a zero address"); require(amount != 0, "Cant add a zero amount"); DistributionData memory rewardsDistribution = DistributionData(destination, amount); distributions.push(rewardsDistribution); emit RewardDistributionAdded(distributions.length - 1, destination, amount); return true; } /** * @notice Deletes a RewardDistribution from the distributions * so it will no longer be included in the call to distributeRewards() * @param index The index of the DistributionData to delete */ function removeRewardDistribution(uint index) external onlyOwner { require(index <= distributions.length - 1, "index out of bounds"); // shift distributions indexes across for (uint i = index; i < distributions.length - 1; i++) { distributions[i] = distributions[i + 1]; } distributions.length--; // Since this function must shift all later entries down to fill the // gap from the one it removed, it could in principle consume an // unbounded amount of gas. However, the number of entries will // presumably always be very low. } /** * @notice Edits a RewardDistribution in the distributions array. * @param index The index of the DistributionData to edit * @param destination The destination address. Send the same address to keep or different address to change it. * @param amount The amount of tokens to edit. Send the same number to keep or change the amount of tokens to send. */ function editRewardDistribution( uint index, address destination, uint amount ) external onlyOwner returns (bool) { require(index <= distributions.length - 1, "index out of bounds"); distributions[index].destination = destination; distributions[index].amount = amount; return true; } function distributeRewards(uint amount) external returns (bool) { require(amount > 0, "Nothing to distribute"); require(msg.sender == authority, "Caller is not authorised"); require(rewardEscrow != address(0), "RewardEscrow is not set"); require(synthetixProxy != address(0), "SynthetixProxy is not set"); require(feePoolProxy != address(0), "FeePoolProxy is not set"); require( IERC20(synthetixProxy).balanceOf(address(this)) >= amount, "RewardsDistribution contract does not have enough tokens to distribute" ); uint remainder = amount; // Iterate the array of distributions sending the configured amounts for (uint i = 0; i < distributions.length; i++) { if (distributions[i].destination != address(0) || distributions[i].amount != 0) { remainder = remainder.sub(distributions[i].amount); // Transfer the SNX IERC20(synthetixProxy).transfer(distributions[i].destination, distributions[i].amount); // If the contract implements RewardsDistributionRecipient.sol, inform it how many SNX its received. bytes memory payload = abi.encodeWithSignature("notifyRewardAmount(uint256)", distributions[i].amount); // solhint-disable avoid-low-level-calls (bool success, ) = distributions[i].destination.call(payload); if (!success) { // Note: we're ignoring the return value as it will fail for contracts that do not implement RewardsDistributionRecipient.sol } } } // After all ditributions have been sent, send the remainder to the RewardsEscrow contract IERC20(synthetixProxy).transfer(rewardEscrow, remainder); // Tell the FeePool how much it has to distribute to the stakers IFeePool(feePoolProxy).setRewardsToDistribute(remainder); emit RewardsDistributed(amount); return true; } /* ========== VIEWS ========== */ /** * @notice Retrieve the length of the distributions array */ function distributionsLength() external view returns (uint) { return distributions.length; } /* ========== Events ========== */ event RewardDistributionAdded(uint index, address destination, uint amount); event RewardsDistributed(uint amount); } // Inheritance // https://docs.synthetix.io/contracts/source/contracts/systemstatus contract SystemStatus is Owned, ISystemStatus { mapping(bytes32 => mapping(address => Status)) public accessControl; uint248 public constant SUSPENSION_REASON_UPGRADE = 1; bytes32 public constant SECTION_SYSTEM = "System"; bytes32 public constant SECTION_ISSUANCE = "Issuance"; bytes32 public constant SECTION_EXCHANGE = "Exchange"; bytes32 public constant SECTION_FUTURES = "Futures"; bytes32 public constant SECTION_SYNTH_EXCHANGE = "SynthExchange"; bytes32 public constant SECTION_SYNTH = "Synth"; bytes32 public constant CONTRACT_NAME = "SystemStatus"; Suspension public systemSuspension; Suspension public issuanceSuspension; Suspension public exchangeSuspension; Suspension public futuresSuspension; mapping(bytes32 => Suspension) public synthExchangeSuspension; mapping(bytes32 => Suspension) public synthSuspension; mapping(bytes32 => Suspension) public futuresMarketSuspension; constructor(address _owner) public Owned(_owner) {} /* ========== VIEWS ========== */ function requireSystemActive() external view { _internalRequireSystemActive(); } function systemSuspended() external view returns (bool) { return systemSuspension.suspended; } function requireIssuanceActive() external view { // Issuance requires the system be active _internalRequireSystemActive(); // and issuance itself of course _internalRequireIssuanceActive(); } function requireExchangeActive() external view { // Exchanging requires the system be active _internalRequireSystemActive(); // and exchanging itself of course _internalRequireExchangeActive(); } function requireSynthExchangeActive(bytes32 currencyKey) external view { // Synth exchange and transfer requires the system be active _internalRequireSystemActive(); _internalRequireSynthExchangeActive(currencyKey); } function requireFuturesActive() external view { _internalRequireSystemActive(); _internalRequireExchangeActive(); _internalRequireFuturesActive(); } /// @notice marketKey doesn't necessarily correspond to asset key function requireFuturesMarketActive(bytes32 marketKey) external view { _internalRequireSystemActive(); _internalRequireExchangeActive(); // exchanging implicitely used _internalRequireFuturesActive(); // futures global flag _internalRequireFuturesMarketActive(marketKey); // specific futures market flag } function synthSuspended(bytes32 currencyKey) external view returns (bool) { return systemSuspension.suspended || synthSuspension[currencyKey].suspended; } function requireSynthActive(bytes32 currencyKey) external view { // Synth exchange and transfer requires the system be active _internalRequireSystemActive(); _internalRequireSynthActive(currencyKey); } function requireSynthsActive(bytes32 sourceCurrencyKey, bytes32 destinationCurrencyKey) external view { // Synth exchange and transfer requires the system be active _internalRequireSystemActive(); _internalRequireSynthActive(sourceCurrencyKey); _internalRequireSynthActive(destinationCurrencyKey); } function requireExchangeBetweenSynthsAllowed(bytes32 sourceCurrencyKey, bytes32 destinationCurrencyKey) external view { // Synth exchange and transfer requires the system be active _internalRequireSystemActive(); // and exchanging must be active _internalRequireExchangeActive(); // and the synth exchanging between the synths must be active _internalRequireSynthExchangeActive(sourceCurrencyKey); _internalRequireSynthExchangeActive(destinationCurrencyKey); // and finally, the synths cannot be suspended _internalRequireSynthActive(sourceCurrencyKey); _internalRequireSynthActive(destinationCurrencyKey); } function isSystemUpgrading() external view returns (bool) { return systemSuspension.suspended && systemSuspension.reason == SUSPENSION_REASON_UPGRADE; } function getSynthExchangeSuspensions(bytes32[] calldata synths) external view returns (bool[] memory exchangeSuspensions, uint256[] memory reasons) { exchangeSuspensions = new bool[](synths.length); reasons = new uint256[](synths.length); for (uint i = 0; i < synths.length; i++) { exchangeSuspensions[i] = synthExchangeSuspension[synths[i]].suspended; reasons[i] = synthExchangeSuspension[synths[i]].reason; } } function getSynthSuspensions(bytes32[] calldata synths) external view returns (bool[] memory suspensions, uint256[] memory reasons) { suspensions = new bool[](synths.length); reasons = new uint256[](synths.length); for (uint i = 0; i < synths.length; i++) { suspensions[i] = synthSuspension[synths[i]].suspended; reasons[i] = synthSuspension[synths[i]].reason; } } /// @notice marketKey doesn't necessarily correspond to asset key function getFuturesMarketSuspensions(bytes32[] calldata marketKeys) external view returns (bool[] memory suspensions, uint256[] memory reasons) { suspensions = new bool[](marketKeys.length); reasons = new uint256[](marketKeys.length); for (uint i = 0; i < marketKeys.length; i++) { suspensions[i] = futuresMarketSuspension[marketKeys[i]].suspended; reasons[i] = futuresMarketSuspension[marketKeys[i]].reason; } } /* ========== MUTATIVE FUNCTIONS ========== */ function updateAccessControl( bytes32 section, address account, bool canSuspend, bool canResume ) external onlyOwner { _internalUpdateAccessControl(section, account, canSuspend, canResume); } function updateAccessControls( bytes32[] calldata sections, address[] calldata accounts, bool[] calldata canSuspends, bool[] calldata canResumes ) external onlyOwner { require( sections.length == accounts.length && accounts.length == canSuspends.length && canSuspends.length == canResumes.length, "Input array lengths must match" ); for (uint i = 0; i < sections.length; i++) { _internalUpdateAccessControl(sections[i], accounts[i], canSuspends[i], canResumes[i]); } } function suspendSystem(uint256 reason) external { _requireAccessToSuspend(SECTION_SYSTEM); systemSuspension.suspended = true; systemSuspension.reason = uint248(reason); emit SystemSuspended(systemSuspension.reason); } function resumeSystem() external { _requireAccessToResume(SECTION_SYSTEM); systemSuspension.suspended = false; emit SystemResumed(uint256(systemSuspension.reason)); systemSuspension.reason = 0; } function suspendIssuance(uint256 reason) external { _requireAccessToSuspend(SECTION_ISSUANCE); issuanceSuspension.suspended = true; issuanceSuspension.reason = uint248(reason); emit IssuanceSuspended(reason); } function resumeIssuance() external { _requireAccessToResume(SECTION_ISSUANCE); issuanceSuspension.suspended = false; emit IssuanceResumed(uint256(issuanceSuspension.reason)); issuanceSuspension.reason = 0; } function suspendExchange(uint256 reason) external { _requireAccessToSuspend(SECTION_EXCHANGE); exchangeSuspension.suspended = true; exchangeSuspension.reason = uint248(reason); emit ExchangeSuspended(reason); } function resumeExchange() external { _requireAccessToResume(SECTION_EXCHANGE); exchangeSuspension.suspended = false; emit ExchangeResumed(uint256(exchangeSuspension.reason)); exchangeSuspension.reason = 0; } function suspendFutures(uint256 reason) external { _requireAccessToSuspend(SECTION_FUTURES); futuresSuspension.suspended = true; futuresSuspension.reason = uint248(reason); emit FuturesSuspended(reason); } function resumeFutures() external { _requireAccessToResume(SECTION_FUTURES); futuresSuspension.suspended = false; emit FuturesResumed(uint256(futuresSuspension.reason)); futuresSuspension.reason = 0; } /// @notice marketKey doesn't necessarily correspond to asset key function suspendFuturesMarket(bytes32 marketKey, uint256 reason) external { bytes32[] memory marketKeys = new bytes32[](1); marketKeys[0] = marketKey; _internalSuspendFuturesMarkets(marketKeys, reason); } /// @notice marketKey doesn't necessarily correspond to asset key function suspendFuturesMarkets(bytes32[] calldata marketKeys, uint256 reason) external { _internalSuspendFuturesMarkets(marketKeys, reason); } /// @notice marketKey doesn't necessarily correspond to asset key function resumeFuturesMarket(bytes32 marketKey) external { bytes32[] memory marketKeys = new bytes32[](1); marketKeys[0] = marketKey; _internalResumeFuturesMarkets(marketKeys); } /// @notice marketKey doesn't necessarily correspond to asset key function resumeFuturesMarkets(bytes32[] calldata marketKeys) external { _internalResumeFuturesMarkets(marketKeys); } function suspendSynthExchange(bytes32 currencyKey, uint256 reason) external { bytes32[] memory currencyKeys = new bytes32[](1); currencyKeys[0] = currencyKey; _internalSuspendSynthExchange(currencyKeys, reason); } function suspendSynthsExchange(bytes32[] calldata currencyKeys, uint256 reason) external { _internalSuspendSynthExchange(currencyKeys, reason); } function resumeSynthExchange(bytes32 currencyKey) external { bytes32[] memory currencyKeys = new bytes32[](1); currencyKeys[0] = currencyKey; _internalResumeSynthsExchange(currencyKeys); } function resumeSynthsExchange(bytes32[] calldata currencyKeys) external { _internalResumeSynthsExchange(currencyKeys); } function suspendSynth(bytes32 currencyKey, uint256 reason) external { bytes32[] memory currencyKeys = new bytes32[](1); currencyKeys[0] = currencyKey; _internalSuspendSynths(currencyKeys, reason); } function suspendSynths(bytes32[] calldata currencyKeys, uint256 reason) external { _internalSuspendSynths(currencyKeys, reason); } function resumeSynth(bytes32 currencyKey) external { bytes32[] memory currencyKeys = new bytes32[](1); currencyKeys[0] = currencyKey; _internalResumeSynths(currencyKeys); } function resumeSynths(bytes32[] calldata currencyKeys) external { _internalResumeSynths(currencyKeys); } /* ========== INTERNAL FUNCTIONS ========== */ function _requireAccessToSuspend(bytes32 section) internal view { require(accessControl[section][msg.sender].canSuspend, "Restricted to access control list"); } function _requireAccessToResume(bytes32 section) internal view { require(accessControl[section][msg.sender].canResume, "Restricted to access control list"); } function _internalRequireSystemActive() internal view { require( !systemSuspension.suspended, systemSuspension.reason == SUSPENSION_REASON_UPGRADE ? "Synthetix is suspended, upgrade in progress... please stand by" : "Synthetix is suspended. Operation prohibited" ); } function _internalRequireIssuanceActive() internal view { require(!issuanceSuspension.suspended, "Issuance is suspended. Operation prohibited"); } function _internalRequireExchangeActive() internal view { require(!exchangeSuspension.suspended, "Exchange is suspended. Operation prohibited"); } function _internalRequireFuturesActive() internal view { require(!futuresSuspension.suspended, "Futures markets are suspended. Operation prohibited"); } function _internalRequireSynthExchangeActive(bytes32 currencyKey) internal view { require(!synthExchangeSuspension[currencyKey].suspended, "Synth exchange suspended. Operation prohibited"); } function _internalRequireSynthActive(bytes32 currencyKey) internal view { require(!synthSuspension[currencyKey].suspended, "Synth is suspended. Operation prohibited"); } function _internalRequireFuturesMarketActive(bytes32 marketKey) internal view { require(!futuresMarketSuspension[marketKey].suspended, "Market suspended"); } function _internalSuspendSynths(bytes32[] memory currencyKeys, uint256 reason) internal { _requireAccessToSuspend(SECTION_SYNTH); for (uint i = 0; i < currencyKeys.length; i++) { bytes32 currencyKey = currencyKeys[i]; synthSuspension[currencyKey].suspended = true; synthSuspension[currencyKey].reason = uint248(reason); emit SynthSuspended(currencyKey, reason); } } function _internalResumeSynths(bytes32[] memory currencyKeys) internal { _requireAccessToResume(SECTION_SYNTH); for (uint i = 0; i < currencyKeys.length; i++) { bytes32 currencyKey = currencyKeys[i]; emit SynthResumed(currencyKey, uint256(synthSuspension[currencyKey].reason)); delete synthSuspension[currencyKey]; } } function _internalSuspendSynthExchange(bytes32[] memory currencyKeys, uint256 reason) internal { _requireAccessToSuspend(SECTION_SYNTH_EXCHANGE); for (uint i = 0; i < currencyKeys.length; i++) { bytes32 currencyKey = currencyKeys[i]; synthExchangeSuspension[currencyKey].suspended = true; synthExchangeSuspension[currencyKey].reason = uint248(reason); emit SynthExchangeSuspended(currencyKey, reason); } } function _internalResumeSynthsExchange(bytes32[] memory currencyKeys) internal { _requireAccessToResume(SECTION_SYNTH_EXCHANGE); for (uint i = 0; i < currencyKeys.length; i++) { bytes32 currencyKey = currencyKeys[i]; emit SynthExchangeResumed(currencyKey, uint256(synthExchangeSuspension[currencyKey].reason)); delete synthExchangeSuspension[currencyKey]; } } function _internalSuspendFuturesMarkets(bytes32[] memory marketKeys, uint256 reason) internal { _requireAccessToSuspend(SECTION_FUTURES); for (uint i = 0; i < marketKeys.length; i++) { bytes32 marketKey = marketKeys[i]; futuresMarketSuspension[marketKey].suspended = true; futuresMarketSuspension[marketKey].reason = uint248(reason); emit FuturesMarketSuspended(marketKey, reason); } } function _internalResumeFuturesMarkets(bytes32[] memory marketKeys) internal { _requireAccessToResume(SECTION_FUTURES); for (uint i = 0; i < marketKeys.length; i++) { bytes32 marketKey = marketKeys[i]; emit FuturesMarketResumed(marketKey, uint256(futuresMarketSuspension[marketKey].reason)); delete futuresMarketSuspension[marketKey]; } } function _internalUpdateAccessControl( bytes32 section, address account, bool canSuspend, bool canResume ) internal { require( section == SECTION_SYSTEM || section == SECTION_ISSUANCE || section == SECTION_EXCHANGE || section == SECTION_FUTURES || section == SECTION_SYNTH_EXCHANGE || section == SECTION_SYNTH, "Invalid section supplied" ); accessControl[section][account].canSuspend = canSuspend; accessControl[section][account].canResume = canResume; emit AccessControlUpdated(section, account, canSuspend, canResume); } /* ========== EVENTS ========== */ event SystemSuspended(uint256 reason); event SystemResumed(uint256 reason); event IssuanceSuspended(uint256 reason); event IssuanceResumed(uint256 reason); event ExchangeSuspended(uint256 reason); event ExchangeResumed(uint256 reason); event FuturesSuspended(uint256 reason); event FuturesResumed(uint256 reason); event SynthExchangeSuspended(bytes32 currencyKey, uint256 reason); event SynthExchangeResumed(bytes32 currencyKey, uint256 reason); event SynthSuspended(bytes32 currencyKey, uint256 reason); event SynthResumed(bytes32 currencyKey, uint256 reason); event FuturesMarketSuspended(bytes32 marketKey, uint256 reason); event FuturesMarketResumed(bytes32 marketKey, uint256 reason); event AccessControlUpdated(bytes32 indexed section, address indexed account, bool canSuspend, bool canResume); } interface ISynthetixNamedContract { // solhint-disable func-name-mixedcase function CONTRACT_NAME() external view returns (bytes32); } // solhint-disable contract-name-camelcase contract Migration_Saiph is BaseMigration { // https://etherscan.io/address/0xEb3107117FEAd7de89Cd14D463D340A2E6917769; address public constant OWNER = 0xEb3107117FEAd7de89Cd14D463D340A2E6917769; // ---------------------------- // EXISTING SYNTHETIX CONTRACTS // ---------------------------- // https://etherscan.io/address/0x823bE81bbF96BEc0e25CA13170F5AaCb5B79ba83 AddressResolver public constant addressresolver_i = AddressResolver(0x823bE81bbF96BEc0e25CA13170F5AaCb5B79ba83); // https://etherscan.io/address/0xC011a73ee8576Fb46F5E1c5751cA3B9Fe0af2a6F Proxy public constant proxysynthetix_i = Proxy(0xC011a73ee8576Fb46F5E1c5751cA3B9Fe0af2a6F); // https://etherscan.io/address/0x696c905F8F8c006cA46e9808fE7e00049507798F SystemStatus public constant systemstatus_i = SystemStatus(0x696c905F8F8c006cA46e9808fE7e00049507798F); // https://etherscan.io/address/0x5b1b5fEa1b99D83aD479dF0C222F0492385381dD LegacyTokenState public constant tokenstatesynthetix_i = LegacyTokenState(0x5b1b5fEa1b99D83aD479dF0C222F0492385381dD); // https://etherscan.io/address/0xb671F2210B1F6621A2607EA63E6B2DC3e2464d1F RewardEscrow public constant rewardescrow_i = RewardEscrow(0xb671F2210B1F6621A2607EA63E6B2DC3e2464d1F); // https://etherscan.io/address/0x29C295B046a73Cde593f21f63091B072d407e3F2 RewardsDistribution public constant rewardsdistribution_i = RewardsDistribution(0x29C295B046a73Cde593f21f63091B072d407e3F2); // https://etherscan.io/address/0x10A5F7D9D65bCc2734763444D4940a31b109275f MultiCollateralSynth public constant synthsusd_i = MultiCollateralSynth(0x10A5F7D9D65bCc2734763444D4940a31b109275f); // https://etherscan.io/address/0x05a9CBe762B36632b3594DA4F082340E0e5343e8 TokenState public constant tokenstatesusd_i = TokenState(0x05a9CBe762B36632b3594DA4F082340E0e5343e8); // https://etherscan.io/address/0x57Ab1ec28D129707052df4dF418D58a2D46d5f51 Proxy public constant proxysusd_i = Proxy(0x57Ab1ec28D129707052df4dF418D58a2D46d5f51); // https://etherscan.io/address/0xa8E31E3C38aDD6052A9407298FAEB8fD393A6cF9 MultiCollateralSynth public constant synthseur_i = MultiCollateralSynth(0xa8E31E3C38aDD6052A9407298FAEB8fD393A6cF9); // https://etherscan.io/address/0x6568D9e750fC44AF00f857885Dfb8281c00529c4 TokenState public constant tokenstateseur_i = TokenState(0x6568D9e750fC44AF00f857885Dfb8281c00529c4); // https://etherscan.io/address/0xD71eCFF9342A5Ced620049e616c5035F1dB98620 ProxyERC20 public constant proxyseur_i = ProxyERC20(0xD71eCFF9342A5Ced620049e616c5035F1dB98620); // https://etherscan.io/address/0xE1cc2332852B2Ac0dA59A1f9D3051829f4eF3c1C MultiCollateralSynth public constant synthsjpy_i = MultiCollateralSynth(0xE1cc2332852B2Ac0dA59A1f9D3051829f4eF3c1C); // https://etherscan.io/address/0x4dFACfB15514C21c991ff75Bc7Bf6Fb1F98361ed TokenState public constant tokenstatesjpy_i = TokenState(0x4dFACfB15514C21c991ff75Bc7Bf6Fb1F98361ed); // https://etherscan.io/address/0xF6b1C627e95BFc3c1b4c9B825a032Ff0fBf3e07d ProxyERC20 public constant proxysjpy_i = ProxyERC20(0xF6b1C627e95BFc3c1b4c9B825a032Ff0fBf3e07d); // https://etherscan.io/address/0xfb020CA7f4e8C4a5bBBe060f59a249c6275d2b69 MultiCollateralSynth public constant synthsaud_i = MultiCollateralSynth(0xfb020CA7f4e8C4a5bBBe060f59a249c6275d2b69); // https://etherscan.io/address/0xCb29D2cf2C65d3Be1d00F07f3441390432D55203 TokenState public constant tokenstatesaud_i = TokenState(0xCb29D2cf2C65d3Be1d00F07f3441390432D55203); // https://etherscan.io/address/0xF48e200EAF9906362BB1442fca31e0835773b8B4 ProxyERC20 public constant proxysaud_i = ProxyERC20(0xF48e200EAF9906362BB1442fca31e0835773b8B4); // https://etherscan.io/address/0xdc883b9d9Ee16f74bE08826E68dF4C9D9d26e8bD MultiCollateralSynth public constant synthsgbp_i = MultiCollateralSynth(0xdc883b9d9Ee16f74bE08826E68dF4C9D9d26e8bD); // https://etherscan.io/address/0x7e88D19A79b291cfE5696d496055f7e57F537A75 TokenState public constant tokenstatesgbp_i = TokenState(0x7e88D19A79b291cfE5696d496055f7e57F537A75); // https://etherscan.io/address/0x97fe22E7341a0Cd8Db6F6C021A24Dc8f4DAD855F ProxyERC20 public constant proxysgbp_i = ProxyERC20(0x97fe22E7341a0Cd8Db6F6C021A24Dc8f4DAD855F); // https://etherscan.io/address/0xBb5b03E920cF702De5A3bA9Fc1445aF4B3919c88 MultiCollateralSynth public constant synthschf_i = MultiCollateralSynth(0xBb5b03E920cF702De5A3bA9Fc1445aF4B3919c88); // https://etherscan.io/address/0x52496fE8a4feaEFe14d9433E00D48E6929c13deC TokenState public constant tokenstateschf_i = TokenState(0x52496fE8a4feaEFe14d9433E00D48E6929c13deC); // https://etherscan.io/address/0x0F83287FF768D1c1e17a42F44d644D7F22e8ee1d ProxyERC20 public constant proxyschf_i = ProxyERC20(0x0F83287FF768D1c1e17a42F44d644D7F22e8ee1d); // https://etherscan.io/address/0xdAe6C79c46aB3B280Ca28259000695529cbD1339 MultiCollateralSynth public constant synthskrw_i = MultiCollateralSynth(0xdAe6C79c46aB3B280Ca28259000695529cbD1339); // https://etherscan.io/address/0x93B6e9FbBd2c32a0DC3C2B943B7C3CBC2fE23730 TokenState public constant tokenstateskrw_i = TokenState(0x93B6e9FbBd2c32a0DC3C2B943B7C3CBC2fE23730); // https://etherscan.io/address/0x269895a3dF4D73b077Fc823dD6dA1B95f72Aaf9B ProxyERC20 public constant proxyskrw_i = ProxyERC20(0x269895a3dF4D73b077Fc823dD6dA1B95f72Aaf9B); // https://etherscan.io/address/0x1cB004a8e84a5CE95C1fF895EE603BaC8EC506c7 MultiCollateralSynth public constant synthsbtc_i = MultiCollateralSynth(0x1cB004a8e84a5CE95C1fF895EE603BaC8EC506c7); // https://etherscan.io/address/0x4F6296455F8d754c19821cF1EC8FeBF2cD456E67 TokenState public constant tokenstatesbtc_i = TokenState(0x4F6296455F8d754c19821cF1EC8FeBF2cD456E67); // https://etherscan.io/address/0xfE18be6b3Bd88A2D2A7f928d00292E7a9963CfC6 ProxyERC20 public constant proxysbtc_i = ProxyERC20(0xfE18be6b3Bd88A2D2A7f928d00292E7a9963CfC6); // https://etherscan.io/address/0x5D4C724BFe3a228Ff0E29125Ac1571FE093700a4 MultiCollateralSynth public constant synthseth_i = MultiCollateralSynth(0x5D4C724BFe3a228Ff0E29125Ac1571FE093700a4); // https://etherscan.io/address/0x34A5ef81d18F3a305aE9C2d7DF42beef4c79031c TokenState public constant tokenstateseth_i = TokenState(0x34A5ef81d18F3a305aE9C2d7DF42beef4c79031c); // https://etherscan.io/address/0x5e74C9036fb86BD7eCdcb084a0673EFc32eA31cb ProxyERC20 public constant proxyseth_i = ProxyERC20(0x5e74C9036fb86BD7eCdcb084a0673EFc32eA31cb); // https://etherscan.io/address/0xDF69bC4541b86Aa4c5A470B4347E730c38b2c3B2 MultiCollateralSynth public constant synthslink_i = MultiCollateralSynth(0xDF69bC4541b86Aa4c5A470B4347E730c38b2c3B2); // https://etherscan.io/address/0x577D4a7395c6A5f46d9981a5F83fa7294926aBB0 TokenState public constant tokenstateslink_i = TokenState(0x577D4a7395c6A5f46d9981a5F83fa7294926aBB0); // https://etherscan.io/address/0xbBC455cb4F1B9e4bFC4B73970d360c8f032EfEE6 ProxyERC20 public constant proxyslink_i = ProxyERC20(0xbBC455cb4F1B9e4bFC4B73970d360c8f032EfEE6); // https://etherscan.io/address/0x91b82d62Ff322b8e02b86f33E9A99a813437830d MultiCollateralSynth public constant synthsada_i = MultiCollateralSynth(0x91b82d62Ff322b8e02b86f33E9A99a813437830d); // https://etherscan.io/address/0x9956c5019a24fbd5B506AD070b771577bAc5c343 TokenState public constant tokenstatesada_i = TokenState(0x9956c5019a24fbd5B506AD070b771577bAc5c343); // https://etherscan.io/address/0xe36E2D3c7c34281FA3bC737950a68571736880A1 ProxyERC20 public constant proxysada_i = ProxyERC20(0xe36E2D3c7c34281FA3bC737950a68571736880A1); // https://etherscan.io/address/0x942Eb6e8c029EB22103743C99985aF4F4515a559 MultiCollateralSynth public constant synthsaave_i = MultiCollateralSynth(0x942Eb6e8c029EB22103743C99985aF4F4515a559); // https://etherscan.io/address/0x9BcED8A8E3Ad81c9b146FFC880358f734A06f7c0 TokenState public constant tokenstatesaave_i = TokenState(0x9BcED8A8E3Ad81c9b146FFC880358f734A06f7c0); // https://etherscan.io/address/0xd2dF355C19471c8bd7D8A3aa27Ff4e26A21b4076 ProxyERC20 public constant proxysaave_i = ProxyERC20(0xd2dF355C19471c8bd7D8A3aa27Ff4e26A21b4076); // https://etherscan.io/address/0x75A0c1597137AA36B40b6a515D997F9a6c6eefEB MultiCollateralSynth public constant synthsdot_i = MultiCollateralSynth(0x75A0c1597137AA36B40b6a515D997F9a6c6eefEB); // https://etherscan.io/address/0x73B1a2643507Cd30F11Dfcf2D974f4373E5BC077 TokenState public constant tokenstatesdot_i = TokenState(0x73B1a2643507Cd30F11Dfcf2D974f4373E5BC077); // https://etherscan.io/address/0x1715AC0743102BF5Cd58EfBB6Cf2dC2685d967b6 ProxyERC20 public constant proxysdot_i = ProxyERC20(0x1715AC0743102BF5Cd58EfBB6Cf2dC2685d967b6); // https://etherscan.io/address/0x07C1E81C345A7c58d7c24072EFc5D929BD0647AD MultiCollateralSynth public constant synthsethbtc_i = MultiCollateralSynth(0x07C1E81C345A7c58d7c24072EFc5D929BD0647AD); // https://etherscan.io/address/0x042A7A0022A7695454ac5Be77a4860e50c9683fC TokenState public constant tokenstatesethbtc_i = TokenState(0x042A7A0022A7695454ac5Be77a4860e50c9683fC); // https://etherscan.io/address/0x104eDF1da359506548BFc7c25bA1E28C16a70235 ProxyERC20 public constant proxysethbtc_i = ProxyERC20(0x104eDF1da359506548BFc7c25bA1E28C16a70235); // https://etherscan.io/address/0x918b1dbf0917FdD74D03fB9434915E2ECEc89286 MultiCollateralSynth public constant synthsdefi_i = MultiCollateralSynth(0x918b1dbf0917FdD74D03fB9434915E2ECEc89286); // https://etherscan.io/address/0x7Ac2D37098a65B0f711CFfA3be635F1E6aCacFaB TokenState public constant tokenstatesdefi_i = TokenState(0x7Ac2D37098a65B0f711CFfA3be635F1E6aCacFaB); // https://etherscan.io/address/0xe1aFe1Fd76Fd88f78cBf599ea1846231B8bA3B6B ProxyERC20 public constant proxysdefi_i = ProxyERC20(0xe1aFe1Fd76Fd88f78cBf599ea1846231B8bA3B6B); // https://etherscan.io/address/0xE5CC99EFA57943F4EA0cE6bed265318697748649 Issuer public constant issuer_i = Issuer(0xE5CC99EFA57943F4EA0cE6bed265318697748649); // ---------------------------------- // NEW CONTRACTS DEPLOYED TO BE ADDED // ---------------------------------- // https://etherscan.io/address/0x08F30Ecf2C15A783083ab9D5b9211c22388d0564 address public constant new_Synthetix_contract = 0x08F30Ecf2C15A783083ab9D5b9211c22388d0564; // https://etherscan.io/address/0xE5CC99EFA57943F4EA0cE6bed265318697748649 address public constant new_Issuer_contract = 0xE5CC99EFA57943F4EA0cE6bed265318697748649; // https://etherscan.io/address/0x10A5F7D9D65bCc2734763444D4940a31b109275f address public constant new_SynthsUSD_contract = 0x10A5F7D9D65bCc2734763444D4940a31b109275f; // https://etherscan.io/address/0xE1cc2332852B2Ac0dA59A1f9D3051829f4eF3c1C address public constant new_SynthsJPY_contract = 0xE1cc2332852B2Ac0dA59A1f9D3051829f4eF3c1C; // https://etherscan.io/address/0xa8E31E3C38aDD6052A9407298FAEB8fD393A6cF9 address public constant new_SynthsEUR_contract = 0xa8E31E3C38aDD6052A9407298FAEB8fD393A6cF9; // https://etherscan.io/address/0xfb020CA7f4e8C4a5bBBe060f59a249c6275d2b69 address public constant new_SynthsAUD_contract = 0xfb020CA7f4e8C4a5bBBe060f59a249c6275d2b69; // https://etherscan.io/address/0xdc883b9d9Ee16f74bE08826E68dF4C9D9d26e8bD address public constant new_SynthsGBP_contract = 0xdc883b9d9Ee16f74bE08826E68dF4C9D9d26e8bD; // https://etherscan.io/address/0xBb5b03E920cF702De5A3bA9Fc1445aF4B3919c88 address public constant new_SynthsCHF_contract = 0xBb5b03E920cF702De5A3bA9Fc1445aF4B3919c88; // https://etherscan.io/address/0xdAe6C79c46aB3B280Ca28259000695529cbD1339 address public constant new_SynthsKRW_contract = 0xdAe6C79c46aB3B280Ca28259000695529cbD1339; // https://etherscan.io/address/0x5D4C724BFe3a228Ff0E29125Ac1571FE093700a4 address public constant new_SynthsETH_contract = 0x5D4C724BFe3a228Ff0E29125Ac1571FE093700a4; // https://etherscan.io/address/0x1cB004a8e84a5CE95C1fF895EE603BaC8EC506c7 address public constant new_SynthsBTC_contract = 0x1cB004a8e84a5CE95C1fF895EE603BaC8EC506c7; // https://etherscan.io/address/0xDF69bC4541b86Aa4c5A470B4347E730c38b2c3B2 address public constant new_SynthsLINK_contract = 0xDF69bC4541b86Aa4c5A470B4347E730c38b2c3B2; // https://etherscan.io/address/0x942Eb6e8c029EB22103743C99985aF4F4515a559 address public constant new_SynthsAAVE_contract = 0x942Eb6e8c029EB22103743C99985aF4F4515a559; // https://etherscan.io/address/0x91b82d62Ff322b8e02b86f33E9A99a813437830d address public constant new_SynthsADA_contract = 0x91b82d62Ff322b8e02b86f33E9A99a813437830d; // https://etherscan.io/address/0x75A0c1597137AA36B40b6a515D997F9a6c6eefEB address public constant new_SynthsDOT_contract = 0x75A0c1597137AA36B40b6a515D997F9a6c6eefEB; // https://etherscan.io/address/0x918b1dbf0917FdD74D03fB9434915E2ECEc89286 address public constant new_SynthsDEFI_contract = 0x918b1dbf0917FdD74D03fB9434915E2ECEc89286; // https://etherscan.io/address/0x07C1E81C345A7c58d7c24072EFc5D929BD0647AD address public constant new_SynthsETHBTC_contract = 0x07C1E81C345A7c58d7c24072EFc5D929BD0647AD; constructor() public BaseMigration(OWNER) {} function contractsRequiringOwnership() public pure returns (address[] memory contracts) { contracts = new address[](52); contracts[0] = address(addressresolver_i); contracts[1] = address(proxysynthetix_i); contracts[2] = address(systemstatus_i); contracts[3] = address(tokenstatesynthetix_i); contracts[4] = address(rewardescrow_i); contracts[5] = address(rewardsdistribution_i); contracts[6] = address(synthsusd_i); contracts[7] = address(tokenstatesusd_i); contracts[8] = address(proxysusd_i); contracts[9] = address(synthseur_i); contracts[10] = address(tokenstateseur_i); contracts[11] = address(proxyseur_i); contracts[12] = address(synthsjpy_i); contracts[13] = address(tokenstatesjpy_i); contracts[14] = address(proxysjpy_i); contracts[15] = address(synthsaud_i); contracts[16] = address(tokenstatesaud_i); contracts[17] = address(proxysaud_i); contracts[18] = address(synthsgbp_i); contracts[19] = address(tokenstatesgbp_i); contracts[20] = address(proxysgbp_i); contracts[21] = address(synthschf_i); contracts[22] = address(tokenstateschf_i); contracts[23] = address(proxyschf_i); contracts[24] = address(synthskrw_i); contracts[25] = address(tokenstateskrw_i); contracts[26] = address(proxyskrw_i); contracts[27] = address(synthsbtc_i); contracts[28] = address(tokenstatesbtc_i); contracts[29] = address(proxysbtc_i); contracts[30] = address(synthseth_i); contracts[31] = address(tokenstateseth_i); contracts[32] = address(proxyseth_i); contracts[33] = address(synthslink_i); contracts[34] = address(tokenstateslink_i); contracts[35] = address(proxyslink_i); contracts[36] = address(synthsada_i); contracts[37] = address(tokenstatesada_i); contracts[38] = address(proxysada_i); contracts[39] = address(synthsaave_i); contracts[40] = address(tokenstatesaave_i); contracts[41] = address(proxysaave_i); contracts[42] = address(synthsdot_i); contracts[43] = address(tokenstatesdot_i); contracts[44] = address(proxysdot_i); contracts[45] = address(synthsethbtc_i); contracts[46] = address(tokenstatesethbtc_i); contracts[47] = address(proxysethbtc_i); contracts[48] = address(synthsdefi_i); contracts[49] = address(tokenstatesdefi_i); contracts[50] = address(proxysdefi_i); contracts[51] = address(issuer_i); } function migrate() external onlyOwner { // ACCEPT OWNERSHIP for all contracts that require ownership to make changes acceptAll(); // MIGRATION // Import all new contracts into the address resolver; addressresolver_importAddresses_0(); // Rebuild the resolver caches in all MixinResolver contracts - batch 1; addressresolver_rebuildCaches_1(); // Rebuild the resolver caches in all MixinResolver contracts - batch 2; addressresolver_rebuildCaches_2(); // Ensure the SNX proxy has the correct Synthetix target set; proxysynthetix_i.setTarget(Proxyable(new_Synthetix_contract)); // Ensure Issuer contract can suspend issuance - see SIP-165; systemstatus_i.updateAccessControl("Issuance", new_Issuer_contract, true, false); // Ensure the Synthetix contract can write to its TokenState contract; tokenstatesynthetix_i.setAssociatedContract(new_Synthetix_contract); // Ensure the legacy RewardEscrow contract is connected to the Synthetix contract; rewardescrow_i.setSynthetix(ISynthetix(new_Synthetix_contract)); // Ensure the RewardsDistribution has Synthetix set as its authority for distribution; rewardsdistribution_i.setAuthority(new_Synthetix_contract); // Ensure the new synth has the totalSupply from the previous one; copyTotalSupplyFrom_sUSD(); // Ensure the sUSD synth can write to its TokenState; tokenstatesusd_i.setAssociatedContract(new_SynthsUSD_contract); // Ensure the sUSD synth Proxy is correctly connected to the Synth; proxysusd_i.setTarget(Proxyable(new_SynthsUSD_contract)); // Ensure the new synth has the totalSupply from the previous one; copyTotalSupplyFrom_sEUR(); // Ensure the sEUR synth can write to its TokenState; tokenstateseur_i.setAssociatedContract(new_SynthsEUR_contract); // Ensure the sEUR synth Proxy is correctly connected to the Synth; proxyseur_i.setTarget(Proxyable(new_SynthsEUR_contract)); // Ensure the new synth has the totalSupply from the previous one; copyTotalSupplyFrom_sJPY(); // Ensure the sJPY synth can write to its TokenState; tokenstatesjpy_i.setAssociatedContract(new_SynthsJPY_contract); // Ensure the sJPY synth Proxy is correctly connected to the Synth; proxysjpy_i.setTarget(Proxyable(new_SynthsJPY_contract)); // Ensure the new synth has the totalSupply from the previous one; copyTotalSupplyFrom_sAUD(); // Ensure the sAUD synth can write to its TokenState; tokenstatesaud_i.setAssociatedContract(new_SynthsAUD_contract); // Ensure the sAUD synth Proxy is correctly connected to the Synth; proxysaud_i.setTarget(Proxyable(new_SynthsAUD_contract)); // Ensure the new synth has the totalSupply from the previous one; copyTotalSupplyFrom_sGBP(); // Ensure the sGBP synth can write to its TokenState; tokenstatesgbp_i.setAssociatedContract(new_SynthsGBP_contract); // Ensure the sGBP synth Proxy is correctly connected to the Synth; proxysgbp_i.setTarget(Proxyable(new_SynthsGBP_contract)); // Ensure the new synth has the totalSupply from the previous one; copyTotalSupplyFrom_sCHF(); // Ensure the sCHF synth can write to its TokenState; tokenstateschf_i.setAssociatedContract(new_SynthsCHF_contract); // Ensure the sCHF synth Proxy is correctly connected to the Synth; proxyschf_i.setTarget(Proxyable(new_SynthsCHF_contract)); // Ensure the new synth has the totalSupply from the previous one; copyTotalSupplyFrom_sKRW(); // Ensure the sKRW synth can write to its TokenState; tokenstateskrw_i.setAssociatedContract(new_SynthsKRW_contract); // Ensure the sKRW synth Proxy is correctly connected to the Synth; proxyskrw_i.setTarget(Proxyable(new_SynthsKRW_contract)); // Ensure the new synth has the totalSupply from the previous one; copyTotalSupplyFrom_sBTC(); // Ensure the sBTC synth can write to its TokenState; tokenstatesbtc_i.setAssociatedContract(new_SynthsBTC_contract); // Ensure the sBTC synth Proxy is correctly connected to the Synth; proxysbtc_i.setTarget(Proxyable(new_SynthsBTC_contract)); // Ensure the new synth has the totalSupply from the previous one; copyTotalSupplyFrom_sETH(); // Ensure the sETH synth can write to its TokenState; tokenstateseth_i.setAssociatedContract(new_SynthsETH_contract); // Ensure the sETH synth Proxy is correctly connected to the Synth; proxyseth_i.setTarget(Proxyable(new_SynthsETH_contract)); // Ensure the new synth has the totalSupply from the previous one; copyTotalSupplyFrom_sLINK(); // Ensure the sLINK synth can write to its TokenState; tokenstateslink_i.setAssociatedContract(new_SynthsLINK_contract); // Ensure the sLINK synth Proxy is correctly connected to the Synth; proxyslink_i.setTarget(Proxyable(new_SynthsLINK_contract)); // Ensure the new synth has the totalSupply from the previous one; copyTotalSupplyFrom_sADA(); // Ensure the sADA synth can write to its TokenState; tokenstatesada_i.setAssociatedContract(new_SynthsADA_contract); // Ensure the sADA synth Proxy is correctly connected to the Synth; proxysada_i.setTarget(Proxyable(new_SynthsADA_contract)); // Ensure the new synth has the totalSupply from the previous one; copyTotalSupplyFrom_sAAVE(); // Ensure the sAAVE synth can write to its TokenState; tokenstatesaave_i.setAssociatedContract(new_SynthsAAVE_contract); // Ensure the sAAVE synth Proxy is correctly connected to the Synth; proxysaave_i.setTarget(Proxyable(new_SynthsAAVE_contract)); // Ensure the new synth has the totalSupply from the previous one; copyTotalSupplyFrom_sDOT(); // Ensure the sDOT synth can write to its TokenState; tokenstatesdot_i.setAssociatedContract(new_SynthsDOT_contract); // Ensure the sDOT synth Proxy is correctly connected to the Synth; proxysdot_i.setTarget(Proxyable(new_SynthsDOT_contract)); // Ensure the new synth has the totalSupply from the previous one; copyTotalSupplyFrom_sETHBTC(); // Ensure the sETHBTC synth can write to its TokenState; tokenstatesethbtc_i.setAssociatedContract(new_SynthsETHBTC_contract); // Ensure the sETHBTC synth Proxy is correctly connected to the Synth; proxysethbtc_i.setTarget(Proxyable(new_SynthsETHBTC_contract)); // Ensure the new synth has the totalSupply from the previous one; copyTotalSupplyFrom_sDEFI(); // Ensure the sDEFI synth can write to its TokenState; tokenstatesdefi_i.setAssociatedContract(new_SynthsDEFI_contract); // Ensure the sDEFI synth Proxy is correctly connected to the Synth; proxysdefi_i.setTarget(Proxyable(new_SynthsDEFI_contract)); // Add synths to the Issuer contract - batch 1; issuer_addSynths_70(); // NOMINATE OWNERSHIP back to owner for aforementioned contracts nominateAll(); } function acceptAll() internal { address[] memory contracts = contractsRequiringOwnership(); for (uint i = 0; i < contracts.length; i++) { Owned(contracts[i]).acceptOwnership(); } } function nominateAll() internal { address[] memory contracts = contractsRequiringOwnership(); for (uint i = 0; i < contracts.length; i++) { returnOwnership(contracts[i]); } } function addressresolver_importAddresses_0() internal { bytes32[] memory addressresolver_importAddresses_names_0_0 = new bytes32[](17); addressresolver_importAddresses_names_0_0[0] = bytes32("Synthetix"); addressresolver_importAddresses_names_0_0[1] = bytes32("Issuer"); addressresolver_importAddresses_names_0_0[2] = bytes32("SynthsUSD"); addressresolver_importAddresses_names_0_0[3] = bytes32("SynthsJPY"); addressresolver_importAddresses_names_0_0[4] = bytes32("SynthsEUR"); addressresolver_importAddresses_names_0_0[5] = bytes32("SynthsAUD"); addressresolver_importAddresses_names_0_0[6] = bytes32("SynthsGBP"); addressresolver_importAddresses_names_0_0[7] = bytes32("SynthsCHF"); addressresolver_importAddresses_names_0_0[8] = bytes32("SynthsKRW"); addressresolver_importAddresses_names_0_0[9] = bytes32("SynthsETH"); addressresolver_importAddresses_names_0_0[10] = bytes32("SynthsBTC"); addressresolver_importAddresses_names_0_0[11] = bytes32("SynthsLINK"); addressresolver_importAddresses_names_0_0[12] = bytes32("SynthsAAVE"); addressresolver_importAddresses_names_0_0[13] = bytes32("SynthsADA"); addressresolver_importAddresses_names_0_0[14] = bytes32("SynthsDOT"); addressresolver_importAddresses_names_0_0[15] = bytes32("SynthsDEFI"); addressresolver_importAddresses_names_0_0[16] = bytes32("SynthsETHBTC"); address[] memory addressresolver_importAddresses_destinations_0_1 = new address[](17); addressresolver_importAddresses_destinations_0_1[0] = address(new_Synthetix_contract); addressresolver_importAddresses_destinations_0_1[1] = address(new_Issuer_contract); addressresolver_importAddresses_destinations_0_1[2] = address(new_SynthsUSD_contract); addressresolver_importAddresses_destinations_0_1[3] = address(new_SynthsJPY_contract); addressresolver_importAddresses_destinations_0_1[4] = address(new_SynthsEUR_contract); addressresolver_importAddresses_destinations_0_1[5] = address(new_SynthsAUD_contract); addressresolver_importAddresses_destinations_0_1[6] = address(new_SynthsGBP_contract); addressresolver_importAddresses_destinations_0_1[7] = address(new_SynthsCHF_contract); addressresolver_importAddresses_destinations_0_1[8] = address(new_SynthsKRW_contract); addressresolver_importAddresses_destinations_0_1[9] = address(new_SynthsETH_contract); addressresolver_importAddresses_destinations_0_1[10] = address(new_SynthsBTC_contract); addressresolver_importAddresses_destinations_0_1[11] = address(new_SynthsLINK_contract); addressresolver_importAddresses_destinations_0_1[12] = address(new_SynthsAAVE_contract); addressresolver_importAddresses_destinations_0_1[13] = address(new_SynthsADA_contract); addressresolver_importAddresses_destinations_0_1[14] = address(new_SynthsDOT_contract); addressresolver_importAddresses_destinations_0_1[15] = address(new_SynthsDEFI_contract); addressresolver_importAddresses_destinations_0_1[16] = address(new_SynthsETHBTC_contract); addressresolver_i.importAddresses( addressresolver_importAddresses_names_0_0, addressresolver_importAddresses_destinations_0_1 ); } function addressresolver_rebuildCaches_1() internal { MixinResolver[] memory addressresolver_rebuildCaches_destinations_1_0 = new MixinResolver[](20); addressresolver_rebuildCaches_destinations_1_0[0] = MixinResolver(0xDA4eF8520b1A57D7d63f1E249606D1A459698876); addressresolver_rebuildCaches_destinations_1_0[1] = MixinResolver(0x0e5fe1b05612581576e9A3dB048416d0B1E3C425); addressresolver_rebuildCaches_destinations_1_0[2] = MixinResolver(0xf79603a71144e415730C1A6f57F366E4Ea962C00); addressresolver_rebuildCaches_destinations_1_0[3] = MixinResolver(0xD64D83829D92B5bdA881f6f61A4e4E27Fc185387); addressresolver_rebuildCaches_destinations_1_0[4] = MixinResolver(new_Issuer_contract); addressresolver_rebuildCaches_destinations_1_0[5] = MixinResolver(0x62922670313bf6b41C580143d1f6C173C5C20019); addressresolver_rebuildCaches_destinations_1_0[6] = MixinResolver(0x39Ea01a0298C315d149a490E34B59Dbf2EC7e48F); addressresolver_rebuildCaches_destinations_1_0[7] = MixinResolver(new_Synthetix_contract); addressresolver_rebuildCaches_destinations_1_0[8] = MixinResolver(0x89FCb32F29e509cc42d0C8b6f058C993013A843F); addressresolver_rebuildCaches_destinations_1_0[9] = MixinResolver(0x3B2f389AeE480238A49E3A9985cd6815370712eB); addressresolver_rebuildCaches_destinations_1_0[10] = MixinResolver(0x1620Aa736939597891C1940CF0d28b82566F9390); addressresolver_rebuildCaches_destinations_1_0[11] = MixinResolver(0xeAcaEd9581294b1b5cfb6B941d4B8B81B2005437); addressresolver_rebuildCaches_destinations_1_0[12] = MixinResolver(0xe533139Af961c9747356D947838c98451015e234); addressresolver_rebuildCaches_destinations_1_0[13] = MixinResolver(new_SynthsUSD_contract); addressresolver_rebuildCaches_destinations_1_0[14] = MixinResolver(new_SynthsEUR_contract); addressresolver_rebuildCaches_destinations_1_0[15] = MixinResolver(new_SynthsJPY_contract); addressresolver_rebuildCaches_destinations_1_0[16] = MixinResolver(new_SynthsAUD_contract); addressresolver_rebuildCaches_destinations_1_0[17] = MixinResolver(new_SynthsGBP_contract); addressresolver_rebuildCaches_destinations_1_0[18] = MixinResolver(new_SynthsCHF_contract); addressresolver_rebuildCaches_destinations_1_0[19] = MixinResolver(new_SynthsKRW_contract); addressresolver_i.rebuildCaches(addressresolver_rebuildCaches_destinations_1_0); } function addressresolver_rebuildCaches_2() internal { MixinResolver[] memory addressresolver_rebuildCaches_destinations_2_0 = new MixinResolver[](16); addressresolver_rebuildCaches_destinations_2_0[0] = MixinResolver(new_SynthsBTC_contract); addressresolver_rebuildCaches_destinations_2_0[1] = MixinResolver(new_SynthsETH_contract); addressresolver_rebuildCaches_destinations_2_0[2] = MixinResolver(new_SynthsLINK_contract); addressresolver_rebuildCaches_destinations_2_0[3] = MixinResolver(new_SynthsADA_contract); addressresolver_rebuildCaches_destinations_2_0[4] = MixinResolver(new_SynthsAAVE_contract); addressresolver_rebuildCaches_destinations_2_0[5] = MixinResolver(new_SynthsDOT_contract); addressresolver_rebuildCaches_destinations_2_0[6] = MixinResolver(new_SynthsETHBTC_contract); addressresolver_rebuildCaches_destinations_2_0[7] = MixinResolver(new_SynthsDEFI_contract); addressresolver_rebuildCaches_destinations_2_0[8] = MixinResolver(0xC1AAE9d18bBe386B102435a8632C8063d31e747C); addressresolver_rebuildCaches_destinations_2_0[9] = MixinResolver(0x067e398605E84F2D0aEEC1806e62768C5110DCc6); addressresolver_rebuildCaches_destinations_2_0[10] = MixinResolver(0x02f9bC46beD33acdB9cb002fe346734CeF8a9480); addressresolver_rebuildCaches_destinations_2_0[11] = MixinResolver(0x5c8344bcdC38F1aB5EB5C1d4a35DdEeA522B5DfA); addressresolver_rebuildCaches_destinations_2_0[12] = MixinResolver(0xaa03aB31b55DceEeF845C8d17890CC61cD98eD04); addressresolver_rebuildCaches_destinations_2_0[13] = MixinResolver(0x1F2c3a1046c32729862fcB038369696e3273a516); addressresolver_rebuildCaches_destinations_2_0[14] = MixinResolver(0x7C22547779c8aa41bAE79E03E8383a0BefBCecf0); addressresolver_rebuildCaches_destinations_2_0[15] = MixinResolver(0x7A3d898b717e50a96fd8b232E9d15F0A547A7eeb); addressresolver_i.rebuildCaches(addressresolver_rebuildCaches_destinations_2_0); } function copyTotalSupplyFrom_sUSD() internal { // https://etherscan.io/address/0x7df9b3f8f1C011D8BD707430e97E747479DD532a; Synth existingSynth = Synth(0x7df9b3f8f1C011D8BD707430e97E747479DD532a); // https://etherscan.io/address/0x10A5F7D9D65bCc2734763444D4940a31b109275f; Synth newSynth = Synth(0x10A5F7D9D65bCc2734763444D4940a31b109275f); newSynth.setTotalSupply(existingSynth.totalSupply()); } function copyTotalSupplyFrom_sEUR() internal { // https://etherscan.io/address/0x1b06a00Df0B27E7871E753720D4917a7D1aac68b; Synth existingSynth = Synth(0x1b06a00Df0B27E7871E753720D4917a7D1aac68b); // https://etherscan.io/address/0xa8E31E3C38aDD6052A9407298FAEB8fD393A6cF9; Synth newSynth = Synth(0xa8E31E3C38aDD6052A9407298FAEB8fD393A6cF9); newSynth.setTotalSupply(existingSynth.totalSupply()); } function copyTotalSupplyFrom_sJPY() internal { // https://etherscan.io/address/0xB82f11f3168Ece7D56fe6a5679567948090de7C5; Synth existingSynth = Synth(0xB82f11f3168Ece7D56fe6a5679567948090de7C5); // https://etherscan.io/address/0xE1cc2332852B2Ac0dA59A1f9D3051829f4eF3c1C; Synth newSynth = Synth(0xE1cc2332852B2Ac0dA59A1f9D3051829f4eF3c1C); newSynth.setTotalSupply(existingSynth.totalSupply()); } function copyTotalSupplyFrom_sAUD() internal { // https://etherscan.io/address/0xC4546bDd93cDAADA6994e84Fb6F2722C620B019C; Synth existingSynth = Synth(0xC4546bDd93cDAADA6994e84Fb6F2722C620B019C); // https://etherscan.io/address/0xfb020CA7f4e8C4a5bBBe060f59a249c6275d2b69; Synth newSynth = Synth(0xfb020CA7f4e8C4a5bBBe060f59a249c6275d2b69); newSynth.setTotalSupply(existingSynth.totalSupply()); } function copyTotalSupplyFrom_sGBP() internal { // https://etherscan.io/address/0xAE7A2C1e326e59f2dB2132652115a59E8Adb5eBf; Synth existingSynth = Synth(0xAE7A2C1e326e59f2dB2132652115a59E8Adb5eBf); // https://etherscan.io/address/0xdc883b9d9Ee16f74bE08826E68dF4C9D9d26e8bD; Synth newSynth = Synth(0xdc883b9d9Ee16f74bE08826E68dF4C9D9d26e8bD); newSynth.setTotalSupply(existingSynth.totalSupply()); } function copyTotalSupplyFrom_sCHF() internal { // https://etherscan.io/address/0xCC83a57B080a4c7C86F0bB892Bc180C8C7F8791d; Synth existingSynth = Synth(0xCC83a57B080a4c7C86F0bB892Bc180C8C7F8791d); // https://etherscan.io/address/0xBb5b03E920cF702De5A3bA9Fc1445aF4B3919c88; Synth newSynth = Synth(0xBb5b03E920cF702De5A3bA9Fc1445aF4B3919c88); newSynth.setTotalSupply(existingSynth.totalSupply()); } function copyTotalSupplyFrom_sKRW() internal { // https://etherscan.io/address/0x527637bE27640d6C3e751d24DC67129A6d13E11C; Synth existingSynth = Synth(0x527637bE27640d6C3e751d24DC67129A6d13E11C); // https://etherscan.io/address/0xdAe6C79c46aB3B280Ca28259000695529cbD1339; Synth newSynth = Synth(0xdAe6C79c46aB3B280Ca28259000695529cbD1339); newSynth.setTotalSupply(existingSynth.totalSupply()); } function copyTotalSupplyFrom_sBTC() internal { // https://etherscan.io/address/0x18FcC34bdEaaF9E3b69D2500343527c0c995b1d6; Synth existingSynth = Synth(0x18FcC34bdEaaF9E3b69D2500343527c0c995b1d6); // https://etherscan.io/address/0x1cB004a8e84a5CE95C1fF895EE603BaC8EC506c7; Synth newSynth = Synth(0x1cB004a8e84a5CE95C1fF895EE603BaC8EC506c7); newSynth.setTotalSupply(existingSynth.totalSupply()); } function copyTotalSupplyFrom_sETH() internal { // https://etherscan.io/address/0x4FB63c954Ef07EC74335Bb53835026C75DD91dC6; Synth existingSynth = Synth(0x4FB63c954Ef07EC74335Bb53835026C75DD91dC6); // https://etherscan.io/address/0x5D4C724BFe3a228Ff0E29125Ac1571FE093700a4; Synth newSynth = Synth(0x5D4C724BFe3a228Ff0E29125Ac1571FE093700a4); newSynth.setTotalSupply(existingSynth.totalSupply()); } function copyTotalSupplyFrom_sLINK() internal { // https://etherscan.io/address/0xe08518bA3d2467F7cA50eFE68AA00C5f78D4f3D6; Synth existingSynth = Synth(0xe08518bA3d2467F7cA50eFE68AA00C5f78D4f3D6); // https://etherscan.io/address/0xDF69bC4541b86Aa4c5A470B4347E730c38b2c3B2; Synth newSynth = Synth(0xDF69bC4541b86Aa4c5A470B4347E730c38b2c3B2); newSynth.setTotalSupply(existingSynth.totalSupply()); } function copyTotalSupplyFrom_sADA() internal { // https://etherscan.io/address/0xB34F4d7c207D8979D05EDb0F63f174764Bd67825; Synth existingSynth = Synth(0xB34F4d7c207D8979D05EDb0F63f174764Bd67825); // https://etherscan.io/address/0x91b82d62Ff322b8e02b86f33E9A99a813437830d; Synth newSynth = Synth(0x91b82d62Ff322b8e02b86f33E9A99a813437830d); newSynth.setTotalSupply(existingSynth.totalSupply()); } function copyTotalSupplyFrom_sAAVE() internal { // https://etherscan.io/address/0x95aE43E5E96314E4afffcf19D9419111cd11169e; Synth existingSynth = Synth(0x95aE43E5E96314E4afffcf19D9419111cd11169e); // https://etherscan.io/address/0x942Eb6e8c029EB22103743C99985aF4F4515a559; Synth newSynth = Synth(0x942Eb6e8c029EB22103743C99985aF4F4515a559); newSynth.setTotalSupply(existingSynth.totalSupply()); } function copyTotalSupplyFrom_sDOT() internal { // https://etherscan.io/address/0x27b45A4208b87A899009f45888139882477Acea5; Synth existingSynth = Synth(0x27b45A4208b87A899009f45888139882477Acea5); // https://etherscan.io/address/0x75A0c1597137AA36B40b6a515D997F9a6c6eefEB; Synth newSynth = Synth(0x75A0c1597137AA36B40b6a515D997F9a6c6eefEB); newSynth.setTotalSupply(existingSynth.totalSupply()); } function copyTotalSupplyFrom_sETHBTC() internal { // https://etherscan.io/address/0x6DF798ec713b33BE823b917F27820f2aA0cf7662; Synth existingSynth = Synth(0x6DF798ec713b33BE823b917F27820f2aA0cf7662); // https://etherscan.io/address/0x07C1E81C345A7c58d7c24072EFc5D929BD0647AD; Synth newSynth = Synth(0x07C1E81C345A7c58d7c24072EFc5D929BD0647AD); newSynth.setTotalSupply(existingSynth.totalSupply()); } function copyTotalSupplyFrom_sDEFI() internal { // https://etherscan.io/address/0xf533aeEe48f0e04E30c2F6A1f19FbB675469a124; Synth existingSynth = Synth(0xf533aeEe48f0e04E30c2F6A1f19FbB675469a124); // https://etherscan.io/address/0x918b1dbf0917FdD74D03fB9434915E2ECEc89286; Synth newSynth = Synth(0x918b1dbf0917FdD74D03fB9434915E2ECEc89286); newSynth.setTotalSupply(existingSynth.totalSupply()); } function issuer_addSynths_70() internal { ISynth[] memory issuer_addSynths_synthsToAdd_70_0 = new ISynth[](15); issuer_addSynths_synthsToAdd_70_0[0] = ISynth(new_SynthsUSD_contract); issuer_addSynths_synthsToAdd_70_0[1] = ISynth(new_SynthsEUR_contract); issuer_addSynths_synthsToAdd_70_0[2] = ISynth(new_SynthsJPY_contract); issuer_addSynths_synthsToAdd_70_0[3] = ISynth(new_SynthsAUD_contract); issuer_addSynths_synthsToAdd_70_0[4] = ISynth(new_SynthsGBP_contract); issuer_addSynths_synthsToAdd_70_0[5] = ISynth(new_SynthsCHF_contract); issuer_addSynths_synthsToAdd_70_0[6] = ISynth(new_SynthsKRW_contract); issuer_addSynths_synthsToAdd_70_0[7] = ISynth(new_SynthsBTC_contract); issuer_addSynths_synthsToAdd_70_0[8] = ISynth(new_SynthsETH_contract); issuer_addSynths_synthsToAdd_70_0[9] = ISynth(new_SynthsLINK_contract); issuer_addSynths_synthsToAdd_70_0[10] = ISynth(new_SynthsADA_contract); issuer_addSynths_synthsToAdd_70_0[11] = ISynth(new_SynthsAAVE_contract); issuer_addSynths_synthsToAdd_70_0[12] = ISynth(new_SynthsDOT_contract); issuer_addSynths_synthsToAdd_70_0[13] = ISynth(new_SynthsETHBTC_contract); issuer_addSynths_synthsToAdd_70_0[14] = ISynth(new_SynthsDEFI_contract); issuer_i.addSynths(issuer_addSynths_synthsToAdd_70_0); } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":false,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnerChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnerNominated","type":"event"},{"constant":true,"inputs":[],"name":"OWNER","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"acceptOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"addressresolver_i","outputs":[{"internalType":"contract AddressResolver","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"contractsRequiringOwnership","outputs":[{"internalType":"address[]","name":"contracts","type":"address[]"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[],"name":"issuer_i","outputs":[{"internalType":"contract Issuer","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"migrate","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"new_Issuer_contract","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"new_Synthetix_contract","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"new_SynthsAAVE_contract","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"new_SynthsADA_contract","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"new_SynthsAUD_contract","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"new_SynthsBTC_contract","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"new_SynthsCHF_contract","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"new_SynthsDEFI_contract","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"new_SynthsDOT_contract","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"new_SynthsETHBTC_contract","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"new_SynthsETH_contract","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"new_SynthsEUR_contract","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"new_SynthsGBP_contract","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"new_SynthsJPY_contract","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"new_SynthsKRW_contract","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"new_SynthsLINK_contract","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"new_SynthsUSD_contract","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"nominateNewOwner","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"nominatedOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"proxysaave_i","outputs":[{"internalType":"contract ProxyERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"proxysada_i","outputs":[{"internalType":"contract ProxyERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"proxysaud_i","outputs":[{"internalType":"contract ProxyERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"proxysbtc_i","outputs":[{"internalType":"contract ProxyERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"proxyschf_i","outputs":[{"internalType":"contract ProxyERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"proxysdefi_i","outputs":[{"internalType":"contract ProxyERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"proxysdot_i","outputs":[{"internalType":"contract ProxyERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"proxyseth_i","outputs":[{"internalType":"contract ProxyERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"proxysethbtc_i","outputs":[{"internalType":"contract ProxyERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"proxyseur_i","outputs":[{"internalType":"contract ProxyERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"proxysgbp_i","outputs":[{"internalType":"contract ProxyERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"proxysjpy_i","outputs":[{"internalType":"contract ProxyERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"proxyskrw_i","outputs":[{"internalType":"contract ProxyERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"proxyslink_i","outputs":[{"internalType":"contract ProxyERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"proxysusd_i","outputs":[{"internalType":"contract Proxy","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"proxysynthetix_i","outputs":[{"internalType":"contract Proxy","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"forContract","type":"address"}],"name":"returnOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"rewardescrow_i","outputs":[{"internalType":"contract RewardEscrow","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"rewardsdistribution_i","outputs":[{"internalType":"contract RewardsDistribution","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"synthsaave_i","outputs":[{"internalType":"contract MultiCollateralSynth","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"synthsada_i","outputs":[{"internalType":"contract MultiCollateralSynth","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"synthsaud_i","outputs":[{"internalType":"contract MultiCollateralSynth","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"synthsbtc_i","outputs":[{"internalType":"contract MultiCollateralSynth","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"synthschf_i","outputs":[{"internalType":"contract MultiCollateralSynth","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"synthsdefi_i","outputs":[{"internalType":"contract MultiCollateralSynth","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"synthsdot_i","outputs":[{"internalType":"contract MultiCollateralSynth","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"synthseth_i","outputs":[{"internalType":"contract MultiCollateralSynth","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"synthsethbtc_i","outputs":[{"internalType":"contract MultiCollateralSynth","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"synthseur_i","outputs":[{"internalType":"contract MultiCollateralSynth","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"synthsgbp_i","outputs":[{"internalType":"contract MultiCollateralSynth","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"synthsjpy_i","outputs":[{"internalType":"contract MultiCollateralSynth","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"synthskrw_i","outputs":[{"internalType":"contract MultiCollateralSynth","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"synthslink_i","outputs":[{"internalType":"contract MultiCollateralSynth","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"synthsusd_i","outputs":[{"internalType":"contract MultiCollateralSynth","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"systemstatus_i","outputs":[{"internalType":"contract SystemStatus","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"tokenstatesaave_i","outputs":[{"internalType":"contract TokenState","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"tokenstatesada_i","outputs":[{"internalType":"contract TokenState","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"tokenstatesaud_i","outputs":[{"internalType":"contract TokenState","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"tokenstatesbtc_i","outputs":[{"internalType":"contract TokenState","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"tokenstateschf_i","outputs":[{"internalType":"contract TokenState","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"tokenstatesdefi_i","outputs":[{"internalType":"contract TokenState","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"tokenstatesdot_i","outputs":[{"internalType":"contract TokenState","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"tokenstateseth_i","outputs":[{"internalType":"contract TokenState","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"tokenstatesethbtc_i","outputs":[{"internalType":"contract TokenState","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"tokenstateseur_i","outputs":[{"internalType":"contract TokenState","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"tokenstatesgbp_i","outputs":[{"internalType":"contract TokenState","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"tokenstatesjpy_i","outputs":[{"internalType":"contract TokenState","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"tokenstateskrw_i","outputs":[{"internalType":"contract TokenState","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"tokenstateslink_i","outputs":[{"internalType":"contract TokenState","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"tokenstatesusd_i","outputs":[{"internalType":"contract TokenState","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"tokenstatesynthetix_i","outputs":[{"internalType":"contract LegacyTokenState","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"}]
Contract Creation Code
60806040523480156200001157600080fd5b5073eb3107117fead7de89cd14d463d340a2e691776980600080546001600160a01b0319166001600160a01b0383161781556040517fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c9162000075918490620000a1565b60405180910390a15050620000f3565b6200009081620000df565b82525050565b6200009081620000cd565b60408101620000b1828562000085565b620000c0602083018462000096565b9392505050565b92915050565b60006001600160a01b038216620000c7565b6000620000c7826000620000c782620000cd565b61513780620001036000396000f3fe608060405234801561001057600080fd5b506004361061045f5760003560e01c806382291efb1161024c578063acec078c11610146578063dc303a90116100c3578063f2b5b2bd11610087578063f2b5b2bd146106df578063f5ba8f72146106e7578063f6f5ac1b146106ef578063fbed4cec146106f7578063fdd1c39c1461070c5761045f565b8063dc303a90146106b7578063dc597030146106bf578063e05f95b9146106c7578063ecfacaff146106cf578063edc34a6a146106d75761045f565b8063c599d5af1161010a578063c599d5af1461068f578063c71f05f814610697578063c90c168c1461069f578063cd4b9539146106a7578063d075a444146106af5761045f565b8063acec078c14610667578063ad10f7371461066f578063b05dd34814610677578063b0630a071461067f578063bddb48a2146106875761045f565b806399ac8a65116101d4578063a5ec5cfd11610198578063a5ec5cfd1461063f578063a636c1fc14610647578063a6d6da101461064f578063a92ddce714610657578063aade6bd31461065f5761045f565b806399ac8a65146106175780639a0a0bc21461061f5780639d12bfc8146106275780639f8c38de1461062f578063a11b4b57146106375761045f565b806389cbfbb91161021b57806389cbfbb9146105ef5780638ce6d4fe146105f75780638da5cb5b146105ff5780638f94e3a8146106075780638fd3ab801461060f5761045f565b806382291efb146105cf578063847d96fb146105d7578063871f10f7146105df57806387e0b96a146105e75761045f565b80634a088bfb1161035d57806359ef6d01116102e55780636b163565116102a95780636b163565146105a75780636f13689b146105af5780636fbab466146105b757806379ba5097146105bf5780637fc13624146105c75761045f565b806359ef6d011461057f5780635cf74efa146105875780635ebae58a1461058f5780636025f05b14610597578063650fa6121461059f5761045f565b80634f8be1891161032c5780634f8be18914610557578063517971fc1461055f57806352c6c1291461056757806353a47bb71461056f57806359af53f4146105775761045f565b80634a088bfb146105375780634b8a16eb1461053f5780634f1e2220146105475780634f5473691461054f5761045f565b806323a873c8116103eb5780633ba82a40116103af5780633ba82a401461050f5780633c41edd01461051757806340183e451461051f57806341698fe8146105275780634606cd6a1461052f5761045f565b806323a873c8146104dc5780632a8e407b146104e45780633440490d146104f757806337e763e5146104ff5780633ad5c80f146105075761045f565b806316376ef81161043257806316376ef8146104a75780631ace0f95146104af5780631e34ea79146104c45780631faf017a146104cc578063232e08c4146104d45761045f565b806306c6468314610464578063117803e31461048257806311bd19d21461048a5780631627540c14610492575b600080fd5b61046c610714565b6040516104799190614f61565b60405180910390f35b61046c61072c565b61046c610744565b6104a56104a0366004614c51565b61075c565b005b61046c6107ba565b6104b76107d2565b6040516104799190614fe2565b6104b76107ea565b6104b7610802565b6104b761081a565b6104b7610832565b6104a56104f2366004614c51565b61084a565b61046c6109d9565b6104b76109f1565b6104b7610a09565b6104b7610a21565b6104b7610a39565b6104b7610a51565b6104b7610a69565b6104b7610744565b6104b7610a81565b6104b7610a99565b61046c6107ea565b6104b7610ab1565b6104b7610ac9565b61046c6107d2565b6104b7610ae1565b61046c610af9565b6104b7610b08565b61046c610b20565b61046c610b38565b6104b7610b50565b61046c610b50565b61046c610b68565b6104b7610b80565b6104b7610b98565b6104b7610bb0565b6104a5610bc8565b6104b7610c64565b6104b7610714565b6104b7610c7c565b6104b7610c94565b6104b7610cac565b61046c610832565b61046c610cc4565b61046c610cdc565b6104b7610ceb565b6104a5610d03565b61046c610cac565b6104b7611ed2565b6104b7610b38565b6104b76107ba565b61046c611eea565b6104b7610b68565b6104b7611f02565b6104b7611f1a565b6104b7611f32565b6104b7611f4a565b6104b7611f62565b6104b7611f7a565b6104b7611f92565b61046c611f02565b6104b7611faa565b6104b7611fc2565b6104b7610cc4565b6104b7611fda565b61046c610a21565b61046c611ff2565b6104b761200a565b6104b7612022565b6104b761203a565b6104b7610b20565b6104b7612052565b6104b7611eea565b6104b761206a565b6104b7612082565b6106ff61209a565b6040516104799190614f8a565b6104b7611ff2565b7391b82d62ff322b8e02b86f33e9a99a813437830d81565b73eb3107117fead7de89cd14d463d340a2e691776981565b73dc883b9d9ee16f74be08826e68df4c9d9d26e8bd81565b610764612e2a565b600180546001600160a01b0319166001600160a01b0383161790556040517f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce22906107af908390614f61565b60405180910390a150565b73bb5b03e920cf702de5a3ba9fc1445af4b3919c8881565b7375a0c1597137aa36b40b6a515d997f9a6c6eefeb81565b73df69bc4541b86aa4c5a470b4347e730c38b2c3b281565b73bbc455cb4f1b9e4bfc4b73970d360c8f032efee681565b73d2df355c19471c8bd7d8a3aa27ff4e26a21b407681565b73fb020ca7f4e8c4a5bbbe060f59a249c6275d2b6981565b60005460405160609161086b916001600160a01b0390911690602401614f61565b60408051601f198184030181529181526020820180516001600160e01b0316630589d50360e21b179052519091506000906001600160a01b038416906108b2908490614f4e565b6000604051808303816000865af19150503d80600081146108ef576040519150601f19603f3d011682016040523d82523d6000602084013e6108f4565b606091505b50509050806109d45760005460405160609161091e916001600160a01b0390911690602401614f61565b60408051601f198184030181529181526020820180516001600160e01b0316635b94db2760e01b179052519091506000906001600160a01b03861690610965908490614f4e565b6000604051808303816000865af19150503d80600081146109a2576040519150601f19603f3d011682016040523d82523d6000602084013e6109a7565b606091505b50509050806109d15760405162461bcd60e51b81526004016109c890615000565b60405180910390fd5b50505b505050565b7308f30ecf2c15a783083ab9d5b9211c22388d056481565b7373b1a2643507cd30f11dfcf2d974f4373e5bc07781565b734f6296455f8d754c19821cf1ec8febf2cd456e6781565b73918b1dbf0917fdd74d03fb9434915e2ecec8928681565b7305a9cbe762b36632b3594da4f082340e0e5343e881565b737e88d19a79b291cfe5696d496055f7e57f537a7581565b73577d4a7395c6a5f46d9981a5f83fa7294926abb081565b735e74c9036fb86bd7ecdcb084a0673efc32ea31cb81565b735b1b5fea1b99d83ad479df0c222f0492385381dd81565b7352496fe8a4feaefe14d9433e00d48e6929c13dec81565b73b671f2210b1f6621a2607ea63e6b2dc3e2464d1f81565b73e1afe1fd76fd88f78cbf599ea1846231b8ba3b6b81565b6001546001600160a01b031681565b73c011a73ee8576fb46f5e1c5751ca3b9fe0af2a6f81565b735d4c724bfe3a228ff0e29125ac1571fe093700a481565b7307c1e81c345a7c58d7c24072efc5d929bd0647ad81565b73dae6c79c46ab3b280ca28259000695529cbd133981565b731cb004a8e84a5ce95c1ff895ee603bac8ec506c781565b7397fe22e7341a0cd8db6f6c021a24dc8f4dad855f81565b73104edf1da359506548bfc7c25ba1e28c16a7023581565b73e36e2d3c7c34281fa3bc737950a68571736880a181565b6001546001600160a01b03163314610bf25760405162461bcd60e51b81526004016109c890614ff0565b6000546001546040517fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c92610c35926001600160a01b0391821692911690614f6f565b60405180910390a160018054600080546001600160a01b03199081166001600160a01b03841617909155169055565b73042a7a0022a7695454ac5be77a4860e50c9683fc81565b7393b6e9fbbd2c32a0dc3c2b943b7c3cbc2fe2373081565b73d71ecff9342a5ced620049e616c5035f1db9862081565b73e1cc2332852b2ac0da59a1f9d3051829f4ef3c1c81565b73e5cc99efa57943f4ea0ce6bed26531869774864981565b6000546001600160a01b031681565b73696c905f8f8c006ca46e9808fe7e00049507798f81565b610d0b612e2a565b610d13612e54565b610d1b612ee2565b610d2361367e565b610d2b613c28565b60405163776d1a0160e01b815273c011a73ee8576fb46f5e1c5751ca3b9fe0af2a6f9063776d1a0190610d76907308f30ecf2c15a783083ab9d5b9211c22388d056490600401614fe2565b600060405180830381600087803b158015610d9057600080fd5b505af1158015610da4573d6000803e3d6000fd5b50506040516348bf197160e01b815273696c905f8f8c006ca46e9808fe7e00049507798f92506348bf19719150610df99073e5cc99efa57943f4ea0ce6bed26531869774864990600190600090600401615010565b600060405180830381600087803b158015610e1357600080fd5b505af1158015610e27573d6000803e3d6000fd5b505060405163297a22e560e11b8152735b1b5fea1b99d83ad479df0c222f0492385381dd92506352f445ca9150610e76907308f30ecf2c15a783083ab9d5b9211c22388d056490600401614f61565b600060405180830381600087803b158015610e9057600080fd5b505af1158015610ea4573d6000803e3d6000fd5b5050604051637f64fced60e11b815273b671f2210b1f6621a2607ea63e6b2dc3e2464d1f925063fec9f9da9150610ef3907308f30ecf2c15a783083ab9d5b9211c22388d056490600401614fe2565b600060405180830381600087803b158015610f0d57600080fd5b505af1158015610f21573d6000803e3d6000fd5b5050604051637a9e5e4b60e01b81527329c295b046a73cde593f21f63091b072d407e3f29250637a9e5e4b9150610f70907308f30ecf2c15a783083ab9d5b9211c22388d056490600401614f61565b600060405180830381600087803b158015610f8a57600080fd5b505af1158015610f9e573d6000803e3d6000fd5b50505050610faa61404d565b60405163297a22e560e11b81527305a9cbe762b36632b3594da4f082340e0e5343e8906352f445ca90610ff5907310a5f7d9d65bcc2734763444d4940a31b109275f90600401614f61565b600060405180830381600087803b15801561100f57600080fd5b505af1158015611023573d6000803e3d6000fd5b505060405163776d1a0160e01b81527357ab1ec28d129707052df4df418d58a2d46d5f51925063776d1a019150611072907310a5f7d9d65bcc2734763444d4940a31b109275f90600401614fe2565b600060405180830381600087803b15801561108c57600080fd5b505af11580156110a0573d6000803e3d6000fd5b505050506110ac61411b565b60405163297a22e560e11b8152736568d9e750fc44af00f857885dfb8281c00529c4906352f445ca906110f79073a8e31e3c38add6052a9407298faeb8fd393a6cf990600401614f61565b600060405180830381600087803b15801561111157600080fd5b505af1158015611125573d6000803e3d6000fd5b505060405163776d1a0160e01b815273d71ecff9342a5ced620049e616c5035f1db98620925063776d1a0191506111749073a8e31e3c38add6052a9407298faeb8fd393a6cf990600401614fe2565b600060405180830381600087803b15801561118e57600080fd5b505af11580156111a2573d6000803e3d6000fd5b505050506111ae614195565b60405163297a22e560e11b8152734dfacfb15514c21c991ff75bc7bf6fb1f98361ed906352f445ca906111f99073e1cc2332852b2ac0da59a1f9d3051829f4ef3c1c90600401614f61565b600060405180830381600087803b15801561121357600080fd5b505af1158015611227573d6000803e3d6000fd5b505060405163776d1a0160e01b815273f6b1c627e95bfc3c1b4c9b825a032ff0fbf3e07d925063776d1a0191506112769073e1cc2332852b2ac0da59a1f9d3051829f4ef3c1c90600401614fe2565b600060405180830381600087803b15801561129057600080fd5b505af11580156112a4573d6000803e3d6000fd5b505050506112b061420f565b60405163297a22e560e11b815273cb29d2cf2c65d3be1d00f07f3441390432d55203906352f445ca906112fb9073fb020ca7f4e8c4a5bbbe060f59a249c6275d2b6990600401614f61565b600060405180830381600087803b15801561131557600080fd5b505af1158015611329573d6000803e3d6000fd5b505060405163776d1a0160e01b815273f48e200eaf9906362bb1442fca31e0835773b8b4925063776d1a0191506113789073fb020ca7f4e8c4a5bbbe060f59a249c6275d2b6990600401614fe2565b600060405180830381600087803b15801561139257600080fd5b505af11580156113a6573d6000803e3d6000fd5b505050506113b2614289565b60405163297a22e560e11b8152737e88d19a79b291cfe5696d496055f7e57f537a75906352f445ca906113fd9073dc883b9d9ee16f74be08826e68df4c9d9d26e8bd90600401614f61565b600060405180830381600087803b15801561141757600080fd5b505af115801561142b573d6000803e3d6000fd5b505060405163776d1a0160e01b81527397fe22e7341a0cd8db6f6c021a24dc8f4dad855f925063776d1a01915061147a9073dc883b9d9ee16f74be08826e68df4c9d9d26e8bd90600401614fe2565b600060405180830381600087803b15801561149457600080fd5b505af11580156114a8573d6000803e3d6000fd5b505050506114b4614303565b60405163297a22e560e11b81527352496fe8a4feaefe14d9433e00d48e6929c13dec906352f445ca906114ff9073bb5b03e920cf702de5a3ba9fc1445af4b3919c8890600401614f61565b600060405180830381600087803b15801561151957600080fd5b505af115801561152d573d6000803e3d6000fd5b505060405163776d1a0160e01b8152730f83287ff768d1c1e17a42f44d644d7f22e8ee1d925063776d1a01915061157c9073bb5b03e920cf702de5a3ba9fc1445af4b3919c8890600401614fe2565b600060405180830381600087803b15801561159657600080fd5b505af11580156115aa573d6000803e3d6000fd5b505050506115b661437d565b60405163297a22e560e11b81527393b6e9fbbd2c32a0dc3c2b943b7c3cbc2fe23730906352f445ca906116019073dae6c79c46ab3b280ca28259000695529cbd133990600401614f61565b600060405180830381600087803b15801561161b57600080fd5b505af115801561162f573d6000803e3d6000fd5b505060405163776d1a0160e01b815273269895a3df4d73b077fc823dd6da1b95f72aaf9b925063776d1a01915061167e9073dae6c79c46ab3b280ca28259000695529cbd133990600401614fe2565b600060405180830381600087803b15801561169857600080fd5b505af11580156116ac573d6000803e3d6000fd5b505050506116b86143f7565b60405163297a22e560e11b8152734f6296455f8d754c19821cf1ec8febf2cd456e67906352f445ca9061170390731cb004a8e84a5ce95c1ff895ee603bac8ec506c790600401614f61565b600060405180830381600087803b15801561171d57600080fd5b505af1158015611731573d6000803e3d6000fd5b505060405163776d1a0160e01b815273fe18be6b3bd88a2d2a7f928d00292e7a9963cfc6925063776d1a01915061178090731cb004a8e84a5ce95c1ff895ee603bac8ec506c790600401614fe2565b600060405180830381600087803b15801561179a57600080fd5b505af11580156117ae573d6000803e3d6000fd5b505050506117ba614471565b60405163297a22e560e11b81527334a5ef81d18f3a305ae9c2d7df42beef4c79031c906352f445ca9061180590735d4c724bfe3a228ff0e29125ac1571fe093700a490600401614f61565b600060405180830381600087803b15801561181f57600080fd5b505af1158015611833573d6000803e3d6000fd5b505060405163776d1a0160e01b8152735e74c9036fb86bd7ecdcb084a0673efc32ea31cb925063776d1a01915061188290735d4c724bfe3a228ff0e29125ac1571fe093700a490600401614fe2565b600060405180830381600087803b15801561189c57600080fd5b505af11580156118b0573d6000803e3d6000fd5b505050506118bc6144eb565b60405163297a22e560e11b815273577d4a7395c6a5f46d9981a5f83fa7294926abb0906352f445ca906119079073df69bc4541b86aa4c5a470b4347e730c38b2c3b290600401614f61565b600060405180830381600087803b15801561192157600080fd5b505af1158015611935573d6000803e3d6000fd5b505060405163776d1a0160e01b815273bbc455cb4f1b9e4bfc4b73970d360c8f032efee6925063776d1a0191506119849073df69bc4541b86aa4c5a470b4347e730c38b2c3b290600401614fe2565b600060405180830381600087803b15801561199e57600080fd5b505af11580156119b2573d6000803e3d6000fd5b505050506119be614565565b60405163297a22e560e11b8152739956c5019a24fbd5b506ad070b771577bac5c343906352f445ca90611a09907391b82d62ff322b8e02b86f33e9a99a813437830d90600401614f61565b600060405180830381600087803b158015611a2357600080fd5b505af1158015611a37573d6000803e3d6000fd5b505060405163776d1a0160e01b815273e36e2d3c7c34281fa3bc737950a68571736880a1925063776d1a019150611a86907391b82d62ff322b8e02b86f33e9a99a813437830d90600401614fe2565b600060405180830381600087803b158015611aa057600080fd5b505af1158015611ab4573d6000803e3d6000fd5b50505050611ac06145df565b60405163297a22e560e11b8152739bced8a8e3ad81c9b146ffc880358f734a06f7c0906352f445ca90611b0b9073942eb6e8c029eb22103743c99985af4f4515a55990600401614f61565b600060405180830381600087803b158015611b2557600080fd5b505af1158015611b39573d6000803e3d6000fd5b505060405163776d1a0160e01b815273d2df355c19471c8bd7d8a3aa27ff4e26a21b4076925063776d1a019150611b889073942eb6e8c029eb22103743c99985af4f4515a55990600401614fe2565b600060405180830381600087803b158015611ba257600080fd5b505af1158015611bb6573d6000803e3d6000fd5b50505050611bc2614659565b60405163297a22e560e11b81527373b1a2643507cd30f11dfcf2d974f4373e5bc077906352f445ca90611c0d907375a0c1597137aa36b40b6a515d997f9a6c6eefeb90600401614f61565b600060405180830381600087803b158015611c2757600080fd5b505af1158015611c3b573d6000803e3d6000fd5b505060405163776d1a0160e01b8152731715ac0743102bf5cd58efbb6cf2dc2685d967b6925063776d1a019150611c8a907375a0c1597137aa36b40b6a515d997f9a6c6eefeb90600401614fe2565b600060405180830381600087803b158015611ca457600080fd5b505af1158015611cb8573d6000803e3d6000fd5b50505050611cc46146d3565b60405163297a22e560e11b815273042a7a0022a7695454ac5be77a4860e50c9683fc906352f445ca90611d0f907307c1e81c345a7c58d7c24072efc5d929bd0647ad90600401614f61565b600060405180830381600087803b158015611d2957600080fd5b505af1158015611d3d573d6000803e3d6000fd5b505060405163776d1a0160e01b815273104edf1da359506548bfc7c25ba1e28c16a70235925063776d1a019150611d8c907307c1e81c345a7c58d7c24072efc5d929bd0647ad90600401614fe2565b600060405180830381600087803b158015611da657600080fd5b505af1158015611dba573d6000803e3d6000fd5b50505050611dc661474d565b60405163297a22e560e11b8152737ac2d37098a65b0f711cffa3be635f1e6acacfab906352f445ca90611e119073918b1dbf0917fdd74d03fb9434915e2ecec8928690600401614f61565b600060405180830381600087803b158015611e2b57600080fd5b505af1158015611e3f573d6000803e3d6000fd5b505060405163776d1a0160e01b815273e1afe1fd76fd88f78cbf599ea1846231b8ba3b6b925063776d1a019150611e8e9073918b1dbf0917fdd74d03fb9434915e2ecec8928690600401614fe2565b600060405180830381600087803b158015611ea857600080fd5b505af1158015611ebc573d6000803e3d6000fd5b50505050611ec86147c7565b611ed0614bf9565b565b7357ab1ec28d129707052df4df418d58a2d46d5f5181565b73942eb6e8c029eb22103743c99985af4f4515a55981565b73a8e31e3c38add6052a9407298faeb8fd393a6cf981565b73f6b1c627e95bfc3c1b4c9b825a032ff0fbf3e07d81565b734dfacfb15514c21c991ff75bc7bf6fb1f98361ed81565b739bced8a8e3ad81c9b146ffc880358f734a06f7c081565b736568d9e750fc44af00f857885dfb8281c00529c481565b73269895a3df4d73b077fc823dd6da1b95f72aaf9b81565b7329c295b046a73cde593f21f63091b072d407e3f281565b739956c5019a24fbd5b506ad070b771577bac5c34381565b73823be81bbf96bec0e25ca13170f5aacb5b79ba8381565b73cb29d2cf2c65d3be1d00f07f3441390432d5520381565b7310a5f7d9d65bcc2734763444d4940a31b109275f81565b730f83287ff768d1c1e17a42f44d644d7f22e8ee1d81565b73f48e200eaf9906362bb1442fca31e0835773b8b481565b737ac2d37098a65b0f711cffa3be635f1e6acacfab81565b7334a5ef81d18f3a305ae9c2d7df42beef4c79031c81565b731715ac0743102bf5cd58efbb6cf2dc2685d967b681565b73fe18be6b3bd88a2d2a7f928d00292e7a9963cfc681565b6040805160348082526106a08201909252606091602082016106808038833901905050905073823be81bbf96bec0e25ca13170f5aacb5b79ba83816000815181106120e157fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073c011a73ee8576fb46f5e1c5751ca3b9fe0af2a6f8160018151811061212357fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073696c905f8f8c006ca46e9808fe7e00049507798f8160028151811061216557fe5b60200260200101906001600160a01b031690816001600160a01b031681525050735b1b5fea1b99d83ad479df0c222f0492385381dd816003815181106121a757fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073b671f2210b1f6621a2607ea63e6b2dc3e2464d1f816004815181106121e957fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507329c295b046a73cde593f21f63091b072d407e3f28160058151811061222b57fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507310a5f7d9d65bcc2734763444d4940a31b109275f8160068151811061226d57fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507305a9cbe762b36632b3594da4f082340e0e5343e8816007815181106122af57fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507357ab1ec28d129707052df4df418d58a2d46d5f51816008815181106122f157fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073a8e31e3c38add6052a9407298faeb8fd393a6cf98160098151811061233357fe5b60200260200101906001600160a01b031690816001600160a01b031681525050736568d9e750fc44af00f857885dfb8281c00529c481600a8151811061237557fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073d71ecff9342a5ced620049e616c5035f1db9862081600b815181106123b757fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073e1cc2332852b2ac0da59a1f9d3051829f4ef3c1c81600c815181106123f957fe5b60200260200101906001600160a01b031690816001600160a01b031681525050734dfacfb15514c21c991ff75bc7bf6fb1f98361ed81600d8151811061243b57fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073f6b1c627e95bfc3c1b4c9b825a032ff0fbf3e07d81600e8151811061247d57fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073fb020ca7f4e8c4a5bbbe060f59a249c6275d2b6981600f815181106124bf57fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073cb29d2cf2c65d3be1d00f07f3441390432d552038160108151811061250157fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073f48e200eaf9906362bb1442fca31e0835773b8b48160118151811061254357fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073dc883b9d9ee16f74be08826e68df4c9d9d26e8bd8160128151811061258557fe5b60200260200101906001600160a01b031690816001600160a01b031681525050737e88d19a79b291cfe5696d496055f7e57f537a75816013815181106125c757fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507397fe22e7341a0cd8db6f6c021a24dc8f4dad855f8160148151811061260957fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073bb5b03e920cf702de5a3ba9fc1445af4b3919c888160158151811061264b57fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507352496fe8a4feaefe14d9433e00d48e6929c13dec8160168151811061268d57fe5b60200260200101906001600160a01b031690816001600160a01b031681525050730f83287ff768d1c1e17a42f44d644d7f22e8ee1d816017815181106126cf57fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073dae6c79c46ab3b280ca28259000695529cbd13398160188151811061271157fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507393b6e9fbbd2c32a0dc3c2b943b7c3cbc2fe237308160198151811061275357fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073269895a3df4d73b077fc823dd6da1b95f72aaf9b81601a8151811061279557fe5b60200260200101906001600160a01b031690816001600160a01b031681525050731cb004a8e84a5ce95c1ff895ee603bac8ec506c781601b815181106127d757fe5b60200260200101906001600160a01b031690816001600160a01b031681525050734f6296455f8d754c19821cf1ec8febf2cd456e6781601c8151811061281957fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073fe18be6b3bd88a2d2a7f928d00292e7a9963cfc681601d8151811061285b57fe5b60200260200101906001600160a01b031690816001600160a01b031681525050735d4c724bfe3a228ff0e29125ac1571fe093700a481601e8151811061289d57fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507334a5ef81d18f3a305ae9c2d7df42beef4c79031c81601f815181106128df57fe5b60200260200101906001600160a01b031690816001600160a01b031681525050735e74c9036fb86bd7ecdcb084a0673efc32ea31cb8160208151811061292157fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073df69bc4541b86aa4c5a470b4347e730c38b2c3b28160218151811061296357fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073577d4a7395c6a5f46d9981a5f83fa7294926abb0816022815181106129a557fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073bbc455cb4f1b9e4bfc4b73970d360c8f032efee6816023815181106129e757fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507391b82d62ff322b8e02b86f33e9a99a813437830d81602481518110612a2957fe5b60200260200101906001600160a01b031690816001600160a01b031681525050739956c5019a24fbd5b506ad070b771577bac5c34381602581518110612a6b57fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073e36e2d3c7c34281fa3bc737950a68571736880a181602681518110612aad57fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073942eb6e8c029eb22103743c99985af4f4515a55981602781518110612aef57fe5b60200260200101906001600160a01b031690816001600160a01b031681525050739bced8a8e3ad81c9b146ffc880358f734a06f7c081602881518110612b3157fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073d2df355c19471c8bd7d8a3aa27ff4e26a21b407681602981518110612b7357fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507375a0c1597137aa36b40b6a515d997f9a6c6eefeb81602a81518110612bb557fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507373b1a2643507cd30f11dfcf2d974f4373e5bc07781602b81518110612bf757fe5b60200260200101906001600160a01b031690816001600160a01b031681525050731715ac0743102bf5cd58efbb6cf2dc2685d967b681602c81518110612c3957fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507307c1e81c345a7c58d7c24072efc5d929bd0647ad81602d81518110612c7b57fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073042a7a0022a7695454ac5be77a4860e50c9683fc81602e81518110612cbd57fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073104edf1da359506548bfc7c25ba1e28c16a7023581602f81518110612cff57fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073918b1dbf0917fdd74d03fb9434915e2ecec8928681603081518110612d4157fe5b60200260200101906001600160a01b031690816001600160a01b031681525050737ac2d37098a65b0f711cffa3be635f1e6acacfab81603181518110612d8357fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073e1afe1fd76fd88f78cbf599ea1846231b8ba3b6b81603281518110612dc557fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073e5cc99efa57943f4ea0ce6bed26531869774864981603381518110612e0757fe5b60200260200101906001600160a01b031690816001600160a01b03168152505090565b6000546001600160a01b03163314611ed05760405162461bcd60e51b81526004016109c890615044565b6060612e5e61209a565b905060005b8151811015612ede57818181518110612e7857fe5b60200260200101516001600160a01b03166379ba50976040518163ffffffff1660e01b8152600401600060405180830381600087803b158015612eba57600080fd5b505af1158015612ece573d6000803e3d6000fd5b505060019092019150612e639050565b5050565b60408051601180825261024082019092526060916020820161022080388339019050509050680a6f2dce8d0cae8d2f60bb1b81600081518110612f2157fe5b6020026020010181815250506524b9b9bab2b960d11b81600181518110612f4457fe5b6020026020010181815250506814de5b9d1a1cd554d160ba1b81600281518110612f6a57fe5b6020026020010181815250506853796e7468734a505960b81b81600381518110612f9057fe5b6020026020010181815250506829bcb73a3439a2aaa960b91b81600481518110612fb657fe5b6020026020010181815250506814de5b9d1a1cd0555160ba1b81600581518110612fdc57fe5b60200260200101818152505068053796e7468734742560bc1b8160068151811061300257fe5b6020026020010181815250506829bcb73a3439a1a42360b91b8160078151811061302857fe5b6020026020010181815250506853796e7468734b525760b81b8160088151811061304e57fe5b602002602001018181525050680a6f2dce8d0e68aa8960bb1b8160098151811061307457fe5b6020026020010181815250506853796e74687342544360b81b81600a8151811061309a57fe5b6020026020010181815250506953796e7468734c494e4b60b01b81600b815181106130c157fe5b6020026020010181815250506953796e7468734141564560b01b81600c815181106130e857fe5b6020026020010181815250506853796e74687341444160b81b81600d8151811061310e57fe5b6020026020010181815250506814de5b9d1a1cd113d560ba1b81600e8151811061313457fe5b6020026020010181815250506953796e7468734445464960b01b81600f8151811061315b57fe5b6020026020010181815250506b53796e74687345544842544360a01b8160108151811061318457fe5b602090810291909101015260408051601180825261024082019092526060918160200160208202803883390190505090507308f30ecf2c15a783083ab9d5b9211c22388d0564816000815181106131d757fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073e5cc99efa57943f4ea0ce6bed2653186977486498160018151811061321957fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507310a5f7d9d65bcc2734763444d4940a31b109275f8160028151811061325b57fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073e1cc2332852b2ac0da59a1f9d3051829f4ef3c1c8160038151811061329d57fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073a8e31e3c38add6052a9407298faeb8fd393a6cf9816004815181106132df57fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073fb020ca7f4e8c4a5bbbe060f59a249c6275d2b698160058151811061332157fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073dc883b9d9ee16f74be08826e68df4c9d9d26e8bd8160068151811061336357fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073bb5b03e920cf702de5a3ba9fc1445af4b3919c88816007815181106133a557fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073dae6c79c46ab3b280ca28259000695529cbd1339816008815181106133e757fe5b60200260200101906001600160a01b031690816001600160a01b031681525050735d4c724bfe3a228ff0e29125ac1571fe093700a48160098151811061342957fe5b60200260200101906001600160a01b031690816001600160a01b031681525050731cb004a8e84a5ce95c1ff895ee603bac8ec506c781600a8151811061346b57fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073df69bc4541b86aa4c5a470b4347e730c38b2c3b281600b815181106134ad57fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073942eb6e8c029eb22103743c99985af4f4515a55981600c815181106134ef57fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507391b82d62ff322b8e02b86f33e9a99a813437830d81600d8151811061353157fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507375a0c1597137aa36b40b6a515d997f9a6c6eefeb81600e8151811061357357fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073918b1dbf0917fdd74d03fb9434915e2ecec8928681600f815181106135b557fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507307c1e81c345a7c58d7c24072efc5d929bd0647ad816010815181106135f757fe5b6001600160a01b039092166020928302919091019091015260405163ab0b8f7760e01b815273823be81bbf96bec0e25ca13170f5aacb5b79ba839063ab0b8f77906136489085908590600401614f9b565b600060405180830381600087803b15801561366257600080fd5b505af1158015613676573d6000803e3d6000fd5b505050505050565b6040805160148082526102a08201909252606091602082016102808038833901905050905073da4ef8520b1a57d7d63f1e249606d1a459698876816000815181106136c557fe5b60200260200101906001600160a01b031690816001600160a01b031681525050730e5fe1b05612581576e9a3db048416d0b1e3c4258160018151811061370757fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073f79603a71144e415730c1a6f57f366e4ea962c008160028151811061374957fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073d64d83829d92b5bda881f6f61a4e4e27fc1853878160038151811061378b57fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073e5cc99efa57943f4ea0ce6bed265318697748649816004815181106137cd57fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507362922670313bf6b41c580143d1f6c173c5c200198160058151811061380f57fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507339ea01a0298c315d149a490e34b59dbf2ec7e48f8160068151811061385157fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507308f30ecf2c15a783083ab9d5b9211c22388d05648160078151811061389357fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507389fcb32f29e509cc42d0c8b6f058c993013a843f816008815181106138d557fe5b60200260200101906001600160a01b031690816001600160a01b031681525050733b2f389aee480238a49e3a9985cd6815370712eb8160098151811061391757fe5b60200260200101906001600160a01b031690816001600160a01b031681525050731620aa736939597891c1940cf0d28b82566f939081600a8151811061395957fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073eacaed9581294b1b5cfb6b941d4b8b81b200543781600b8151811061399b57fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073e533139af961c9747356d947838c98451015e23481600c815181106139dd57fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507310a5f7d9d65bcc2734763444d4940a31b109275f81600d81518110613a1f57fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073a8e31e3c38add6052a9407298faeb8fd393a6cf981600e81518110613a6157fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073e1cc2332852b2ac0da59a1f9d3051829f4ef3c1c81600f81518110613aa357fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073fb020ca7f4e8c4a5bbbe060f59a249c6275d2b6981601081518110613ae557fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073dc883b9d9ee16f74be08826e68df4c9d9d26e8bd81601181518110613b2757fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073bb5b03e920cf702de5a3ba9fc1445af4b3919c8881601281518110613b6957fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073dae6c79c46ab3b280ca28259000695529cbd133981601381518110613bab57fe5b6001600160a01b039092166020928302919091019091015260405163766f781560e01b815273823be81bbf96bec0e25ca13170f5aacb5b79ba839063766f781590613bfa908490600401614fd1565b600060405180830381600087803b158015613c1457600080fd5b505af11580156109d1573d6000803e3d6000fd5b60408051601080825261022082019092526060916020820161020080388339019050509050731cb004a8e84a5ce95c1ff895ee603bac8ec506c781600081518110613c6f57fe5b60200260200101906001600160a01b031690816001600160a01b031681525050735d4c724bfe3a228ff0e29125ac1571fe093700a481600181518110613cb157fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073df69bc4541b86aa4c5a470b4347e730c38b2c3b281600281518110613cf357fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507391b82d62ff322b8e02b86f33e9a99a813437830d81600381518110613d3557fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073942eb6e8c029eb22103743c99985af4f4515a55981600481518110613d7757fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507375a0c1597137aa36b40b6a515d997f9a6c6eefeb81600581518110613db957fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507307c1e81c345a7c58d7c24072efc5d929bd0647ad81600681518110613dfb57fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073918b1dbf0917fdd74d03fb9434915e2ecec8928681600781518110613e3d57fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073c1aae9d18bbe386b102435a8632c8063d31e747c81600881518110613e7f57fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073067e398605e84f2d0aeec1806e62768c5110dcc681600981518110613ec157fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507302f9bc46bed33acdb9cb002fe346734cef8a948081600a81518110613f0357fe5b60200260200101906001600160a01b031690816001600160a01b031681525050735c8344bcdc38f1ab5eb5c1d4a35ddeea522b5dfa81600b81518110613f4557fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073aa03ab31b55dceeef845c8d17890cc61cd98ed0481600c81518110613f8757fe5b60200260200101906001600160a01b031690816001600160a01b031681525050731f2c3a1046c32729862fcb038369696e3273a51681600d81518110613fc957fe5b60200260200101906001600160a01b031690816001600160a01b031681525050737c22547779c8aa41bae79e03e8383a0befbcecf081600e8151811061400b57fe5b60200260200101906001600160a01b031690816001600160a01b031681525050737a3d898b717e50a96fd8b232e9d15f0a547a7eeb81600f81518110613bab57fe5b6000737df9b3f8f1c011d8bd707430e97e747479dd532a905060007310a5f7d9d65bcc2734763444d4940a31b109275f9050806001600160a01b031663f7ea7a3d836001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156140c757600080fd5b505afa1580156140db573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506140ff9190810190614c77565b6040518263ffffffff1660e01b81526004016136489190615054565b6000731b06a00df0b27e7871e753720d4917a7d1aac68b9050600073a8e31e3c38add6052a9407298faeb8fd393a6cf99050806001600160a01b031663f7ea7a3d836001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156140c757600080fd5b600073b82f11f3168ece7d56fe6a5679567948090de7c59050600073e1cc2332852b2ac0da59a1f9d3051829f4ef3c1c9050806001600160a01b031663f7ea7a3d836001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156140c757600080fd5b600073c4546bdd93cdaada6994e84fb6f2722c620b019c9050600073fb020ca7f4e8c4a5bbbe060f59a249c6275d2b699050806001600160a01b031663f7ea7a3d836001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156140c757600080fd5b600073ae7a2c1e326e59f2db2132652115a59e8adb5ebf9050600073dc883b9d9ee16f74be08826e68df4c9d9d26e8bd9050806001600160a01b031663f7ea7a3d836001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156140c757600080fd5b600073cc83a57b080a4c7c86f0bb892bc180c8c7f8791d9050600073bb5b03e920cf702de5a3ba9fc1445af4b3919c889050806001600160a01b031663f7ea7a3d836001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156140c757600080fd5b600073527637be27640d6c3e751d24dc67129a6d13e11c9050600073dae6c79c46ab3b280ca28259000695529cbd13399050806001600160a01b031663f7ea7a3d836001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156140c757600080fd5b60007318fcc34bdeaaf9e3b69d2500343527c0c995b1d690506000731cb004a8e84a5ce95c1ff895ee603bac8ec506c79050806001600160a01b031663f7ea7a3d836001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156140c757600080fd5b6000734fb63c954ef07ec74335bb53835026c75dd91dc690506000735d4c724bfe3a228ff0e29125ac1571fe093700a49050806001600160a01b031663f7ea7a3d836001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156140c757600080fd5b600073e08518ba3d2467f7ca50efe68aa00c5f78d4f3d69050600073df69bc4541b86aa4c5a470b4347e730c38b2c3b29050806001600160a01b031663f7ea7a3d836001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156140c757600080fd5b600073b34f4d7c207d8979d05edb0f63f174764bd67825905060007391b82d62ff322b8e02b86f33e9a99a813437830d9050806001600160a01b031663f7ea7a3d836001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156140c757600080fd5b60007395ae43e5e96314e4afffcf19d9419111cd11169e9050600073942eb6e8c029eb22103743c99985af4f4515a5599050806001600160a01b031663f7ea7a3d836001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156140c757600080fd5b60007327b45a4208b87a899009f45888139882477acea5905060007375a0c1597137aa36b40b6a515d997f9a6c6eefeb9050806001600160a01b031663f7ea7a3d836001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156140c757600080fd5b6000736df798ec713b33be823b917f27820f2aa0cf7662905060007307c1e81c345a7c58d7c24072efc5d929bd0647ad9050806001600160a01b031663f7ea7a3d836001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156140c757600080fd5b600073f533aeee48f0e04e30c2f6a1f19fbb675469a1249050600073918b1dbf0917fdd74d03fb9434915e2ecec892869050806001600160a01b031663f7ea7a3d836001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156140c757600080fd5b60408051600f8082526102008201909252606091602082016101e0803883390190505090507310a5f7d9d65bcc2734763444d4940a31b109275f8160008151811061480e57fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073a8e31e3c38add6052a9407298faeb8fd393a6cf98160018151811061485057fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073e1cc2332852b2ac0da59a1f9d3051829f4ef3c1c8160028151811061489257fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073fb020ca7f4e8c4a5bbbe060f59a249c6275d2b69816003815181106148d457fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073dc883b9d9ee16f74be08826e68df4c9d9d26e8bd8160048151811061491657fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073bb5b03e920cf702de5a3ba9fc1445af4b3919c888160058151811061495857fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073dae6c79c46ab3b280ca28259000695529cbd13398160068151811061499a57fe5b60200260200101906001600160a01b031690816001600160a01b031681525050731cb004a8e84a5ce95c1ff895ee603bac8ec506c7816007815181106149dc57fe5b60200260200101906001600160a01b031690816001600160a01b031681525050735d4c724bfe3a228ff0e29125ac1571fe093700a481600881518110614a1e57fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073df69bc4541b86aa4c5a470b4347e730c38b2c3b281600981518110614a6057fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507391b82d62ff322b8e02b86f33e9a99a813437830d81600a81518110614aa257fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073942eb6e8c029eb22103743c99985af4f4515a55981600b81518110614ae457fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507375a0c1597137aa36b40b6a515d997f9a6c6eefeb81600c81518110614b2657fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507307c1e81c345a7c58d7c24072efc5d929bd0647ad81600d81518110614b6857fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073918b1dbf0917fdd74d03fb9434915e2ecec8928681600e81518110614baa57fe5b6001600160a01b03909216602092830291909101909101526040516347a9b6db60e01b815273e5cc99efa57943f4ea0ce6bed265318697748649906347a9b6db90613bfa908490600401614fc0565b6060614c0361209a565b905060005b8151811015612ede57614c2d828281518110614c2057fe5b602002602001015161084a565b600101614c08565b8035614c40816150d4565b92915050565b8051614c40816150eb565b600060208284031215614c6357600080fd5b6000614c6f8484614c35565b949350505050565b600060208284031215614c8957600080fd5b6000614c6f8484614c46565b6000614ca18383614cc1565b505060200190565b6000614ca18383614e1c565b6000614ca18383614e54565b614cca8161507a565b82525050565b6000614cdb82615068565b614ce5818561506c565b9350614cf083615062565b8060005b83811015614d1e578151614d088882614c95565b9750614d1383615062565b925050600101614cf4565b509495945050505050565b6000614d3482615068565b614d3e818561506c565b9350614d4983615062565b8060005b83811015614d1e578151614d618882614ca9565b9750614d6c83615062565b925050600101614d4d565b6000614d8282615068565b614d8c818561506c565b9350614d9783615062565b8060005b83811015614d1e578151614daf8882614cb5565b9750614dba83615062565b925050600101614d9b565b6000614dd082615068565b614dda818561506c565b9350614de583615062565b8060005b83811015614d1e578151614dfd8882614cb5565b9750614e0883615062565b925050600101614de9565b614cca81615085565b614cca8161508a565b6000614e3082615068565b614e3a8185615075565b9350614e4a8185602086016150a4565b9290920192915050565b614cca81615099565b6000614e6a60358361506c565b7f596f75206d757374206265206e6f6d696e61746564206265666f726520796f7581527402063616e20616363657074206f776e65727368697605c1b602082015260400192915050565b6000614ec160188361506c565b7f4c6567616379206e6f6d696e6174696f6e206661696c65640000000000000000815260200192915050565b6749737375616e636560c01b9052565b6000614f0a602f8361506c565b7f4f6e6c792074686520636f6e7472616374206f776e6572206d6179207065726681526e37b936903a3434b99030b1ba34b7b760891b602082015260400192915050565b6000614f5a8284614e25565b9392505050565b60208101614c408284614cc1565b60408101614f7d8285614cc1565b614f5a6020830184614cc1565b60208082528101614f5a8184614cd0565b60408082528101614fac8185614d29565b90508181036020830152614c6f8184614cd0565b60208082528101614f5a8184614d77565b60208082528101614f5a8184614dc5565b60208101614c408284614e54565b60208082528101614c4081614e5d565b60208082528101614c4081614eb4565b6080810161501d82614eed565b61502a6020830186614cc1565b6150376040830185614e13565b614c6f6060830184614e13565b60208082528101614c4081614efd565b60208101614c408284614e1c565b60200190565b5190565b90815260200190565b919050565b6000614c408261508d565b151590565b90565b6001600160a01b031690565b6000614c408261507a565b60005b838110156150bf5781810151838201526020016150a7565b838111156150ce576000848401525b50505050565b6150dd8161507a565b81146150e857600080fd5b50565b6150dd8161508a56fea365627a7a723158204904fbb1198d4f4c63a608b70374136ae0d31a7e9e14261c29623f2d15813b6a6c6578706572696d656e74616cf564736f6c63430005100040
Deployed Bytecode
0x608060405234801561001057600080fd5b506004361061045f5760003560e01c806382291efb1161024c578063acec078c11610146578063dc303a90116100c3578063f2b5b2bd11610087578063f2b5b2bd146106df578063f5ba8f72146106e7578063f6f5ac1b146106ef578063fbed4cec146106f7578063fdd1c39c1461070c5761045f565b8063dc303a90146106b7578063dc597030146106bf578063e05f95b9146106c7578063ecfacaff146106cf578063edc34a6a146106d75761045f565b8063c599d5af1161010a578063c599d5af1461068f578063c71f05f814610697578063c90c168c1461069f578063cd4b9539146106a7578063d075a444146106af5761045f565b8063acec078c14610667578063ad10f7371461066f578063b05dd34814610677578063b0630a071461067f578063bddb48a2146106875761045f565b806399ac8a65116101d4578063a5ec5cfd11610198578063a5ec5cfd1461063f578063a636c1fc14610647578063a6d6da101461064f578063a92ddce714610657578063aade6bd31461065f5761045f565b806399ac8a65146106175780639a0a0bc21461061f5780639d12bfc8146106275780639f8c38de1461062f578063a11b4b57146106375761045f565b806389cbfbb91161021b57806389cbfbb9146105ef5780638ce6d4fe146105f75780638da5cb5b146105ff5780638f94e3a8146106075780638fd3ab801461060f5761045f565b806382291efb146105cf578063847d96fb146105d7578063871f10f7146105df57806387e0b96a146105e75761045f565b80634a088bfb1161035d57806359ef6d01116102e55780636b163565116102a95780636b163565146105a75780636f13689b146105af5780636fbab466146105b757806379ba5097146105bf5780637fc13624146105c75761045f565b806359ef6d011461057f5780635cf74efa146105875780635ebae58a1461058f5780636025f05b14610597578063650fa6121461059f5761045f565b80634f8be1891161032c5780634f8be18914610557578063517971fc1461055f57806352c6c1291461056757806353a47bb71461056f57806359af53f4146105775761045f565b80634a088bfb146105375780634b8a16eb1461053f5780634f1e2220146105475780634f5473691461054f5761045f565b806323a873c8116103eb5780633ba82a40116103af5780633ba82a401461050f5780633c41edd01461051757806340183e451461051f57806341698fe8146105275780634606cd6a1461052f5761045f565b806323a873c8146104dc5780632a8e407b146104e45780633440490d146104f757806337e763e5146104ff5780633ad5c80f146105075761045f565b806316376ef81161043257806316376ef8146104a75780631ace0f95146104af5780631e34ea79146104c45780631faf017a146104cc578063232e08c4146104d45761045f565b806306c6468314610464578063117803e31461048257806311bd19d21461048a5780631627540c14610492575b600080fd5b61046c610714565b6040516104799190614f61565b60405180910390f35b61046c61072c565b61046c610744565b6104a56104a0366004614c51565b61075c565b005b61046c6107ba565b6104b76107d2565b6040516104799190614fe2565b6104b76107ea565b6104b7610802565b6104b761081a565b6104b7610832565b6104a56104f2366004614c51565b61084a565b61046c6109d9565b6104b76109f1565b6104b7610a09565b6104b7610a21565b6104b7610a39565b6104b7610a51565b6104b7610a69565b6104b7610744565b6104b7610a81565b6104b7610a99565b61046c6107ea565b6104b7610ab1565b6104b7610ac9565b61046c6107d2565b6104b7610ae1565b61046c610af9565b6104b7610b08565b61046c610b20565b61046c610b38565b6104b7610b50565b61046c610b50565b61046c610b68565b6104b7610b80565b6104b7610b98565b6104b7610bb0565b6104a5610bc8565b6104b7610c64565b6104b7610714565b6104b7610c7c565b6104b7610c94565b6104b7610cac565b61046c610832565b61046c610cc4565b61046c610cdc565b6104b7610ceb565b6104a5610d03565b61046c610cac565b6104b7611ed2565b6104b7610b38565b6104b76107ba565b61046c611eea565b6104b7610b68565b6104b7611f02565b6104b7611f1a565b6104b7611f32565b6104b7611f4a565b6104b7611f62565b6104b7611f7a565b6104b7611f92565b61046c611f02565b6104b7611faa565b6104b7611fc2565b6104b7610cc4565b6104b7611fda565b61046c610a21565b61046c611ff2565b6104b761200a565b6104b7612022565b6104b761203a565b6104b7610b20565b6104b7612052565b6104b7611eea565b6104b761206a565b6104b7612082565b6106ff61209a565b6040516104799190614f8a565b6104b7611ff2565b7391b82d62ff322b8e02b86f33e9a99a813437830d81565b73eb3107117fead7de89cd14d463d340a2e691776981565b73dc883b9d9ee16f74be08826e68df4c9d9d26e8bd81565b610764612e2a565b600180546001600160a01b0319166001600160a01b0383161790556040517f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce22906107af908390614f61565b60405180910390a150565b73bb5b03e920cf702de5a3ba9fc1445af4b3919c8881565b7375a0c1597137aa36b40b6a515d997f9a6c6eefeb81565b73df69bc4541b86aa4c5a470b4347e730c38b2c3b281565b73bbc455cb4f1b9e4bfc4b73970d360c8f032efee681565b73d2df355c19471c8bd7d8a3aa27ff4e26a21b407681565b73fb020ca7f4e8c4a5bbbe060f59a249c6275d2b6981565b60005460405160609161086b916001600160a01b0390911690602401614f61565b60408051601f198184030181529181526020820180516001600160e01b0316630589d50360e21b179052519091506000906001600160a01b038416906108b2908490614f4e565b6000604051808303816000865af19150503d80600081146108ef576040519150601f19603f3d011682016040523d82523d6000602084013e6108f4565b606091505b50509050806109d45760005460405160609161091e916001600160a01b0390911690602401614f61565b60408051601f198184030181529181526020820180516001600160e01b0316635b94db2760e01b179052519091506000906001600160a01b03861690610965908490614f4e565b6000604051808303816000865af19150503d80600081146109a2576040519150601f19603f3d011682016040523d82523d6000602084013e6109a7565b606091505b50509050806109d15760405162461bcd60e51b81526004016109c890615000565b60405180910390fd5b50505b505050565b7308f30ecf2c15a783083ab9d5b9211c22388d056481565b7373b1a2643507cd30f11dfcf2d974f4373e5bc07781565b734f6296455f8d754c19821cf1ec8febf2cd456e6781565b73918b1dbf0917fdd74d03fb9434915e2ecec8928681565b7305a9cbe762b36632b3594da4f082340e0e5343e881565b737e88d19a79b291cfe5696d496055f7e57f537a7581565b73577d4a7395c6a5f46d9981a5f83fa7294926abb081565b735e74c9036fb86bd7ecdcb084a0673efc32ea31cb81565b735b1b5fea1b99d83ad479df0c222f0492385381dd81565b7352496fe8a4feaefe14d9433e00d48e6929c13dec81565b73b671f2210b1f6621a2607ea63e6b2dc3e2464d1f81565b73e1afe1fd76fd88f78cbf599ea1846231b8ba3b6b81565b6001546001600160a01b031681565b73c011a73ee8576fb46f5e1c5751ca3b9fe0af2a6f81565b735d4c724bfe3a228ff0e29125ac1571fe093700a481565b7307c1e81c345a7c58d7c24072efc5d929bd0647ad81565b73dae6c79c46ab3b280ca28259000695529cbd133981565b731cb004a8e84a5ce95c1ff895ee603bac8ec506c781565b7397fe22e7341a0cd8db6f6c021a24dc8f4dad855f81565b73104edf1da359506548bfc7c25ba1e28c16a7023581565b73e36e2d3c7c34281fa3bc737950a68571736880a181565b6001546001600160a01b03163314610bf25760405162461bcd60e51b81526004016109c890614ff0565b6000546001546040517fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c92610c35926001600160a01b0391821692911690614f6f565b60405180910390a160018054600080546001600160a01b03199081166001600160a01b03841617909155169055565b73042a7a0022a7695454ac5be77a4860e50c9683fc81565b7393b6e9fbbd2c32a0dc3c2b943b7c3cbc2fe2373081565b73d71ecff9342a5ced620049e616c5035f1db9862081565b73e1cc2332852b2ac0da59a1f9d3051829f4ef3c1c81565b73e5cc99efa57943f4ea0ce6bed26531869774864981565b6000546001600160a01b031681565b73696c905f8f8c006ca46e9808fe7e00049507798f81565b610d0b612e2a565b610d13612e54565b610d1b612ee2565b610d2361367e565b610d2b613c28565b60405163776d1a0160e01b815273c011a73ee8576fb46f5e1c5751ca3b9fe0af2a6f9063776d1a0190610d76907308f30ecf2c15a783083ab9d5b9211c22388d056490600401614fe2565b600060405180830381600087803b158015610d9057600080fd5b505af1158015610da4573d6000803e3d6000fd5b50506040516348bf197160e01b815273696c905f8f8c006ca46e9808fe7e00049507798f92506348bf19719150610df99073e5cc99efa57943f4ea0ce6bed26531869774864990600190600090600401615010565b600060405180830381600087803b158015610e1357600080fd5b505af1158015610e27573d6000803e3d6000fd5b505060405163297a22e560e11b8152735b1b5fea1b99d83ad479df0c222f0492385381dd92506352f445ca9150610e76907308f30ecf2c15a783083ab9d5b9211c22388d056490600401614f61565b600060405180830381600087803b158015610e9057600080fd5b505af1158015610ea4573d6000803e3d6000fd5b5050604051637f64fced60e11b815273b671f2210b1f6621a2607ea63e6b2dc3e2464d1f925063fec9f9da9150610ef3907308f30ecf2c15a783083ab9d5b9211c22388d056490600401614fe2565b600060405180830381600087803b158015610f0d57600080fd5b505af1158015610f21573d6000803e3d6000fd5b5050604051637a9e5e4b60e01b81527329c295b046a73cde593f21f63091b072d407e3f29250637a9e5e4b9150610f70907308f30ecf2c15a783083ab9d5b9211c22388d056490600401614f61565b600060405180830381600087803b158015610f8a57600080fd5b505af1158015610f9e573d6000803e3d6000fd5b50505050610faa61404d565b60405163297a22e560e11b81527305a9cbe762b36632b3594da4f082340e0e5343e8906352f445ca90610ff5907310a5f7d9d65bcc2734763444d4940a31b109275f90600401614f61565b600060405180830381600087803b15801561100f57600080fd5b505af1158015611023573d6000803e3d6000fd5b505060405163776d1a0160e01b81527357ab1ec28d129707052df4df418d58a2d46d5f51925063776d1a019150611072907310a5f7d9d65bcc2734763444d4940a31b109275f90600401614fe2565b600060405180830381600087803b15801561108c57600080fd5b505af11580156110a0573d6000803e3d6000fd5b505050506110ac61411b565b60405163297a22e560e11b8152736568d9e750fc44af00f857885dfb8281c00529c4906352f445ca906110f79073a8e31e3c38add6052a9407298faeb8fd393a6cf990600401614f61565b600060405180830381600087803b15801561111157600080fd5b505af1158015611125573d6000803e3d6000fd5b505060405163776d1a0160e01b815273d71ecff9342a5ced620049e616c5035f1db98620925063776d1a0191506111749073a8e31e3c38add6052a9407298faeb8fd393a6cf990600401614fe2565b600060405180830381600087803b15801561118e57600080fd5b505af11580156111a2573d6000803e3d6000fd5b505050506111ae614195565b60405163297a22e560e11b8152734dfacfb15514c21c991ff75bc7bf6fb1f98361ed906352f445ca906111f99073e1cc2332852b2ac0da59a1f9d3051829f4ef3c1c90600401614f61565b600060405180830381600087803b15801561121357600080fd5b505af1158015611227573d6000803e3d6000fd5b505060405163776d1a0160e01b815273f6b1c627e95bfc3c1b4c9b825a032ff0fbf3e07d925063776d1a0191506112769073e1cc2332852b2ac0da59a1f9d3051829f4ef3c1c90600401614fe2565b600060405180830381600087803b15801561129057600080fd5b505af11580156112a4573d6000803e3d6000fd5b505050506112b061420f565b60405163297a22e560e11b815273cb29d2cf2c65d3be1d00f07f3441390432d55203906352f445ca906112fb9073fb020ca7f4e8c4a5bbbe060f59a249c6275d2b6990600401614f61565b600060405180830381600087803b15801561131557600080fd5b505af1158015611329573d6000803e3d6000fd5b505060405163776d1a0160e01b815273f48e200eaf9906362bb1442fca31e0835773b8b4925063776d1a0191506113789073fb020ca7f4e8c4a5bbbe060f59a249c6275d2b6990600401614fe2565b600060405180830381600087803b15801561139257600080fd5b505af11580156113a6573d6000803e3d6000fd5b505050506113b2614289565b60405163297a22e560e11b8152737e88d19a79b291cfe5696d496055f7e57f537a75906352f445ca906113fd9073dc883b9d9ee16f74be08826e68df4c9d9d26e8bd90600401614f61565b600060405180830381600087803b15801561141757600080fd5b505af115801561142b573d6000803e3d6000fd5b505060405163776d1a0160e01b81527397fe22e7341a0cd8db6f6c021a24dc8f4dad855f925063776d1a01915061147a9073dc883b9d9ee16f74be08826e68df4c9d9d26e8bd90600401614fe2565b600060405180830381600087803b15801561149457600080fd5b505af11580156114a8573d6000803e3d6000fd5b505050506114b4614303565b60405163297a22e560e11b81527352496fe8a4feaefe14d9433e00d48e6929c13dec906352f445ca906114ff9073bb5b03e920cf702de5a3ba9fc1445af4b3919c8890600401614f61565b600060405180830381600087803b15801561151957600080fd5b505af115801561152d573d6000803e3d6000fd5b505060405163776d1a0160e01b8152730f83287ff768d1c1e17a42f44d644d7f22e8ee1d925063776d1a01915061157c9073bb5b03e920cf702de5a3ba9fc1445af4b3919c8890600401614fe2565b600060405180830381600087803b15801561159657600080fd5b505af11580156115aa573d6000803e3d6000fd5b505050506115b661437d565b60405163297a22e560e11b81527393b6e9fbbd2c32a0dc3c2b943b7c3cbc2fe23730906352f445ca906116019073dae6c79c46ab3b280ca28259000695529cbd133990600401614f61565b600060405180830381600087803b15801561161b57600080fd5b505af115801561162f573d6000803e3d6000fd5b505060405163776d1a0160e01b815273269895a3df4d73b077fc823dd6da1b95f72aaf9b925063776d1a01915061167e9073dae6c79c46ab3b280ca28259000695529cbd133990600401614fe2565b600060405180830381600087803b15801561169857600080fd5b505af11580156116ac573d6000803e3d6000fd5b505050506116b86143f7565b60405163297a22e560e11b8152734f6296455f8d754c19821cf1ec8febf2cd456e67906352f445ca9061170390731cb004a8e84a5ce95c1ff895ee603bac8ec506c790600401614f61565b600060405180830381600087803b15801561171d57600080fd5b505af1158015611731573d6000803e3d6000fd5b505060405163776d1a0160e01b815273fe18be6b3bd88a2d2a7f928d00292e7a9963cfc6925063776d1a01915061178090731cb004a8e84a5ce95c1ff895ee603bac8ec506c790600401614fe2565b600060405180830381600087803b15801561179a57600080fd5b505af11580156117ae573d6000803e3d6000fd5b505050506117ba614471565b60405163297a22e560e11b81527334a5ef81d18f3a305ae9c2d7df42beef4c79031c906352f445ca9061180590735d4c724bfe3a228ff0e29125ac1571fe093700a490600401614f61565b600060405180830381600087803b15801561181f57600080fd5b505af1158015611833573d6000803e3d6000fd5b505060405163776d1a0160e01b8152735e74c9036fb86bd7ecdcb084a0673efc32ea31cb925063776d1a01915061188290735d4c724bfe3a228ff0e29125ac1571fe093700a490600401614fe2565b600060405180830381600087803b15801561189c57600080fd5b505af11580156118b0573d6000803e3d6000fd5b505050506118bc6144eb565b60405163297a22e560e11b815273577d4a7395c6a5f46d9981a5f83fa7294926abb0906352f445ca906119079073df69bc4541b86aa4c5a470b4347e730c38b2c3b290600401614f61565b600060405180830381600087803b15801561192157600080fd5b505af1158015611935573d6000803e3d6000fd5b505060405163776d1a0160e01b815273bbc455cb4f1b9e4bfc4b73970d360c8f032efee6925063776d1a0191506119849073df69bc4541b86aa4c5a470b4347e730c38b2c3b290600401614fe2565b600060405180830381600087803b15801561199e57600080fd5b505af11580156119b2573d6000803e3d6000fd5b505050506119be614565565b60405163297a22e560e11b8152739956c5019a24fbd5b506ad070b771577bac5c343906352f445ca90611a09907391b82d62ff322b8e02b86f33e9a99a813437830d90600401614f61565b600060405180830381600087803b158015611a2357600080fd5b505af1158015611a37573d6000803e3d6000fd5b505060405163776d1a0160e01b815273e36e2d3c7c34281fa3bc737950a68571736880a1925063776d1a019150611a86907391b82d62ff322b8e02b86f33e9a99a813437830d90600401614fe2565b600060405180830381600087803b158015611aa057600080fd5b505af1158015611ab4573d6000803e3d6000fd5b50505050611ac06145df565b60405163297a22e560e11b8152739bced8a8e3ad81c9b146ffc880358f734a06f7c0906352f445ca90611b0b9073942eb6e8c029eb22103743c99985af4f4515a55990600401614f61565b600060405180830381600087803b158015611b2557600080fd5b505af1158015611b39573d6000803e3d6000fd5b505060405163776d1a0160e01b815273d2df355c19471c8bd7d8a3aa27ff4e26a21b4076925063776d1a019150611b889073942eb6e8c029eb22103743c99985af4f4515a55990600401614fe2565b600060405180830381600087803b158015611ba257600080fd5b505af1158015611bb6573d6000803e3d6000fd5b50505050611bc2614659565b60405163297a22e560e11b81527373b1a2643507cd30f11dfcf2d974f4373e5bc077906352f445ca90611c0d907375a0c1597137aa36b40b6a515d997f9a6c6eefeb90600401614f61565b600060405180830381600087803b158015611c2757600080fd5b505af1158015611c3b573d6000803e3d6000fd5b505060405163776d1a0160e01b8152731715ac0743102bf5cd58efbb6cf2dc2685d967b6925063776d1a019150611c8a907375a0c1597137aa36b40b6a515d997f9a6c6eefeb90600401614fe2565b600060405180830381600087803b158015611ca457600080fd5b505af1158015611cb8573d6000803e3d6000fd5b50505050611cc46146d3565b60405163297a22e560e11b815273042a7a0022a7695454ac5be77a4860e50c9683fc906352f445ca90611d0f907307c1e81c345a7c58d7c24072efc5d929bd0647ad90600401614f61565b600060405180830381600087803b158015611d2957600080fd5b505af1158015611d3d573d6000803e3d6000fd5b505060405163776d1a0160e01b815273104edf1da359506548bfc7c25ba1e28c16a70235925063776d1a019150611d8c907307c1e81c345a7c58d7c24072efc5d929bd0647ad90600401614fe2565b600060405180830381600087803b158015611da657600080fd5b505af1158015611dba573d6000803e3d6000fd5b50505050611dc661474d565b60405163297a22e560e11b8152737ac2d37098a65b0f711cffa3be635f1e6acacfab906352f445ca90611e119073918b1dbf0917fdd74d03fb9434915e2ecec8928690600401614f61565b600060405180830381600087803b158015611e2b57600080fd5b505af1158015611e3f573d6000803e3d6000fd5b505060405163776d1a0160e01b815273e1afe1fd76fd88f78cbf599ea1846231b8ba3b6b925063776d1a019150611e8e9073918b1dbf0917fdd74d03fb9434915e2ecec8928690600401614fe2565b600060405180830381600087803b158015611ea857600080fd5b505af1158015611ebc573d6000803e3d6000fd5b50505050611ec86147c7565b611ed0614bf9565b565b7357ab1ec28d129707052df4df418d58a2d46d5f5181565b73942eb6e8c029eb22103743c99985af4f4515a55981565b73a8e31e3c38add6052a9407298faeb8fd393a6cf981565b73f6b1c627e95bfc3c1b4c9b825a032ff0fbf3e07d81565b734dfacfb15514c21c991ff75bc7bf6fb1f98361ed81565b739bced8a8e3ad81c9b146ffc880358f734a06f7c081565b736568d9e750fc44af00f857885dfb8281c00529c481565b73269895a3df4d73b077fc823dd6da1b95f72aaf9b81565b7329c295b046a73cde593f21f63091b072d407e3f281565b739956c5019a24fbd5b506ad070b771577bac5c34381565b73823be81bbf96bec0e25ca13170f5aacb5b79ba8381565b73cb29d2cf2c65d3be1d00f07f3441390432d5520381565b7310a5f7d9d65bcc2734763444d4940a31b109275f81565b730f83287ff768d1c1e17a42f44d644d7f22e8ee1d81565b73f48e200eaf9906362bb1442fca31e0835773b8b481565b737ac2d37098a65b0f711cffa3be635f1e6acacfab81565b7334a5ef81d18f3a305ae9c2d7df42beef4c79031c81565b731715ac0743102bf5cd58efbb6cf2dc2685d967b681565b73fe18be6b3bd88a2d2a7f928d00292e7a9963cfc681565b6040805160348082526106a08201909252606091602082016106808038833901905050905073823be81bbf96bec0e25ca13170f5aacb5b79ba83816000815181106120e157fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073c011a73ee8576fb46f5e1c5751ca3b9fe0af2a6f8160018151811061212357fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073696c905f8f8c006ca46e9808fe7e00049507798f8160028151811061216557fe5b60200260200101906001600160a01b031690816001600160a01b031681525050735b1b5fea1b99d83ad479df0c222f0492385381dd816003815181106121a757fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073b671f2210b1f6621a2607ea63e6b2dc3e2464d1f816004815181106121e957fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507329c295b046a73cde593f21f63091b072d407e3f28160058151811061222b57fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507310a5f7d9d65bcc2734763444d4940a31b109275f8160068151811061226d57fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507305a9cbe762b36632b3594da4f082340e0e5343e8816007815181106122af57fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507357ab1ec28d129707052df4df418d58a2d46d5f51816008815181106122f157fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073a8e31e3c38add6052a9407298faeb8fd393a6cf98160098151811061233357fe5b60200260200101906001600160a01b031690816001600160a01b031681525050736568d9e750fc44af00f857885dfb8281c00529c481600a8151811061237557fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073d71ecff9342a5ced620049e616c5035f1db9862081600b815181106123b757fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073e1cc2332852b2ac0da59a1f9d3051829f4ef3c1c81600c815181106123f957fe5b60200260200101906001600160a01b031690816001600160a01b031681525050734dfacfb15514c21c991ff75bc7bf6fb1f98361ed81600d8151811061243b57fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073f6b1c627e95bfc3c1b4c9b825a032ff0fbf3e07d81600e8151811061247d57fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073fb020ca7f4e8c4a5bbbe060f59a249c6275d2b6981600f815181106124bf57fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073cb29d2cf2c65d3be1d00f07f3441390432d552038160108151811061250157fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073f48e200eaf9906362bb1442fca31e0835773b8b48160118151811061254357fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073dc883b9d9ee16f74be08826e68df4c9d9d26e8bd8160128151811061258557fe5b60200260200101906001600160a01b031690816001600160a01b031681525050737e88d19a79b291cfe5696d496055f7e57f537a75816013815181106125c757fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507397fe22e7341a0cd8db6f6c021a24dc8f4dad855f8160148151811061260957fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073bb5b03e920cf702de5a3ba9fc1445af4b3919c888160158151811061264b57fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507352496fe8a4feaefe14d9433e00d48e6929c13dec8160168151811061268d57fe5b60200260200101906001600160a01b031690816001600160a01b031681525050730f83287ff768d1c1e17a42f44d644d7f22e8ee1d816017815181106126cf57fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073dae6c79c46ab3b280ca28259000695529cbd13398160188151811061271157fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507393b6e9fbbd2c32a0dc3c2b943b7c3cbc2fe237308160198151811061275357fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073269895a3df4d73b077fc823dd6da1b95f72aaf9b81601a8151811061279557fe5b60200260200101906001600160a01b031690816001600160a01b031681525050731cb004a8e84a5ce95c1ff895ee603bac8ec506c781601b815181106127d757fe5b60200260200101906001600160a01b031690816001600160a01b031681525050734f6296455f8d754c19821cf1ec8febf2cd456e6781601c8151811061281957fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073fe18be6b3bd88a2d2a7f928d00292e7a9963cfc681601d8151811061285b57fe5b60200260200101906001600160a01b031690816001600160a01b031681525050735d4c724bfe3a228ff0e29125ac1571fe093700a481601e8151811061289d57fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507334a5ef81d18f3a305ae9c2d7df42beef4c79031c81601f815181106128df57fe5b60200260200101906001600160a01b031690816001600160a01b031681525050735e74c9036fb86bd7ecdcb084a0673efc32ea31cb8160208151811061292157fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073df69bc4541b86aa4c5a470b4347e730c38b2c3b28160218151811061296357fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073577d4a7395c6a5f46d9981a5f83fa7294926abb0816022815181106129a557fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073bbc455cb4f1b9e4bfc4b73970d360c8f032efee6816023815181106129e757fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507391b82d62ff322b8e02b86f33e9a99a813437830d81602481518110612a2957fe5b60200260200101906001600160a01b031690816001600160a01b031681525050739956c5019a24fbd5b506ad070b771577bac5c34381602581518110612a6b57fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073e36e2d3c7c34281fa3bc737950a68571736880a181602681518110612aad57fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073942eb6e8c029eb22103743c99985af4f4515a55981602781518110612aef57fe5b60200260200101906001600160a01b031690816001600160a01b031681525050739bced8a8e3ad81c9b146ffc880358f734a06f7c081602881518110612b3157fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073d2df355c19471c8bd7d8a3aa27ff4e26a21b407681602981518110612b7357fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507375a0c1597137aa36b40b6a515d997f9a6c6eefeb81602a81518110612bb557fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507373b1a2643507cd30f11dfcf2d974f4373e5bc07781602b81518110612bf757fe5b60200260200101906001600160a01b031690816001600160a01b031681525050731715ac0743102bf5cd58efbb6cf2dc2685d967b681602c81518110612c3957fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507307c1e81c345a7c58d7c24072efc5d929bd0647ad81602d81518110612c7b57fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073042a7a0022a7695454ac5be77a4860e50c9683fc81602e81518110612cbd57fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073104edf1da359506548bfc7c25ba1e28c16a7023581602f81518110612cff57fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073918b1dbf0917fdd74d03fb9434915e2ecec8928681603081518110612d4157fe5b60200260200101906001600160a01b031690816001600160a01b031681525050737ac2d37098a65b0f711cffa3be635f1e6acacfab81603181518110612d8357fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073e1afe1fd76fd88f78cbf599ea1846231b8ba3b6b81603281518110612dc557fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073e5cc99efa57943f4ea0ce6bed26531869774864981603381518110612e0757fe5b60200260200101906001600160a01b031690816001600160a01b03168152505090565b6000546001600160a01b03163314611ed05760405162461bcd60e51b81526004016109c890615044565b6060612e5e61209a565b905060005b8151811015612ede57818181518110612e7857fe5b60200260200101516001600160a01b03166379ba50976040518163ffffffff1660e01b8152600401600060405180830381600087803b158015612eba57600080fd5b505af1158015612ece573d6000803e3d6000fd5b505060019092019150612e639050565b5050565b60408051601180825261024082019092526060916020820161022080388339019050509050680a6f2dce8d0cae8d2f60bb1b81600081518110612f2157fe5b6020026020010181815250506524b9b9bab2b960d11b81600181518110612f4457fe5b6020026020010181815250506814de5b9d1a1cd554d160ba1b81600281518110612f6a57fe5b6020026020010181815250506853796e7468734a505960b81b81600381518110612f9057fe5b6020026020010181815250506829bcb73a3439a2aaa960b91b81600481518110612fb657fe5b6020026020010181815250506814de5b9d1a1cd0555160ba1b81600581518110612fdc57fe5b60200260200101818152505068053796e7468734742560bc1b8160068151811061300257fe5b6020026020010181815250506829bcb73a3439a1a42360b91b8160078151811061302857fe5b6020026020010181815250506853796e7468734b525760b81b8160088151811061304e57fe5b602002602001018181525050680a6f2dce8d0e68aa8960bb1b8160098151811061307457fe5b6020026020010181815250506853796e74687342544360b81b81600a8151811061309a57fe5b6020026020010181815250506953796e7468734c494e4b60b01b81600b815181106130c157fe5b6020026020010181815250506953796e7468734141564560b01b81600c815181106130e857fe5b6020026020010181815250506853796e74687341444160b81b81600d8151811061310e57fe5b6020026020010181815250506814de5b9d1a1cd113d560ba1b81600e8151811061313457fe5b6020026020010181815250506953796e7468734445464960b01b81600f8151811061315b57fe5b6020026020010181815250506b53796e74687345544842544360a01b8160108151811061318457fe5b602090810291909101015260408051601180825261024082019092526060918160200160208202803883390190505090507308f30ecf2c15a783083ab9d5b9211c22388d0564816000815181106131d757fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073e5cc99efa57943f4ea0ce6bed2653186977486498160018151811061321957fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507310a5f7d9d65bcc2734763444d4940a31b109275f8160028151811061325b57fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073e1cc2332852b2ac0da59a1f9d3051829f4ef3c1c8160038151811061329d57fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073a8e31e3c38add6052a9407298faeb8fd393a6cf9816004815181106132df57fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073fb020ca7f4e8c4a5bbbe060f59a249c6275d2b698160058151811061332157fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073dc883b9d9ee16f74be08826e68df4c9d9d26e8bd8160068151811061336357fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073bb5b03e920cf702de5a3ba9fc1445af4b3919c88816007815181106133a557fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073dae6c79c46ab3b280ca28259000695529cbd1339816008815181106133e757fe5b60200260200101906001600160a01b031690816001600160a01b031681525050735d4c724bfe3a228ff0e29125ac1571fe093700a48160098151811061342957fe5b60200260200101906001600160a01b031690816001600160a01b031681525050731cb004a8e84a5ce95c1ff895ee603bac8ec506c781600a8151811061346b57fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073df69bc4541b86aa4c5a470b4347e730c38b2c3b281600b815181106134ad57fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073942eb6e8c029eb22103743c99985af4f4515a55981600c815181106134ef57fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507391b82d62ff322b8e02b86f33e9a99a813437830d81600d8151811061353157fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507375a0c1597137aa36b40b6a515d997f9a6c6eefeb81600e8151811061357357fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073918b1dbf0917fdd74d03fb9434915e2ecec8928681600f815181106135b557fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507307c1e81c345a7c58d7c24072efc5d929bd0647ad816010815181106135f757fe5b6001600160a01b039092166020928302919091019091015260405163ab0b8f7760e01b815273823be81bbf96bec0e25ca13170f5aacb5b79ba839063ab0b8f77906136489085908590600401614f9b565b600060405180830381600087803b15801561366257600080fd5b505af1158015613676573d6000803e3d6000fd5b505050505050565b6040805160148082526102a08201909252606091602082016102808038833901905050905073da4ef8520b1a57d7d63f1e249606d1a459698876816000815181106136c557fe5b60200260200101906001600160a01b031690816001600160a01b031681525050730e5fe1b05612581576e9a3db048416d0b1e3c4258160018151811061370757fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073f79603a71144e415730c1a6f57f366e4ea962c008160028151811061374957fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073d64d83829d92b5bda881f6f61a4e4e27fc1853878160038151811061378b57fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073e5cc99efa57943f4ea0ce6bed265318697748649816004815181106137cd57fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507362922670313bf6b41c580143d1f6c173c5c200198160058151811061380f57fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507339ea01a0298c315d149a490e34b59dbf2ec7e48f8160068151811061385157fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507308f30ecf2c15a783083ab9d5b9211c22388d05648160078151811061389357fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507389fcb32f29e509cc42d0c8b6f058c993013a843f816008815181106138d557fe5b60200260200101906001600160a01b031690816001600160a01b031681525050733b2f389aee480238a49e3a9985cd6815370712eb8160098151811061391757fe5b60200260200101906001600160a01b031690816001600160a01b031681525050731620aa736939597891c1940cf0d28b82566f939081600a8151811061395957fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073eacaed9581294b1b5cfb6b941d4b8b81b200543781600b8151811061399b57fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073e533139af961c9747356d947838c98451015e23481600c815181106139dd57fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507310a5f7d9d65bcc2734763444d4940a31b109275f81600d81518110613a1f57fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073a8e31e3c38add6052a9407298faeb8fd393a6cf981600e81518110613a6157fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073e1cc2332852b2ac0da59a1f9d3051829f4ef3c1c81600f81518110613aa357fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073fb020ca7f4e8c4a5bbbe060f59a249c6275d2b6981601081518110613ae557fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073dc883b9d9ee16f74be08826e68df4c9d9d26e8bd81601181518110613b2757fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073bb5b03e920cf702de5a3ba9fc1445af4b3919c8881601281518110613b6957fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073dae6c79c46ab3b280ca28259000695529cbd133981601381518110613bab57fe5b6001600160a01b039092166020928302919091019091015260405163766f781560e01b815273823be81bbf96bec0e25ca13170f5aacb5b79ba839063766f781590613bfa908490600401614fd1565b600060405180830381600087803b158015613c1457600080fd5b505af11580156109d1573d6000803e3d6000fd5b60408051601080825261022082019092526060916020820161020080388339019050509050731cb004a8e84a5ce95c1ff895ee603bac8ec506c781600081518110613c6f57fe5b60200260200101906001600160a01b031690816001600160a01b031681525050735d4c724bfe3a228ff0e29125ac1571fe093700a481600181518110613cb157fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073df69bc4541b86aa4c5a470b4347e730c38b2c3b281600281518110613cf357fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507391b82d62ff322b8e02b86f33e9a99a813437830d81600381518110613d3557fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073942eb6e8c029eb22103743c99985af4f4515a55981600481518110613d7757fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507375a0c1597137aa36b40b6a515d997f9a6c6eefeb81600581518110613db957fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507307c1e81c345a7c58d7c24072efc5d929bd0647ad81600681518110613dfb57fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073918b1dbf0917fdd74d03fb9434915e2ecec8928681600781518110613e3d57fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073c1aae9d18bbe386b102435a8632c8063d31e747c81600881518110613e7f57fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073067e398605e84f2d0aeec1806e62768c5110dcc681600981518110613ec157fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507302f9bc46bed33acdb9cb002fe346734cef8a948081600a81518110613f0357fe5b60200260200101906001600160a01b031690816001600160a01b031681525050735c8344bcdc38f1ab5eb5c1d4a35ddeea522b5dfa81600b81518110613f4557fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073aa03ab31b55dceeef845c8d17890cc61cd98ed0481600c81518110613f8757fe5b60200260200101906001600160a01b031690816001600160a01b031681525050731f2c3a1046c32729862fcb038369696e3273a51681600d81518110613fc957fe5b60200260200101906001600160a01b031690816001600160a01b031681525050737c22547779c8aa41bae79e03e8383a0befbcecf081600e8151811061400b57fe5b60200260200101906001600160a01b031690816001600160a01b031681525050737a3d898b717e50a96fd8b232e9d15f0a547a7eeb81600f81518110613bab57fe5b6000737df9b3f8f1c011d8bd707430e97e747479dd532a905060007310a5f7d9d65bcc2734763444d4940a31b109275f9050806001600160a01b031663f7ea7a3d836001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156140c757600080fd5b505afa1580156140db573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506140ff9190810190614c77565b6040518263ffffffff1660e01b81526004016136489190615054565b6000731b06a00df0b27e7871e753720d4917a7d1aac68b9050600073a8e31e3c38add6052a9407298faeb8fd393a6cf99050806001600160a01b031663f7ea7a3d836001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156140c757600080fd5b600073b82f11f3168ece7d56fe6a5679567948090de7c59050600073e1cc2332852b2ac0da59a1f9d3051829f4ef3c1c9050806001600160a01b031663f7ea7a3d836001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156140c757600080fd5b600073c4546bdd93cdaada6994e84fb6f2722c620b019c9050600073fb020ca7f4e8c4a5bbbe060f59a249c6275d2b699050806001600160a01b031663f7ea7a3d836001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156140c757600080fd5b600073ae7a2c1e326e59f2db2132652115a59e8adb5ebf9050600073dc883b9d9ee16f74be08826e68df4c9d9d26e8bd9050806001600160a01b031663f7ea7a3d836001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156140c757600080fd5b600073cc83a57b080a4c7c86f0bb892bc180c8c7f8791d9050600073bb5b03e920cf702de5a3ba9fc1445af4b3919c889050806001600160a01b031663f7ea7a3d836001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156140c757600080fd5b600073527637be27640d6c3e751d24dc67129a6d13e11c9050600073dae6c79c46ab3b280ca28259000695529cbd13399050806001600160a01b031663f7ea7a3d836001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156140c757600080fd5b60007318fcc34bdeaaf9e3b69d2500343527c0c995b1d690506000731cb004a8e84a5ce95c1ff895ee603bac8ec506c79050806001600160a01b031663f7ea7a3d836001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156140c757600080fd5b6000734fb63c954ef07ec74335bb53835026c75dd91dc690506000735d4c724bfe3a228ff0e29125ac1571fe093700a49050806001600160a01b031663f7ea7a3d836001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156140c757600080fd5b600073e08518ba3d2467f7ca50efe68aa00c5f78d4f3d69050600073df69bc4541b86aa4c5a470b4347e730c38b2c3b29050806001600160a01b031663f7ea7a3d836001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156140c757600080fd5b600073b34f4d7c207d8979d05edb0f63f174764bd67825905060007391b82d62ff322b8e02b86f33e9a99a813437830d9050806001600160a01b031663f7ea7a3d836001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156140c757600080fd5b60007395ae43e5e96314e4afffcf19d9419111cd11169e9050600073942eb6e8c029eb22103743c99985af4f4515a5599050806001600160a01b031663f7ea7a3d836001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156140c757600080fd5b60007327b45a4208b87a899009f45888139882477acea5905060007375a0c1597137aa36b40b6a515d997f9a6c6eefeb9050806001600160a01b031663f7ea7a3d836001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156140c757600080fd5b6000736df798ec713b33be823b917f27820f2aa0cf7662905060007307c1e81c345a7c58d7c24072efc5d929bd0647ad9050806001600160a01b031663f7ea7a3d836001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156140c757600080fd5b600073f533aeee48f0e04e30c2f6a1f19fbb675469a1249050600073918b1dbf0917fdd74d03fb9434915e2ecec892869050806001600160a01b031663f7ea7a3d836001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156140c757600080fd5b60408051600f8082526102008201909252606091602082016101e0803883390190505090507310a5f7d9d65bcc2734763444d4940a31b109275f8160008151811061480e57fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073a8e31e3c38add6052a9407298faeb8fd393a6cf98160018151811061485057fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073e1cc2332852b2ac0da59a1f9d3051829f4ef3c1c8160028151811061489257fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073fb020ca7f4e8c4a5bbbe060f59a249c6275d2b69816003815181106148d457fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073dc883b9d9ee16f74be08826e68df4c9d9d26e8bd8160048151811061491657fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073bb5b03e920cf702de5a3ba9fc1445af4b3919c888160058151811061495857fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073dae6c79c46ab3b280ca28259000695529cbd13398160068151811061499a57fe5b60200260200101906001600160a01b031690816001600160a01b031681525050731cb004a8e84a5ce95c1ff895ee603bac8ec506c7816007815181106149dc57fe5b60200260200101906001600160a01b031690816001600160a01b031681525050735d4c724bfe3a228ff0e29125ac1571fe093700a481600881518110614a1e57fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073df69bc4541b86aa4c5a470b4347e730c38b2c3b281600981518110614a6057fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507391b82d62ff322b8e02b86f33e9a99a813437830d81600a81518110614aa257fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073942eb6e8c029eb22103743c99985af4f4515a55981600b81518110614ae457fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507375a0c1597137aa36b40b6a515d997f9a6c6eefeb81600c81518110614b2657fe5b60200260200101906001600160a01b031690816001600160a01b0316815250507307c1e81c345a7c58d7c24072efc5d929bd0647ad81600d81518110614b6857fe5b60200260200101906001600160a01b031690816001600160a01b03168152505073918b1dbf0917fdd74d03fb9434915e2ecec8928681600e81518110614baa57fe5b6001600160a01b03909216602092830291909101909101526040516347a9b6db60e01b815273e5cc99efa57943f4ea0ce6bed265318697748649906347a9b6db90613bfa908490600401614fc0565b6060614c0361209a565b905060005b8151811015612ede57614c2d828281518110614c2057fe5b602002602001015161084a565b600101614c08565b8035614c40816150d4565b92915050565b8051614c40816150eb565b600060208284031215614c6357600080fd5b6000614c6f8484614c35565b949350505050565b600060208284031215614c8957600080fd5b6000614c6f8484614c46565b6000614ca18383614cc1565b505060200190565b6000614ca18383614e1c565b6000614ca18383614e54565b614cca8161507a565b82525050565b6000614cdb82615068565b614ce5818561506c565b9350614cf083615062565b8060005b83811015614d1e578151614d088882614c95565b9750614d1383615062565b925050600101614cf4565b509495945050505050565b6000614d3482615068565b614d3e818561506c565b9350614d4983615062565b8060005b83811015614d1e578151614d618882614ca9565b9750614d6c83615062565b925050600101614d4d565b6000614d8282615068565b614d8c818561506c565b9350614d9783615062565b8060005b83811015614d1e578151614daf8882614cb5565b9750614dba83615062565b925050600101614d9b565b6000614dd082615068565b614dda818561506c565b9350614de583615062565b8060005b83811015614d1e578151614dfd8882614cb5565b9750614e0883615062565b925050600101614de9565b614cca81615085565b614cca8161508a565b6000614e3082615068565b614e3a8185615075565b9350614e4a8185602086016150a4565b9290920192915050565b614cca81615099565b6000614e6a60358361506c565b7f596f75206d757374206265206e6f6d696e61746564206265666f726520796f7581527402063616e20616363657074206f776e65727368697605c1b602082015260400192915050565b6000614ec160188361506c565b7f4c6567616379206e6f6d696e6174696f6e206661696c65640000000000000000815260200192915050565b6749737375616e636560c01b9052565b6000614f0a602f8361506c565b7f4f6e6c792074686520636f6e7472616374206f776e6572206d6179207065726681526e37b936903a3434b99030b1ba34b7b760891b602082015260400192915050565b6000614f5a8284614e25565b9392505050565b60208101614c408284614cc1565b60408101614f7d8285614cc1565b614f5a6020830184614cc1565b60208082528101614f5a8184614cd0565b60408082528101614fac8185614d29565b90508181036020830152614c6f8184614cd0565b60208082528101614f5a8184614d77565b60208082528101614f5a8184614dc5565b60208101614c408284614e54565b60208082528101614c4081614e5d565b60208082528101614c4081614eb4565b6080810161501d82614eed565b61502a6020830186614cc1565b6150376040830185614e13565b614c6f6060830184614e13565b60208082528101614c4081614efd565b60208101614c408284614e1c565b60200190565b5190565b90815260200190565b919050565b6000614c408261508d565b151590565b90565b6001600160a01b031690565b6000614c408261507a565b60005b838110156150bf5781810151838201526020016150a7565b838111156150ce576000848401525b50505050565b6150dd8161507a565b81146150e857600080fd5b50565b6150dd8161508a56fea365627a7a723158204904fbb1198d4f4c63a608b70374136ae0d31a7e9e14261c29623f2d15813b6a6c6578706572696d656e74616cf564736f6c63430005100040
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.