Overview
ETH Balance
0 ETH
ETH Value
$0.00More Info
Private Name Tags
ContractCreator
Sponsored
Latest 25 from a total of 36,033 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Withdraw All Mar... | 122336288 | 96 days ago | IN | 0 ETH | 0.000010237998 | ||||
Close Position | 117619384 | 206 days ago | IN | 0 ETH | 0.0000004128 | ||||
Withdraw All Mar... | 117619322 | 206 days ago | IN | 0 ETH | 0.000002453877 | ||||
Withdraw All Mar... | 117028836 | 219 days ago | IN | 0 ETH | 0.000217249453 | ||||
Withdraw All Mar... | 117028735 | 219 days ago | IN | 0 ETH | 0.00021166497 | ||||
Transfer Margin | 113335026 | 305 days ago | IN | 0 ETH | 0.000044225227 | ||||
Transfer Margin | 112401068 | 326 days ago | IN | 0 ETH | 0.000031585131 | ||||
Liquidate Positi... | 108562556 | 415 days ago | IN | 0 ETH | 0.000080525411 | ||||
Liquidate Positi... | 108562538 | 415 days ago | IN | 0 ETH | 0.000083114065 | ||||
Liquidate Positi... | 108562536 | 415 days ago | IN | 0 ETH | 0.000082683426 | ||||
Liquidate Positi... | 108562478 | 415 days ago | IN | 0 ETH | 0.000091275031 | ||||
Liquidate Positi... | 108561566 | 415 days ago | IN | 0 ETH | 0.000111776382 | ||||
Liquidate Positi... | 108561558 | 415 days ago | IN | 0 ETH | 0.000104438668 | ||||
Liquidate Positi... | 108561552 | 415 days ago | IN | 0 ETH | 0.000097923749 | ||||
Liquidate Positi... | 108561548 | 415 days ago | IN | 0 ETH | 0.000099162344 | ||||
Liquidate Positi... | 108561542 | 415 days ago | IN | 0 ETH | 0.000101017352 | ||||
Liquidate Positi... | 108561539 | 415 days ago | IN | 0 ETH | 0.000100753595 | ||||
Liquidate Positi... | 108561536 | 415 days ago | IN | 0 ETH | 0.000100943845 | ||||
Liquidate Positi... | 108561531 | 415 days ago | IN | 0 ETH | 0.000099246643 | ||||
Liquidate Positi... | 108561528 | 415 days ago | IN | 0 ETH | 0.000098385505 | ||||
Liquidate Positi... | 108561525 | 415 days ago | IN | 0 ETH | 0.000098006869 | ||||
Liquidate Positi... | 108561514 | 415 days ago | IN | 0 ETH | 0.000099111292 | ||||
Liquidate Positi... | 108561511 | 415 days ago | IN | 0 ETH | 0.000100275801 | ||||
Liquidate Positi... | 108561508 | 415 days ago | IN | 0 ETH | 0.000100343939 | ||||
Liquidate Positi... | 108561506 | 415 days ago | IN | 0 ETH | 0.000100493535 |
Latest 25 internal transactions (View All)
Advanced mode:
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
107446018 | 441 days ago | 0 ETH | ||||
107446018 | 441 days ago | 0 ETH | ||||
107446018 | 441 days ago | 0 ETH | ||||
107446018 | 441 days ago | 0 ETH | ||||
107446018 | 441 days ago | 0 ETH | ||||
107317810 | 444 days ago | 0 ETH | ||||
107317810 | 444 days ago | 0 ETH | ||||
107317810 | 444 days ago | 0 ETH | ||||
107317810 | 444 days ago | 0 ETH | ||||
107317810 | 444 days ago | 0 ETH | ||||
107317810 | 444 days ago | 0 ETH | ||||
107317810 | 444 days ago | 0 ETH | ||||
107317810 | 444 days ago | 0 ETH | ||||
107317810 | 444 days ago | 0 ETH | ||||
107317810 | 444 days ago | 0 ETH | ||||
107317810 | 444 days ago | 0 ETH | ||||
107317810 | 444 days ago | 0 ETH | ||||
107317810 | 444 days ago | 0 ETH | ||||
107317810 | 444 days ago | 0 ETH | ||||
107317810 | 444 days ago | 0 ETH | ||||
107317810 | 444 days ago | 0 ETH | ||||
107317810 | 444 days ago | 0 ETH | ||||
107317810 | 444 days ago | 0 ETH | ||||
107317722 | 444 days ago | 0 ETH | ||||
107317722 | 444 days ago | 0 ETH |
Loading...
Loading
Contract Name:
FuturesMarket
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 optimistic.etherscan.io on 2022-03-17 */ /* ____ __ __ __ _ / __/__ __ ___ / /_ / / ___ / /_ (_)__ __ _\ \ / // // _ \/ __// _ \/ -_)/ __// / \ \ / /___/ \_, //_//_/\__//_//_/\__/ \__//_/ /_\_\ /___/ * Synthetix: FuturesMarket.sol * * Latest source (may be newer): https://github.com/Synthetixio/synthetix/blob/master/contracts/FuturesMarket.sol * Docs: https://docs.synthetix.io/contracts/FuturesMarket * * Contract Dependencies: * - FuturesMarketBase * - IAddressResolver * - IFuturesMarket * - IFuturesMarketBaseTypes * - MixinFuturesMarketSettings * - MixinFuturesNextPriceOrders * - MixinFuturesViews * - MixinResolver * - Owned * Libraries: * - SafeDecimalMath * - SafeMath * - SignedSafeDecimalMath * - SignedSafeMath * * 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 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 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 liquidateDelinquentAccount( address account, uint susdAmount, address liquidator ) external returns (uint totalRedeemed, uint amountToLiquidate); function setCurrentPeriodId(uint128 periodId) external; } // 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); } // 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); } // 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/MixinFuturesMarketSettings contract MixinFuturesMarketSettings is MixinResolver { /* ========== CONSTANTS ========== */ bytes32 internal constant SETTING_CONTRACT_NAME = "FuturesMarketSettings"; /* ---------- Parameter Names ---------- */ // Per-market settings bytes32 internal constant PARAMETER_TAKER_FEE = "takerFee"; bytes32 internal constant PARAMETER_MAKER_FEE = "makerFee"; bytes32 internal constant PARAMETER_TAKER_FEE_NEXT_PRICE = "takerFeeNextPrice"; bytes32 internal constant PARAMETER_MAKER_FEE_NEXT_PRICE = "makerFeeNextPrice"; bytes32 internal constant PARAMETER_NEXT_PRICE_CONFIRM_WINDOW = "nextPriceConfirmWindow"; bytes32 internal constant PARAMETER_MAX_LEVERAGE = "maxLeverage"; bytes32 internal constant PARAMETER_MAX_MARKET_VALUE = "maxMarketValueUSD"; bytes32 internal constant PARAMETER_MAX_FUNDING_RATE = "maxFundingRate"; bytes32 internal constant PARAMETER_MIN_SKEW_SCALE = "skewScaleUSD"; // Global settings // minimum liquidation fee payable to liquidator bytes32 internal constant SETTING_MIN_KEEPER_FEE = "futuresMinKeeperFee"; // liquidation fee basis points payed to liquidator bytes32 internal constant SETTING_LIQUIDATION_FEE_RATIO = "futuresLiquidationFeeRatio"; // liquidation buffer to prevent negative margin upon liquidation bytes32 internal constant SETTING_LIQUIDATION_BUFFER_RATIO = "futuresLiquidationBufferRatio"; bytes32 internal constant SETTING_MIN_INITIAL_MARGIN = "futuresMinInitialMargin"; /* ---------- Address Resolver Configuration ---------- */ bytes32 internal constant CONTRACT_FLEXIBLESTORAGE = "FlexibleStorage"; /* ========== CONSTRUCTOR ========== */ constructor(address _resolver) internal MixinResolver(_resolver) {} /* ========== VIEWS ========== */ 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)); } /* ---------- Internals ---------- */ function _parameter(bytes32 _marketKey, bytes32 key) internal view returns (uint value) { return _flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, keccak256(abi.encodePacked(_marketKey, key))); } function _takerFee(bytes32 _marketKey) internal view returns (uint) { return _parameter(_marketKey, PARAMETER_TAKER_FEE); } function _makerFee(bytes32 _marketKey) internal view returns (uint) { return _parameter(_marketKey, PARAMETER_MAKER_FEE); } function _takerFeeNextPrice(bytes32 _marketKey) internal view returns (uint) { return _parameter(_marketKey, PARAMETER_TAKER_FEE_NEXT_PRICE); } function _makerFeeNextPrice(bytes32 _marketKey) internal view returns (uint) { return _parameter(_marketKey, PARAMETER_MAKER_FEE_NEXT_PRICE); } function _nextPriceConfirmWindow(bytes32 _marketKey) internal view returns (uint) { return _parameter(_marketKey, PARAMETER_NEXT_PRICE_CONFIRM_WINDOW); } function _maxLeverage(bytes32 _marketKey) internal view returns (uint) { return _parameter(_marketKey, PARAMETER_MAX_LEVERAGE); } function _maxMarketValueUSD(bytes32 _marketKey) internal view returns (uint) { return _parameter(_marketKey, PARAMETER_MAX_MARKET_VALUE); } function _skewScaleUSD(bytes32 _marketKey) internal view returns (uint) { return _parameter(_marketKey, PARAMETER_MIN_SKEW_SCALE); } function _maxFundingRate(bytes32 _marketKey) internal view returns (uint) { return _parameter(_marketKey, PARAMETER_MAX_FUNDING_RATE); } function _minKeeperFee() internal view returns (uint) { return _flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_MIN_KEEPER_FEE); } function _liquidationFeeRatio() internal view returns (uint) { return _flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_LIQUIDATION_FEE_RATIO); } function _liquidationBufferRatio() internal view returns (uint) { return _flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_LIQUIDATION_BUFFER_RATIO); } function _minInitialMargin() internal view returns (uint) { return _flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_MIN_INITIAL_MARGIN); } } interface IFuturesMarketBaseTypes { /* ========== TYPES ========== */ enum Status { Ok, InvalidPrice, PriceOutOfBounds, CanLiquidate, CannotLiquidate, MaxMarketSizeExceeded, MaxLeverageExceeded, InsufficientMargin, NotPermitted, NilOrder, NoPositionOpen, PriceTooVolatile } // If margin/size are positive, the position is long; if negative then it is short. struct Position { uint64 id; uint64 lastFundingIndex; uint128 margin; uint128 lastPrice; int128 size; } // next-price order storage struct NextPriceOrder { int128 sizeDelta; // difference in position to pass to modifyPosition uint128 targetRoundId; // price oracle roundId using which price this order needs to exucted uint128 commitDeposit; // the commitDeposit paid upon submitting that needs to be refunded if order succeeds uint128 keeperDeposit; // the keeperDeposit paid upon submitting that needs to be paid / refunded on tx confirmation bytes32 trackingCode; // tracking code to emit on execution for volume source fee sharing } } /** * @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; } } // SPDX-License-Identifier: MIT /* The MIT License (MIT) Copyright (c) 2016-2020 zOS Global Limited 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 SOFTWARE. */ /* * When we upgrade to solidity v0.6.0 or above, we should be able to * just do import `"openzeppelin-solidity-3.0.0/contracts/math/SignedSafeMath.sol";` * wherever this is used. */ /** * @title SignedSafeMath * @dev Signed math operations with safety checks that revert on error. */ library SignedSafeMath { int256 private constant _INT256_MIN = -2**255; /** * @dev Returns the multiplication of two signed integers, reverting on * overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * * - Multiplication cannot overflow. */ function mul(int256 a, int256 b) internal pure returns (int256) { // 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-contracts/pull/522 if (a == 0) { return 0; } require(!(a == -1 && b == _INT256_MIN), "SignedSafeMath: multiplication overflow"); int256 c = a * b; require(c / a == b, "SignedSafeMath: multiplication overflow"); return c; } /** * @dev Returns the integer division of two signed 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(int256 a, int256 b) internal pure returns (int256) { require(b != 0, "SignedSafeMath: division by zero"); require(!(b == -1 && a == _INT256_MIN), "SignedSafeMath: division overflow"); int256 c = a / b; return c; } /** * @dev Returns the subtraction of two signed integers, reverting on * overflow. * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(int256 a, int256 b) internal pure returns (int256) { int256 c = a - b; require((b >= 0 && c <= a) || (b < 0 && c > a), "SignedSafeMath: subtraction overflow"); return c; } /** * @dev Returns the addition of two signed integers, reverting on * overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * * - Addition cannot overflow. */ function add(int256 a, int256 b) internal pure returns (int256) { int256 c = a + b; require((b >= 0 && c >= a) || (b < 0 && c < a), "SignedSafeMath: addition overflow"); return c; } } // TODO: Test suite // https://docs.synthetix.io/contracts/SignedSafeDecimalMath library SignedSafeDecimalMath { using SignedSafeMath for int; /* Number of decimal places in the representations. */ uint8 public constant decimals = 18; uint8 public constant highPrecisionDecimals = 27; /* The number representing 1.0. */ int public constant UNIT = int(10**uint(decimals)); /* The number representing 1.0 for higher fidelity numbers. */ int public constant PRECISE_UNIT = int(10**uint(highPrecisionDecimals)); int private constant UNIT_TO_HIGH_PRECISION_CONVERSION_FACTOR = int(10**uint(highPrecisionDecimals - decimals)); /** * @return Provides an interface to UNIT. */ function unit() external pure returns (int) { return UNIT; } /** * @return Provides an interface to PRECISE_UNIT. */ function preciseUnit() external pure returns (int) { return PRECISE_UNIT; } /** * @dev Rounds an input with an extra zero of precision, returning the result without the extra zero. * Half increments round away from zero; positive numbers at a half increment are rounded up, * while negative such numbers are rounded down. This behaviour is designed to be consistent with the * unsigned version of this library (SafeDecimalMath). */ function _roundDividingByTen(int valueTimesTen) private pure returns (int) { int increment; if (valueTimesTen % 10 >= 5) { increment = 10; } else if (valueTimesTen % 10 <= -5) { increment = -10; } return (valueTimesTen + increment) / 10; } /** * @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(int x, int y) internal pure returns (int) { /* 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( int x, int y, int precisionUnit ) private pure returns (int) { /* Divide by UNIT to remove the extra factor introduced by the product. */ int quotientTimesTen = x.mul(y) / (precisionUnit / 10); return _roundDividingByTen(quotientTimesTen); } /** * @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(int x, int y) internal pure returns (int) { 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(int x, int y) internal pure returns (int) { 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(int x, int y) internal pure returns (int) { /* 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( int x, int y, int precisionUnit ) private pure returns (int) { int resultTimesTen = x.mul(precisionUnit * 10).div(y); return _roundDividingByTen(resultTimesTen); } /** * @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(int x, int y) internal pure returns (int) { 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(int x, int y) internal pure returns (int) { return _divideDecimalRound(x, y, PRECISE_UNIT); } /** * @dev Convert a standard decimal representation to a high precision one. */ function decimalToPreciseDecimal(int i) internal pure returns (int) { return i.mul(UNIT_TO_HIGH_PRECISION_CONVERSION_FACTOR); } /** * @dev Convert a high precision decimal to a standard decimal representation. */ function preciseDecimalToDecimal(int i) internal pure returns (int) { int quotientTimesTen = i / (UNIT_TO_HIGH_PRECISION_CONVERSION_FACTOR / 10); return _roundDividingByTen(quotientTimesTen); } } // 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)); } } // https://docs.synthetix.io/contracts/source/interfaces/IExchangeCircuitBreaker interface IExchangeCircuitBreaker { // Views function exchangeRates() external view returns (address); function rateWithInvalid(bytes32 currencyKey) external view returns (uint, bool); function priceDeviationThresholdFactor() external view returns (uint); function isDeviationAboveThreshold(uint base, uint comparison) external view returns (bool); function lastExchangeRate(bytes32 currencyKey) external view returns (uint); // Mutative functions function resetLastExchangeRate(bytes32[] calldata currencyKeys) external; function rateWithBreakCircuit(bytes32 currencyKey) external returns (uint lastValidRate, bool circuitBroken); } // 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); } 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/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 ) 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/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; } // 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); } // Inheritance // Libraries // Internal references /* * Synthetic Futures * ================= * * Futures markets allow users leveraged exposure to an asset, long or short. * A user must post some margin in order to open a futures account, and profits/losses are * continually tallied against this margin. If a user's margin runs out, then their position is closed * by a liquidation keeper, which is rewarded with a flat fee extracted from the margin. * * The Synthetix debt pool is effectively the counterparty to each trade, so if a particular position * is in profit, then the debt pool pays by issuing sUSD into their margin account, * while if the position makes a loss then the debt pool burns sUSD from the margin, reducing the * debt load in the system. * * As the debt pool underwrites all positions, the debt-inflation risk to the system is proportional to the * long-short skew in the market. It is therefore in the interest of the system to reduce the skew. * To encourage the minimisation of the skew, each position is charged a funding rate, which increases with * the size of the skew. The funding rate is charged continuously, and positions on the heavier side of the * market are charged the current funding rate times the notional value of their position, while positions * on the lighter side are paid at the same rate to keep their positions open. * As the funding rate is the same (but negated) on both sides of the market, there is an excess quantity of * funding being charged, which is collected by the debt pool, and serves to reduce the system debt. * * To combat front-running, the system does not confirm a user's order until the next price is received from * the oracle. Therefore opening a position is a three stage procedure: depositing margin, submitting an order, * and waiting for that order to be confirmed. The last transaction is performed by a keeper, * once a price update is detected. * * The contract architecture is as follows: * * - FuturesMarket.sol: one of these exists per asset. Margin is maintained isolated per market. * * - FuturesMarketManager.sol: the manager keeps track of which markets exist, and is the main window between * futures markets and the rest of the system. It accumulates the total debt * over all markets, and issues and burns sUSD on each market's behalf. * * - FuturesMarketSettings.sol: Holds the settings for each market in the global FlexibleStorage instance used * by SystemSettings, and provides an interface to modify these values. Other than * the base asset, these settings determine the behaviour of each market. * See that contract for descriptions of the meanings of each setting. * * Each futures market and the manager operates behind a proxy, and for efficiency they communicate with one another * using their underlying implementations. * * Technical note: internal functions within the FuturesMarket contract assume the following: * * - prices passed into them are valid; * * - funding has already been recomputed up to the current time (hence unrecorded funding is nil); * * - the account being managed was not liquidated in the same transaction; */ interface IFuturesMarketManagerInternal { function issueSUSD(address account, uint amount) external; function burnSUSD(address account, uint amount) external returns (uint postReclamationAmount); function payFee(uint amount) external; } // https://docs.synthetix.io/contracts/source/contracts/FuturesMarket contract FuturesMarketBase is MixinFuturesMarketSettings, IFuturesMarketBaseTypes { /* ========== LIBRARIES ========== */ using SafeMath for uint; using SignedSafeMath for int; using SignedSafeDecimalMath for int; using SafeDecimalMath for uint; /* ========== CONSTANTS ========== */ // This is the same unit as used inside `SignedSafeDecimalMath`. int private constant _UNIT = int(10**uint(18)); //slither-disable-next-line naming-convention bytes32 internal constant sUSD = "sUSD"; /* ========== STATE VARIABLES ========== */ // The market identifier in the futures system (manager + settings). Multiple markets can co-exist // for the same asset in order to allow migrations. bytes32 public marketKey; // The asset being traded in this market. This should be a valid key into the ExchangeRates contract. bytes32 public baseAsset; // The total number of base units in long and short positions. uint128 public marketSize; /* * The net position in base units of the whole market. * When this is positive, longs outweigh shorts. When it is negative, shorts outweigh longs. */ int128 public marketSkew; /* * The funding sequence allows constant-time calculation of the funding owed to a given position. * Each entry in the sequence holds the net funding accumulated per base unit since the market was created. * Then to obtain the net funding over a particular interval, subtract the start point's sequence entry * from the end point's sequence entry. * Positions contain the funding sequence entry at the time they were confirmed; so to compute * the net funding on a given position, obtain from this sequence the net funding per base unit * since the position was confirmed and multiply it by the position size. */ uint32 public fundingLastRecomputed; int128[] public fundingSequence; /* * Each user's position. Multiple positions can always be merged, so each user has * only have one position at a time. */ mapping(address => Position) public positions; /* * This holds the value: sum_{p in positions}{p.margin - p.size * (p.lastPrice + fundingSequence[p.lastFundingIndex])} * Then marketSkew * (price + _nextFundingEntry()) + _entryDebtCorrection yields the total system debt, * which is equivalent to the sum of remaining margins in all positions. */ int128 internal _entryDebtCorrection; // This increments for each position; zero reflects a position that does not exist. uint64 internal _nextPositionId = 1; // Holds the revert message for each type of error. mapping(uint8 => string) internal _errorMessages; /* ---------- Address Resolver Configuration ---------- */ bytes32 internal constant CONTRACT_CIRCUIT_BREAKER = "ExchangeCircuitBreaker"; bytes32 internal constant CONTRACT_EXCHANGER = "Exchanger"; bytes32 internal constant CONTRACT_FUTURESMARKETMANAGER = "FuturesMarketManager"; bytes32 internal constant CONTRACT_FUTURESMARKETSETTINGS = "FuturesMarketSettings"; bytes32 internal constant CONTRACT_SYSTEMSTATUS = "SystemStatus"; // convenience struct for passing params between position modification helper functions struct TradeParams { int sizeDelta; uint price; uint takerFee; uint makerFee; bytes32 trackingCode; // optional tracking code for volume source fee sharing } /* ========== CONSTRUCTOR ========== */ constructor( address _resolver, bytes32 _baseAsset, bytes32 _marketKey ) public MixinFuturesMarketSettings(_resolver) { baseAsset = _baseAsset; marketKey = _marketKey; // Initialise the funding sequence with 0 initially accrued, so that the first usable funding index is 1. fundingSequence.push(0); // Set up the mapping between error codes and their revert messages. _errorMessages[uint8(Status.InvalidPrice)] = "Invalid price"; _errorMessages[uint8(Status.PriceOutOfBounds)] = "Price out of acceptable range"; _errorMessages[uint8(Status.CanLiquidate)] = "Position can be liquidated"; _errorMessages[uint8(Status.CannotLiquidate)] = "Position cannot be liquidated"; _errorMessages[uint8(Status.MaxMarketSizeExceeded)] = "Max market size exceeded"; _errorMessages[uint8(Status.MaxLeverageExceeded)] = "Max leverage exceeded"; _errorMessages[uint8(Status.InsufficientMargin)] = "Insufficient margin"; _errorMessages[uint8(Status.NotPermitted)] = "Not permitted by this address"; _errorMessages[uint8(Status.NilOrder)] = "Cannot submit empty order"; _errorMessages[uint8(Status.NoPositionOpen)] = "No position open"; _errorMessages[uint8(Status.PriceTooVolatile)] = "Price too volatile"; } /* ========== VIEWS ========== */ /* ---------- External Contracts ---------- */ function resolverAddressesRequired() public view returns (bytes32[] memory addresses) { bytes32[] memory existingAddresses = MixinFuturesMarketSettings.resolverAddressesRequired(); bytes32[] memory newAddresses = new bytes32[](5); newAddresses[0] = CONTRACT_EXCHANGER; newAddresses[1] = CONTRACT_CIRCUIT_BREAKER; newAddresses[2] = CONTRACT_FUTURESMARKETMANAGER; newAddresses[3] = CONTRACT_FUTURESMARKETSETTINGS; newAddresses[4] = CONTRACT_SYSTEMSTATUS; addresses = combineArrays(existingAddresses, newAddresses); } function _exchangeCircuitBreaker() internal view returns (IExchangeCircuitBreaker) { return IExchangeCircuitBreaker(requireAndGetAddress(CONTRACT_CIRCUIT_BREAKER)); } function _exchanger() internal view returns (IExchanger) { return IExchanger(requireAndGetAddress(CONTRACT_EXCHANGER)); } function _systemStatus() internal view returns (ISystemStatus) { return ISystemStatus(requireAndGetAddress(CONTRACT_SYSTEMSTATUS)); } function _manager() internal view returns (IFuturesMarketManagerInternal) { return IFuturesMarketManagerInternal(requireAndGetAddress(CONTRACT_FUTURESMARKETMANAGER)); } function _settings() internal view returns (address) { return requireAndGetAddress(CONTRACT_FUTURESMARKETSETTINGS); } /* ---------- Market Details ---------- */ /* * The size of the skew relative to the size of the market skew scaler. * This value can be outside of [-1, 1] values. * Scaler used for skew is at skewScaleUSD to prevent extreme funding rates for small markets. */ function _proportionalSkew(uint price) internal view returns (int) { // marketSize is in baseAsset units so we need to convert from USD units require(price > 0, "price can't be zero"); uint skewScaleBaseAsset = _skewScaleUSD(marketKey).divideDecimal(price); require(skewScaleBaseAsset != 0, "skewScale is zero"); // don't divide by zero return int(marketSkew).divideDecimal(int(skewScaleBaseAsset)); } function _currentFundingRate(uint price) internal view returns (int) { int maxFundingRate = int(_maxFundingRate(marketKey)); // Note the minus sign: funding flows in the opposite direction to the skew. return _min(_max(-_UNIT, -_proportionalSkew(price)), _UNIT).multiplyDecimal(maxFundingRate); } function _unrecordedFunding(uint price) internal view returns (int funding) { int elapsed = int(block.timestamp.sub(fundingLastRecomputed)); // The current funding rate, rescaled to a percentage per second. int currentFundingRatePerSecond = _currentFundingRate(price) / 1 days; return currentFundingRatePerSecond.multiplyDecimal(int(price)).mul(elapsed); } /* * The new entry in the funding sequence, appended when funding is recomputed. It is the sum of the * last entry and the unrecorded funding, so the sequence accumulates running total over the market's lifetime. */ function _nextFundingEntry(uint price) internal view returns (int funding) { return int(fundingSequence[_latestFundingIndex()]).add(_unrecordedFunding(price)); } function _netFundingPerUnit(uint startIndex, uint price) internal view returns (int) { // Compute the net difference between start and end indices. return _nextFundingEntry(price).sub(fundingSequence[startIndex]); } /* ---------- Position Details ---------- */ /* * Determines whether a change in a position's size would violate the max market value constraint. */ function _orderSizeTooLarge( uint maxSize, int oldSize, int newSize ) internal view returns (bool) { // Allow users to reduce an order no matter the market conditions. if (_sameSide(oldSize, newSize) && _abs(newSize) <= _abs(oldSize)) { return false; } // Either the user is flipping sides, or they are increasing an order on the same side they're already on; // we check that the side of the market their order is on would not break the limit. int newSkew = int(marketSkew).sub(oldSize).add(newSize); int newMarketSize = int(marketSize).sub(_signedAbs(oldSize)).add(_signedAbs(newSize)); int newSideSize; if (0 < newSize) { // long case: marketSize + skew // = (|longSize| + |shortSize|) + (longSize + shortSize) // = 2 * longSize newSideSize = newMarketSize.add(newSkew); } else { // short case: marketSize - skew // = (|longSize| + |shortSize|) - (longSize + shortSize) // = 2 * -shortSize newSideSize = newMarketSize.sub(newSkew); } // newSideSize still includes an extra factor of 2 here, so we will divide by 2 in the actual condition if (maxSize < _abs(newSideSize.div(2))) { return true; } return false; } function _notionalValue(int positionSize, uint price) internal pure returns (int value) { return positionSize.multiplyDecimal(int(price)); } function _profitLoss(Position memory position, uint price) internal pure returns (int pnl) { int priceShift = int(price).sub(int(position.lastPrice)); return int(position.size).multiplyDecimal(priceShift); } function _accruedFunding(Position memory position, uint price) internal view returns (int funding) { uint lastModifiedIndex = position.lastFundingIndex; if (lastModifiedIndex == 0) { return 0; // The position does not exist -- no funding. } int net = _netFundingPerUnit(lastModifiedIndex, price); return int(position.size).multiplyDecimal(net); } /* * The initial margin of a position, plus any PnL and funding it has accrued. The resulting value may be negative. */ function _marginPlusProfitFunding(Position memory position, uint price) internal view returns (int) { int funding = _accruedFunding(position, price); return int(position.margin).add(_profitLoss(position, price)).add(funding); } /* * The value in a position's margin after a deposit or withdrawal, accounting for funding and profit. * If the resulting margin would be negative or below the liquidation threshold, an appropriate error is returned. * If the result is not an error, callers of this function that use it to update a position's margin * must ensure that this is accompanied by a corresponding debt correction update, as per `_applyDebtCorrection`. */ function _recomputeMarginWithDelta( Position memory position, uint price, int marginDelta ) internal view returns (uint margin, Status statusCode) { int newMargin = _marginPlusProfitFunding(position, price).add(marginDelta); if (newMargin < 0) { return (0, Status.InsufficientMargin); } uint uMargin = uint(newMargin); int positionSize = int(position.size); // minimum margin beyond which position can be liquidated uint lMargin = _liquidationMargin(positionSize, price); if (positionSize != 0 && uMargin <= lMargin) { return (uMargin, Status.CanLiquidate); } return (uMargin, Status.Ok); } function _remainingMargin(Position memory position, uint price) internal view returns (uint) { int remaining = _marginPlusProfitFunding(position, price); // If the margin went past zero, the position should have been liquidated - return zero remaining margin. return uint(_max(0, remaining)); } function _accessibleMargin(Position memory position, uint price) internal view returns (uint) { // Ugly solution to rounding safety: leave up to an extra tenth of a cent in the account/leverage // This should guarantee that the value returned here can always been withdrawn, but there may be // a little extra actually-accessible value left over, depending on the position size and margin. uint milli = uint(_UNIT / 1000); int maxLeverage = int(_maxLeverage(marketKey).sub(milli)); uint inaccessible = _abs(_notionalValue(position.size, price).divideDecimal(maxLeverage)); // If the user has a position open, we'll enforce a min initial margin requirement. if (0 < inaccessible) { uint minInitialMargin = _minInitialMargin(); if (inaccessible < minInitialMargin) { inaccessible = minInitialMargin; } inaccessible = inaccessible.add(milli); } uint remaining = _remainingMargin(position, price); if (remaining <= inaccessible) { return 0; } return remaining.sub(inaccessible); } /** * The fee charged from the margin during liquidation. Fee is proportional to position size * but is at least the _minKeeperFee() of sUSD to prevent underincentivising * liquidations of small positions. * @param positionSize size of position in fixed point decimal baseAsset units * @param price price of single baseAsset unit in sUSD fixed point decimal units * @return lFee liquidation fee to be paid to liquidator in sUSD fixed point decimal units */ function _liquidationFee(int positionSize, uint price) internal view returns (uint lFee) { // size * price * fee-ratio uint proportionalFee = _abs(positionSize).multiplyDecimal(price).multiplyDecimal(_liquidationFeeRatio()); uint minFee = _minKeeperFee(); // max(proportionalFee, minFee) - to prevent not incentivising liquidations enough return proportionalFee > minFee ? proportionalFee : minFee; // not using _max() helper because it's for signed ints } /** * The minimal margin at which liquidation can happen. Is the sum of liquidationBuffer and liquidationFee * @param positionSize size of position in fixed point decimal baseAsset units * @param price price of single baseAsset unit in sUSD fixed point decimal units * @return lMargin liquidation margin to maintain in sUSD fixed point decimal units * @dev The liquidation margin contains a buffer that is proportional to the position * size. The buffer should prevent liquidation happenning at negative margin (due to next price being worse) * so that stakers would not leak value to liquidators through minting rewards that are not from the * account's margin. */ function _liquidationMargin(int positionSize, uint price) internal view returns (uint lMargin) { uint liquidationBuffer = _abs(positionSize).multiplyDecimal(price).multiplyDecimal(_liquidationBufferRatio()); return liquidationBuffer.add(_liquidationFee(positionSize, price)); } function _canLiquidate(Position memory position, uint price) internal view returns (bool) { // No liquidating empty positions. if (position.size == 0) { return false; } return _remainingMargin(position, price) <= _liquidationMargin(int(position.size), price); } function _currentLeverage( Position memory position, uint price, uint remainingMargin_ ) internal pure returns (int leverage) { // No position is open, or it is ready to be liquidated; leverage goes to nil if (remainingMargin_ == 0) { return 0; } return _notionalValue(position.size, price).divideDecimal(int(remainingMargin_)); } function _orderFee(TradeParams memory params, uint dynamicFeeRate) internal view returns (uint fee) { // usd value of the difference in position int notionalDiff = params.sizeDelta.multiplyDecimal(int(params.price)); // If the order is submitted on the same side as the skew (increasing it) - the taker fee is charged. // Otherwise if the order is opposite to the skew, the maker fee is charged. // the case where the order flips the skew is ignored for simplicity due to being negligible // in both size of effect and frequency of occurrence uint staticRate = _sameSide(notionalDiff, marketSkew) ? params.takerFee : params.makerFee; uint feeRate = staticRate.add(dynamicFeeRate); return _abs(notionalDiff.multiplyDecimal(int(feeRate))); } /// Uses the exchanger to get the dynamic fee (SIP-184) for trading from sUSD to baseAsset /// this assumes dynamic fee is symmetric in direction of trade. /// @dev this is a pretty expensive action in terms of execution gas as it queries a lot /// of past rates from oracle. Shoudn't be much of an issue on a rollup though. function _dynamicFeeRate() internal view returns (uint feeRate, bool tooVolatile) { return _exchanger().dynamicFeeRateForExchange(sUSD, baseAsset); } function _latestFundingIndex() internal view returns (uint) { return fundingSequence.length.sub(1); // at least one element is pushed in constructor } function _postTradeDetails(Position memory oldPos, TradeParams memory params) internal view returns ( Position memory newPosition, uint fee, Status tradeStatus ) { // Reverts if the user is trying to submit a size-zero order. if (params.sizeDelta == 0) { return (oldPos, 0, Status.NilOrder); } // The order is not submitted if the user's existing position needs to be liquidated. if (_canLiquidate(oldPos, params.price)) { return (oldPos, 0, Status.CanLiquidate); } // get the dynamic fee rate SIP-184 (uint dynamicFeeRate, bool tooVolatile) = _dynamicFeeRate(); if (tooVolatile) { return (oldPos, 0, Status.PriceTooVolatile); } // calculate the total fee for exchange fee = _orderFee(params, dynamicFeeRate); // Deduct the fee. // It is an error if the realised margin minus the fee is negative or subject to liquidation. (uint newMargin, Status status) = _recomputeMarginWithDelta(oldPos, params.price, -int(fee)); if (_isError(status)) { return (oldPos, 0, status); } // construct new position Position memory newPos = Position({ id: oldPos.id, lastFundingIndex: uint64(_latestFundingIndex()), margin: uint128(newMargin), lastPrice: uint128(params.price), size: int128(int(oldPos.size).add(params.sizeDelta)) }); // always allow to decrease a position, otherwise a margin of minInitialMargin can never // decrease a position as the price goes against them. // we also add the paid out fee for the minInitialMargin because otherwise minInitialMargin // is never the actual minMargin, because the first trade will always deduct // a fee (so the margin that otherwise would need to be transferred would have to include the future // fee as well, making the UX and definition of min-margin confusing). bool positionDecreasing = _sameSide(oldPos.size, newPos.size) && _abs(newPos.size) < _abs(oldPos.size); if (!positionDecreasing) { // minMargin + fee <= margin is equivalent to minMargin <= margin - fee // except that we get a nicer error message if fee > margin, rather than arithmetic overflow. if (uint(newPos.margin).add(fee) < _minInitialMargin()) { return (oldPos, 0, Status.InsufficientMargin); } } // check that new position margin is above liquidation margin // (above, in _recomputeMarginWithDelta() we checked the old position, here we check the new one) // Liquidation margin is considered without a fee, because it wouldn't make sense to allow // a trade that will make the position liquidatable. if (newMargin <= _liquidationMargin(newPos.size, params.price)) { return (newPos, 0, Status.CanLiquidate); } // Check that the maximum leverage is not exceeded when considering new margin including the paid fee. // The paid fee is considered for the benefit of UX of allowed max leverage, otherwise, the actual // max leverage is always below the max leverage parameter since the fee paid for a trade reduces the margin. // We'll allow a little extra headroom for rounding errors. { // stack too deep int leverage = int(newPos.size).multiplyDecimal(int(params.price)).divideDecimal(int(newMargin.add(fee))); if (_maxLeverage(marketKey).add(uint(_UNIT) / 100) < _abs(leverage)) { return (oldPos, 0, Status.MaxLeverageExceeded); } } // Check that the order isn't too large for the market. // Allow a bit of extra value in case of rounding errors. if ( _orderSizeTooLarge( uint(int(_maxMarketValueUSD(marketKey).add(100 * uint(_UNIT))).divideDecimal(int(params.price))), oldPos.size, newPos.size ) ) { return (oldPos, 0, Status.MaxMarketSizeExceeded); } return (newPos, fee, Status.Ok); } /* ---------- 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)); } function _max(int x, int y) internal pure returns (int) { return x < y ? y : x; } function _min(int x, int y) internal pure returns (int) { return x < y ? x : y; } // True if and only if two positions a and b are on the same side of the market; // that is, if they have the same sign, or either of them is zero. function _sameSide(int a, int b) internal pure returns (bool) { return (a >= 0) == (b >= 0); } /* * True if and only if the given status indicates an error. */ function _isError(Status status) internal pure returns (bool) { return status != Status.Ok; } /* * Revert with an appropriate message if the first argument is true. */ function _revertIfError(bool isError, Status status) internal view { if (isError) { revert(_errorMessages[uint8(status)]); } } /* * Revert with an appropriate message if the input is an error. */ function _revertIfError(Status status) internal view { if (_isError(status)) { revert(_errorMessages[uint8(status)]); } } /* * The current base price from the oracle, and whether that price was invalid. Zero prices count as invalid. * Public because used both externally and internally */ function assetPrice() public view returns (uint price, bool invalid) { (price, invalid) = _exchangeCircuitBreaker().rateWithInvalid(baseAsset); // Ensure we catch uninitialised rates or suspended state / synth invalid = invalid || price == 0 || _systemStatus().synthSuspended(baseAsset); return (price, invalid); } /* ========== MUTATIVE FUNCTIONS ========== */ /* ---------- Market Operations ---------- */ /* * The current base price, reverting if it is invalid, or if system or synth is suspended. * This is mutative because the circuit breaker stores the last price on every invocation. */ function _assetPriceRequireSystemChecks() internal returns (uint) { // check that futures market isn't suspended, revert with appropriate message _systemStatus().requireFuturesMarketActive(marketKey); // asset and market may be different // check that synth is active, and wasn't suspended, revert with appropriate message _systemStatus().requireSynthActive(baseAsset); // check if circuit breaker if price is within deviation tolerance and system & synth is active // note: rateWithBreakCircuit (mutative) is used here instead of rateWithInvalid (view). This is // despite reverting immediately after if circuit is broken, which may seem silly. // This is in order to persist last-rate in exchangeCircuitBreaker in the happy case // because last-rate is what used for measuring the deviation for subsequent trades. (uint price, bool circuitBroken) = _exchangeCircuitBreaker().rateWithBreakCircuit(baseAsset); // revert if price is invalid or circuit was broken // note: we revert here, which means that circuit is not really broken (is not persisted), this is // because the futures methods and interface are designed for reverts, and do not support no-op // return values. _revertIfError(circuitBroken, Status.InvalidPrice); return price; } function _recomputeFunding(uint price) internal returns (uint lastIndex) { uint sequenceLengthBefore = fundingSequence.length; int funding = _nextFundingEntry(price); fundingSequence.push(int128(funding)); fundingLastRecomputed = uint32(block.timestamp); emit FundingRecomputed(funding, sequenceLengthBefore, fundingLastRecomputed); return sequenceLengthBefore; } /** * Pushes a new entry to the funding sequence at the current price and funding rate. * @dev Admin only method accessible to FuturesMarketSettings. This is admin only because: * - When system parameters change, funding should be recomputed, but system may be paused * during that time for any reason, so this method needs to work even if system is paused. * But in that case, it shouldn't be accessible to external accounts. */ function recomputeFunding() external returns (uint lastIndex) { // only FuturesMarketSettings is allowed to use this method _revertIfError(msg.sender != _settings(), Status.NotPermitted); // This method is the only mutative method that uses the view _assetPrice() // and not the mutative _assetPriceRequireSystemChecks() that reverts on system flags. // This is because this method is used by system settings when changing funding related // parameters, so needs to function even when system / market is paused. E.g. to facilitate // market migration. (uint price, bool invalid) = assetPrice(); // A check for a valid price is still in place, to ensure that a system settings action // doesn't take place when the price is invalid (e.g. some oracle issue). require(!invalid, "Invalid price"); return _recomputeFunding(price); } /* * The impact of a given position on the debt correction. */ function _positionDebtCorrection(Position memory position) internal view returns (int) { /** This method only returns the correction term for the debt calculation of the position, and not it's debt. This is needed for keeping track of the _marketDebt() in an efficient manner to allow O(1) marketDebt calculation in _marketDebt(). Explanation of the full market debt calculation from the SIP https://sips.synthetix.io/sips/sip-80/: The overall market debt is the sum of the remaining margin in all positions. The intuition is that the debt of a single position is the value withdrawn upon closing that position. single position remaining margin = initial-margin + profit-loss + accrued-funding = = initial-margin + q * (price - last-price) + q * funding-accrued-per-unit = initial-margin + q * price - q * last-price + q * (funding - initial-funding) Total debt = sum ( position remaining margins ) = sum ( initial-margin + q * price - q * last-price + q * (funding - initial-funding) ) = sum( q * price ) + sum( q * funding ) + sum( initial-margin - q * last-price - q * initial-funding ) = skew * price + skew * funding + sum( initial-margin - q * ( last-price + initial-funding ) ) = skew (price + funding) + sum( initial-margin - q * ( last-price + initial-funding ) ) The last term: sum( initial-margin - q * ( last-price + initial-funding ) ) being the position debt correction that is tracked with each position change using this method. The first term and the full debt calculation using current skew, price, and funding is calculated globally in _marketDebt(). */ return int(position.margin).sub( int(position.size).multiplyDecimal(int(position.lastPrice).add(fundingSequence[position.lastFundingIndex])) ); } function _marketDebt(uint price) internal view returns (uint) { // short circuit and also convenient during setup if (marketSkew == 0 && _entryDebtCorrection == 0) { // if these are 0, the resulting calculation is necessarily zero as well return 0; } // see comment explaining this calculation in _positionDebtCorrection() int priceWithFunding = int(price).add(_nextFundingEntry(price)); int totalDebt = int(marketSkew).multiplyDecimal(priceWithFunding).add(_entryDebtCorrection); return uint(_max(totalDebt, 0)); } /* * Alter the debt correction to account for the net result of altering a position. */ function _applyDebtCorrection(Position memory newPosition, Position memory oldPosition) internal { int newCorrection = _positionDebtCorrection(newPosition); int oldCorrection = _positionDebtCorrection(oldPosition); _entryDebtCorrection = int128(int(_entryDebtCorrection).add(newCorrection).sub(oldCorrection)); } function _transferMargin( int marginDelta, uint price, address sender ) internal { // Transfer no tokens if marginDelta is 0 uint absDelta = _abs(marginDelta); if (marginDelta > 0) { // A positive margin delta corresponds to a deposit, which will be burnt from their // sUSD balance and credited to their margin account. // Ensure we handle reclamation when burning tokens. uint postReclamationAmount = _manager().burnSUSD(sender, absDelta); if (postReclamationAmount != absDelta) { // If balance was insufficient, the actual delta will be smaller marginDelta = int(postReclamationAmount); } } else if (marginDelta < 0) { // A negative margin delta corresponds to a withdrawal, which will be minted into // their sUSD balance, and debited from their margin account. _manager().issueSUSD(sender, absDelta); } else { // Zero delta is a no-op return; } Position storage position = positions[sender]; _updatePositionMargin(position, price, marginDelta); emit MarginTransferred(sender, marginDelta); emit PositionModified(position.id, sender, position.margin, position.size, 0, price, _latestFundingIndex(), 0); } // updates the stored position margin in place (on the stored position) function _updatePositionMargin( Position storage position, uint price, int marginDelta ) internal { Position memory oldPosition = position; // Determine new margin, ensuring that the result is positive. (uint margin, Status status) = _recomputeMarginWithDelta(oldPosition, price, marginDelta); _revertIfError(status); // Update the debt correction. int positionSize = position.size; uint fundingIndex = _latestFundingIndex(); _applyDebtCorrection( Position(0, uint64(fundingIndex), uint128(margin), uint128(price), int128(positionSize)), Position(0, position.lastFundingIndex, position.margin, position.lastPrice, int128(positionSize)) ); // Update the account's position with the realised margin. position.margin = uint128(margin); // We only need to update their funding/PnL details if they actually have a position open if (positionSize != 0) { position.lastPrice = uint128(price); position.lastFundingIndex = uint64(fundingIndex); // The user can always decrease their margin if they have no position, or as long as: // * they have sufficient margin to do so // * the resulting margin would not be lower than the liquidation margin or min initial margin // * the resulting leverage is lower than the maximum leverage if (marginDelta < 0) { _revertIfError( (margin < _minInitialMargin()) || (margin <= _liquidationMargin(position.size, price)) || (_maxLeverage(marketKey) < _abs(_currentLeverage(position, price, margin))), Status.InsufficientMargin ); } } } /* * Alter the amount of margin in a position. A positive input triggers a deposit; a negative one, a * withdrawal. The margin will be burnt or issued directly into/out of the caller's sUSD wallet. * Reverts on deposit if the caller lacks a sufficient sUSD balance. * Reverts on withdrawal if the amount to be withdrawn would expose an open position to liquidation. */ function transferMargin(int marginDelta) external { uint price = _assetPriceRequireSystemChecks(); _recomputeFunding(price); _transferMargin(marginDelta, price, msg.sender); } /* * Withdraws all accessible margin in a position. This will leave some remaining margin * in the account if the caller has a position open. Equivalent to `transferMargin(-accessibleMargin(sender))`. */ function withdrawAllMargin() external { address sender = msg.sender; uint price = _assetPriceRequireSystemChecks(); _recomputeFunding(price); int marginDelta = -int(_accessibleMargin(positions[sender], price)); _transferMargin(marginDelta, price, sender); } function _trade(address sender, TradeParams memory params) internal { Position storage position = positions[sender]; Position memory oldPosition = position; // Compute the new position after performing the trade (Position memory newPosition, uint fee, Status status) = _postTradeDetails(oldPosition, params); _revertIfError(status); // Update the aggregated market size and skew with the new order size marketSkew = int128(int(marketSkew).add(newPosition.size).sub(oldPosition.size)); marketSize = uint128(uint(marketSize).add(_abs(newPosition.size)).sub(_abs(oldPosition.size))); // Send the fee to the fee pool if (0 < fee) { _manager().payFee(fee); // emit tracking code event if (params.trackingCode != bytes32(0)) { emit FuturesTracking(params.trackingCode, baseAsset, marketKey, params.sizeDelta, fee); } } // Update the margin, and apply the resulting debt correction position.margin = newPosition.margin; _applyDebtCorrection(newPosition, oldPosition); // Record the trade uint64 id = oldPosition.id; uint fundingIndex = _latestFundingIndex(); if (newPosition.size == 0) { // If the position is being closed, we no longer need to track these details. delete position.id; delete position.size; delete position.lastPrice; delete position.lastFundingIndex; } else { if (oldPosition.size == 0) { // New positions get new ids. id = _nextPositionId; _nextPositionId += 1; } position.id = id; position.size = newPosition.size; position.lastPrice = uint128(params.price); position.lastFundingIndex = uint64(fundingIndex); } // emit the modification event emit PositionModified( id, sender, newPosition.margin, newPosition.size, params.sizeDelta, params.price, fundingIndex, fee ); } /* * Adjust the sender's position size. * Reverts if the resulting position is too large, outside the max leverage, or is liquidating. */ function modifyPosition(int sizeDelta) external { _modifyPosition(sizeDelta, bytes32(0)); } /* * Same as modifyPosition, but emits an event with the passed tracking code to * allow offchain calculations for fee sharing with originating integrations */ function modifyPositionWithTracking(int sizeDelta, bytes32 trackingCode) external { _modifyPosition(sizeDelta, trackingCode); } function _modifyPosition(int sizeDelta, bytes32 trackingCode) internal { uint price = _assetPriceRequireSystemChecks(); _recomputeFunding(price); _trade( msg.sender, TradeParams({ sizeDelta: sizeDelta, price: price, takerFee: _takerFee(marketKey), makerFee: _makerFee(marketKey), trackingCode: trackingCode }) ); } /* * Submit an order to close a position. */ function closePosition() external { _closePosition(bytes32(0)); } /// Same as closePosition, but emits an even with the trackingCode for volume source fee sharing function closePositionWithTracking(bytes32 trackingCode) external { _closePosition(trackingCode); } function _closePosition(bytes32 trackingCode) internal { int size = positions[msg.sender].size; _revertIfError(size == 0, Status.NoPositionOpen); uint price = _assetPriceRequireSystemChecks(); _recomputeFunding(price); _trade( msg.sender, TradeParams({ sizeDelta: -size, price: price, takerFee: _takerFee(marketKey), makerFee: _makerFee(marketKey), trackingCode: trackingCode }) ); } function _liquidatePosition( address account, address liquidator, uint price ) internal { Position storage position = positions[account]; // get remaining margin for sending any leftover buffer to fee pool uint remMargin = _remainingMargin(position, price); // Record updates to market size and debt. int positionSize = position.size; uint positionId = position.id; marketSkew = int128(int(marketSkew).sub(positionSize)); marketSize = uint128(uint(marketSize).sub(_abs(positionSize))); uint fundingIndex = _latestFundingIndex(); _applyDebtCorrection( Position(0, uint64(fundingIndex), 0, uint128(price), 0), Position(0, position.lastFundingIndex, position.margin, position.lastPrice, int128(positionSize)) ); // Close the position itself. delete positions[account]; // Issue the reward to the liquidator. uint liqFee = _liquidationFee(positionSize, price); _manager().issueSUSD(liquidator, liqFee); emit PositionModified(positionId, account, 0, 0, 0, price, fundingIndex, 0); emit PositionLiquidated(positionId, account, liquidator, positionSize, price, liqFee); // Send any positive margin buffer to the fee pool if (remMargin > liqFee) { _manager().payFee(remMargin.sub(liqFee)); } } /* * Liquidate a position if its remaining margin is below the liquidation fee. This succeeds if and only if * `canLiquidate(account)` is true, and reverts otherwise. * Upon liquidation, the position will be closed, and the liquidation fee minted into the liquidator's account. */ function liquidatePosition(address account) external { uint price = _assetPriceRequireSystemChecks(); _recomputeFunding(price); _revertIfError(!_canLiquidate(positions[account], price), Status.CannotLiquidate); _liquidatePosition(account, msg.sender, price); } /* ========== EVENTS ========== */ event MarginTransferred(address indexed account, int marginDelta); event PositionModified( uint indexed id, address indexed account, uint margin, int size, int tradeSize, uint lastPrice, uint fundingIndex, uint fee ); event PositionLiquidated( uint indexed id, address indexed account, address indexed liquidator, int size, uint price, uint fee ); event FundingRecomputed(int funding, uint index, uint timestamp); event FuturesTracking(bytes32 indexed trackingCode, bytes32 baseAsset, bytes32 marketKey, int sizeDelta, uint fee); } // Inheritance /** Mixin that implements NextPrice orders mechanism for the futures market. The purpose of the mechanism is to allow reduced fees for trades that commit to next price instead of current price. Specifically, this should serve funding rate arbitrageurs, such that funding rate arb is profitable for smaller skews. This in turn serves the protocol by reducing the skew, and so the risk to the debt pool, and funding rate for traders. The fees can be reduced when comitting to next price, because front-running (MEV and oracle delay) is less of a risk when committing to next price. The relative complexity of the mechanism is due to having to enforce the "commitment" to the trade without either introducing free (or cheap) optionality to cause cancellations, and without large sacrifices to the UX / risk of the traders (e.g. blocking all actions, or penalizing failures too much). */ contract MixinFuturesNextPriceOrders is FuturesMarketBase { /// @dev Holds a mapping of accounts to orders. Only one order per account is supported mapping(address => NextPriceOrder) public nextPriceOrders; ///// Mutative methods /** * @notice submits an order to be filled at a price of the next oracle update. * Reverts if a previous order still exists (wasn't executed or cancelled). * Reverts if the order cannot be filled at current price to prevent witholding commitFee for * incorrectly submitted orders (that cannot be filled). * @param sizeDelta size in baseAsset (notional terms) of the order, similar to `modifyPosition` interface */ function submitNextPriceOrder(int sizeDelta) external { _submitNextPriceOrder(sizeDelta, bytes32(0)); } /// same as submitNextPriceOrder but emits an event with the tracking code /// to allow volume source fee sharing for integrations function submitNextPriceOrderWithTracking(int sizeDelta, bytes32 trackingCode) external { _submitNextPriceOrder(sizeDelta, trackingCode); } function _submitNextPriceOrder(int sizeDelta, bytes32 trackingCode) internal { // check that a previous order doesn't exist require(nextPriceOrders[msg.sender].sizeDelta == 0, "previous order exists"); // storage position as it's going to be modified to deduct commitFee and keeperFee Position storage position = positions[msg.sender]; // to prevent submitting bad orders in good faith and being charged commitDeposit for them // simulate the order with current price and market and check that the order doesn't revert uint price = _assetPriceRequireSystemChecks(); uint fundingIndex = _recomputeFunding(price); TradeParams memory params = TradeParams({ sizeDelta: sizeDelta, price: price, takerFee: _takerFeeNextPrice(marketKey), makerFee: _makerFeeNextPrice(marketKey), trackingCode: trackingCode }); (, , Status status) = _postTradeDetails(position, params); _revertIfError(status); // deduct fees from margin uint commitDeposit = _nextPriceCommitDeposit(params); uint keeperDeposit = _minKeeperFee(); _updatePositionMargin(position, price, -int(commitDeposit + keeperDeposit)); // emit event for modifying the position (subtracting the fees from margin) emit PositionModified(position.id, msg.sender, position.margin, position.size, 0, price, fundingIndex, 0); // create order uint targetRoundId = _exchangeRates().getCurrentRoundId(baseAsset) + 1; // next round NextPriceOrder memory order = NextPriceOrder({ sizeDelta: int128(sizeDelta), targetRoundId: uint128(targetRoundId), commitDeposit: uint128(commitDeposit), keeperDeposit: uint128(keeperDeposit), trackingCode: trackingCode }); // emit event emit NextPriceOrderSubmitted( msg.sender, order.sizeDelta, order.targetRoundId, order.commitDeposit, order.keeperDeposit, order.trackingCode ); // store order nextPriceOrders[msg.sender] = order; } /** * @notice Cancels an existing order for an account. * Anyone can call this method for any account, but only the account owner * can cancel their own order during the period when it can still potentially be executed (before it becomes stale). * Only after the order becomes stale, can anyone else (e.g. a keeper) cancel the order for the keeperFee. * Cancelling the order: * - Removes the stored order. * - commitFee (deducted during submission) is sent to the fee pool. * - keeperFee (deducted during submission) is refunded into margin if it's the account holder, * or send to the msg.sender if it's not the account holder. * @param account the account for which the stored order should be cancelled */ function cancelNextPriceOrder(address account) external { // important!! order of the account, not the msg.sender NextPriceOrder memory order = nextPriceOrders[account]; // check that a previous order exists require(order.sizeDelta != 0, "no previous order"); uint currentRoundId = _exchangeRates().getCurrentRoundId(baseAsset); if (account == msg.sender) { // this is account owner // refund keeper fee to margin Position storage position = positions[account]; uint price = _assetPriceRequireSystemChecks(); uint fundingIndex = _recomputeFunding(price); _updatePositionMargin(position, price, int(order.keeperDeposit)); // emit event for modifying the position (add the fee to margin) emit PositionModified(position.id, account, position.margin, position.size, 0, price, fundingIndex, 0); } else { // this is someone else (like a keeper) // cancellation by third party is only possible when execution cannot be attempted any longer // otherwise someone might try to grief an account by cancelling for the keeper fee require(_confirmationWindowOver(currentRoundId, order.targetRoundId), "cannot be cancelled by keeper yet"); // send keeper fee to keeper _manager().issueSUSD(msg.sender, order.keeperDeposit); } // pay the commitDeposit as fee to the FeePool _manager().payFee(order.commitDeposit); // remove stored order // important!! position of the account, not the msg.sender delete nextPriceOrders[account]; // emit event emit NextPriceOrderRemoved( account, currentRoundId, order.sizeDelta, order.targetRoundId, order.commitDeposit, order.keeperDeposit, order.trackingCode ); } /** * @notice Tries to execute a previously submitted next-price order. * Reverts if: * - There is no order * - Target roundId wasn't reached yet * - Order is stale (target roundId is too low compared to current roundId). * - Order fails for accounting reason (e.g. margin was removed, leverage exceeded, etc) * If order reverts, it has to be removed by calling cancelNextPriceOrder(). * Anyone can call this method for any account. * If this is called by the account holder - the keeperFee is refunded into margin, * otherwise it sent to the msg.sender. * @param account address of the account for which to try to execute a next-price order */ function executeNextPriceOrder(address account) external { // important!: order of the account, not the sender! NextPriceOrder memory order = nextPriceOrders[account]; // check that a previous order exists require(order.sizeDelta != 0, "no previous order"); // check round-Id uint currentRoundId = _exchangeRates().getCurrentRoundId(baseAsset); require(order.targetRoundId <= currentRoundId, "target roundId not reached"); // check order is not too old to execute // we cannot allow executing old orders because otherwise future knowledge // can be used to trigger failures of orders that are more profitable // then the commitFee that was charged, or can be used to confirm // orders that are more profitable than known then (which makes this into a "cheap option"). require(!_confirmationWindowOver(currentRoundId, order.targetRoundId), "order too old, use cancel"); // handle the fees and refunds according to the mechanism rules uint toRefund = order.commitDeposit; // refund the commitment deposit // refund keeperFee to margin if it's the account holder if (msg.sender == account) { toRefund += order.keeperDeposit; } else { _manager().issueSUSD(msg.sender, order.keeperDeposit); } Position storage position = positions[account]; uint currentPrice = _assetPriceRequireSystemChecks(); uint fundingIndex = _recomputeFunding(currentPrice); // refund the commitFee (and possibly the keeperFee) to the margin before executing the order // if the order later fails this is reverted of course _updatePositionMargin(position, currentPrice, int(toRefund)); // emit event for modifying the position (refunding fee/s) emit PositionModified(position.id, account, position.margin, position.size, 0, currentPrice, fundingIndex, 0); // the correct price for the past round (uint pastPrice, ) = _exchangeRates().rateAndTimestampAtRound(baseAsset, order.targetRoundId); // execute or revert _trade( account, TradeParams({ sizeDelta: order.sizeDelta, // using the pastPrice from the target roundId price: pastPrice, // the funding is applied only from order confirmation time takerFee: _takerFeeNextPrice(marketKey), makerFee: _makerFeeNextPrice(marketKey), trackingCode: order.trackingCode }) ); // remove stored order delete nextPriceOrders[account]; // emit event emit NextPriceOrderRemoved( account, currentRoundId, order.sizeDelta, order.targetRoundId, order.commitDeposit, order.keeperDeposit, order.trackingCode ); } ///// Internal views // confirmation window is over when current roundId is more than nextPriceConfirmWindow // rounds after target roundId function _confirmationWindowOver(uint currentRoundId, uint targetRoundId) internal view returns (bool) { return (currentRoundId > targetRoundId) && (currentRoundId - targetRoundId > _nextPriceConfirmWindow(marketKey)); // don't underflow } // convenience view to access exchangeRates contract for methods that are not exposed // via _exchangeCircuitBreaker() contract function _exchangeRates() internal view returns (IExchangeRates) { return IExchangeRates(_exchangeCircuitBreaker().exchangeRates()); } // calculate the commitFee, which is the fee that would be charged on the order if it was spot function _nextPriceCommitDeposit(TradeParams memory params) internal view returns (uint) { // modify params to spot fee params.takerFee = _takerFee(marketKey); params.makerFee = _makerFee(marketKey); // Commit fee is equal to the spot fee that would be paid. // This is to prevent free cancellation manipulations (by e.g. withdrawing the margin). // The dynamic fee rate is passed as 0 since for the purposes of the commitment deposit // it is not important since at the time of order execution it will be refunded and the correct // dynamic fee will be charged. return _orderFee(params, 0); } ///// Events event NextPriceOrderSubmitted( address indexed account, int sizeDelta, uint targetRoundId, uint commitDeposit, uint keeperDeposit, bytes32 trackingCode ); event NextPriceOrderRemoved( address indexed account, uint currentRoundId, int sizeDelta, uint targetRoundId, uint commitDeposit, uint keeperDeposit, bytes32 trackingCode ); } // Inheritance /** * A mixin that implements vairous useful views that are used externally but * aren't used inside the core contract (so don't need to clutter the contract file) */ contract MixinFuturesViews is FuturesMarketBase { /* * Sizes of the long and short sides of the market (in sUSD) */ function marketSizes() public view returns (uint long, uint short) { int size = int(marketSize); int skew = marketSkew; return (_abs(size.add(skew).div(2)), _abs(size.sub(skew).div(2))); } /* * The debt contributed by this market to the overall system. * The total market debt is equivalent to the sum of remaining margins in all open positions. */ function marketDebt() external view returns (uint debt, bool invalid) { (uint price, bool isInvalid) = assetPrice(); return (_marketDebt(price), isInvalid); } /* * The current funding rate as determined by the market skew; this is returned as a percentage per day. * If this is positive, shorts pay longs, if it is negative, longs pay shorts. */ function currentFundingRate() external view returns (int) { (uint price, ) = assetPrice(); return _currentFundingRate(price); } /* * The funding per base unit accrued since the funding rate was last recomputed, which has not yet * been persisted in the funding sequence. */ function unrecordedFunding() external view returns (int funding, bool invalid) { (uint price, bool isInvalid) = assetPrice(); return (_unrecordedFunding(price), isInvalid); } /* * The number of entries in the funding sequence. */ function fundingSequenceLength() external view returns (uint) { return fundingSequence.length; } /* * The notional value of a position is its size multiplied by the current price. Margin and leverage are ignored. */ function notionalValue(address account) external view returns (int value, bool invalid) { (uint price, bool isInvalid) = assetPrice(); return (_notionalValue(positions[account].size, price), isInvalid); } /* * The PnL of a position is the change in its notional value. Funding is not taken into account. */ function profitLoss(address account) external view returns (int pnl, bool invalid) { (uint price, bool isInvalid) = assetPrice(); return (_profitLoss(positions[account], price), isInvalid); } /* * The funding accrued in a position since it was opened; this does not include PnL. */ function accruedFunding(address account) external view returns (int funding, bool invalid) { (uint price, bool isInvalid) = assetPrice(); return (_accruedFunding(positions[account], price), isInvalid); } /* * The initial margin plus profit and funding; returns zero balance if losses exceed the initial margin. */ function remainingMargin(address account) external view returns (uint marginRemaining, bool invalid) { (uint price, bool isInvalid) = assetPrice(); return (_remainingMargin(positions[account], price), isInvalid); } /* * The approximate amount of margin the user may withdraw given their current position; this underestimates the * true value slightly. */ function accessibleMargin(address account) external view returns (uint marginAccessible, bool invalid) { (uint price, bool isInvalid) = assetPrice(); return (_accessibleMargin(positions[account], price), isInvalid); } /* * The price at which a position is subject to liquidation; otherwise the price at which the user's remaining * margin has run out. When they have just enough margin left to pay a liquidator, then they are liquidated. * If a position is long, then it is safe as long as the current price is above the liquidation price; if it is * short, then it is safe whenever the current price is below the liquidation price. * A position's accurate liquidation price can move around slightly due to accrued funding. */ function liquidationPrice(address account) external view returns (uint price, bool invalid) { (uint aPrice, bool isInvalid) = assetPrice(); uint liqPrice = _approxLiquidationPrice(positions[account], aPrice); return (liqPrice, isInvalid); } /** * The fee paid to liquidator in the event of successful liquidation of an account at current price. * Returns 0 if account cannot be liquidated right now. * @param account address of the trader's account * @return fee that will be paid for liquidating the account if it can be liquidated * in sUSD fixed point decimal units or 0 if account is not liquidatable. */ function liquidationFee(address account) external view returns (uint) { (uint price, bool invalid) = assetPrice(); if (!invalid && _canLiquidate(positions[account], price)) { return _liquidationFee(int(positions[account].size), price); } else { // theoretically we can calculate a value, but this value is always incorrect because // it's for a price at which liquidation cannot happen - so is misleading, because // it won't be paid, and what will be paid is a different fee (for a different price) return 0; } } /* * True if and only if a position is ready to be liquidated. */ function canLiquidate(address account) external view returns (bool) { (uint price, bool invalid) = assetPrice(); return !invalid && _canLiquidate(positions[account], price); } /* * Reports the fee for submitting an order of a given size. Orders that increase the skew will be more * expensive than ones that decrease it. Dynamic fee is added according to the recent volatility * according to SIP-184. * @param sizeDelta size of the order in baseAsset units (negative numbers for shorts / selling) * @return fee in sUSD decimal, and invalid boolean flag for invalid rates or dynamic fee that is * too high due to recent volatility. */ function orderFee(int sizeDelta) external view returns (uint fee, bool invalid) { (uint price, bool isInvalid) = assetPrice(); (uint dynamicFeeRate, bool tooVolatile) = _dynamicFeeRate(); TradeParams memory params = TradeParams({ sizeDelta: sizeDelta, price: price, takerFee: _takerFee(marketKey), makerFee: _makerFee(marketKey), trackingCode: bytes32(0) }); return (_orderFee(params, dynamicFeeRate), isInvalid || tooVolatile); } /* * Returns all new position details if a given order from `sender` was confirmed at the current price. */ function postTradeDetails(int sizeDelta, address sender) external view returns ( uint margin, int size, uint price, uint liqPrice, uint fee, Status status ) { bool invalid; (price, invalid) = assetPrice(); if (invalid) { return (0, 0, 0, 0, 0, Status.InvalidPrice); } TradeParams memory params = TradeParams({ sizeDelta: sizeDelta, price: price, takerFee: _takerFee(marketKey), makerFee: _makerFee(marketKey), trackingCode: bytes32(0) }); (Position memory newPosition, uint fee_, Status status_) = _postTradeDetails(positions[sender], params); liqPrice = _approxLiquidationPrice(newPosition, newPosition.lastPrice); return (newPosition.margin, newPosition.size, newPosition.lastPrice, liqPrice, fee_, status_); } /// helper methods calculates the approximate liquidation price function _approxLiquidationPrice(Position memory position, uint currentPrice) internal view returns (uint) { int positionSize = int(position.size); // short circuit if (positionSize == 0) { return 0; } // price = lastPrice + (liquidationMargin - margin) / positionSize - netAccrued int fundingPerUnit = _netFundingPerUnit(position.lastFundingIndex, currentPrice); // minimum margin beyond which position can be liqudiated uint liqMargin = _liquidationMargin(positionSize, currentPrice); // A position can be liquidated whenever: // remainingMargin <= liquidationMargin // Hence, expanding the definition of remainingMargin the exact price // at which a position can first be liquidated is: // margin + profitLoss + funding = liquidationMargin // substitute with: profitLoss = (price - last-price) * positionSize // and also with: funding = netFundingPerUnit * positionSize // we get: margin + (price - last-price) * positionSize + netFundingPerUnit * positionSize = liquidationMargin // moving around: price = lastPrice + (liquidationMargin - margin) / positionSize - netFundingPerUnit int result = int(position.lastPrice).add(int(liqMargin).sub(int(position.margin)).divideDecimal(positionSize)).sub( fundingPerUnit ); // If the user has leverage less than 1, their liquidation price may actually be negative; return 0 instead. return uint(_max(0, result)); } } interface IFuturesMarket { /* ========== FUNCTION INTERFACE ========== */ /* ---------- Market Details ---------- */ function marketKey() external view returns (bytes32 key); function baseAsset() external view returns (bytes32 key); function marketSize() external view returns (uint128 size); function marketSkew() external view returns (int128 skew); function fundingLastRecomputed() external view returns (uint32 timestamp); function fundingSequence(uint index) external view returns (int128 netFunding); function positions(address account) external view returns ( uint64 id, uint64 fundingIndex, uint128 margin, uint128 lastPrice, int128 size ); function assetPrice() external view returns (uint price, bool invalid); function marketSizes() external view returns (uint long, uint short); function marketDebt() external view returns (uint debt, bool isInvalid); function currentFundingRate() external view returns (int fundingRate); function unrecordedFunding() external view returns (int funding, bool invalid); function fundingSequenceLength() external view returns (uint length); /* ---------- Position Details ---------- */ function notionalValue(address account) external view returns (int value, bool invalid); function profitLoss(address account) external view returns (int pnl, bool invalid); function accruedFunding(address account) external view returns (int funding, bool invalid); function remainingMargin(address account) external view returns (uint marginRemaining, bool invalid); function accessibleMargin(address account) external view returns (uint marginAccessible, bool invalid); function liquidationPrice(address account) external view returns (uint price, bool invalid); function liquidationFee(address account) external view returns (uint); function canLiquidate(address account) external view returns (bool); function orderFee(int sizeDelta) external view returns (uint fee, bool invalid); function postTradeDetails(int sizeDelta, address sender) external view returns ( uint margin, int size, uint price, uint liqPrice, uint fee, IFuturesMarketBaseTypes.Status status ); /* ---------- Market Operations ---------- */ function recomputeFunding() external returns (uint lastIndex); function transferMargin(int marginDelta) external; function withdrawAllMargin() external; function modifyPosition(int sizeDelta) external; function modifyPositionWithTracking(int sizeDelta, bytes32 trackingCode) external; function submitNextPriceOrder(int sizeDelta) external; function submitNextPriceOrderWithTracking(int sizeDelta, bytes32 trackingCode) external; function cancelNextPriceOrder(address account) external; function executeNextPriceOrder(address account) external; function closePosition() external; function closePositionWithTracking(bytes32 trackingCode) external; function liquidatePosition(address account) external; } // Inheritance /* * Synthetic Futures * ================= * * Futures markets allow users leveraged exposure to an asset, long or short. * A user must post some margin in order to open a futures account, and profits/losses are * continually tallied against this margin. If a user's margin runs out, then their position is closed * by a liquidation keeper, which is rewarded with a flat fee extracted from the margin. * * The Synthetix debt pool is effectively the counterparty to each trade, so if a particular position * is in profit, then the debt pool pays by issuing sUSD into their margin account, * while if the position makes a loss then the debt pool burns sUSD from the margin, reducing the * debt load in the system. * * As the debt pool underwrites all positions, the debt-inflation risk to the system is proportional to the * long-short skew in the market. It is therefore in the interest of the system to reduce the skew. * To encourage the minimisation of the skew, each position is charged a funding rate, which increases with * the size of the skew. The funding rate is charged continuously, and positions on the heavier side of the * market are charged the current funding rate times the notional value of their position, while positions * on the lighter side are paid at the same rate to keep their positions open. * As the funding rate is the same (but negated) on both sides of the market, there is an excess quantity of * funding being charged, which is collected by the debt pool, and serves to reduce the system debt. * * The contract architecture is as follows: * * - FuturesMarket.sol: one of these exists per asset. Margin is maintained isolated per market. * this contract is composed of several mixins: `base` contains all the core logic, * `nextPrice` contains the next-price order flows, and `views` contains logic * that is only used by external / manager contracts. * * - FuturesMarketManager.sol: the manager keeps track of which markets exist, and is the main window between * futures markets and the rest of the system. It accumulates the total debt * over all markets, and issues and burns sUSD on each market's behalf. * * - FuturesMarketSettings.sol: Holds the settings for each market in the global FlexibleStorage instance used * by SystemSettings, and provides an interface to modify these values. Other than * the base asset, these settings determine the behaviour of each market. * See that contract for descriptions of the meanings of each setting. * * Technical note: internal functions within the FuturesMarket contract assume the following: * * - prices passed into them are valid; * * - funding has already been recomputed up to the current time (hence unrecorded funding is nil); * * - the account being managed was not liquidated in the same transaction; */ // https://docs.synthetix.io/contracts/source/contracts/FuturesMarket contract FuturesMarket is IFuturesMarket, FuturesMarketBase, MixinFuturesNextPriceOrders, MixinFuturesViews { constructor( address _resolver, bytes32 _baseAsset, bytes32 _marketKey ) public FuturesMarketBase(_resolver, _baseAsset, _marketKey) {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"address","name":"_resolver","type":"address"},{"internalType":"bytes32","name":"_baseAsset","type":"bytes32"},{"internalType":"bytes32","name":"_marketKey","type":"bytes32"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"name","type":"bytes32"},{"indexed":false,"internalType":"address","name":"destination","type":"address"}],"name":"CacheUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"int256","name":"funding","type":"int256"},{"indexed":false,"internalType":"uint256","name":"index","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"FundingRecomputed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"trackingCode","type":"bytes32"},{"indexed":false,"internalType":"bytes32","name":"baseAsset","type":"bytes32"},{"indexed":false,"internalType":"bytes32","name":"marketKey","type":"bytes32"},{"indexed":false,"internalType":"int256","name":"sizeDelta","type":"int256"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"FuturesTracking","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"int256","name":"marginDelta","type":"int256"}],"name":"MarginTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"currentRoundId","type":"uint256"},{"indexed":false,"internalType":"int256","name":"sizeDelta","type":"int256"},{"indexed":false,"internalType":"uint256","name":"targetRoundId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"commitDeposit","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"keeperDeposit","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"trackingCode","type":"bytes32"}],"name":"NextPriceOrderRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"int256","name":"sizeDelta","type":"int256"},{"indexed":false,"internalType":"uint256","name":"targetRoundId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"commitDeposit","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"keeperDeposit","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"trackingCode","type":"bytes32"}],"name":"NextPriceOrderSubmitted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"liquidator","type":"address"},{"indexed":false,"internalType":"int256","name":"size","type":"int256"},{"indexed":false,"internalType":"uint256","name":"price","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"PositionLiquidated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"margin","type":"uint256"},{"indexed":false,"internalType":"int256","name":"size","type":"int256"},{"indexed":false,"internalType":"int256","name":"tradeSize","type":"int256"},{"indexed":false,"internalType":"uint256","name":"lastPrice","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fundingIndex","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"PositionModified","type":"event"},{"constant":true,"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"accessibleMargin","outputs":[{"internalType":"uint256","name":"marginAccessible","type":"uint256"},{"internalType":"bool","name":"invalid","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"accruedFunding","outputs":[{"internalType":"int256","name":"funding","type":"int256"},{"internalType":"bool","name":"invalid","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"assetPrice","outputs":[{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"bool","name":"invalid","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"baseAsset","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"canLiquidate","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"cancelNextPriceOrder","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"closePosition","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"trackingCode","type":"bytes32"}],"name":"closePositionWithTracking","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"currentFundingRate","outputs":[{"internalType":"int256","name":"","type":"int256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"executeNextPriceOrder","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"fundingLastRecomputed","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"fundingSequence","outputs":[{"internalType":"int128","name":"","type":"int128"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"fundingSequenceLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"isResolverCached","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"liquidatePosition","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"liquidationFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"liquidationPrice","outputs":[{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"bool","name":"invalid","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"marketDebt","outputs":[{"internalType":"uint256","name":"debt","type":"uint256"},{"internalType":"bool","name":"invalid","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"marketKey","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"marketSize","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"marketSizes","outputs":[{"internalType":"uint256","name":"long","type":"uint256"},{"internalType":"uint256","name":"short","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"marketSkew","outputs":[{"internalType":"int128","name":"","type":"int128"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"int256","name":"sizeDelta","type":"int256"}],"name":"modifyPosition","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"int256","name":"sizeDelta","type":"int256"},{"internalType":"bytes32","name":"trackingCode","type":"bytes32"}],"name":"modifyPositionWithTracking","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"nextPriceOrders","outputs":[{"internalType":"int128","name":"sizeDelta","type":"int128"},{"internalType":"uint128","name":"targetRoundId","type":"uint128"},{"internalType":"uint128","name":"commitDeposit","type":"uint128"},{"internalType":"uint128","name":"keeperDeposit","type":"uint128"},{"internalType":"bytes32","name":"trackingCode","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"notionalValue","outputs":[{"internalType":"int256","name":"value","type":"int256"},{"internalType":"bool","name":"invalid","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"int256","name":"sizeDelta","type":"int256"}],"name":"orderFee","outputs":[{"internalType":"uint256","name":"fee","type":"uint256"},{"internalType":"bool","name":"invalid","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"positions","outputs":[{"internalType":"uint64","name":"id","type":"uint64"},{"internalType":"uint64","name":"lastFundingIndex","type":"uint64"},{"internalType":"uint128","name":"margin","type":"uint128"},{"internalType":"uint128","name":"lastPrice","type":"uint128"},{"internalType":"int128","name":"size","type":"int128"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"int256","name":"sizeDelta","type":"int256"},{"internalType":"address","name":"sender","type":"address"}],"name":"postTradeDetails","outputs":[{"internalType":"uint256","name":"margin","type":"uint256"},{"internalType":"int256","name":"size","type":"int256"},{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"uint256","name":"liqPrice","type":"uint256"},{"internalType":"uint256","name":"fee","type":"uint256"},{"internalType":"enum IFuturesMarketBaseTypes.Status","name":"status","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"profitLoss","outputs":[{"internalType":"int256","name":"pnl","type":"int256"},{"internalType":"bool","name":"invalid","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"rebuildCache","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"recomputeFunding","outputs":[{"internalType":"uint256","name":"lastIndex","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"remainingMargin","outputs":[{"internalType":"uint256","name":"marginRemaining","type":"uint256"},{"internalType":"bool","name":"invalid","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"resolver","outputs":[{"internalType":"contract AddressResolver","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"resolverAddressesRequired","outputs":[{"internalType":"bytes32[]","name":"addresses","type":"bytes32[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"int256","name":"sizeDelta","type":"int256"}],"name":"submitNextPriceOrder","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"int256","name":"sizeDelta","type":"int256"},{"internalType":"bytes32","name":"trackingCode","type":"bytes32"}],"name":"submitNextPriceOrderWithTracking","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"int256","name":"marginDelta","type":"int256"}],"name":"transferMargin","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"unrecordedFunding","outputs":[{"internalType":"int256","name":"funding","type":"int256"},{"internalType":"bool","name":"invalid","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"withdrawAllMargin","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
608060405260088054600160801b600160c01b031916600160801b1790553480156200002a57600080fd5b50604051620055e6380380620055e6833981810160405260608110156200005057600080fd5b508051602080830151604093840151600080546001600160a01b0319166001600160a01b038616178155600383905560028281556006805460018181019092557ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f9281049290920180549282166010026101000a6001600160801b0302199092169091558651808801909752600d87526c496e76616c696420707269636560981b87860190815291526009909352935192939092909184918491849162000139917f92e85d02570a8092d09a6e3a57665bc3815a2699a4074001bf1ccabf660f5a36916200058e565b5060408051808201909152601d81527f5072696365206f7574206f662061636365707461626c652072616e676500000060208083019182526002600052600990529051620001a9917f6cde3cea4b3a3fb2488b2808bae7556f4a405e50f65e1794383bc026131b13c3916200058e565b5060408051808201909152601a81527f506f736974696f6e2063616e206265206c6971756964617465640000000000006020808301918252600360005260099052905162000219917fc575c31fea594a6eb97c8e9d3f9caee4c16218c6ef37e923234c0fe9014a61e7916200058e565b5060408051808201909152601d81527f506f736974696f6e2063616e6e6f74206265206c6971756964617465640000006020808301918252600460005260099052905162000289917f8dc18c4ccfd75f5c815b63770fa542fd953e8fef7e0e44bbdd4913470ce7e9cb916200058e565b5060408051808201909152601881527f4d6178206d61726b65742073697a65206578636565646564000000000000000060208083019182526005600052600990529051620002f9917f74b05292d1d4b2b48b65261b07099d24244bcb069f138d9a6bfdcf776becac4c916200058e565b5060408051808201909152601581527f4d6178206c6576657261676520657863656564656400000000000000000000006020808301918252600660005260099052905162000369917fbb6daa0c283751197dfdc76590680f9005e97d6f23870deb1164ab60b28b9f5f916200058e565b5060408051808201909152601381527f496e73756666696369656e74206d617267696e0000000000000000000000000060208083019182526007600052600990529051620003d9917fae6299332bcd708cd60e3a8defa55de28078a50a4cf2b3de3a546253240ff9e1916200058e565b5060408051808201909152601d81527f4e6f74207065726d6974746564206279207468697320616464726573730000006020808301918252600860005260099052905162000449917fc7694af312c4f286114180fd0ba6a52461fcee8a381636770b19a343af92538a916200058e565b5060408051808201909152601981527f43616e6e6f74207375626d697420656d707479206f726465720000000000000060208083019182526009600081905290529051620004b9917f87e8a52529e8ece4ef759037313542a6429ff494a9fab9027fb79db90124eba6916200058e565b5060408051808201909152601081526f2737903837b9b4ba34b7b71037b832b760811b6020808301918252600a6000526009905290516200051c917f502e20e4e219e0c509d693958f17384c185f07a810a5d31c46c2be981e979c25916200058e565b50604080518082019091526012815271507269636520746f6f20766f6c6174696c6560701b6020808301918252600b60005260099052905162000581917f0d9cf2cd531699eed8dd34e40ff2884a14a698c4898184fba85194e6f6772d24916200058e565b5050505050505062000633565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10620005d157805160ff191683800117855562000601565b8280016001018555821562000601579182015b8281111562000601578251825591602001919060010190620005e4565b506200060f92915062000613565b5090565b6200063091905b808211156200060f57600081556001016200061a565b90565b614fa380620006436000396000f3fe608060405234801561001057600080fd5b50600436106102475760003560e01c8063785cdeec1161013b578063b895daab116100b8578063cdf456e11161007c578063cdf456e114610788578063d24378eb14610790578063d7103a4614610798578063e8c63470146107a0578063eb56105d146107a857610247565b8063b895daab14610706578063b9f4ff551461072c578063c393d0e314610752578063c8023af41461075a578063cded0cea1461078057610247565b8063996c61ad116100ff578063996c61ad146106075780639cfbf4e41461067a578063a28a2bc0146106a0578063a8c92cf6146106c3578063b111dfac146106e057610247565b8063785cdeec1461053e57806388a3c84814610564578063899ffef414610581578063917e77f5146105d9578063964db90c146105e157610247565b80633aef4d0b116101c957806355f575101161018d57806355f57510146104795780635a1cbd2b146104e75780635fc890c2146104ef57806374185360146105105780637498a0f01461051857610247565b80633aef4d0b146103af57806341108cf2146103c9578063450adee0146103e65780634eb985cc1461040c57806355b9437a1461041457610247565b80631bf556d0116102105780631bf556d01461030e57806327b9a236146103345780632af64bd3146103555780632b58ecef146103715780632f07449f1461039257610247565b8062f6beff1461024c57806304f3bcec146102715780630b198211146102955780631172cb24146102b25780631906652f146102d8575b600080fd5b61026f6004803603604081101561026257600080fd5b50803590602001356107cc565b005b6102796107da565b604080516001600160a01b039092168252519081900360200190f35b61026f600480360360208110156102ab57600080fd5b50356107e9565b61026f600480360360208110156102c857600080fd5b50356001600160a01b03166107f7565b6102f5600480360360208110156102ee57600080fd5b5035610c10565b6040805192835290151560208301528051918290030190f35b6102f56004803603602081101561032457600080fd5b50356001600160a01b0316610c98565b61033c610d39565b6040805163ffffffff9092168252519081900360200190f35b61035d610d45565b604080519115158252519081900360200190f35b610379610e4f565b60408051600f92830b90920b8252519081900360200190f35b61026f600480360360208110156103a857600080fd5b5035610e5f565b6103b7610e6a565b60408051918252519081900360200190f35b610379600480360360208110156103df57600080fd5b5035610e87565b61026f600480360360208110156103fc57600080fd5b50356001600160a01b0316610ebb565b6103b7611459565b61043a6004803603602081101561042a57600080fd5b50356001600160a01b03166114e4565b60408051600f96870b90960b86526001600160801b03948516602087015292841685840152921660608401526080830191909152519081900360a00190f35b61049f6004803603602081101561048f57600080fd5b50356001600160a01b0316611524565b604080516001600160401b0396871681529490951660208501526001600160801b039283168486015291166060830152600f90810b900b608082015290519081900360a00190f35b61026f611570565b6104f7611620565b6040805192835260208301919091528051918290030190f35b61026f61168c565b61026f6004803603602081101561052e57600080fd5b50356001600160a01b0316611853565b6102f56004803603602081101561055457600080fd5b50356001600160a01b0316611904565b61026f6004803603602081101561057a57600080fd5b503561199b565b6105896119bc565b60408051602080825283518183015283519192839290830191858101910280838360005b838110156105c55781810151838201526020016105ad565b505050509050019250505060405180910390f35b6102f5611ad3565b6102f5600480360360208110156105f757600080fd5b50356001600160a01b0316611af7565b6106336004803603604081101561061d57600080fd5b50803590602001356001600160a01b0316611b9c565b6040518087815260200186815260200185815260200184815260200183815260200182600b81111561066157fe5b60ff168152602001965050505050505060405180910390f35b6102f56004803603602081101561069057600080fd5b50356001600160a01b0316611d03565b61026f600480360360408110156106b657600080fd5b5080359060200135611d9a565b61026f600480360360208110156106d957600080fd5b5035611da4565b6102f5600480360360208110156106f657600080fd5b50356001600160a01b0316611dad565b6102f56004803603602081101561071c57600080fd5b50356001600160a01b0316611e44565b61035d6004803603602081101561074257600080fd5b50356001600160a01b0316611e8b565b61026f611f33565b6103b76004803603602081101561077057600080fd5b50356001600160a01b0316611f3f565b6103b761202a565b6103b7612030565b6102f5612036565b6103b761214d565b6102f5612153565b6107b061216e565b604080516001600160801b039092168252519081900360200190f35b6107d6828261217d565b5050565b6000546001600160a01b031681565b6107f481600061217d565b50565b6107ff614dff565b506001600160a01b0381166000908152600a6020908152604091829020825160a0810184528154600f81810b810b810b8084526001600160801b03600160801b938490048116968501969096526001850154808716978501979097529190950490931660608201526002909101546080820152910b6108b9576040805162461bcd60e51b8152602060048201526011602482015270373790383932bb34b7bab99037b93232b960791b604482015290519081900360640190fd5b60006108c361254d565b6001600160a01b0316637a018a1e6003546040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561090857600080fd5b505afa15801561091c573d6000803e3d6000fd5b505050506040513d602081101561093257600080fd5b505190506001600160a01b038316331415610a11576001600160a01b0383166000908152600760205260408120906109686125c0565b9050600061097582612726565b905061098f838387606001516001600160801b0316612804565b82546001840154604080516001600160801b03600160801b808604919091168252909204600f90810b900b60208301526000828201819052606083018690526080830185905260a0830152516001600160a01b038916926001600160401b031691600080516020614e60833981519152919081900360c00190a3505050610af0565b610a288183602001516001600160801b0316612abc565b610a635760405162461bcd60e51b8152600401808060200182810382526021815260200180614f2a6021913960400191505060405180910390fd5b610a6b612ae1565b6001600160a01b031663a7b5833f3384606001516040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b03168152602001826001600160801b0316815260200192505050600060405180830381600087803b158015610ad757600080fd5b505af1158015610aeb573d6000803e3d6000fd5b505050505b610af8612ae1565b6001600160a01b031663d289ade283604001516040518263ffffffff1660e01b815260040180826001600160801b03168152602001915050600060405180830381600087803b158015610b4a57600080fd5b505af1158015610b5e573d6000803e3d6000fd5b505050506001600160a01b0383166000818152600a602090815260408083208381556001810184905560020192909255845185820151868401516060808901516080808b015188518b8152600f9790970b978701979097526001600160801b039485168689015292841691850191909152919091169082015260a081019190915290517fa2c30b7be4efaff9ad0f74d77ec9a944a7cc92a7b8eb941fbfc6489fb19370f39181900360c00190a2505050565b600080600080610c1e612036565b91509150600080610c2d612b08565b91509150610c39614e2d565b6040518060a00160405280898152602001868152602001610c5b600254612ba2565b8152602001610c6b600254612bb9565b815260006020909101529050610c818184612bd0565b8480610c8a5750825b965096505050505050915091565b600080600080610ca6612036565b6001600160a01b038716600090815260076020908152604091829020825160a08101845281546001600160401b038082168352600160401b820416938201939093526001600160801b03600160801b9384900481169482019490945260019091015492831660608201529104600f90810b810b900b60808201529193509150610d2f9083612c52565b9350915050915091565b60055463ffffffff1681565b60006060610d516119bc565b905060005b8151811015610e45576000828281518110610d6d57fe5b602090810291909101810151600081815260018352604080822054915481516321f8a72160e01b81526004810185905291519395506001600160a01b03928316949216926321f8a72192602480840193919291829003018186803b158015610dd457600080fd5b505afa158015610de8573d6000803e3d6000fd5b505050506040513d6020811015610dfe57600080fd5b50516001600160a01b0316141580610e2b57506000818152600160205260409020546001600160a01b0316155b15610e3c5760009350505050610e4c565b50600101610d56565b5060019150505b90565b600454600160801b9004600f0b81565b6107f4816000612ca2565b600080610e75612036565b509050610e8181612cfb565b91505090565b60068181548110610e9457fe5b9060005260206000209060029182820401919006601002915054906101000a9004600f0b81565b610ec3614dff565b506001600160a01b0381166000908152600a6020908152604091829020825160a0810184528154600f81810b810b810b8084526001600160801b03600160801b938490048116968501969096526001850154808716978501979097529190950490931660608201526002909101546080820152910b610f7d576040805162461bcd60e51b8152602060048201526011602482015270373790383932bb34b7bab99037b93232b960791b604482015290519081900360640190fd5b6000610f8761254d565b6001600160a01b0316637a018a1e6003546040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b158015610fcc57600080fd5b505afa158015610fe0573d6000803e3d6000fd5b505050506040513d6020811015610ff657600080fd5b505160208301519091506001600160801b031681101561105d576040805162461bcd60e51b815260206004820152601a60248201527f74617267657420726f756e644964206e6f742072656163686564000000000000604482015290519081900360640190fd5b6110748183602001516001600160801b0316612abc565b156110c6576040805162461bcd60e51b815260206004820152601960248201527f6f7264657220746f6f206f6c642c207573652063616e63656c00000000000000604482015290519081900360640190fd5b60408201516001600160801b0316336001600160a01b03851614156110f95760608301516001600160801b031601611186565b611101612ae1565b6001600160a01b031663a7b5833f3385606001516040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b03168152602001826001600160801b0316815260200192505050600060405180830381600087803b15801561116d57600080fd5b505af1158015611181573d6000803e3d6000fd5b505050505b6001600160a01b0384166000908152600760205260408120906111a76125c0565b905060006111b482612726565b90506111c1838386612804565b82546001840154604080516001600160801b03600160801b808604919091168252909204600f90810b900b60208301526000828201819052606083018690526080830185905260a0830152516001600160a01b038a16926001600160401b031691600080516020614e60833981519152919081900360c00190a3600061124561254d565b6001600160a01b031663fdadbc7e60035489602001516040518363ffffffff1660e01b815260040180838152602001826001600160801b0316815260200192505050604080518083038186803b15801561129e57600080fd5b505afa1580156112b2573d6000803e3d6000fd5b505050506040513d60408110156112c857600080fd5b50516040805160a0810182528951600f0b81526020810183905260025492935061131c928b928201906112fa90612d51565b815260200161130a600254612d71565b81526020018a60800151815250612d91565b600a6000896001600160a01b03166001600160a01b03168152602001908152602001600020600080820160006101000a8154906001600160801b0302191690556000820160106101000a8154906001600160801b0302191690556001820160006101000a8154906001600160801b0302191690556001820160106101000a8154906001600160801b03021916905560028201600090555050876001600160a01b03167fa2c30b7be4efaff9ad0f74d77ec9a944a7cc92a7b8eb941fbfc6489fb19370f38789600001518a602001518b604001518c606001518d608001516040518087815260200186600f0b8152602001856001600160801b03168152602001846001600160801b03168152602001836001600160801b03168152602001828152602001965050505050505060405180910390a25050505050505050565b600061148261146661315c565b6001600160a01b0316336001600160a01b031614156008613175565b60008061148d612036565b9150915080156114d4576040805162461bcd60e51b815260206004820152600d60248201526c496e76616c696420707269636560981b604482015290519081900360640190fd5b6114dd82612726565b9250505090565b600a60205260009081526040902080546001820154600290920154600f82900b926001600160801b03600160801b93849004811693828216939204169085565b600760205260009081526040902080546001909101546001600160401b0380831692600160401b8104909116916001600160801b03600160801b928390048116929082169104600f0b85565b33600061157b6125c0565b905061158681612726565b506001600160a01b0382166000908152600760209081526040808320815160a08101835281546001600160401b038082168352600160401b820416948201949094526001600160801b03600160801b948590048116938201939093526001909101549182166060820152919004600f90810b810b900b608082015261160b9083613236565b600003905061161b8183856132f2565b505050565b60045460009081906001600160801b03811690600160801b9004600f90810b900b61166a6116656002611659858563ffffffff61353916565b9063ffffffff61359e16565b613656565b6116826116656002611659868663ffffffff61366116565b9350935050509091565b60606116966119bc565b905060005b81518110156107d65760008282815181106116b257fe5b60209081029190910181015160008054604080517f5265736f6c766572206d697373696e67207461726765743a2000000000000000818701526039808201869052825180830390910181526059820180845263dacb2d0160e01b9052605d8201868152607d83019384528151609d840152815196985094966001600160a01b039094169563dacb2d0195899592949093909260bd90910191908501908083838c5b8381101561176b578181015183820152602001611753565b50505050905090810190601f1680156117985780820380516001836020036101000a031916815260200191505b50935050505060206040518083038186803b1580156117b657600080fd5b505afa1580156117ca573d6000803e3d6000fd5b505050506040513d60208110156117e057600080fd5b505160008381526001602090815260409182902080546001600160a01b0319166001600160a01b03851690811790915582518681529182015281519293507f88a93678a3692f6789d9546fc621bf7234b101ddb7d4fe479455112831b8aa68929081900390910190a1505060010161169b565b600061185d6125c0565b905061186881612726565b506001600160a01b038216600090815260076020908152604091829020825160a08101845281546001600160401b038082168352600160401b820416938201939093526001600160801b03600160801b9384900481169482019490945260019091015492831660608201529104600f90810b810b900b60808201526118f9906118f190836136c6565b156004613175565b6107d6823383613704565b600080600080611912612036565b6001600160a01b038716600090815260076020908152604091829020825160a08101845281546001600160401b038082168352600160401b820416938201939093526001600160801b03600160801b9384900481169482019490945260019091015492831660608201529104600f90810b810b900b60808201529193509150610d2f9083613236565b60006119a56125c0565b90506119b081612726565b506107d68282336132f2565b6060806119c7613aba565b60408051600580825260c08201909252919250606091906020820160a0803883390190505090506822bc31b430b733b2b960b91b81600081518110611a0857fe5b6020026020010181815250507522bc31b430b733b2a1b4b931bab4ba213932b0b5b2b960511b81600181518110611a3b57fe5b60200260200101818152505073233aba3ab932b9a6b0b935b2ba26b0b730b3b2b960611b81600281518110611a6c57fe5b602002602001018181525050600080516020614e8083398151915281600381518110611a9457fe5b6020026020010181815250506b53797374656d53746174757360a01b81600481518110611abd57fe5b6020026020010181815250506114dd8282613b0b565b600080600080611ae1612036565b91509150611aee82613bc7565b93509150509091565b600080600080611b05612036565b6001600160a01b0387166000908152600760209081526040808320815160a08101835281546001600160401b038082168352600160401b820416948201949094526001600160801b03600160801b948590048116938201939093526001909101549182166060820152919004600f90810b810b900b608082015292945090925090611b909084613c1f565b94509092505050915091565b6000806000806000806000611baf612036565b90955090508015611bd457506000955085945084935083925082915060019050611cf9565b611bdc614e2d565b6040518060a001604052808b8152602001878152602001611bfe600254612ba2565b8152602001611c0e600254612bb9565b815260006020909101529050611c22614dff565b6001600160a01b038a166000908152600760209081526040808320815160a08101835281546001600160401b038082168352600160401b820416948201949094526001600160801b03600160801b948590048116938201939093526001909101549182166060820152919004600f90810b810b900b60808201528190611ca89085613cb6565b925092509250611cc58384606001516001600160801b0316613c1f565b604084015160808501516060909501516001600160801b039182169d50600f9590950b9b5090931698509196509450925050505b9295509295509295565b600080600080611d11612036565b6001600160a01b038716600090815260076020908152604091829020825160a08101845281546001600160401b038082168352600160401b820416938201939093526001600160801b03600160801b9384900481169482019490945260019091015492831660608201529104600f90810b810b900b60808201529193509150610d2f9083613f90565b6107d68282612ca2565b6107f481613fb2565b600080600080611dbb612036565b6001600160a01b038716600090815260076020908152604091829020825160a08101845281546001600160401b038082168352600160401b820416938201939093526001600160801b03600160801b9384900481169482019490945260019091015492831660608201529104600f90810b810b900b60808201529193509150610d2f908361403b565b600080600080611e52612036565b6001600160a01b0387166000908152600760205260409020600101549193509150610d2f90600160801b9004600f90810b900b83614079565b6000806000611e98612036565b9150915080158015611f2957506001600160a01b038416600090815260076020908152604091829020825160a08101845281546001600160401b038082168352600160401b820416938201939093526001600160801b03600160801b9384900481169482019490945260019091015492831660608201529104600f90810b810b900b6080820152611f2990836136c6565b925050505b919050565b611f3d6000613fb2565b565b6000806000611f4c612036565b9150915080158015611fdd57506001600160a01b038416600090815260076020908152604091829020825160a08101845281546001600160401b038082168352600160401b820416938201939093526001600160801b03600160801b9384900481169482019490945260019091015492831660608201529104600f90810b810b900b6080820152611fdd90836136c6565b1561201f576001600160a01b03841660009081526007602052604090206001015461201690600160801b9004600f90810b900b8361408b565b92505050611f2e565b600092505050611f2e565b60065490565b60035481565b6000806120416140d5565b6001600160a01b031663cb1ec3176003546040518263ffffffff1660e01b815260040180828152602001915050604080518083038186803b15801561208557600080fd5b505afa158015612099573d6000803e3d6000fd5b505050506040513d60408110156120af57600080fd5b508051602090910151909250905080806120c7575081155b8061214757506120d56140f9565b6001600160a01b031663b431c0ea6003546040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561211a57600080fd5b505afa15801561212e573d6000803e3d6000fd5b505050506040513d602081101561214457600080fd5b50515b90509091565b60025481565b600080600080612161612036565b91509150611aee82614113565b6004546001600160801b031681565b336000908152600a6020526040902054600f90810b900b156121de576040805162461bcd60e51b815260206004820152601560248201527470726576696f7573206f726465722065786973747360581b604482015290519081900360640190fd5b336000908152600760205260408120906121f66125c0565b9050600061220382612726565b905061220d614e2d565b6040518060a0016040528087815260200184815260200161222f600254612d51565b815260200161223f600254612d71565b815260209081018790526040805160a08101825287546001600160401b038082168352600160401b820416938201939093526001600160801b03600160801b9384900481169282019290925260018801549182166060820152919004600f90810b810b900b60808201529091506000906122b99083613cb6565b925050506122c6816141b0565b60006122d1836141ce565b905060006122dd6141fc565b90506122ef8787838501600003612804565b86546001880154604080516001600160801b03600160801b808604919091168252909204600f90810b900b60208301526000828201819052606083018a90526080830189905260a08301525133926001600160401b031691600080516020614e60833981519152919081900360c00190a3600061236a61254d565b6001600160a01b0316637a018a1e6003546040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b1580156123af57600080fd5b505afa1580156123c3573d6000803e3d6000fd5b505050506040513d60208110156123d957600080fd5b505160010190506123e8614dff565b6040518060a001604052808c600f0b8152602001836001600160801b03168152602001856001600160801b03168152602001846001600160801b031681526020018b8152509050336001600160a01b03167f83f1fe372b879782cdc7a779adb56774bf755506d7486939437b40b64ba15927826000015183602001518460400151856060015186608001516040518086600f0b8152602001856001600160801b03168152602001846001600160801b03168152602001836001600160801b031681526020018281526020019550505050505060405180910390a2336000908152600a602090815260409182902083518154928501516001600160801b03908116600160801b908102600f9390930b82166001600160801b03199586161782169290921783559385015160018301805460608801518716909302918616929094169190911790931692909217905560809091015160029091015550505050505050505050565b60006125576140d5565b6001600160a01b0316634ffcd9df6040518163ffffffff1660e01b815260040160206040518083038186803b15801561258f57600080fd5b505afa1580156125a3573d6000803e3d6000fd5b505050506040513d60208110156125b957600080fd5b5051905090565b60006125ca6140f9565b6001600160a01b031663856aae6c6002546040518263ffffffff1660e01b81526004018082815260200191505060006040518083038186803b15801561260f57600080fd5b505afa158015612623573d6000803e3d6000fd5b5050505061262f6140f9565b6001600160a01b03166342a28e216003546040518263ffffffff1660e01b81526004018082815260200191505060006040518083038186803b15801561267457600080fd5b505afa158015612688573d6000803e3d6000fd5b505050506000806126976140d5565b6001600160a01b031663a47af19e6003546040518263ffffffff1660e01b8152600401808281526020019150506040805180830381600087803b1580156126dd57600080fd5b505af11580156126f1573d6000803e3d6000fd5b505050506040513d604081101561270757600080fd5b5080516020909101519092509050612720816001613175565b50905090565b6006546000908161273684614275565b6006805460018181018355600092909252600281047ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f018054600f85900b6001600160801b03908116601094909516939093026101000a93840292909302199092161790556005805463ffffffff42811663ffffffff19909216919091179182905560408051848152602081018790529290911682820152519192507f368c0bb7dd8da0fb0bedc14e770da5778b8f19a5820635ccd99285d0ffdb6954919081900360600190a15092915050565b61280c614dff565b506040805160a08101825284546001600160401b038082168352600160401b82041660208301526001600160801b03600160801b9182900481169383019390935260018601549283166060830152909104600f90810b810b900b60808201526000806128798386866142cb565b91509150612886816141b0565b6001860154600160801b9004600f90810b900b60006128a3614346565b90506129906040518060a0016040528060006001600160401b03168152602001836001600160401b03168152602001866001600160801b03168152602001896001600160801b0316815260200184600f0b8152506040518060a0016040528060006001600160401b031681526020018b60000160089054906101000a90046001600160401b03166001600160401b031681526020018b60000160109054906101000a90046001600160801b03166001600160801b031681526020018b60010160009054906101000a90046001600160801b03166001600160801b0316815260200185600f0b81525061435d565b87546001600160801b03808616600160801b0291161788558115612ab2576001880180546001600160801b0319166001600160801b038916179055875467ffffffffffffffff60401b1916600160401b6001600160401b038316021788556000861215612ab257612ab2612a026143c3565b851080612a2b57506001890154612a2790600160801b9004600f90810b900b89614446565b8511155b80612aab57506040805160a0810182528a546001600160401b038082168352600160401b82041660208301526001600160801b03600160801b9182900481169383019390935260018c01549283166060830152909104600f90810b810b900b6080820152612a9e90611665908a88614470565b612aa9600254614494565b105b6007613175565b5050505050505050565b60008183118015612ad85750612ad36002546144ae565b828403115b90505b92915050565b6000612b0373233aba3ab932b9a6b0b935b2ba26b0b730b3b2b960611b6144d3565b905090565b600080612b136145b0565b6001600160a01b031663c39def0b631cd554d160e21b6003546040518363ffffffff1660e01b81526004018083815260200182815260200192505050604080518083038186803b158015612b6657600080fd5b505afa158015612b7a573d6000803e3d6000fd5b505050506040513d6040811015612b9057600080fd5b50805160209091015190925090509091565b6000612adb826774616b657246656560c01b6145c7565b6000612adb82676d616b657246656560c01b6145c7565b602082015182516000918291612beb9163ffffffff61468b16565b90506000612c0b82600460109054906101000a9004600f0b600f0b6146b5565b612c19578460600151612c1f565b84604001515b90506000612c33828663ffffffff6146c216565b9050612c48611665848363ffffffff61468b16565b9695505050505050565b60208201516000906001600160401b031680612c72576000915050612adb565b6000612c7e828561471c565b6080860151909150612c9990600f0b8263ffffffff61468b16565b95945050505050565b6000612cac6125c0565b9050612cb781612726565b5061161b336040518060a00160405280868152602001848152602001612cde600254612ba2565b8152602001612cee600254612bb9565b8152602001859052612d91565b600080612d0960025461475d565b9050612d4a81612d3e612d30670de0b6b3a763ffff19612d288861477a565b60000361484c565b670de0b6b3a7640000614861565b9063ffffffff61468b16565b9392505050565b6000612adb827074616b65724665654e657874507269636560781b6145c7565b6000612adb82706d616b65724665654e657874507269636560781b6145c7565b6001600160a01b0382166000908152600760205260409020612db1614dff565b506040805160a08101825282546001600160401b038082168352600160401b82041660208301526001600160801b03600160801b9182900481169383019390935260018401549283166060830152909104600f90810b810b900b6080820152612e18614dff565b600080612e258487613cb6565b925092509250612e34816141b0565b60808085015190840151600454612e7392600f90810b92612e6792600160801b9004820b820b910b63ffffffff61353916565b9063ffffffff61366116565b60048054600f92830b6001600160801b03908116600160801b0291161790556080850151612edd91612ea691900b613656565b612ed1612eb98660800151600f0b613656565b6004546001600160801b03169063ffffffff6146c216565b9063ffffffff61487716565b600480546001600160801b0319166001600160801b03929092169190911790558115612fc757612f0b612ae1565b6001600160a01b031663d289ade2836040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b158015612f5057600080fd5b505af1158015612f64573d6000803e3d6000fd5b50505050608086015115612fc757608080870151600354600254895160408051938452602084019290925282820152606082018690525191927fc4be15f09b70f2353d054e324d6774570c38bcd0420aac862188ae7dcba2c6f692918290030190a25b604083015185546001600160801b03918216600160801b029116178555612fee838561435d565b83516000612ffa614346565b90508460800151600f0b60001415613026578654600060018901556001600160801b03191687556130df565b6080860151600f0b61306a576008805467ffffffffffffffff60801b198116600160801b918290046001600160401b03908116600181019091169092021790915591505b8654608086015160018901805460208c01516001600160801b03908116600f9490940b8116600160801b029116176001600160801b0319169190911790556001600160401b03828116600160401b0267ffffffffffffffff60401b1991851667ffffffffffffffff1990931692909217161787555b6040808601516080808801518b516020808e015186516001600160801b039096168652600f9390930b90850152838501526060830152810183905260a0810186905290516001600160a01b038b16916001600160401b03851691600080516020614e608339815191529181900360c00190a3505050505050505050565b6000612b03600080516020614e808339815191526144d3565b81156107d6576009600082600b81111561318b57fe5b60ff1681526020808201929092526040908101600020905162461bcd60e51b81526004810192835281546002600019610100600184161502019091160460248201819052919291829160440190849080156132275780601f106131fc57610100808354040283529160200191613227565b820191906000526020600020905b81548152906001019060200180831161320a57829003601f168201915b50509250505060405180910390fd5b6000806103e8670de0b6b3a7640000059050600061325982612ed1600254614494565b90506000613281611665836132758960800151600f0b89614079565b9063ffffffff6148d416565b905080156132b55760006132936143c3565b9050808210156132a1578091505b6132b1828563ffffffff6146c216565b9150505b60006132c18787613f90565b90508181116132d7576000945050505050612adb565b6132e7818363ffffffff61487716565b979650505050505050565b60006132fd84613656565b905060008413156133b0576000613312612ae1565b6001600160a01b0316636f9a0ca684846040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b0316815260200182815260200192505050602060405180830381600087803b15801561337157600080fd5b505af1158015613385573d6000803e3d6000fd5b505050506040513d602081101561339b57600080fd5b505190508181146133aa578094505b50613443565b600084121561343d576133c1612ae1565b6001600160a01b031663a7b5833f83836040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b0316815260200182815260200192505050600060405180830381600087803b15801561342057600080fd5b505af1158015613434573d6000803e3d6000fd5b50505050613443565b5061161b565b6001600160a01b0382166000908152600760205260409020613466818587612804565b6040805186815290516001600160a01b038516917fe20b33a51269d7e4c48682ccfacaf1ca004fdec7b161e7098e4847a0c05d0ce2919081900360200190a2805460018201546001600160a01b038516916001600160401b03811691600080516020614e60833981519152916001600160801b03600160801b91829004169104600f0b6000896134f4614346565b604080516001600160801b039096168652600f9490940b60208601528484019290925260608401526080830152600060a0830152519081900360c00190a35050505050565b600082820181831280159061354e5750838112155b80613563575060008312801561356357508381125b612ad85760405162461bcd60e51b8152600401808060200182810382526021815260200180614ea06021913960400191505060405180910390fd5b6000816135f2576040805162461bcd60e51b815260206004820181905260248201527f5369676e6564536166654d6174683a206469766973696f6e206279207a65726f604482015290519081900360640190fd5b816000191480156136065750600160ff1b83145b156136425760405162461bcd60e51b8152600401808060200182810382526021815260200180614ec16021913960400191505060405180910390fd5b600082848161364d57fe5b05949350505050565b6000612adb826148f2565b60008183038183128015906136765750838113155b8061368b575060008312801561368b57508381135b612ad85760405162461bcd60e51b8152600401808060200182810382526024815260200180614f4b6024913960400191505060405180910390fd5b60008260800151600f0b600014156136e057506000612adb565b6136f18360800151600f0b83614446565b6136fb8484613f90565b11159392505050565b6001600160a01b0383166000908152600760209081526040808320815160a08101835281546001600160401b038082168352600160401b820416948201949094526001600160801b03600160801b948590048116938201939093526001820154928316606082015292909104600f90810b810b900b6080830152919061378a9084613f90565b60018301548354600454929350600160801b91829004600f90810b810b936001600160401b03909216926137c99204810b900b8363ffffffff61366116565b600460106101000a8154816001600160801b030219169083600f0b6001600160801b031602179055506138166137fe83613656565b6004546001600160801b03169063ffffffff61487716565b600480546001600160801b0319166001600160801b03929092169190911790556000613840614346565b6040805160a0808201835260008083526001600160401b038086166020808601919091528486018390526001600160801b038d81166060808801919091526080808801869052885196870189529486528d54600160401b810490941692860192909252600160801b90920482169584019590955260018b01541693820193909352600f87900b928101929092529192506138da919061435d565b6001600160a01b0388166000908152600760205260408120818155600101819055613905848861408b565b905061390f612ae1565b6001600160a01b031663a7b5833f89836040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b0316815260200182815260200192505050600060405180830381600087803b15801561396e57600080fd5b505af1158015613982573d6000803e3d6000fd5b505060408051600080825260208201819052818301819052606082018c90526080820187905260a082015290516001600160a01b038d169350869250600080516020614e608339815191529181900360c00190a3876001600160a01b0316896001600160a01b0316847f62e7eb6698aabc6740afc94f06bbdfb947fc109fd24d4adb26014d44053ac2c3878b8660405180848152602001838152602001828152602001935050505060405180910390a480851115613aaf57613a42612ae1565b6001600160a01b031663d289ade2613a60878463ffffffff61487716565b6040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b158015613a9657600080fd5b505af1158015613aaa573d6000803e3d6000fd5b505050505b505050505050505050565b604080516001808252818301909252606091602080830190803883390190505090506e466c657869626c6553746f7261676560881b81600081518110613afc57fe5b60200260200101818152505090565b60608151835101604051908082528060200260200182016040528015613b3b578160200160208202803883390190505b50905060005b8351811015613b7d57838181518110613b5657fe5b6020026020010151828281518110613b6a57fe5b6020908102919091010152600101613b41565b5060005b8251811015613bc057828181518110613b9657fe5b6020026020010151828286510181518110613bad57fe5b6020908102919091010152600101613b81565b5092915050565b6005546000908190613be490429063ffffffff9081169061487716565b9050600062015180613bf585612cfb565b81613bfc57fe5b059050611f2982613c13838763ffffffff61468b16565b9063ffffffff61490816565b6080820151600090600f0b80613c39576000915050612adb565b6000613c5285602001516001600160401b03168561471c565b90506000613c608386614446565b90506000613ca983612e67613c8f876132758c604001516001600160801b03168861366190919063ffffffff16565b60608b01516001600160801b03169063ffffffff61353916565b90506132e760008261484c565b613cbe614dff565b81516000908190613cd85750839150600090506009613f89565b613ce68585602001516136c6565b15613cfa5750839150600090506003613f89565b600080613d05612b08565b915091508015613d22575085935060009250600b9150613f899050565b613d2c8683612bd0565b9350600080613d43898960200151886000036142cb565b91509150613d50816149b1565b15613d6757889650600095509350613f8992505050565b613d6f614dff565b6040518060a001604052808b600001516001600160401b03168152602001613d95614346565b6001600160401b03168152602001846001600160801b031681526020018a602001516001600160801b03168152602001613de38b600001518d60800151600f0b61353990919063ffffffff16565b600f0b81525090506000613e058b60800151600f0b8360800151600f0b6146b5565b8015613e2e5750613e1c8b60800151600f0b613656565b613e2c8360800151600f0b613656565b105b905080613e7857613e3d6143c3565b6040830151613e5b906001600160801b03168a63ffffffff6146c216565b1015613e7857508997506000965060079550613f89945050505050565b613e8d8260800151600f0b8b60200151614446565b8411613ea8575096506000955060039450613f899350505050565b6000613edb613ebd868b63ffffffff6146c216565b6132758d602001518660800151600f0b61468b90919063ffffffff16565b9050613ee681613656565b613f0c6064670de0b6b3a764000004613f00600254614494565b9063ffffffff6146c216565b1015613f2a57508a98506000975060069650613f8995505050505050565b50613f61613f4c8b602001516132756012600a0a606402613f006002546149c8565b8c60800151600f0b8460800151600f0b6149e8565b15613f7d57508997506000965060059550613f89945050505050565b50965060009450505050505b9250925092565b600080613f9d8484614ae6565b9050613faa60008261484c565b949350505050565b33600090815260076020526040902060010154600160801b9004600f90810b900b613fdf8115600a613175565b6000613fe96125c0565b9050613ff481612726565b5061161b336040518060a0016040528085600003815260200184815260200161401e600254612ba2565b815260200161402e600254612bb9565b8152602001869052612d91565b60008061405e84606001516001600160801b03168461366190919063ffffffff16565b6080850151909150613faa90600f0b8263ffffffff61468b16565b6000612ad8838363ffffffff61468b16565b6000806140b2614099614b20565b6140a6856140a688613656565b9063ffffffff614ba316565b905060006140be6141fc565b90508082116140cd5780612c99565b509392505050565b6000612b037522bc31b430b733b2a1b4b931bab4ba213932b0b5b2b960511b6144d3565b6000612b036b53797374656d53746174757360a01b6144d3565b600454600090600160801b9004600f90810b900b15801561413b5750600854600f90810b900b155b1561414857506000611f2e565b600061416361415684614275565b849063ffffffff61353916565b6008546004549192506000916141a391600f90810b810b9161419791600160801b909104810b900b8563ffffffff61468b16565b9063ffffffff61353916565b9050611f2981600061484c565b6141b9816149b1565b156107f4576009600082600b81111561318b57fe5b60006141db600254612ba2565b60408301526002546141ec90612bb9565b6060830152612adb826000612bd0565b6000614206614bcd565b6001600160a01b03166323257c2b600080516020614e8083398151915272667574757265734d696e4b656570657246656560681b6040518363ffffffff1660e01b8152600401808381526020018281526020019250505060206040518083038186803b15801561258f57600080fd5b6000612adb61428383613bc7565b600661428d614346565b8154811061429757fe5b90600052602060002090600291828204019190066010029054906101000a9004600f0b600f0b61353990919063ffffffff16565b60008060006142de846141978888614ae6565b905060008112156142f75750600091506007905061433e565b60808601518190600f0b600061430d8289614446565b9050811580159061431e5750808311155b1561433357826003955095505050505061433e565b509093506000925050505b935093915050565b600654600090612b0390600163ffffffff61487716565b600061436883614bea565b9050600061437583614bea565b600854909150614398908290612e6790600f90810b900b8563ffffffff61353916565b60088054600f9290920b6001600160801b03166001600160801b031990921691909117905550505050565b60006143cd614bcd565b6001600160a01b03166323257c2b600080516020614e808339815191527f667574757265734d696e496e697469616c4d617267696e0000000000000000006040518363ffffffff1660e01b8152600401808381526020018281526020019250505060206040518083038186803b15801561258f57600080fd5b600080614454614099614c74565b9050613faa614463858561408b565b829063ffffffff6146c216565b60008161447f57506000612d4a565b613faa826132758660800151600f0b86614079565b6000612adb826a6d61784c6576657261676560a81b6145c7565b6000612adb82756e6578745072696365436f6e6669726d57696e646f7760501b6145c7565b600081815260016020908152604080832054815170026b4b9b9b4b7339030b2323932b9b99d1607d1b9381019390935260318084018690528251808503909101815260519093019091526001600160a01b03169081613bc05760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561457557818101518382015260200161455d565b50505050905090810190601f1680156145a25780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b6000612b036822bc31b430b733b2b960b91b6144d3565b60006145d1614bcd565b6001600160a01b03166323257c2b600080516020614e8083398151915285856040516020018083815260200182815260200192505050604051602081830303815290604052805190602001206040518363ffffffff1660e01b8152600401808381526020018281526020019250505060206040518083038186803b15801561465857600080fd5b505afa15801561466c573d6000803e3d6000fd5b505050506040513d602081101561468257600080fd5b50519392505050565b6000670de0b6b3a76400006146a6848463ffffffff61490816565b816146ad57fe5b059392505050565b6000908113159112151490565b600082820183811015612ad8576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b6000612ad86006848154811061472e57fe5b90600052602060002090600291828204019190066010029054906101000a9004600f0b600f0b612e6784614275565b6000612adb826d6d617846756e64696e675261746560901b6145c7565b60008082116147c6576040805162461bcd60e51b815260206004820152601360248201527270726963652063616e2774206265207a65726f60681b604482015290519081900360640190fd5b60006147e3836147d7600254614cf7565b9063ffffffff614d1216565b90508061482b576040805162461bcd60e51b8152602060048201526011602482015270736b65775363616c65206973207a65726f60781b604482015290519081900360640190fd5b600454612d4a90600160801b9004600f90810b900b8263ffffffff6148d416565b600081831261485b5782612ad8565b50919050565b60008183126148705781612ad8565b5090919050565b6000828211156148ce576040805162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b50900390565b6000612ad88261165985670de0b6b3a764000063ffffffff61490816565b60008082126149015781612adb565b5060000390565b60008261491757506000612adb565b8260001914801561492b5750600160ff1b82145b156149675760405162461bcd60e51b8152600401808060200182810382526027815260200180614f036027913960400191505060405180910390fd5b8282028284828161497457fe5b0514612ad85760405162461bcd60e51b8152600401808060200182810382526027815260200180614f036027913960400191505060405180910390fd5b60008082600b8111156149c057fe5b141592915050565b6000612adb82701b585e13585c9ad95d15985b1d595554d1607a1b6145c7565b60006149f483836146b5565b8015614a105750614a0483613656565b614a0d83613656565b11155b15614a1d57506000612d4a565b600454600090614a4790849061419790600160801b9004600f90810b900b8763ffffffff61366116565b90506000614a7b614a57856148f2565b614197614a63886148f2565b6004546001600160801b03169063ffffffff61366116565b905060008460001215614a9f57614a98828463ffffffff61353916565b9050614ab2565b614aaf828463ffffffff61366116565b90505b614ac661166582600263ffffffff61359e16565b871015614ad95760019350505050612d4a565b5060009695505050505050565b600080614af38484612c52565b9050613faa81614197614b06878761403b565b60408801516001600160801b03169063ffffffff61353916565b6000614b2a614bcd565b6001600160a01b03166323257c2b600080516020614e808339815191527f667574757265734c69717569646174696f6e466565526174696f0000000000006040518363ffffffff1660e01b8152600401808381526020018281526020019250505060206040518083038186803b15801561258f57600080fd5b6000670de0b6b3a7640000614bbe848463ffffffff614d3c16565b81614bc557fe5b049392505050565b6000612b036e466c657869626c6553746f7261676560881b6144d3565b6000612adb614c5a614c46600685602001516001600160401b031681548110614c0f57fe5b6000918252602090912060028204015460608701516001600160801b0316916001166010026101000a9004600f90810b900b613539565b6080850151600f0b9063ffffffff61468b16565b60408401516001600160801b03169063ffffffff61366116565b6000614c7e614bcd565b6001600160a01b03166323257c2b600080516020614e808339815191527f667574757265734c69717569646174696f6e427566666572526174696f0000006040518363ffffffff1660e01b8152600401808381526020018281526020019250505060206040518083038186803b15801561258f57600080fd5b6000612adb826b1cdad95dd4d8d85b195554d160a21b6145c7565b6000612ad882614d3085670de0b6b3a764000063ffffffff614d3c16565b9063ffffffff614d9516565b600082614d4b57506000612adb565b82820282848281614d5857fe5b0414612ad85760405162461bcd60e51b8152600401808060200182810382526021815260200180614ee26021913960400191505060405180910390fd5b6000808211614deb576040805162461bcd60e51b815260206004820152601a60248201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604482015290519081900360640190fd5b6000828481614df657fe5b04949350505050565b6040805160a08101825260008082526020820181905291810182905260608101829052608081019190915290565b6040518060a001604052806000815260200160008152602001600081526020016000815260200160008019168152509056fe930fd93131df035ac630ef616ad4212af6370377bf327e905c2724cd01d95097467574757265734d61726b657453657474696e677300000000000000000000005369676e6564536166654d6174683a206164646974696f6e206f766572666c6f775369676e6564536166654d6174683a206469766973696f6e206f766572666c6f77536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f775369676e6564536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f7763616e6e6f742062652063616e63656c6c6564206279206b6565706572207965745369676e6564536166654d6174683a207375627472616374696f6e206f766572666c6f77a265627a7a723158201dc3cd2b6f60f91da7f0edd24595e4923455f34c48b265647c4b551b0a64e3a164736f6c634300051000320000000000000000000000001cb059b7e74fd21665968c908806143e744d5f3073425443000000000000000000000000000000000000000000000000000000007342544300000000000000000000000000000000000000000000000000000000
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106102475760003560e01c8063785cdeec1161013b578063b895daab116100b8578063cdf456e11161007c578063cdf456e114610788578063d24378eb14610790578063d7103a4614610798578063e8c63470146107a0578063eb56105d146107a857610247565b8063b895daab14610706578063b9f4ff551461072c578063c393d0e314610752578063c8023af41461075a578063cded0cea1461078057610247565b8063996c61ad116100ff578063996c61ad146106075780639cfbf4e41461067a578063a28a2bc0146106a0578063a8c92cf6146106c3578063b111dfac146106e057610247565b8063785cdeec1461053e57806388a3c84814610564578063899ffef414610581578063917e77f5146105d9578063964db90c146105e157610247565b80633aef4d0b116101c957806355f575101161018d57806355f57510146104795780635a1cbd2b146104e75780635fc890c2146104ef57806374185360146105105780637498a0f01461051857610247565b80633aef4d0b146103af57806341108cf2146103c9578063450adee0146103e65780634eb985cc1461040c57806355b9437a1461041457610247565b80631bf556d0116102105780631bf556d01461030e57806327b9a236146103345780632af64bd3146103555780632b58ecef146103715780632f07449f1461039257610247565b8062f6beff1461024c57806304f3bcec146102715780630b198211146102955780631172cb24146102b25780631906652f146102d8575b600080fd5b61026f6004803603604081101561026257600080fd5b50803590602001356107cc565b005b6102796107da565b604080516001600160a01b039092168252519081900360200190f35b61026f600480360360208110156102ab57600080fd5b50356107e9565b61026f600480360360208110156102c857600080fd5b50356001600160a01b03166107f7565b6102f5600480360360208110156102ee57600080fd5b5035610c10565b6040805192835290151560208301528051918290030190f35b6102f56004803603602081101561032457600080fd5b50356001600160a01b0316610c98565b61033c610d39565b6040805163ffffffff9092168252519081900360200190f35b61035d610d45565b604080519115158252519081900360200190f35b610379610e4f565b60408051600f92830b90920b8252519081900360200190f35b61026f600480360360208110156103a857600080fd5b5035610e5f565b6103b7610e6a565b60408051918252519081900360200190f35b610379600480360360208110156103df57600080fd5b5035610e87565b61026f600480360360208110156103fc57600080fd5b50356001600160a01b0316610ebb565b6103b7611459565b61043a6004803603602081101561042a57600080fd5b50356001600160a01b03166114e4565b60408051600f96870b90960b86526001600160801b03948516602087015292841685840152921660608401526080830191909152519081900360a00190f35b61049f6004803603602081101561048f57600080fd5b50356001600160a01b0316611524565b604080516001600160401b0396871681529490951660208501526001600160801b039283168486015291166060830152600f90810b900b608082015290519081900360a00190f35b61026f611570565b6104f7611620565b6040805192835260208301919091528051918290030190f35b61026f61168c565b61026f6004803603602081101561052e57600080fd5b50356001600160a01b0316611853565b6102f56004803603602081101561055457600080fd5b50356001600160a01b0316611904565b61026f6004803603602081101561057a57600080fd5b503561199b565b6105896119bc565b60408051602080825283518183015283519192839290830191858101910280838360005b838110156105c55781810151838201526020016105ad565b505050509050019250505060405180910390f35b6102f5611ad3565b6102f5600480360360208110156105f757600080fd5b50356001600160a01b0316611af7565b6106336004803603604081101561061d57600080fd5b50803590602001356001600160a01b0316611b9c565b6040518087815260200186815260200185815260200184815260200183815260200182600b81111561066157fe5b60ff168152602001965050505050505060405180910390f35b6102f56004803603602081101561069057600080fd5b50356001600160a01b0316611d03565b61026f600480360360408110156106b657600080fd5b5080359060200135611d9a565b61026f600480360360208110156106d957600080fd5b5035611da4565b6102f5600480360360208110156106f657600080fd5b50356001600160a01b0316611dad565b6102f56004803603602081101561071c57600080fd5b50356001600160a01b0316611e44565b61035d6004803603602081101561074257600080fd5b50356001600160a01b0316611e8b565b61026f611f33565b6103b76004803603602081101561077057600080fd5b50356001600160a01b0316611f3f565b6103b761202a565b6103b7612030565b6102f5612036565b6103b761214d565b6102f5612153565b6107b061216e565b604080516001600160801b039092168252519081900360200190f35b6107d6828261217d565b5050565b6000546001600160a01b031681565b6107f481600061217d565b50565b6107ff614dff565b506001600160a01b0381166000908152600a6020908152604091829020825160a0810184528154600f81810b810b810b8084526001600160801b03600160801b938490048116968501969096526001850154808716978501979097529190950490931660608201526002909101546080820152910b6108b9576040805162461bcd60e51b8152602060048201526011602482015270373790383932bb34b7bab99037b93232b960791b604482015290519081900360640190fd5b60006108c361254d565b6001600160a01b0316637a018a1e6003546040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561090857600080fd5b505afa15801561091c573d6000803e3d6000fd5b505050506040513d602081101561093257600080fd5b505190506001600160a01b038316331415610a11576001600160a01b0383166000908152600760205260408120906109686125c0565b9050600061097582612726565b905061098f838387606001516001600160801b0316612804565b82546001840154604080516001600160801b03600160801b808604919091168252909204600f90810b900b60208301526000828201819052606083018690526080830185905260a0830152516001600160a01b038916926001600160401b031691600080516020614e60833981519152919081900360c00190a3505050610af0565b610a288183602001516001600160801b0316612abc565b610a635760405162461bcd60e51b8152600401808060200182810382526021815260200180614f2a6021913960400191505060405180910390fd5b610a6b612ae1565b6001600160a01b031663a7b5833f3384606001516040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b03168152602001826001600160801b0316815260200192505050600060405180830381600087803b158015610ad757600080fd5b505af1158015610aeb573d6000803e3d6000fd5b505050505b610af8612ae1565b6001600160a01b031663d289ade283604001516040518263ffffffff1660e01b815260040180826001600160801b03168152602001915050600060405180830381600087803b158015610b4a57600080fd5b505af1158015610b5e573d6000803e3d6000fd5b505050506001600160a01b0383166000818152600a602090815260408083208381556001810184905560020192909255845185820151868401516060808901516080808b015188518b8152600f9790970b978701979097526001600160801b039485168689015292841691850191909152919091169082015260a081019190915290517fa2c30b7be4efaff9ad0f74d77ec9a944a7cc92a7b8eb941fbfc6489fb19370f39181900360c00190a2505050565b600080600080610c1e612036565b91509150600080610c2d612b08565b91509150610c39614e2d565b6040518060a00160405280898152602001868152602001610c5b600254612ba2565b8152602001610c6b600254612bb9565b815260006020909101529050610c818184612bd0565b8480610c8a5750825b965096505050505050915091565b600080600080610ca6612036565b6001600160a01b038716600090815260076020908152604091829020825160a08101845281546001600160401b038082168352600160401b820416938201939093526001600160801b03600160801b9384900481169482019490945260019091015492831660608201529104600f90810b810b900b60808201529193509150610d2f9083612c52565b9350915050915091565b60055463ffffffff1681565b60006060610d516119bc565b905060005b8151811015610e45576000828281518110610d6d57fe5b602090810291909101810151600081815260018352604080822054915481516321f8a72160e01b81526004810185905291519395506001600160a01b03928316949216926321f8a72192602480840193919291829003018186803b158015610dd457600080fd5b505afa158015610de8573d6000803e3d6000fd5b505050506040513d6020811015610dfe57600080fd5b50516001600160a01b0316141580610e2b57506000818152600160205260409020546001600160a01b0316155b15610e3c5760009350505050610e4c565b50600101610d56565b5060019150505b90565b600454600160801b9004600f0b81565b6107f4816000612ca2565b600080610e75612036565b509050610e8181612cfb565b91505090565b60068181548110610e9457fe5b9060005260206000209060029182820401919006601002915054906101000a9004600f0b81565b610ec3614dff565b506001600160a01b0381166000908152600a6020908152604091829020825160a0810184528154600f81810b810b810b8084526001600160801b03600160801b938490048116968501969096526001850154808716978501979097529190950490931660608201526002909101546080820152910b610f7d576040805162461bcd60e51b8152602060048201526011602482015270373790383932bb34b7bab99037b93232b960791b604482015290519081900360640190fd5b6000610f8761254d565b6001600160a01b0316637a018a1e6003546040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b158015610fcc57600080fd5b505afa158015610fe0573d6000803e3d6000fd5b505050506040513d6020811015610ff657600080fd5b505160208301519091506001600160801b031681101561105d576040805162461bcd60e51b815260206004820152601a60248201527f74617267657420726f756e644964206e6f742072656163686564000000000000604482015290519081900360640190fd5b6110748183602001516001600160801b0316612abc565b156110c6576040805162461bcd60e51b815260206004820152601960248201527f6f7264657220746f6f206f6c642c207573652063616e63656c00000000000000604482015290519081900360640190fd5b60408201516001600160801b0316336001600160a01b03851614156110f95760608301516001600160801b031601611186565b611101612ae1565b6001600160a01b031663a7b5833f3385606001516040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b03168152602001826001600160801b0316815260200192505050600060405180830381600087803b15801561116d57600080fd5b505af1158015611181573d6000803e3d6000fd5b505050505b6001600160a01b0384166000908152600760205260408120906111a76125c0565b905060006111b482612726565b90506111c1838386612804565b82546001840154604080516001600160801b03600160801b808604919091168252909204600f90810b900b60208301526000828201819052606083018690526080830185905260a0830152516001600160a01b038a16926001600160401b031691600080516020614e60833981519152919081900360c00190a3600061124561254d565b6001600160a01b031663fdadbc7e60035489602001516040518363ffffffff1660e01b815260040180838152602001826001600160801b0316815260200192505050604080518083038186803b15801561129e57600080fd5b505afa1580156112b2573d6000803e3d6000fd5b505050506040513d60408110156112c857600080fd5b50516040805160a0810182528951600f0b81526020810183905260025492935061131c928b928201906112fa90612d51565b815260200161130a600254612d71565b81526020018a60800151815250612d91565b600a6000896001600160a01b03166001600160a01b03168152602001908152602001600020600080820160006101000a8154906001600160801b0302191690556000820160106101000a8154906001600160801b0302191690556001820160006101000a8154906001600160801b0302191690556001820160106101000a8154906001600160801b03021916905560028201600090555050876001600160a01b03167fa2c30b7be4efaff9ad0f74d77ec9a944a7cc92a7b8eb941fbfc6489fb19370f38789600001518a602001518b604001518c606001518d608001516040518087815260200186600f0b8152602001856001600160801b03168152602001846001600160801b03168152602001836001600160801b03168152602001828152602001965050505050505060405180910390a25050505050505050565b600061148261146661315c565b6001600160a01b0316336001600160a01b031614156008613175565b60008061148d612036565b9150915080156114d4576040805162461bcd60e51b815260206004820152600d60248201526c496e76616c696420707269636560981b604482015290519081900360640190fd5b6114dd82612726565b9250505090565b600a60205260009081526040902080546001820154600290920154600f82900b926001600160801b03600160801b93849004811693828216939204169085565b600760205260009081526040902080546001909101546001600160401b0380831692600160401b8104909116916001600160801b03600160801b928390048116929082169104600f0b85565b33600061157b6125c0565b905061158681612726565b506001600160a01b0382166000908152600760209081526040808320815160a08101835281546001600160401b038082168352600160401b820416948201949094526001600160801b03600160801b948590048116938201939093526001909101549182166060820152919004600f90810b810b900b608082015261160b9083613236565b600003905061161b8183856132f2565b505050565b60045460009081906001600160801b03811690600160801b9004600f90810b900b61166a6116656002611659858563ffffffff61353916565b9063ffffffff61359e16565b613656565b6116826116656002611659868663ffffffff61366116565b9350935050509091565b60606116966119bc565b905060005b81518110156107d65760008282815181106116b257fe5b60209081029190910181015160008054604080517f5265736f6c766572206d697373696e67207461726765743a2000000000000000818701526039808201869052825180830390910181526059820180845263dacb2d0160e01b9052605d8201868152607d83019384528151609d840152815196985094966001600160a01b039094169563dacb2d0195899592949093909260bd90910191908501908083838c5b8381101561176b578181015183820152602001611753565b50505050905090810190601f1680156117985780820380516001836020036101000a031916815260200191505b50935050505060206040518083038186803b1580156117b657600080fd5b505afa1580156117ca573d6000803e3d6000fd5b505050506040513d60208110156117e057600080fd5b505160008381526001602090815260409182902080546001600160a01b0319166001600160a01b03851690811790915582518681529182015281519293507f88a93678a3692f6789d9546fc621bf7234b101ddb7d4fe479455112831b8aa68929081900390910190a1505060010161169b565b600061185d6125c0565b905061186881612726565b506001600160a01b038216600090815260076020908152604091829020825160a08101845281546001600160401b038082168352600160401b820416938201939093526001600160801b03600160801b9384900481169482019490945260019091015492831660608201529104600f90810b810b900b60808201526118f9906118f190836136c6565b156004613175565b6107d6823383613704565b600080600080611912612036565b6001600160a01b038716600090815260076020908152604091829020825160a08101845281546001600160401b038082168352600160401b820416938201939093526001600160801b03600160801b9384900481169482019490945260019091015492831660608201529104600f90810b810b900b60808201529193509150610d2f9083613236565b60006119a56125c0565b90506119b081612726565b506107d68282336132f2565b6060806119c7613aba565b60408051600580825260c08201909252919250606091906020820160a0803883390190505090506822bc31b430b733b2b960b91b81600081518110611a0857fe5b6020026020010181815250507522bc31b430b733b2a1b4b931bab4ba213932b0b5b2b960511b81600181518110611a3b57fe5b60200260200101818152505073233aba3ab932b9a6b0b935b2ba26b0b730b3b2b960611b81600281518110611a6c57fe5b602002602001018181525050600080516020614e8083398151915281600381518110611a9457fe5b6020026020010181815250506b53797374656d53746174757360a01b81600481518110611abd57fe5b6020026020010181815250506114dd8282613b0b565b600080600080611ae1612036565b91509150611aee82613bc7565b93509150509091565b600080600080611b05612036565b6001600160a01b0387166000908152600760209081526040808320815160a08101835281546001600160401b038082168352600160401b820416948201949094526001600160801b03600160801b948590048116938201939093526001909101549182166060820152919004600f90810b810b900b608082015292945090925090611b909084613c1f565b94509092505050915091565b6000806000806000806000611baf612036565b90955090508015611bd457506000955085945084935083925082915060019050611cf9565b611bdc614e2d565b6040518060a001604052808b8152602001878152602001611bfe600254612ba2565b8152602001611c0e600254612bb9565b815260006020909101529050611c22614dff565b6001600160a01b038a166000908152600760209081526040808320815160a08101835281546001600160401b038082168352600160401b820416948201949094526001600160801b03600160801b948590048116938201939093526001909101549182166060820152919004600f90810b810b900b60808201528190611ca89085613cb6565b925092509250611cc58384606001516001600160801b0316613c1f565b604084015160808501516060909501516001600160801b039182169d50600f9590950b9b5090931698509196509450925050505b9295509295509295565b600080600080611d11612036565b6001600160a01b038716600090815260076020908152604091829020825160a08101845281546001600160401b038082168352600160401b820416938201939093526001600160801b03600160801b9384900481169482019490945260019091015492831660608201529104600f90810b810b900b60808201529193509150610d2f9083613f90565b6107d68282612ca2565b6107f481613fb2565b600080600080611dbb612036565b6001600160a01b038716600090815260076020908152604091829020825160a08101845281546001600160401b038082168352600160401b820416938201939093526001600160801b03600160801b9384900481169482019490945260019091015492831660608201529104600f90810b810b900b60808201529193509150610d2f908361403b565b600080600080611e52612036565b6001600160a01b0387166000908152600760205260409020600101549193509150610d2f90600160801b9004600f90810b900b83614079565b6000806000611e98612036565b9150915080158015611f2957506001600160a01b038416600090815260076020908152604091829020825160a08101845281546001600160401b038082168352600160401b820416938201939093526001600160801b03600160801b9384900481169482019490945260019091015492831660608201529104600f90810b810b900b6080820152611f2990836136c6565b925050505b919050565b611f3d6000613fb2565b565b6000806000611f4c612036565b9150915080158015611fdd57506001600160a01b038416600090815260076020908152604091829020825160a08101845281546001600160401b038082168352600160401b820416938201939093526001600160801b03600160801b9384900481169482019490945260019091015492831660608201529104600f90810b810b900b6080820152611fdd90836136c6565b1561201f576001600160a01b03841660009081526007602052604090206001015461201690600160801b9004600f90810b900b8361408b565b92505050611f2e565b600092505050611f2e565b60065490565b60035481565b6000806120416140d5565b6001600160a01b031663cb1ec3176003546040518263ffffffff1660e01b815260040180828152602001915050604080518083038186803b15801561208557600080fd5b505afa158015612099573d6000803e3d6000fd5b505050506040513d60408110156120af57600080fd5b508051602090910151909250905080806120c7575081155b8061214757506120d56140f9565b6001600160a01b031663b431c0ea6003546040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561211a57600080fd5b505afa15801561212e573d6000803e3d6000fd5b505050506040513d602081101561214457600080fd5b50515b90509091565b60025481565b600080600080612161612036565b91509150611aee82614113565b6004546001600160801b031681565b336000908152600a6020526040902054600f90810b900b156121de576040805162461bcd60e51b815260206004820152601560248201527470726576696f7573206f726465722065786973747360581b604482015290519081900360640190fd5b336000908152600760205260408120906121f66125c0565b9050600061220382612726565b905061220d614e2d565b6040518060a0016040528087815260200184815260200161222f600254612d51565b815260200161223f600254612d71565b815260209081018790526040805160a08101825287546001600160401b038082168352600160401b820416938201939093526001600160801b03600160801b9384900481169282019290925260018801549182166060820152919004600f90810b810b900b60808201529091506000906122b99083613cb6565b925050506122c6816141b0565b60006122d1836141ce565b905060006122dd6141fc565b90506122ef8787838501600003612804565b86546001880154604080516001600160801b03600160801b808604919091168252909204600f90810b900b60208301526000828201819052606083018a90526080830189905260a08301525133926001600160401b031691600080516020614e60833981519152919081900360c00190a3600061236a61254d565b6001600160a01b0316637a018a1e6003546040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b1580156123af57600080fd5b505afa1580156123c3573d6000803e3d6000fd5b505050506040513d60208110156123d957600080fd5b505160010190506123e8614dff565b6040518060a001604052808c600f0b8152602001836001600160801b03168152602001856001600160801b03168152602001846001600160801b031681526020018b8152509050336001600160a01b03167f83f1fe372b879782cdc7a779adb56774bf755506d7486939437b40b64ba15927826000015183602001518460400151856060015186608001516040518086600f0b8152602001856001600160801b03168152602001846001600160801b03168152602001836001600160801b031681526020018281526020019550505050505060405180910390a2336000908152600a602090815260409182902083518154928501516001600160801b03908116600160801b908102600f9390930b82166001600160801b03199586161782169290921783559385015160018301805460608801518716909302918616929094169190911790931692909217905560809091015160029091015550505050505050505050565b60006125576140d5565b6001600160a01b0316634ffcd9df6040518163ffffffff1660e01b815260040160206040518083038186803b15801561258f57600080fd5b505afa1580156125a3573d6000803e3d6000fd5b505050506040513d60208110156125b957600080fd5b5051905090565b60006125ca6140f9565b6001600160a01b031663856aae6c6002546040518263ffffffff1660e01b81526004018082815260200191505060006040518083038186803b15801561260f57600080fd5b505afa158015612623573d6000803e3d6000fd5b5050505061262f6140f9565b6001600160a01b03166342a28e216003546040518263ffffffff1660e01b81526004018082815260200191505060006040518083038186803b15801561267457600080fd5b505afa158015612688573d6000803e3d6000fd5b505050506000806126976140d5565b6001600160a01b031663a47af19e6003546040518263ffffffff1660e01b8152600401808281526020019150506040805180830381600087803b1580156126dd57600080fd5b505af11580156126f1573d6000803e3d6000fd5b505050506040513d604081101561270757600080fd5b5080516020909101519092509050612720816001613175565b50905090565b6006546000908161273684614275565b6006805460018181018355600092909252600281047ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f018054600f85900b6001600160801b03908116601094909516939093026101000a93840292909302199092161790556005805463ffffffff42811663ffffffff19909216919091179182905560408051848152602081018790529290911682820152519192507f368c0bb7dd8da0fb0bedc14e770da5778b8f19a5820635ccd99285d0ffdb6954919081900360600190a15092915050565b61280c614dff565b506040805160a08101825284546001600160401b038082168352600160401b82041660208301526001600160801b03600160801b9182900481169383019390935260018601549283166060830152909104600f90810b810b900b60808201526000806128798386866142cb565b91509150612886816141b0565b6001860154600160801b9004600f90810b900b60006128a3614346565b90506129906040518060a0016040528060006001600160401b03168152602001836001600160401b03168152602001866001600160801b03168152602001896001600160801b0316815260200184600f0b8152506040518060a0016040528060006001600160401b031681526020018b60000160089054906101000a90046001600160401b03166001600160401b031681526020018b60000160109054906101000a90046001600160801b03166001600160801b031681526020018b60010160009054906101000a90046001600160801b03166001600160801b0316815260200185600f0b81525061435d565b87546001600160801b03808616600160801b0291161788558115612ab2576001880180546001600160801b0319166001600160801b038916179055875467ffffffffffffffff60401b1916600160401b6001600160401b038316021788556000861215612ab257612ab2612a026143c3565b851080612a2b57506001890154612a2790600160801b9004600f90810b900b89614446565b8511155b80612aab57506040805160a0810182528a546001600160401b038082168352600160401b82041660208301526001600160801b03600160801b9182900481169383019390935260018c01549283166060830152909104600f90810b810b900b6080820152612a9e90611665908a88614470565b612aa9600254614494565b105b6007613175565b5050505050505050565b60008183118015612ad85750612ad36002546144ae565b828403115b90505b92915050565b6000612b0373233aba3ab932b9a6b0b935b2ba26b0b730b3b2b960611b6144d3565b905090565b600080612b136145b0565b6001600160a01b031663c39def0b631cd554d160e21b6003546040518363ffffffff1660e01b81526004018083815260200182815260200192505050604080518083038186803b158015612b6657600080fd5b505afa158015612b7a573d6000803e3d6000fd5b505050506040513d6040811015612b9057600080fd5b50805160209091015190925090509091565b6000612adb826774616b657246656560c01b6145c7565b6000612adb82676d616b657246656560c01b6145c7565b602082015182516000918291612beb9163ffffffff61468b16565b90506000612c0b82600460109054906101000a9004600f0b600f0b6146b5565b612c19578460600151612c1f565b84604001515b90506000612c33828663ffffffff6146c216565b9050612c48611665848363ffffffff61468b16565b9695505050505050565b60208201516000906001600160401b031680612c72576000915050612adb565b6000612c7e828561471c565b6080860151909150612c9990600f0b8263ffffffff61468b16565b95945050505050565b6000612cac6125c0565b9050612cb781612726565b5061161b336040518060a00160405280868152602001848152602001612cde600254612ba2565b8152602001612cee600254612bb9565b8152602001859052612d91565b600080612d0960025461475d565b9050612d4a81612d3e612d30670de0b6b3a763ffff19612d288861477a565b60000361484c565b670de0b6b3a7640000614861565b9063ffffffff61468b16565b9392505050565b6000612adb827074616b65724665654e657874507269636560781b6145c7565b6000612adb82706d616b65724665654e657874507269636560781b6145c7565b6001600160a01b0382166000908152600760205260409020612db1614dff565b506040805160a08101825282546001600160401b038082168352600160401b82041660208301526001600160801b03600160801b9182900481169383019390935260018401549283166060830152909104600f90810b810b900b6080820152612e18614dff565b600080612e258487613cb6565b925092509250612e34816141b0565b60808085015190840151600454612e7392600f90810b92612e6792600160801b9004820b820b910b63ffffffff61353916565b9063ffffffff61366116565b60048054600f92830b6001600160801b03908116600160801b0291161790556080850151612edd91612ea691900b613656565b612ed1612eb98660800151600f0b613656565b6004546001600160801b03169063ffffffff6146c216565b9063ffffffff61487716565b600480546001600160801b0319166001600160801b03929092169190911790558115612fc757612f0b612ae1565b6001600160a01b031663d289ade2836040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b158015612f5057600080fd5b505af1158015612f64573d6000803e3d6000fd5b50505050608086015115612fc757608080870151600354600254895160408051938452602084019290925282820152606082018690525191927fc4be15f09b70f2353d054e324d6774570c38bcd0420aac862188ae7dcba2c6f692918290030190a25b604083015185546001600160801b03918216600160801b029116178555612fee838561435d565b83516000612ffa614346565b90508460800151600f0b60001415613026578654600060018901556001600160801b03191687556130df565b6080860151600f0b61306a576008805467ffffffffffffffff60801b198116600160801b918290046001600160401b03908116600181019091169092021790915591505b8654608086015160018901805460208c01516001600160801b03908116600f9490940b8116600160801b029116176001600160801b0319169190911790556001600160401b03828116600160401b0267ffffffffffffffff60401b1991851667ffffffffffffffff1990931692909217161787555b6040808601516080808801518b516020808e015186516001600160801b039096168652600f9390930b90850152838501526060830152810183905260a0810186905290516001600160a01b038b16916001600160401b03851691600080516020614e608339815191529181900360c00190a3505050505050505050565b6000612b03600080516020614e808339815191526144d3565b81156107d6576009600082600b81111561318b57fe5b60ff1681526020808201929092526040908101600020905162461bcd60e51b81526004810192835281546002600019610100600184161502019091160460248201819052919291829160440190849080156132275780601f106131fc57610100808354040283529160200191613227565b820191906000526020600020905b81548152906001019060200180831161320a57829003601f168201915b50509250505060405180910390fd5b6000806103e8670de0b6b3a7640000059050600061325982612ed1600254614494565b90506000613281611665836132758960800151600f0b89614079565b9063ffffffff6148d416565b905080156132b55760006132936143c3565b9050808210156132a1578091505b6132b1828563ffffffff6146c216565b9150505b60006132c18787613f90565b90508181116132d7576000945050505050612adb565b6132e7818363ffffffff61487716565b979650505050505050565b60006132fd84613656565b905060008413156133b0576000613312612ae1565b6001600160a01b0316636f9a0ca684846040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b0316815260200182815260200192505050602060405180830381600087803b15801561337157600080fd5b505af1158015613385573d6000803e3d6000fd5b505050506040513d602081101561339b57600080fd5b505190508181146133aa578094505b50613443565b600084121561343d576133c1612ae1565b6001600160a01b031663a7b5833f83836040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b0316815260200182815260200192505050600060405180830381600087803b15801561342057600080fd5b505af1158015613434573d6000803e3d6000fd5b50505050613443565b5061161b565b6001600160a01b0382166000908152600760205260409020613466818587612804565b6040805186815290516001600160a01b038516917fe20b33a51269d7e4c48682ccfacaf1ca004fdec7b161e7098e4847a0c05d0ce2919081900360200190a2805460018201546001600160a01b038516916001600160401b03811691600080516020614e60833981519152916001600160801b03600160801b91829004169104600f0b6000896134f4614346565b604080516001600160801b039096168652600f9490940b60208601528484019290925260608401526080830152600060a0830152519081900360c00190a35050505050565b600082820181831280159061354e5750838112155b80613563575060008312801561356357508381125b612ad85760405162461bcd60e51b8152600401808060200182810382526021815260200180614ea06021913960400191505060405180910390fd5b6000816135f2576040805162461bcd60e51b815260206004820181905260248201527f5369676e6564536166654d6174683a206469766973696f6e206279207a65726f604482015290519081900360640190fd5b816000191480156136065750600160ff1b83145b156136425760405162461bcd60e51b8152600401808060200182810382526021815260200180614ec16021913960400191505060405180910390fd5b600082848161364d57fe5b05949350505050565b6000612adb826148f2565b60008183038183128015906136765750838113155b8061368b575060008312801561368b57508381135b612ad85760405162461bcd60e51b8152600401808060200182810382526024815260200180614f4b6024913960400191505060405180910390fd5b60008260800151600f0b600014156136e057506000612adb565b6136f18360800151600f0b83614446565b6136fb8484613f90565b11159392505050565b6001600160a01b0383166000908152600760209081526040808320815160a08101835281546001600160401b038082168352600160401b820416948201949094526001600160801b03600160801b948590048116938201939093526001820154928316606082015292909104600f90810b810b900b6080830152919061378a9084613f90565b60018301548354600454929350600160801b91829004600f90810b810b936001600160401b03909216926137c99204810b900b8363ffffffff61366116565b600460106101000a8154816001600160801b030219169083600f0b6001600160801b031602179055506138166137fe83613656565b6004546001600160801b03169063ffffffff61487716565b600480546001600160801b0319166001600160801b03929092169190911790556000613840614346565b6040805160a0808201835260008083526001600160401b038086166020808601919091528486018390526001600160801b038d81166060808801919091526080808801869052885196870189529486528d54600160401b810490941692860192909252600160801b90920482169584019590955260018b01541693820193909352600f87900b928101929092529192506138da919061435d565b6001600160a01b0388166000908152600760205260408120818155600101819055613905848861408b565b905061390f612ae1565b6001600160a01b031663a7b5833f89836040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b0316815260200182815260200192505050600060405180830381600087803b15801561396e57600080fd5b505af1158015613982573d6000803e3d6000fd5b505060408051600080825260208201819052818301819052606082018c90526080820187905260a082015290516001600160a01b038d169350869250600080516020614e608339815191529181900360c00190a3876001600160a01b0316896001600160a01b0316847f62e7eb6698aabc6740afc94f06bbdfb947fc109fd24d4adb26014d44053ac2c3878b8660405180848152602001838152602001828152602001935050505060405180910390a480851115613aaf57613a42612ae1565b6001600160a01b031663d289ade2613a60878463ffffffff61487716565b6040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b158015613a9657600080fd5b505af1158015613aaa573d6000803e3d6000fd5b505050505b505050505050505050565b604080516001808252818301909252606091602080830190803883390190505090506e466c657869626c6553746f7261676560881b81600081518110613afc57fe5b60200260200101818152505090565b60608151835101604051908082528060200260200182016040528015613b3b578160200160208202803883390190505b50905060005b8351811015613b7d57838181518110613b5657fe5b6020026020010151828281518110613b6a57fe5b6020908102919091010152600101613b41565b5060005b8251811015613bc057828181518110613b9657fe5b6020026020010151828286510181518110613bad57fe5b6020908102919091010152600101613b81565b5092915050565b6005546000908190613be490429063ffffffff9081169061487716565b9050600062015180613bf585612cfb565b81613bfc57fe5b059050611f2982613c13838763ffffffff61468b16565b9063ffffffff61490816565b6080820151600090600f0b80613c39576000915050612adb565b6000613c5285602001516001600160401b03168561471c565b90506000613c608386614446565b90506000613ca983612e67613c8f876132758c604001516001600160801b03168861366190919063ffffffff16565b60608b01516001600160801b03169063ffffffff61353916565b90506132e760008261484c565b613cbe614dff565b81516000908190613cd85750839150600090506009613f89565b613ce68585602001516136c6565b15613cfa5750839150600090506003613f89565b600080613d05612b08565b915091508015613d22575085935060009250600b9150613f899050565b613d2c8683612bd0565b9350600080613d43898960200151886000036142cb565b91509150613d50816149b1565b15613d6757889650600095509350613f8992505050565b613d6f614dff565b6040518060a001604052808b600001516001600160401b03168152602001613d95614346565b6001600160401b03168152602001846001600160801b031681526020018a602001516001600160801b03168152602001613de38b600001518d60800151600f0b61353990919063ffffffff16565b600f0b81525090506000613e058b60800151600f0b8360800151600f0b6146b5565b8015613e2e5750613e1c8b60800151600f0b613656565b613e2c8360800151600f0b613656565b105b905080613e7857613e3d6143c3565b6040830151613e5b906001600160801b03168a63ffffffff6146c216565b1015613e7857508997506000965060079550613f89945050505050565b613e8d8260800151600f0b8b60200151614446565b8411613ea8575096506000955060039450613f899350505050565b6000613edb613ebd868b63ffffffff6146c216565b6132758d602001518660800151600f0b61468b90919063ffffffff16565b9050613ee681613656565b613f0c6064670de0b6b3a764000004613f00600254614494565b9063ffffffff6146c216565b1015613f2a57508a98506000975060069650613f8995505050505050565b50613f61613f4c8b602001516132756012600a0a606402613f006002546149c8565b8c60800151600f0b8460800151600f0b6149e8565b15613f7d57508997506000965060059550613f89945050505050565b50965060009450505050505b9250925092565b600080613f9d8484614ae6565b9050613faa60008261484c565b949350505050565b33600090815260076020526040902060010154600160801b9004600f90810b900b613fdf8115600a613175565b6000613fe96125c0565b9050613ff481612726565b5061161b336040518060a0016040528085600003815260200184815260200161401e600254612ba2565b815260200161402e600254612bb9565b8152602001869052612d91565b60008061405e84606001516001600160801b03168461366190919063ffffffff16565b6080850151909150613faa90600f0b8263ffffffff61468b16565b6000612ad8838363ffffffff61468b16565b6000806140b2614099614b20565b6140a6856140a688613656565b9063ffffffff614ba316565b905060006140be6141fc565b90508082116140cd5780612c99565b509392505050565b6000612b037522bc31b430b733b2a1b4b931bab4ba213932b0b5b2b960511b6144d3565b6000612b036b53797374656d53746174757360a01b6144d3565b600454600090600160801b9004600f90810b900b15801561413b5750600854600f90810b900b155b1561414857506000611f2e565b600061416361415684614275565b849063ffffffff61353916565b6008546004549192506000916141a391600f90810b810b9161419791600160801b909104810b900b8563ffffffff61468b16565b9063ffffffff61353916565b9050611f2981600061484c565b6141b9816149b1565b156107f4576009600082600b81111561318b57fe5b60006141db600254612ba2565b60408301526002546141ec90612bb9565b6060830152612adb826000612bd0565b6000614206614bcd565b6001600160a01b03166323257c2b600080516020614e8083398151915272667574757265734d696e4b656570657246656560681b6040518363ffffffff1660e01b8152600401808381526020018281526020019250505060206040518083038186803b15801561258f57600080fd5b6000612adb61428383613bc7565b600661428d614346565b8154811061429757fe5b90600052602060002090600291828204019190066010029054906101000a9004600f0b600f0b61353990919063ffffffff16565b60008060006142de846141978888614ae6565b905060008112156142f75750600091506007905061433e565b60808601518190600f0b600061430d8289614446565b9050811580159061431e5750808311155b1561433357826003955095505050505061433e565b509093506000925050505b935093915050565b600654600090612b0390600163ffffffff61487716565b600061436883614bea565b9050600061437583614bea565b600854909150614398908290612e6790600f90810b900b8563ffffffff61353916565b60088054600f9290920b6001600160801b03166001600160801b031990921691909117905550505050565b60006143cd614bcd565b6001600160a01b03166323257c2b600080516020614e808339815191527f667574757265734d696e496e697469616c4d617267696e0000000000000000006040518363ffffffff1660e01b8152600401808381526020018281526020019250505060206040518083038186803b15801561258f57600080fd5b600080614454614099614c74565b9050613faa614463858561408b565b829063ffffffff6146c216565b60008161447f57506000612d4a565b613faa826132758660800151600f0b86614079565b6000612adb826a6d61784c6576657261676560a81b6145c7565b6000612adb82756e6578745072696365436f6e6669726d57696e646f7760501b6145c7565b600081815260016020908152604080832054815170026b4b9b9b4b7339030b2323932b9b99d1607d1b9381019390935260318084018690528251808503909101815260519093019091526001600160a01b03169081613bc05760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561457557818101518382015260200161455d565b50505050905090810190601f1680156145a25780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b6000612b036822bc31b430b733b2b960b91b6144d3565b60006145d1614bcd565b6001600160a01b03166323257c2b600080516020614e8083398151915285856040516020018083815260200182815260200192505050604051602081830303815290604052805190602001206040518363ffffffff1660e01b8152600401808381526020018281526020019250505060206040518083038186803b15801561465857600080fd5b505afa15801561466c573d6000803e3d6000fd5b505050506040513d602081101561468257600080fd5b50519392505050565b6000670de0b6b3a76400006146a6848463ffffffff61490816565b816146ad57fe5b059392505050565b6000908113159112151490565b600082820183811015612ad8576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b6000612ad86006848154811061472e57fe5b90600052602060002090600291828204019190066010029054906101000a9004600f0b600f0b612e6784614275565b6000612adb826d6d617846756e64696e675261746560901b6145c7565b60008082116147c6576040805162461bcd60e51b815260206004820152601360248201527270726963652063616e2774206265207a65726f60681b604482015290519081900360640190fd5b60006147e3836147d7600254614cf7565b9063ffffffff614d1216565b90508061482b576040805162461bcd60e51b8152602060048201526011602482015270736b65775363616c65206973207a65726f60781b604482015290519081900360640190fd5b600454612d4a90600160801b9004600f90810b900b8263ffffffff6148d416565b600081831261485b5782612ad8565b50919050565b60008183126148705781612ad8565b5090919050565b6000828211156148ce576040805162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b50900390565b6000612ad88261165985670de0b6b3a764000063ffffffff61490816565b60008082126149015781612adb565b5060000390565b60008261491757506000612adb565b8260001914801561492b5750600160ff1b82145b156149675760405162461bcd60e51b8152600401808060200182810382526027815260200180614f036027913960400191505060405180910390fd5b8282028284828161497457fe5b0514612ad85760405162461bcd60e51b8152600401808060200182810382526027815260200180614f036027913960400191505060405180910390fd5b60008082600b8111156149c057fe5b141592915050565b6000612adb82701b585e13585c9ad95d15985b1d595554d1607a1b6145c7565b60006149f483836146b5565b8015614a105750614a0483613656565b614a0d83613656565b11155b15614a1d57506000612d4a565b600454600090614a4790849061419790600160801b9004600f90810b900b8763ffffffff61366116565b90506000614a7b614a57856148f2565b614197614a63886148f2565b6004546001600160801b03169063ffffffff61366116565b905060008460001215614a9f57614a98828463ffffffff61353916565b9050614ab2565b614aaf828463ffffffff61366116565b90505b614ac661166582600263ffffffff61359e16565b871015614ad95760019350505050612d4a565b5060009695505050505050565b600080614af38484612c52565b9050613faa81614197614b06878761403b565b60408801516001600160801b03169063ffffffff61353916565b6000614b2a614bcd565b6001600160a01b03166323257c2b600080516020614e808339815191527f667574757265734c69717569646174696f6e466565526174696f0000000000006040518363ffffffff1660e01b8152600401808381526020018281526020019250505060206040518083038186803b15801561258f57600080fd5b6000670de0b6b3a7640000614bbe848463ffffffff614d3c16565b81614bc557fe5b049392505050565b6000612b036e466c657869626c6553746f7261676560881b6144d3565b6000612adb614c5a614c46600685602001516001600160401b031681548110614c0f57fe5b6000918252602090912060028204015460608701516001600160801b0316916001166010026101000a9004600f90810b900b613539565b6080850151600f0b9063ffffffff61468b16565b60408401516001600160801b03169063ffffffff61366116565b6000614c7e614bcd565b6001600160a01b03166323257c2b600080516020614e808339815191527f667574757265734c69717569646174696f6e427566666572526174696f0000006040518363ffffffff1660e01b8152600401808381526020018281526020019250505060206040518083038186803b15801561258f57600080fd5b6000612adb826b1cdad95dd4d8d85b195554d160a21b6145c7565b6000612ad882614d3085670de0b6b3a764000063ffffffff614d3c16565b9063ffffffff614d9516565b600082614d4b57506000612adb565b82820282848281614d5857fe5b0414612ad85760405162461bcd60e51b8152600401808060200182810382526021815260200180614ee26021913960400191505060405180910390fd5b6000808211614deb576040805162461bcd60e51b815260206004820152601a60248201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604482015290519081900360640190fd5b6000828481614df657fe5b04949350505050565b6040805160a08101825260008082526020820181905291810182905260608101829052608081019190915290565b6040518060a001604052806000815260200160008152602001600081526020016000815260200160008019168152509056fe930fd93131df035ac630ef616ad4212af6370377bf327e905c2724cd01d95097467574757265734d61726b657453657474696e677300000000000000000000005369676e6564536166654d6174683a206164646974696f6e206f766572666c6f775369676e6564536166654d6174683a206469766973696f6e206f766572666c6f77536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f775369676e6564536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f7763616e6e6f742062652063616e63656c6c6564206279206b6565706572207965745369676e6564536166654d6174683a207375627472616374696f6e206f766572666c6f77a265627a7a723158201dc3cd2b6f60f91da7f0edd24595e4923455f34c48b265647c4b551b0a64e3a164736f6c63430005100032
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000001cb059b7e74fd21665968c908806143e744d5f3073425443000000000000000000000000000000000000000000000000000000007342544300000000000000000000000000000000000000000000000000000000
-----Decoded View---------------
Arg [0] : _resolver (address): 0x1Cb059b7e74fD21665968C908806143E744D5F30
Arg [1] : _baseAsset (bytes32): 0x7342544300000000000000000000000000000000000000000000000000000000
Arg [2] : _marketKey (bytes32): 0x7342544300000000000000000000000000000000000000000000000000000000
-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 0000000000000000000000001cb059b7e74fd21665968c908806143e744d5f30
Arg [1] : 7342544300000000000000000000000000000000000000000000000000000000
Arg [2] : 7342544300000000000000000000000000000000000000000000000000000000
Library Used
SafeDecimalMath : 0x0142f40c25ce1f1177ed131101fa19217396cb88SystemSettingsLib : 0x478d479bac034f89fa28d8d2ab430ec973a3a0acSignedSafeDecimalMath : 0x253914cf059f4c3e277c28060c404acfc38fb6e2
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
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.