Source Code
Overview
ETH Balance
0 ETH
More Info
ContractCreator
Multichain Info
N/A
Latest 24 internal transactions
Advanced mode:
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
9662503 | 256 days ago | 1 wei | ||||
9662503 | 256 days ago | 1 wei | ||||
9649014 | 256 days ago | 1 wei | ||||
9649014 | 256 days ago | 1 wei | ||||
9648952 | 256 days ago | 1 wei | ||||
9648952 | 256 days ago | 1 wei | ||||
9595147 | 257 days ago | 1 wei | ||||
9595147 | 257 days ago | 1 wei | ||||
9542180 | 258 days ago | 1 wei | ||||
9542180 | 258 days ago | 1 wei | ||||
9520408 | 259 days ago | 1 wei | ||||
9520408 | 259 days ago | 1 wei | ||||
9300651 | 264 days ago | 1 wei | ||||
9300651 | 264 days ago | 1 wei | ||||
9269766 | 265 days ago | 1 wei | ||||
9269766 | 265 days ago | 1 wei | ||||
9267300 | 265 days ago | 1 wei | ||||
9267300 | 265 days ago | 1 wei | ||||
9257809 | 265 days ago | 1 wei | ||||
9257809 | 265 days ago | 1 wei | ||||
9238297 | 266 days ago | 1 wei | ||||
9238297 | 266 days ago | 1 wei | ||||
9231576 | 266 days ago | 1 wei | ||||
9231576 | 266 days ago | 1 wei |
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Similar Match Source Code This contract matches the deployed Bytecode of the Source Code for Contract 0x2A19D346...F13508A3A The constructor portion of the code might be different and could alter the actual behaviour of the contract
Contract Name:
PerpsV2MarketDelayedExecution
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 sepolia-optimism.etherscan.io on 2024-01-28 */ /* ____ __ __ __ _ / __/__ __ ___ / /_ / / ___ / /_ (_)__ __ _\ \ / // // _ \/ __// _ \/ -_)/ __// / \ \ / /___/ \_, //_//_/\__//_//_/\__/ \__//_/ /_\_\ /___/ * Synthetix: PerpsV2MarketDelayedExecution.sol * * Latest source (may be newer): https://github.com/Synthetixio/synthetix/blob/master/contracts/PerpsV2MarketDelayedExecution.sol * Docs: https://docs.synthetix.io/contracts/PerpsV2MarketDelayedExecution * * Contract Dependencies: * - IAddressResolver * - IPerpsV2MarketBaseTypes * - IPerpsV2MarketDelayedExecution * - MixinPerpsV2MarketSettings * - MixinResolver * - Owned * - PerpsV2MarketBase * - PerpsV2MarketProxyable * - Proxyable * Libraries: * - SafeDecimalMath * - SafeMath * - SignedSafeDecimalMath * - SignedSafeMath * * MIT License * =========== * * Copyright (c) 2024 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); } // Inheritance // Internal references // https://docs.synthetix.io/contracts/source/contracts/proxy contract Proxy is Owned { Proxyable public target; constructor(address _owner) public Owned(_owner) {} function setTarget(Proxyable _target) external onlyOwner { target = _target; emit TargetUpdated(_target); } function _emit( bytes calldata callData, uint numTopics, bytes32 topic1, bytes32 topic2, bytes32 topic3, bytes32 topic4 ) external onlyTarget { uint size = callData.length; bytes memory _callData = callData; assembly { /* The first 32 bytes of callData contain its length (as specified by the abi). * Length is assumed to be a uint256 and therefore maximum of 32 bytes * in length. It is also leftpadded to be a multiple of 32 bytes. * This means moving call_data across 32 bytes guarantees we correctly access * the data itself. */ switch numTopics case 0 { log0(add(_callData, 32), size) } case 1 { log1(add(_callData, 32), size, topic1) } case 2 { log2(add(_callData, 32), size, topic1, topic2) } case 3 { log3(add(_callData, 32), size, topic1, topic2, topic3) } case 4 { log4(add(_callData, 32), size, topic1, topic2, topic3, topic4) } } } // solhint-disable no-complex-fallback function() external payable { // Mutable call setting Proxyable.messageSender as this is using call not delegatecall target.setMessageSender(msg.sender); assembly { let free_ptr := mload(0x40) calldatacopy(free_ptr, 0, calldatasize) /* We must explicitly forward ether to the underlying contract as well. */ let result := call(gas, sload(target_slot), callvalue, free_ptr, calldatasize, 0, 0) returndatacopy(free_ptr, 0, returndatasize) if iszero(result) { revert(free_ptr, returndatasize) } return(free_ptr, returndatasize) } } modifier onlyTarget { require(Proxyable(msg.sender) == target, "Must be proxy target"); _; } event TargetUpdated(Proxyable newTarget); } // Inheritance // Internal references // https://docs.synthetix.io/contracts/source/contracts/proxyable contract Proxyable is Owned { // This contract should be treated like an abstract contract /* The proxy this contract exists behind. */ Proxy public proxy; /* The caller of the proxy, passed through to this contract. * Note that every function using this member must apply the onlyProxy or * optionalProxy modifiers, otherwise their invocations can use stale values. */ address public messageSender; constructor(address payable _proxy) internal { // This contract is abstract, and thus cannot be instantiated directly require(owner != address(0), "Owner must be set"); proxy = Proxy(_proxy); emit ProxyUpdated(_proxy); } function setProxy(address payable _proxy) external onlyOwner { proxy = Proxy(_proxy); emit ProxyUpdated(_proxy); } function setMessageSender(address sender) external onlyProxy { messageSender = sender; } modifier onlyProxy { _onlyProxy(); _; } function _onlyProxy() private view { require(Proxy(msg.sender) == proxy, "Only the proxy can call"); } modifier optionalProxy { _optionalProxy(); _; } function _optionalProxy() private { if (Proxy(msg.sender) != proxy && messageSender != msg.sender) { messageSender = msg.sender; } } modifier optionalProxy_onlyOwner { _optionalProxy_onlyOwner(); _; } // solhint-disable-next-line func-name-mixedcase function _optionalProxy_onlyOwner() private { if (Proxy(msg.sender) != proxy && messageSender != msg.sender) { messageSender = msg.sender; } require(messageSender == owner, "Owner only function"); } event ProxyUpdated(address proxyAddress); } // https://docs.synthetix.io/contracts/source/interfaces/iaddressresolver interface IAddressResolver { function getAddress(bytes32 name) external view returns (address); function getSynth(bytes32 key) external view returns (address); function requireAndGetAddress(bytes32 name, string calldata reason) external view returns (address); } // https://docs.synthetix.io/contracts/source/interfaces/isynth interface ISynth { // Views function currencyKey() external view returns (bytes32); function transferableSynths(address account) external view returns (uint); // Mutative functions function transferAndSettle(address to, uint value) external returns (bool); function transferFromAndSettle( address from, address to, uint value ) external returns (bool); // Restricted: used internally to Synthetix function burn(address account, uint amount) external; function issue(address account, uint amount) external; } // https://docs.synthetix.io/contracts/source/interfaces/iissuer interface IIssuer { // Views function allNetworksDebtInfo() external view returns ( uint256 debt, uint256 sharesSupply, bool isStale ); function anySynthOrSNXRateIsInvalid() external view returns (bool anyRateInvalid); function availableCurrencyKeys() external view returns (bytes32[] memory); function availableSynthCount() external view returns (uint); function availableSynths(uint index) external view returns (ISynth); function canBurnSynths(address account) external view returns (bool); function collateral(address account) external view returns (uint); function collateralisationRatio(address issuer) external view returns (uint); function collateralisationRatioAndAnyRatesInvalid(address _issuer) external view returns (uint cratio, bool anyRateIsInvalid); function debtBalanceOf(address issuer, bytes32 currencyKey) external view returns (uint debtBalance); function issuanceRatio() external view returns (uint); function lastIssueEvent(address account) external view returns (uint); function maxIssuableSynths(address issuer) external view returns (uint maxIssuable); function minimumStakeTime() external view returns (uint); function remainingIssuableSynths(address issuer) external view returns ( uint maxIssuable, uint alreadyIssued, uint totalSystemDebt ); function synths(bytes32 currencyKey) external view returns (ISynth); function getSynths(bytes32[] calldata currencyKeys) external view returns (ISynth[] memory); function synthsByAddress(address synthAddress) external view returns (bytes32); function totalIssuedSynths(bytes32 currencyKey, bool excludeOtherCollateral) external view returns (uint); function transferableSynthetixAndAnyRateIsInvalid(address account, uint balance) external view returns (uint transferable, bool anyRateIsInvalid); function liquidationAmounts(address account, bool isSelfLiquidation) external view returns ( uint totalRedeemed, uint debtToRemove, uint escrowToLiquidate, uint initialDebtBalance ); // Restricted: used internally to Synthetix function addSynths(ISynth[] calldata synthsToAdd) external; function issueSynths(address from, uint amount) external; function issueSynthsOnBehalf( address issueFor, address from, uint amount ) external; function issueMaxSynths(address from) external; function issueMaxSynthsOnBehalf(address issueFor, address from) external; function burnSynths(address from, uint amount) external; function burnSynthsOnBehalf( address burnForAddress, address from, uint amount ) external; function burnSynthsToTarget(address from) external; function burnSynthsToTargetOnBehalf(address burnForAddress, address from) external; function burnForRedemption( address deprecatedSynthProxy, address account, uint balance ) external; function setCurrentPeriodId(uint128 periodId) external; function liquidateAccount(address account, bool isSelfLiquidation) external returns ( uint totalRedeemed, uint debtRemoved, uint escrowToLiquidate ); function issueSynthsWithoutDebt( bytes32 currencyKey, address to, uint amount ) external returns (bool rateInvalid); function burnSynthsWithoutDebt( bytes32 currencyKey, address to, uint amount ) external returns (bool rateInvalid); function modifyDebtSharesForMigration(address account, uint amount) 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; } pragma experimental ABIEncoderV2; // Internal references // https://docs.synthetix.io/contracts/source/contracts/MixinPerpsV2MarketSettings contract MixinPerpsV2MarketSettings is MixinResolver { /* ========== CONSTANTS ========== */ bytes32 internal constant SETTING_CONTRACT_NAME = "PerpsV2MarketSettings"; /* ---------- 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_DELAYED_ORDER = "takerFeeDelayedOrder"; bytes32 internal constant PARAMETER_MAKER_FEE_DELAYED_ORDER = "makerFeeDelayedOrder"; bytes32 internal constant PARAMETER_TAKER_FEE_OFFCHAIN_DELAYED_ORDER = "takerFeeOffchainDelayedOrder"; bytes32 internal constant PARAMETER_MAKER_FEE_OFFCHAIN_DELAYED_ORDER = "makerFeeOffchainDelayedOrder"; bytes32 internal constant PARAMETER_NEXT_PRICE_CONFIRM_WINDOW = "nextPriceConfirmWindow"; bytes32 internal constant PARAMETER_DELAYED_ORDER_CONFIRM_WINDOW = "delayedOrderConfirmWindow"; bytes32 internal constant PARAMETER_OFFCHAIN_DELAYED_ORDER_MIN_AGE = "offchainDelayedOrderMinAge"; bytes32 internal constant PARAMETER_OFFCHAIN_DELAYED_ORDER_MAX_AGE = "offchainDelayedOrderMaxAge"; bytes32 internal constant PARAMETER_MAX_LEVERAGE = "maxLeverage"; bytes32 internal constant PARAMETER_MAX_MARKET_VALUE = "maxMarketValue"; bytes32 internal constant PARAMETER_MAX_FUNDING_VELOCITY = "maxFundingVelocity"; bytes32 internal constant PARAMETER_MIN_SKEW_SCALE = "skewScale"; bytes32 internal constant PARAMETER_MIN_DELAY_TIME_DELTA = "minDelayTimeDelta"; bytes32 internal constant PARAMETER_MAX_DELAY_TIME_DELTA = "maxDelayTimeDelta"; bytes32 internal constant PARAMETER_OFFCHAIN_MARKET_KEY = "offchainMarketKey"; bytes32 internal constant PARAMETER_OFFCHAIN_PRICE_DIVERGENCE = "offchainPriceDivergence"; bytes32 internal constant PARAMETER_LIQUIDATION_PREMIUM_MULTIPLIER = "liquidationPremiumMultiplier"; bytes32 internal constant PARAMETER_MAX_LIQUIDAION_DELTA = "maxLiquidationDelta"; bytes32 internal constant PARAMETER_MAX_LIQUIDATION_PD = "maxPD"; // liquidation buffer to prevent negative margin upon liquidation bytes32 internal constant PARAMETER_LIQUIDATION_BUFFER_RATIO = "liquidationBufferRatio"; // Global settings // minimum liquidation fee payable to liquidator bytes32 internal constant SETTING_MIN_KEEPER_FEE = "perpsV2MinKeeperFee"; // maximum liquidation fee payable to liquidator bytes32 internal constant SETTING_MAX_KEEPER_FEE = "perpsV2MaxKeeperFee"; // liquidation fee basis points payed to liquidator bytes32 internal constant SETTING_LIQUIDATION_FEE_RATIO = "perpsV2LiquidationFeeRatio"; // minimum initial margin bytes32 internal constant SETTING_MIN_INITIAL_MARGIN = "perpsV2MinInitialMargin"; // fixed liquidation fee to be paid to liquidator keeper (not flagger) bytes32 internal constant SETTING_KEEPER_LIQUIRATION_FEE = "keeperLiquidationFee"; /* ---------- 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 _takerFeeDelayedOrder(bytes32 _marketKey) internal view returns (uint) { return _parameter(_marketKey, PARAMETER_TAKER_FEE_DELAYED_ORDER); } function _makerFeeDelayedOrder(bytes32 _marketKey) internal view returns (uint) { return _parameter(_marketKey, PARAMETER_MAKER_FEE_DELAYED_ORDER); } function _takerFeeOffchainDelayedOrder(bytes32 _marketKey) internal view returns (uint) { return _parameter(_marketKey, PARAMETER_TAKER_FEE_OFFCHAIN_DELAYED_ORDER); } function _makerFeeOffchainDelayedOrder(bytes32 _marketKey) internal view returns (uint) { return _parameter(_marketKey, PARAMETER_MAKER_FEE_OFFCHAIN_DELAYED_ORDER); } function _nextPriceConfirmWindow(bytes32 _marketKey) internal view returns (uint) { return _parameter(_marketKey, PARAMETER_NEXT_PRICE_CONFIRM_WINDOW); } function _delayedOrderConfirmWindow(bytes32 _marketKey) internal view returns (uint) { return _parameter(_marketKey, PARAMETER_DELAYED_ORDER_CONFIRM_WINDOW); } function _offchainDelayedOrderMinAge(bytes32 _marketKey) internal view returns (uint) { return _parameter(_marketKey, PARAMETER_OFFCHAIN_DELAYED_ORDER_MIN_AGE); } function _offchainDelayedOrderMaxAge(bytes32 _marketKey) internal view returns (uint) { return _parameter(_marketKey, PARAMETER_OFFCHAIN_DELAYED_ORDER_MAX_AGE); } function _maxLeverage(bytes32 _marketKey) internal view returns (uint) { return _parameter(_marketKey, PARAMETER_MAX_LEVERAGE); } function _maxMarketValue(bytes32 _marketKey) internal view returns (uint) { return _parameter(_marketKey, PARAMETER_MAX_MARKET_VALUE); } function _skewScale(bytes32 _marketKey) internal view returns (uint) { return _parameter(_marketKey, PARAMETER_MIN_SKEW_SCALE); } function _maxFundingVelocity(bytes32 _marketKey) internal view returns (uint) { return _parameter(_marketKey, PARAMETER_MAX_FUNDING_VELOCITY); } function _minDelayTimeDelta(bytes32 _marketKey) internal view returns (uint) { return _parameter(_marketKey, PARAMETER_MIN_DELAY_TIME_DELTA); } function _maxDelayTimeDelta(bytes32 _marketKey) internal view returns (uint) { return _parameter(_marketKey, PARAMETER_MAX_DELAY_TIME_DELTA); } function _offchainMarketKey(bytes32 _marketKey) internal view returns (bytes32) { return _flexibleStorage().getBytes32Value( SETTING_CONTRACT_NAME, keccak256(abi.encodePacked(_marketKey, PARAMETER_OFFCHAIN_MARKET_KEY)) ); } function _offchainPriceDivergence(bytes32 _marketKey) internal view returns (uint) { return _parameter(_marketKey, PARAMETER_OFFCHAIN_PRICE_DIVERGENCE); } function _liquidationPremiumMultiplier(bytes32 _marketKey) internal view returns (uint) { return _parameter(_marketKey, PARAMETER_LIQUIDATION_PREMIUM_MULTIPLIER); } function _maxLiquidationDelta(bytes32 _marketKey) internal view returns (uint) { return _parameter(_marketKey, PARAMETER_MAX_LIQUIDAION_DELTA); } function _maxPD(bytes32 _marketKey) internal view returns (uint) { return _parameter(_marketKey, PARAMETER_MAX_LIQUIDATION_PD); } function _liquidationBufferRatio(bytes32 _marketKey) internal view returns (uint) { return _parameter(_marketKey, PARAMETER_LIQUIDATION_BUFFER_RATIO); } function _minKeeperFee() internal view returns (uint) { return _flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_MIN_KEEPER_FEE); } function _maxKeeperFee() internal view returns (uint) { return _flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_MAX_KEEPER_FEE); } function _liquidationFeeRatio() internal view returns (uint) { return _flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_LIQUIDATION_FEE_RATIO); } function _minInitialMargin() internal view returns (uint) { return _flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_MIN_INITIAL_MARGIN); } function _keeperLiquidationFee() internal view returns (uint) { return _flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_KEEPER_LIQUIRATION_FEE); } } interface IPerpsV2MarketBaseTypes { /* ========== TYPES ========== */ enum OrderType {Atomic, Delayed, Offchain} enum Status { Ok, InvalidPrice, InvalidOrderType, PriceOutOfBounds, CanLiquidate, CannotLiquidate, MaxMarketSizeExceeded, MaxLeverageExceeded, InsufficientMargin, NotPermitted, NilOrder, NoPositionOpen, PriceTooVolatile, PriceImpactToleranceExceeded, PositionFlagged, PositionNotFlagged } // 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; } // Delayed order storage struct DelayedOrder { bool isOffchain; // flag indicating the delayed order is offchain int128 sizeDelta; // difference in position to pass to modifyPosition uint128 desiredFillPrice; // desired fill price as usd used on fillPrice at execution uint128 targetRoundId; // price oracle roundId using which price this order needs to executed 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 uint256 executableAtTime; // The timestamp at which this order is executable at uint256 intentionTime; // The block timestamp of submission 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/IDirectIntegration interface IDirectIntegrationManager { struct ParameterIntegrationSettings { bytes32 currencyKey; address dexPriceAggregator; address atomicEquivalentForDexPricing; uint atomicExchangeFeeRate; uint atomicTwapWindow; uint atomicMaxVolumePerBlock; uint atomicVolatilityConsiderationWindow; uint atomicVolatilityUpdateThreshold; uint exchangeFeeRate; uint exchangeMaxDynamicFee; uint exchangeDynamicFeeRounds; uint exchangeDynamicFeeThreshold; uint exchangeDynamicFeeWeightDecay; } function getExchangeParameters(address integration, bytes32 key) external view returns (ParameterIntegrationSettings memory settings); function setExchangeParameters( address integration, bytes32[] calldata currencyKeys, ParameterIntegrationSettings calldata params ) external; } // 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 effectiveAtomicValueAndRates( IDirectIntegrationManager.ParameterIntegrationSettings calldata sourceSettings, uint sourceAmount, IDirectIntegrationManager.ParameterIntegrationSettings calldata destinationSettings, IDirectIntegrationManager.ParameterIntegrationSettings calldata usdSettings ) external view returns ( uint value, uint systemValue, uint systemSourceRate, uint systemDestinationRate ); function getCurrentRoundId(bytes32 currencyKey) external view returns (uint); function getLastRoundIdBeforeElapsedSecs( bytes32 currencyKey, uint startingRoundId, uint startingTimestamp, uint timediff ) external view returns (uint); function lastRateUpdateTimes(bytes32 currencyKey) external view returns (uint256); function rateAndTimestampAtRound(bytes32 currencyKey, uint roundId) external view returns (uint rate, uint time); function rateAndUpdatedTime(bytes32 currencyKey) external view returns (uint rate, uint time); function rateAndInvalid(bytes32 currencyKey) external view returns (uint rate, bool isInvalid); function rateForCurrency(bytes32 currencyKey) external view returns (uint); function rateIsFlagged(bytes32 currencyKey) external view returns (bool); function rateIsInvalid(bytes32 currencyKey) external view returns (bool); function rateIsStale(bytes32 currencyKey) external view returns (bool); function rateStalePeriod() external view returns (uint); function ratesAndUpdatedTimeForCurrencyLastNRounds( bytes32 currencyKey, uint numRounds, uint roundId ) external view returns (uint[] memory rates, uint[] memory times); function ratesAndInvalidForCurrencies(bytes32[] calldata currencyKeys) external view returns (uint[] memory rates, bool anyRateInvalid); function ratesForCurrencies(bytes32[] calldata currencyKeys) external view returns (uint[] memory); function synthTooVolatileForAtomicExchange(bytes32 currencyKey) external view returns (bool); function synthTooVolatileForAtomicExchange(IDirectIntegrationManager.ParameterIntegrationSettings calldata settings) external view returns (bool); function rateWithSafetyChecks(bytes32 currencyKey) external returns ( uint rate, bool broken, bool invalid ); } interface 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; uint sourceAmountAfterSettlement; } // Views function calculateAmountAfterSettlement( address from, bytes32 currencyKey, uint amount, uint refunded ) external view returns (uint amountAfterSettlement); function isSynthRateInvalid(bytes32 currencyKey) external view returns (bool); function maxSecsLeftInWaitingPeriod(address account, bytes32 currencyKey) external view returns (uint); function settlementOwing(address account, bytes32 currencyKey) external view returns ( uint reclaimAmount, uint rebateAmount, uint numEntries ); function hasWaitingPeriodOrSettlementOwing(address account, bytes32 currencyKey) external view returns (bool); function feeRateForExchange(bytes32 sourceCurrencyKey, bytes32 destinationCurrencyKey) external view returns (uint); function dynamicFeeRateForExchange(bytes32 sourceCurrencyKey, bytes32 destinationCurrencyKey) external view returns (uint feeRate, bool tooVolatile); function getAmountsForExchange( uint sourceAmount, bytes32 sourceCurrencyKey, bytes32 destinationCurrencyKey ) external view returns ( uint amountReceived, uint fee, uint exchangeFeeRate ); function priceDeviationThresholdFactor() external view returns (uint); function waitingPeriodSecs() external view returns (uint); function lastExchangeRate(bytes32 currencyKey) external view returns (uint); // Mutative functions function exchange( address exchangeForAddress, address from, bytes32 sourceCurrencyKey, uint sourceAmount, bytes32 destinationCurrencyKey, address destinationAddress, bool virtualSynth, address rewardAddress, bytes32 trackingCode ) external returns (uint amountReceived, IVirtualSynth vSynth); function exchangeAtomically( address from, bytes32 sourceCurrencyKey, uint sourceAmount, bytes32 destinationCurrencyKey, address destinationAddress, bytes32 trackingCode, uint minAmount ) external returns (uint amountReceived); function settle(address from, bytes32 currencyKey) external returns ( uint reclaimed, uint refunded, uint numEntries ); } // Used to have strongly-typed access to internal mutative functions in Synthetix interface ISynthetixInternal { function emitExchangeTracking( bytes32 trackingCode, bytes32 toCurrencyKey, uint256 toAmount, uint256 fee ) external; function emitSynthExchange( address account, bytes32 fromCurrencyKey, uint fromAmount, bytes32 toCurrencyKey, uint toAmount, address toAddress ) external; function emitAtomicSynthExchange( address account, bytes32 fromCurrencyKey, uint fromAmount, bytes32 toCurrencyKey, uint toAmount, address toAddress ) external; function emitExchangeReclaim( address account, bytes32 currencyKey, uint amount ) external; function emitExchangeRebate( address account, bytes32 currencyKey, uint amount ) external; } interface IExchangerInternalDebtCache { function updateCachedSynthDebtsWithRates(bytes32[] calldata currencyKeys, uint[] calldata currencyRates) external; function updateCachedSynthDebts(bytes32[] calldata currencyKeys) external; } // 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; } interface IFuturesMarketManager { function markets(uint index, uint pageSize) external view returns (address[] memory); function markets( uint index, uint pageSize, bool proxiedMarkets ) external view returns (address[] memory); function numMarkets() external view returns (uint); function numMarkets(bool proxiedMarkets) external view returns (uint); function allMarkets() external view returns (address[] memory); function allMarkets(bool proxiedMarkets) external view returns (address[] memory); function marketForKey(bytes32 marketKey) external view returns (address); function marketsForKeys(bytes32[] calldata marketKeys) external view returns (address[] memory); function totalDebt() external view returns (uint debt, bool isInvalid); function isEndorsed(address account) external view returns (bool); function allEndorsedAddresses() external view returns (address[] memory); function addEndorsedAddresses(address[] calldata addresses) external; function removeEndorsedAddresses(address[] calldata addresses) external; } // https://docs.synthetix.io/contracts/source/contracts/PerpsV2MarketState interface IPerpsV2MarketState { function marketKey() external view returns (bytes32); function baseAsset() external view returns (bytes32); function marketSize() external view returns (uint128); function marketSkew() external view returns (int128); function fundingLastRecomputed() external view returns (uint32); function fundingSequence(uint) external view returns (int128); function fundingRateLastRecomputed() external view returns (int128); function positions(address) external view returns (IPerpsV2MarketBaseTypes.Position memory); function delayedOrders(address) external view returns (IPerpsV2MarketBaseTypes.DelayedOrder memory); function positionFlagger(address) external view returns (address); function entryDebtCorrection() external view returns (int128); function nextPositionId() external view returns (uint64); function fundingSequenceLength() external view returns (uint); function isFlagged(address) external view returns (bool); function getPositionAddressesPage(uint, uint) external view returns (address[] memory); function getPositionAddressesLength() external view returns (uint); function getDelayedOrderAddressesPage(uint, uint) external view returns (address[] memory); function getDelayedOrderAddressesLength() external view returns (uint); function getFlaggedAddressesPage(uint, uint) external view returns (address[] memory); function getFlaggedAddressesLength() external view returns (uint); function setMarketKey(bytes32) external; function setBaseAsset(bytes32) external; function setMarketSize(uint128) external; function setEntryDebtCorrection(int128) external; function setNextPositionId(uint64) external; function setMarketSkew(int128) external; function setFundingLastRecomputed(uint32) external; function setFundingRateLastRecomputed(int128 _fundingRateLastRecomputed) external; function pushFundingSequence(int128) external; function updatePosition( address account, uint64 id, uint64 lastFundingIndex, uint128 margin, uint128 lastPrice, int128 size ) external; function updateDelayedOrder( address account, bool isOffchain, int128 sizeDelta, uint128 desiredFillPrice, uint128 targetRoundId, uint128 commitDeposit, uint128 keeperDeposit, uint256 executableAtTime, uint256 intentionTime, bytes32 trackingCode ) external; function deletePosition(address) external; function deleteDelayedOrder(address) external; function flag(address account, address flagger) external; function unflag(address) external; } // Inheritance // Libraries // Internal references // Internal references // Use internal interface (external functions not present in IFuturesMarketManager) interface IFuturesMarketManagerInternal { function issueSUSD(address account, uint amount) external; function burnSUSD(address account, uint amount) external returns (uint postReclamationAmount); function payFee(uint amount) external; function isEndorsed(address account) external view returns (bool); } // https://docs.synthetix.io/contracts/source/contracts/PerpsV2MarketBase contract PerpsV2MarketBase is Owned, MixinPerpsV2MarketSettings, IPerpsV2MarketBaseTypes { /* ========== LIBRARIES ========== */ using SafeMath for uint; using SafeDecimalMath for uint; using SignedSafeMath for int; using SignedSafeDecimalMath for int; /* ========== 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 ========== */ IPerpsV2MarketState public marketState; /* ---------- Address Resolver Configuration ---------- */ bytes32 private constant CONTRACT_EXRATES = "ExchangeRates"; bytes32 internal constant CONTRACT_EXCHANGER = "Exchanger"; bytes32 internal constant CONTRACT_SYSTEMSTATUS = "SystemStatus"; bytes32 internal constant CONTRACT_FUTURESMARKETMANAGER = "FuturesMarketManager"; bytes32 internal constant CONTRACT_PERPSV2MARKETSETTINGS = "PerpsV2MarketSettings"; bytes32 internal constant CONTRACT_PERPSV2EXCHANGERATE = "PerpsV2ExchangeRate"; bytes32 internal constant CONTRACT_FLEXIBLESTORAGE = "FlexibleStorage"; // Holds the revert message for each type of error. mapping(uint8 => string) internal _errorMessages; // convenience struct for passing params between position modification helper functions struct TradeParams { int sizeDelta; uint oraclePrice; uint fillPrice; uint desiredFillPrice; uint takerFee; uint makerFee; bytes32 trackingCode; // optional tracking code for volume source fee sharing } /* ========== CONSTRUCTOR ========== */ constructor( address _marketState, address _owner, address _resolver ) public MixinPerpsV2MarketSettings(_resolver) Owned(_owner) { marketState = IPerpsV2MarketState(_marketState); // Set up the mapping between error codes and their revert messages. _errorMessages[uint8(Status.InvalidPrice)] = "Invalid price"; _errorMessages[uint8(Status.InvalidOrderType)] = "Invalid order type"; _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"; _errorMessages[uint8(Status.PriceImpactToleranceExceeded)] = "Price impact exceeded"; _errorMessages[uint8(Status.PositionFlagged)] = "Position flagged"; _errorMessages[uint8(Status.PositionNotFlagged)] = "Position not flagged"; } /* ---------- External Contracts ---------- */ function resolverAddressesRequired() public view returns (bytes32[] memory addresses) { bytes32[] memory existingAddresses = MixinPerpsV2MarketSettings.resolverAddressesRequired(); bytes32[] memory newAddresses = new bytes32[](7); newAddresses[0] = CONTRACT_EXCHANGER; newAddresses[1] = CONTRACT_EXRATES; newAddresses[2] = CONTRACT_SYSTEMSTATUS; newAddresses[3] = CONTRACT_FUTURESMARKETMANAGER; newAddresses[4] = CONTRACT_PERPSV2MARKETSETTINGS; newAddresses[5] = CONTRACT_PERPSV2EXCHANGERATE; newAddresses[6] = CONTRACT_FLEXIBLESTORAGE; addresses = combineArrays(existingAddresses, newAddresses); } function _exchangeRates() internal view returns (IExchangeRates) { return IExchangeRates(requireAndGetAddress(CONTRACT_EXRATES)); } 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_PERPSV2MARKETSETTINGS); } /* ---------- Market Details ---------- */ function _baseAsset() internal view returns (bytes32) { return marketState.baseAsset(); } function _marketKey() internal view returns (bytes32) { return marketState.marketKey(); } /* * Returns the pSkew = skew / skewScale capping the pSkew between [-1, 1]. */ function _proportionalSkew() internal view returns (int) { int pSkew = int(marketState.marketSkew()).divideDecimal(int(_skewScale(_marketKey()))); // Ensures the proportionalSkew is between -1 and 1. return _min(_max(-_UNIT, pSkew), _UNIT); } function _proportionalElapsed() internal view returns (int) { return int(block.timestamp.sub(marketState.fundingLastRecomputed())).divideDecimal(1 days); } function _currentFundingVelocity() internal view returns (int) { int maxFundingVelocity = int(_maxFundingVelocity(_marketKey())); return _proportionalSkew().multiplyDecimal(maxFundingVelocity); } /* * @dev Retrieves the _current_ funding rate given the current market conditions. * * This is used during funding computation _before_ the market is modified (e.g. closing or * opening a position). However, called via the `currentFundingRate` view, will return the * 'instantaneous' funding rate. It's similar but subtle in that velocity now includes the most * recent skew modification. * * There is no variance in computation but will be affected based on outside modifications to * the market skew, max funding velocity, price, and time delta. */ function _currentFundingRate() internal view returns (int) { // calculations: // - velocity = proportional_skew * max_funding_velocity // - proportional_skew = skew / skew_scale // // example: // - prev_funding_rate = 0 // - prev_velocity = 0.0025 // - time_delta = 29,000s // - max_funding_velocity = 0.025 (2.5%) // - skew = 300 // - skew_scale = 10,000 // // note: prev_velocity just refs to the velocity _before_ modifying the market skew. // // funding_rate = prev_funding_rate + prev_velocity * (time_delta / seconds_in_day) // funding_rate = 0 + 0.0025 * (29,000 / 86,400) // = 0 + 0.0025 * 0.33564815 // = 0.00083912 return int(marketState.fundingRateLastRecomputed()).add( _currentFundingVelocity().multiplyDecimal(_proportionalElapsed()) ); } function _unrecordedFunding(uint price) internal view returns (int) { int nextFundingRate = _currentFundingRate(); // note the minus sign: funding flows in the opposite direction to the skew. int avgFundingRate = -(int(marketState.fundingRateLastRecomputed()).add(nextFundingRate)).divideDecimal(_UNIT * 2); return avgFundingRate.multiplyDecimal(_proportionalElapsed()).multiplyDecimal(int(price)); } /* * 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) { return int(marketState.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(marketState.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(marketState.marketSkew()).sub(oldSize).add(newSize); int newMarketSize = int(marketState.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)); } /* * @dev Similar to _remainingMargin except it accounts for the premium and fees to be paid upon liquidation. */ function _remainingLiquidatableMargin(Position memory position, uint price) internal view returns (uint) { int remaining = _marginPlusProfitFunding(position, price).sub(int(_liquidationPremium(position.size, price))); 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 be 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 between _minKeeperFee() and _maxKeeperFee() expressed in sUSD to prevent underincentivising * liquidations of small positions, or overpaying. * @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 maxFee = _maxKeeperFee(); uint cappedProportionalFee = proportionalFee > maxFee ? maxFee : proportionalFee; uint minFee = _minKeeperFee(); // max(proportionalFee, minFee) - to prevent not incentivising liquidations enough return cappedProportionalFee > minFee ? cappedProportionalFee : minFee; // not using _max() helper because it's for signed ints } /** * The minimal margin at which liquidation can happen. * Is the sum of liquidationBuffer, liquidationFee (for flagger) and keeperLiquidationFee (for liquidator) * @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 happening 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(_marketKey())); return liquidationBuffer.add(_liquidationFee(positionSize, price)).add(_keeperLiquidationFee()); } /** * @dev This is the additional premium we charge upon liquidation. * * Similar to fillPrice, but we disregard the skew (by assuming it's zero). Which is basically the calculation * when we compute as if taking the position from 0 to x. In practice, the premium component of the * liquidation will just be (size / skewScale) * (size * price). * * It adds a configurable multiplier that can be used to increase the margin that goes to feePool. * * For instance, if size of the liquidation position is 100, oracle price is 1200 and skewScale is 1M then, * * size = abs(-100) * = 100 * premium = 100 / 1000000 * (100 * 1200) * multiplier * = 12 * multiplier * if multiplier is set to 1 * = 12 * 1 = 12 * * @param positionSize Size of the position we want to liquidate * @param currentPrice The current oracle price (not fillPrice) * @return The premium to be paid upon liquidation in sUSD */ function _liquidationPremium(int positionSize, uint currentPrice) internal view returns (uint) { if (positionSize == 0) { return 0; } // note: this is the same as fillPrice() where the skew is 0. uint notional = _abs(_notionalValue(positionSize, currentPrice)); return _abs(positionSize).divideDecimal(_skewScale(_marketKey())).multiplyDecimal(notional).multiplyDecimal( _liquidationPremiumMultiplier(_marketKey()) ); } function _canLiquidate(Position memory position, uint price) internal view returns (bool) { // No liquidating empty positions. if (position.size == 0) { return false; } return _remainingLiquidatableMargin(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 (using the p/d-adjusted price). int marketSkew = marketState.marketSkew(); int notionalDiff = params.sizeDelta.multiplyDecimal(int(params.fillPrice)); // minimum fee to pay regardless (due to dynamic fees). uint baseFee = _abs(notionalDiff).multiplyDecimal(dynamicFeeRate); // does this trade keep the skew on one side? if (_sameSide(marketSkew + params.sizeDelta, marketSkew)) { // use a flat maker/taker fee for the entire size depending on whether the skew is increased or reduced. // // 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. uint staticRate = _sameSide(notionalDiff, marketState.marketSkew()) ? params.takerFee : params.makerFee; return baseFee + _abs(notionalDiff.multiplyDecimal(int(staticRate))); } // this trade flips the skew. // // the proportion of size that moves in the direction after the flip should not be considered // as a maker (reducing skew) as it's now taking (increasing skew) in the opposite direction. hence, // a different fee is applied on the proportion increasing the skew. // proportion of size that's on the other direction uint takerSize = _abs((marketSkew + params.sizeDelta).divideDecimal(params.sizeDelta)); uint makerSize = uint(_UNIT) - takerSize; uint takerFee = _abs(notionalDiff).multiplyDecimal(takerSize).multiplyDecimal(params.takerFee); uint makerFee = _abs(notionalDiff).multiplyDecimal(makerSize).multiplyDecimal(params.makerFee); return baseFee + takerFee + makerFee; } /// 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. Shouldn'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 marketState.fundingSequenceLength().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.oraclePrice)) { 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.fillPrice, -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.fillPrice), 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 (but including premium), because it wouldn't make sense to allow // a trade that will make the position liquidatable. // // note: we use `oraclePrice` here as `liquidationPremium` calcs premium based not current skew. uint liqPremium = _liquidationPremium(newPos.size, params.oraclePrice); uint liqMargin = _liquidationMargin(newPos.size, params.oraclePrice).add(liqPremium); if (newMargin <= liqMargin) { 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.fillPrice)).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 markets. if (_orderSizeTooLarge(_maxMarketValue(_marketKey()), oldPos.size, newPos.size)) { return (oldPos, 0, Status.MaxMarketSizeExceeded); } return (newPos, fee, Status.Ok); } /* ---------- Utilities ---------- */ /* * 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() internal view returns (uint price, bool invalid) { (price, invalid) = _exchangeRates().rateAndInvalid(_baseAsset()); // Ensure we catch uninitialised rates or suspended state / synth invalid = invalid || price == 0 || _systemStatus().synthSuspended(_baseAsset()); return (price, invalid); } /* * @dev SIP-279 fillPrice price at which a trade is executed against accounting for how this position's * size impacts the skew. If the size contracts the skew (reduces) then a discount is applied on the price * whereas expanding the skew incurs an additional premium. */ function _fillPrice(int size, uint price) internal view returns (uint) { int skew = marketState.marketSkew(); int skewScale = int(_skewScale(_marketKey())); int pdBefore = skew.divideDecimal(skewScale); int pdAfter = skew.add(size).divideDecimal(skewScale); int priceBefore = int(price).add(int(price).multiplyDecimal(pdBefore)); int priceAfter = int(price).add(int(price).multiplyDecimal(pdAfter)); // How is the p/d-adjusted price calculated using an example: // // price = $1200 USD (oracle) // size = 100 // skew = 0 // skew_scale = 1,000,000 (1M) // // Then, // // pd_before = 0 / 1,000,000 // = 0 // pd_after = (0 + 100) / 1,000,000 // = 100 / 1,000,000 // = 0.0001 // // price_before = 1200 * (1 + pd_before) // = 1200 * (1 + 0) // = 1200 // price_after = 1200 * (1 + pd_after) // = 1200 * (1 + 0.0001) // = 1200 * (1.0001) // = 1200.12 // Finally, // // fill_price = (price_before + price_after) / 2 // = (1200 + 1200.12) / 2 // = 1200.06 return uint(priceBefore.add(priceAfter).divideDecimal(_UNIT * 2)); } /* * 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) || (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)]); } } } // Inheritance // https://docs.synthetix.io/contracts/source/contracts/PerpsV2MarketProxyable contract PerpsV2MarketProxyable is PerpsV2MarketBase, Proxyable { /* ========== CONSTRUCTOR ========== */ constructor( address payable _proxy, address _marketState, address _owner, address _resolver ) public PerpsV2MarketBase(_marketState, _owner, _resolver) Proxyable(_proxy) {} /* ---------- Market Operations ---------- */ /* * 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); marketState.setEntryDebtCorrection( int128(int(marketState.entryDebtCorrection()).add(newCorrection).sub(oldCorrection)) ); } /* * 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(marketState.fundingSequence(position.lastFundingIndex)) ) ); } /* * 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(bool checkOffchainMarket) 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()); if (checkOffchainMarket) { // offchain PerpsV2 virtual market _systemStatus().requireFuturesMarketActive(_offchainMarketKey(_marketKey())); } // 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, bool staleOrInvalid) = _exchangeRates().rateWithSafetyChecks(_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 || staleOrInvalid, Status.InvalidPrice); return price; } /** TODO: Docs */ function _assertFillPrice( uint fillPrice, uint desiredFillPrice, int sizeDelta ) internal view returns (uint) { _revertIfError( sizeDelta > 0 ? fillPrice > desiredFillPrice : fillPrice < desiredFillPrice, Status.PriceImpactToleranceExceeded ); return fillPrice; } function _recomputeFunding(uint price) internal returns (uint lastIndex) { uint sequenceLengthBefore = marketState.fundingSequenceLength(); int fundingRate = _currentFundingRate(); int funding = _nextFundingEntry(price); marketState.pushFundingSequence(int128(funding)); marketState.setFundingLastRecomputed(uint32(block.timestamp)); marketState.setFundingRateLastRecomputed(int128(fundingRate)); emitFundingRecomputed(funding, fundingRate, sequenceLengthBefore, marketState.fundingLastRecomputed()); return sequenceLengthBefore; } // updates the stored position margin in place (on the stored position) function _updatePositionMargin( address account, Position memory position, int orderSizeDelta, 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. uint fundingIndex = _latestFundingIndex(); _applyDebtCorrection( Position(0, uint64(fundingIndex), uint128(margin), uint128(price), int128(position.size)), Position(0, position.lastFundingIndex, position.margin, position.lastPrice, int128(position.size)) ); // 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 (position.size != 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: // * the resulting margin would not be lower than the liquidation margin or min initial margin // * liqMargin accounting for the liqPremium if (marginDelta < 0) { // note: We .add `liqPremium` to increase the req margin to avoid entering into liquidation uint liqPremium = _liquidationPremium(position.size, price); uint liqMargin = _liquidationMargin(position.size, price).add(liqPremium); _revertIfError(margin <= liqMargin, Status.InsufficientMargin); // `marginDelta` can be decreasing (due to e.g. fees). However, price could also have moved in the // opposite direction resulting in a loss. A reduced remainingMargin to calc currentLeverage can // put the position above maxLeverage. // // To account for this, a check on `positionDecreasing` ensures that we can always perform this action // so long as we're reducing the position size and not liquidatable. int newPositionSize = int(position.size).add(orderSizeDelta); bool positionDecreasing = _sameSide(position.size, newPositionSize) && _abs(newPositionSize) < _abs(position.size); if (!positionDecreasing) { _revertIfError( _maxLeverage(_marketKey()) < _abs(_currentLeverage(position, price, margin)), Status.MaxLeverageExceeded ); _revertIfError(margin < _minInitialMargin(), Status.InsufficientMargin); } } } // persist position changes marketState.updatePosition( account, position.id, position.lastFundingIndex, position.margin, position.lastPrice, position.size ); } function _trade(address sender, TradeParams memory params) internal notFlagged(sender) { Position memory position = marketState.positions(sender); Position memory oldPosition = Position({ id: position.id, lastFundingIndex: position.lastFundingIndex, margin: position.margin, lastPrice: position.lastPrice, size: position.size }); // Compute the new position after performing the trade (Position memory newPosition, uint fee, Status status) = _postTradeDetails(oldPosition, params); _revertIfError(status); _assertFillPrice(params.fillPrice, params.desiredFillPrice, params.sizeDelta); // Update the aggregated market size and skew with the new order size marketState.setMarketSkew(int128(int(marketState.marketSkew()).add(newPosition.size).sub(oldPosition.size))); marketState.setMarketSize( uint128(uint(marketState.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)) { emitPerpsTracking(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 = marketState.nextPositionId(); marketState.setNextPositionId(id + 1); } position.id = id; position.size = newPosition.size; position.lastPrice = uint128(params.fillPrice); position.lastFundingIndex = uint64(fundingIndex); } // persist position changes marketState.updatePosition( sender, position.id, position.lastFundingIndex, position.margin, position.lastPrice, position.size ); // emit the modification event emitPositionModified( id, sender, newPosition.margin, newPosition.size, params.sizeDelta, params.fillPrice, fundingIndex, fee, marketState.marketSkew() ); } /* ========== EVENTS ========== */ function addressToBytes32(address input) internal pure returns (bytes32) { return bytes32(uint256(uint160(input))); } event PositionModified( uint indexed id, address indexed account, uint margin, int size, int tradeSize, uint lastPrice, uint fundingIndex, uint fee, int skew ); bytes32 internal constant POSITIONMODIFIED_SIG = keccak256("PositionModified(uint256,address,uint256,int256,int256,uint256,uint256,uint256,int256)"); function emitPositionModified( uint id, address account, uint margin, int size, int tradeSize, uint lastPrice, uint fundingIndex, uint fee, int skew ) internal { proxy._emit( abi.encode(margin, size, tradeSize, lastPrice, fundingIndex, fee, skew), 3, POSITIONMODIFIED_SIG, bytes32(id), addressToBytes32(account), 0 ); } event FundingRecomputed(int funding, int fundingRate, uint index, uint timestamp); bytes32 internal constant FUNDINGRECOMPUTED_SIG = keccak256("FundingRecomputed(int256,int256,uint256,uint256)"); function emitFundingRecomputed( int funding, int fundingRate, uint index, uint timestamp ) internal { proxy._emit(abi.encode(funding, fundingRate, index, timestamp), 1, FUNDINGRECOMPUTED_SIG, 0, 0, 0); } event PerpsTracking(bytes32 indexed trackingCode, bytes32 baseAsset, bytes32 marketKey, int sizeDelta, uint fee); bytes32 internal constant PERPSTRACKING_SIG = keccak256("PerpsTracking(bytes32,bytes32,bytes32,int256,uint256)"); function emitPerpsTracking( bytes32 trackingCode, bytes32 baseAsset, bytes32 marketKey, int sizeDelta, uint fee ) internal { proxy._emit(abi.encode(baseAsset, marketKey, sizeDelta, fee), 2, PERPSTRACKING_SIG, trackingCode, 0, 0); } /* ========== MODIFIERS ========== */ modifier flagged(address account) { if (!marketState.isFlagged(account)) { revert(_errorMessages[uint8(Status.PositionNotFlagged)]); } _; } modifier notFlagged(address account) { if (marketState.isFlagged(account)) { revert(_errorMessages[uint8(Status.PositionFlagged)]); } _; } } interface IPerpsV2MarketDelayedExecution { function executeDelayedOrder(address account) external; function executeOffchainDelayedOrder(address account, bytes[] calldata priceUpdateData) external payable; function cancelDelayedOrder(address account) external; function cancelOffchainDelayedOrder(address account) external; } // import "@pythnetwork/pyth-sdk-solidity/PythStructs.sol"; contract PythStructs { // A price with a degree of uncertainty, represented as a price +- a confidence interval. // // The confidence interval roughly corresponds to the standard error of a normal distribution. // Both the price and confidence are stored in a fixed-point numeric representation, // `x * (10^expo)`, where `expo` is the exponent. // // Please refer to the documentation at https://docs.pyth.network/consumers/best-practices for how // to how this price safely. struct Price { // Price int64 price; // Confidence interval around the price uint64 conf; // Price exponent int32 expo; // Unix timestamp describing when the price was published uint publishTime; } // PriceFeed represents a current aggregate price from pyth publisher feeds. struct PriceFeed { // The price ID. bytes32 id; // Latest available price Price price; // Latest available exponentially-weighted moving average price Price emaPrice; } } // import "@pythnetwork/pyth-sdk-solidity/IPyth.sol"; /// @title Consume prices from the Pyth Network (https://pyth.network/). /// @dev Please refer to the guidance at https://docs.pyth.network/consumers/best-practices for how to consume prices safely. /// @author Pyth Data Association interface IPyth { /// @dev Emitted when the price feed with `id` has received a fresh update. /// @param id The Pyth Price Feed ID. /// @param publishTime Publish time of the given price update. /// @param price Price of the given price update. /// @param conf Confidence interval of the given price update. event PriceFeedUpdate(bytes32 indexed id, uint64 publishTime, int64 price, uint64 conf); /// @dev Emitted when a batch price update is processed successfully. /// @param chainId ID of the source chain that the batch price update comes from. /// @param sequenceNumber Sequence number of the batch price update. event BatchPriceFeedUpdate(uint16 chainId, uint64 sequenceNumber); /// @notice Returns the period (in seconds) that a price feed is considered valid since its publish time function getValidTimePeriod() external view returns (uint validTimePeriod); /// @notice Returns the price and confidence interval. /// @dev Reverts if the price has not been updated within the last `getValidTimePeriod()` seconds. /// @param id The Pyth Price Feed ID of which to fetch the price and confidence interval. /// @return price - please read the documentation of PythStructs.Price to understand how to use this safely. function getPrice(bytes32 id) external view returns (PythStructs.Price memory price); /// @notice Returns the exponentially-weighted moving average price and confidence interval. /// @dev Reverts if the EMA price is not available. /// @param id The Pyth Price Feed ID of which to fetch the EMA price and confidence interval. /// @return price - please read the documentation of PythStructs.Price to understand how to use this safely. function getEmaPrice(bytes32 id) external view returns (PythStructs.Price memory price); /// @notice Returns the price of a price feed without any sanity checks. /// @dev This function returns the most recent price update in this contract without any recency checks. /// This function is unsafe as the returned price update may be arbitrarily far in the past. /// /// Users of this function should check the `publishTime` in the price to ensure that the returned price is /// sufficiently recent for their application. If you are considering using this function, it may be /// safer / easier to use either `getPrice` or `getPriceNoOlderThan`. /// @return price - please read the documentation of PythStructs.Price to understand how to use this safely. function getPriceUnsafe(bytes32 id) external view returns (PythStructs.Price memory price); /// @notice Returns the price that is no older than `age` seconds of the current time. /// @dev This function is a sanity-checked version of `getPriceUnsafe` which is useful in /// applications that require a sufficiently-recent price. Reverts if the price wasn't updated sufficiently /// recently. /// @return price - please read the documentation of PythStructs.Price to understand how to use this safely. function getPriceNoOlderThan(bytes32 id, uint age) external view returns (PythStructs.Price memory price); /// @notice Returns the exponentially-weighted moving average price of a price feed without any sanity checks. /// @dev This function returns the same price as `getEmaPrice` in the case where the price is available. /// However, if the price is not recent this function returns the latest available price. /// /// The returned price can be from arbitrarily far in the past; this function makes no guarantees that /// the returned price is recent or useful for any particular application. /// /// Users of this function should check the `publishTime` in the price to ensure that the returned price is /// sufficiently recent for their application. If you are considering using this function, it may be /// safer / easier to use either `getEmaPrice` or `getEmaPriceNoOlderThan`. /// @return price - please read the documentation of PythStructs.Price to understand how to use this safely. function getEmaPriceUnsafe(bytes32 id) external view returns (PythStructs.Price memory price); /// @notice Returns the exponentially-weighted moving average price that is no older than `age` seconds /// of the current time. /// @dev This function is a sanity-checked version of `getEmaPriceUnsafe` which is useful in /// applications that require a sufficiently-recent price. Reverts if the price wasn't updated sufficiently /// recently. /// @return price - please read the documentation of PythStructs.Price to understand how to use this safely. function getEmaPriceNoOlderThan(bytes32 id, uint age) external view returns (PythStructs.Price memory price); /// @notice Update price feeds with given update messages. /// This method requires the caller to pay a fee in wei; the required fee can be computed by calling /// `getUpdateFee` with the length of the `updateData` array. /// Prices will be updated if they are more recent than the current stored prices. /// The call will succeed even if the update is not the most recent. /// @dev Reverts if the transferred fee is not sufficient or the updateData is invalid. /// @param updateData Array of price update data. function updatePriceFeeds(bytes[] calldata updateData) external payable; /// @notice Wrapper around updatePriceFeeds that rejects fast if a price update is not necessary. A price update is /// necessary if the current on-chain publishTime is older than the given publishTime. It relies solely on the /// given `publishTimes` for the price feeds and does not read the actual price update publish time within `updateData`. /// /// This method requires the caller to pay a fee in wei; the required fee can be computed by calling /// `getUpdateFee` with the length of the `updateData` array. /// /// `priceIds` and `publishTimes` are two arrays with the same size that correspond to senders known publishTime /// of each priceId when calling this method. If all of price feeds within `priceIds` have updated and have /// a newer or equal publish time than the given publish time, it will reject the transaction to save gas. /// Otherwise, it calls updatePriceFeeds method to update the prices. /// /// @dev Reverts if update is not needed or the transferred fee is not sufficient or the updateData is invalid. /// @param updateData Array of price update data. /// @param priceIds Array of price ids. /// @param publishTimes Array of publishTimes. `publishTimes[i]` corresponds to known `publishTime` of `priceIds[i]` function updatePriceFeedsIfNecessary( bytes[] calldata updateData, bytes32[] calldata priceIds, uint64[] calldata publishTimes ) external payable; /// @notice Returns the required fee to update an array of price updates. /// @param updateData Array of price update data. /// @return feeAmount The required fee in Wei. function getUpdateFee(bytes[] calldata updateData) external view returns (uint feeAmount); /// @notice Parse `updateData` and return price feeds of the given `priceIds` if they are all published /// within `minPublishTime` and `maxPublishTime`. /// /// You can use this method if you want to use a Pyth price at a fixed time and not the most recent price; /// otherwise, please consider using `updatePriceFeeds`. This method does not store the price updates on-chain. /// /// This method requires the caller to pay a fee in wei; the required fee can be computed by calling /// `getUpdateFee` with the length of the `updateData` array. /// /// /// @dev Reverts if the transferred fee is not sufficient or the updateData is invalid or there is /// no update for any of the given `priceIds` within the given time range. /// @param updateData Array of price update data. /// @param priceIds Array of price ids. /// @param minPublishTime minimum acceptable publishTime for the given `priceIds`. /// @param maxPublishTime maximum acceptable publishTime for the given `priceIds`. /// @return priceFeeds Array of the price feeds corresponding to the given `priceIds` (with the same order). function parsePriceFeedUpdates( bytes[] calldata updateData, bytes32[] calldata priceIds, uint64 minPublishTime, uint64 maxPublishTime ) external payable returns (PythStructs.PriceFeed[] memory priceFeeds); } // https://docs.synthetix.io/contracts/source/contracts/IPerpsV2ExchangeRate interface IPerpsV2ExchangeRate { function setOffchainOracle(IPyth _offchainOracle) external; function setOffchainPriceFeedId(bytes32 assetId, bytes32 priceFeedId) external; /* ========== VIEWS ========== */ function offchainOracle() external view returns (IPyth); function offchainPriceFeedId(bytes32 assetId) external view returns (bytes32); /* ---------- priceFeeds mutation ---------- */ function updatePythPrice(address sender, bytes[] calldata priceUpdateData) external payable; // it is a view but it can revert function resolveAndGetPrice(bytes32 assetId, uint maxAge) external view returns (uint price, uint publishTime); // it is a view but it can revert function resolveAndGetLatestPrice(bytes32 assetId) external view returns (uint price, uint publishTime); } // Inheritance // Reference /** Contract that implements DelayedOrders (offchain) mechanism for the PerpsV2 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 committing 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). */ // https://docs.synthetix.io/contracts/source/contracts/PerpsV2MarketDelayedExecution contract PerpsV2MarketDelayedExecution is IPerpsV2MarketDelayedExecution, PerpsV2MarketProxyable { /* ========== CONSTRUCTOR ========== */ constructor( address payable _proxy, address _marketState, address _owner, address _resolver ) public PerpsV2MarketProxyable(_proxy, _marketState, _owner, _resolver) {} function _perpsV2ExchangeRate() internal view returns (IPerpsV2ExchangeRate) { return IPerpsV2ExchangeRate(requireAndGetAddress(CONTRACT_PERPSV2EXCHANGERATE)); } ///// Mutative methods /** * @notice Tries to execute a previously submitted delayed 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) * - Time delay and target round has not yet been reached * If order reverts, it has to be removed by calling cancelDelayedOrder(). * 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 delayed order */ function executeDelayedOrder(address account) external onlyProxy { // important!: order of the account, not the sender! DelayedOrder memory order = marketState.delayedOrders(account); // check that a previous order exists require(order.sizeDelta != 0, "no previous order"); require(!order.isOffchain, "use offchain method"); uint currentRoundId = _exchangeRates().getCurrentRoundId(_baseAsset()); require( block.timestamp >= order.executableAtTime || order.targetRoundId <= currentRoundId, "executability 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(order.executableAtTime, currentRoundId, order.targetRoundId), "order too old, use cancel" ); // price depends on whether the delay or price update has reached/occurred first _executeDelayedOrder( account, order, _assetPriceRequireSystemChecks(false), currentRoundId, _takerFeeDelayedOrder(_marketKey()), _makerFeeDelayedOrder(_marketKey()) ); } /** * @notice Tries to execute a previously submitted delayed 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) * - Time delay and target round has not yet been reached * If order reverts, it has to be removed by calling cancelDelayedOrder(). * 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 delayed order */ function executeOffchainDelayedOrder(address account, bytes[] calldata priceUpdateData) external payable onlyProxy { // important!: order of the account, not the sender! DelayedOrder memory order = marketState.delayedOrders(account); // check that a previous order exists require(order.sizeDelta != 0, "no previous order"); require(order.isOffchain, "use onchain method"); // update price feed (this is payable) _perpsV2ExchangeRate().updatePythPrice.value(msg.value)(messageSender, priceUpdateData); // get latest price for asset uint maxAge = _offchainDelayedOrderMaxAge(_marketKey()); uint minAge = _offchainDelayedOrderMinAge(_marketKey()); (uint currentPrice, uint executionTimestamp) = _offchainAssetPriceRequireSystemChecks(maxAge); require((executionTimestamp > order.intentionTime), "price not updated"); require((executionTimestamp - order.intentionTime > minAge), "executability not reached"); require((block.timestamp - order.intentionTime < maxAge), "order too old, use cancel"); _executeDelayedOrder( account, order, currentPrice, 0, _takerFeeOffchainDelayedOrder(_marketKey()), _makerFeeOffchainDelayedOrder(_marketKey()) ); } /** * @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 cancelDelayedOrder(address account) external onlyProxy { // important!! order of the account, not the msg.sender DelayedOrder memory order = marketState.delayedOrders(account); // check that a previous order exists require(order.sizeDelta != 0, "no previous order"); require(!order.isOffchain, "use offchain method"); _cancelDelayedOrder(account, order); } /** * @notice Cancels an existing order for an account. * Anyone can call this method for any account after the order becomes stale 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 cancelOffchainDelayedOrder(address account) external onlyProxy { // important!! order of the account, not the msg.sender DelayedOrder memory order = marketState.delayedOrders(account); // check that a previous order exists require(order.sizeDelta != 0, "no previous order"); require(order.isOffchain, "use onchain method"); _cancelDelayedOrder(account, order); } function _confirmCanCancel(DelayedOrder memory order, uint currentRoundId) internal { if (order.isOffchain) { require(block.timestamp - order.intentionTime > _offchainDelayedOrderMaxAge(_marketKey()), "cannot cancel yet"); } else { require( _confirmationWindowOver(order.executableAtTime, currentRoundId, order.targetRoundId), "cannot be cancelled by keeper yet" ); } } ///// Internal /// confirmation window is over when: /// 1. current roundId is more than nextPriceConfirmWindow rounds after target roundId /// 2. or executableAtTime - block.timestamp is more than delayedOrderConfirmWindow /// /// if either conditions are met, an order is considered to have exceeded the window. function _confirmationWindowOver( uint executableAtTime, uint currentRoundId, uint targetRoundId ) internal view returns (bool) { bytes32 marketKey = _marketKey(); return (block.timestamp > executableAtTime && (block.timestamp - executableAtTime) > _delayedOrderConfirmWindow(marketKey)) || ((currentRoundId > targetRoundId) && (currentRoundId - targetRoundId > _nextPriceConfirmWindow(marketKey))); // don't underflow } /* * The current base price, reverting if it is invalid, or if system or synth is suspended. */ function _offchainAssetPriceRequireSystemChecks(uint maxAge) internal returns (uint price, uint publishTime) { // Onchain oracle asset price uint onchainPrice = _assetPriceRequireSystemChecks(true); (price, publishTime) = _perpsV2ExchangeRate().resolveAndGetPrice(_baseAsset(), maxAge); require(onchainPrice > 0 && price > 0, "invalid, price is 0"); uint delta = (onchainPrice > price) ? onchainPrice.divideDecimal(price).sub(SafeDecimalMath.unit()) : price.divideDecimal(onchainPrice).sub(SafeDecimalMath.unit()); require(_offchainPriceDivergence(_marketKey()) > delta, "price divergence too high"); return (price, publishTime); } function _cancelDelayedOrder(address account, DelayedOrder memory order) internal { uint currentRoundId = _exchangeRates().getCurrentRoundId(_baseAsset()); _confirmCanCancel(order, currentRoundId); if (account == messageSender) { // this is account owner - refund keeper fee to margin Position memory position = marketState.positions(account); // cancelling an order does not induce a fillPrice as no skew has moved. uint price = _assetPriceRequireSystemChecks(false); uint fundingIndex = _recomputeFunding(price); _updatePositionMargin(account, position, order.sizeDelta, price, int(order.keeperDeposit)); // emit event for modifying the position (add the fee to margin) emitPositionModified( position.id, account, position.margin, position.size, 0, price, fundingIndex, 0, marketState.marketSkew() ); } else { // send keeper fee to keeper _manager().issueSUSD(messageSender, order.keeperDeposit); } // note: pay debt pool in the event there is any commitFee // // this should never occur but may during release as there may be lingering orders to be cancelled // which was submitted with a commitFee either before or during the upgrade. if (order.commitDeposit > 0) { _manager().payFee(order.commitDeposit); } // important!! position of the account, not the msg.sender marketState.deleteDelayedOrder(account); emitDelayedOrderRemoved(account, currentRoundId, order); } function _executeDelayedOrder( address account, DelayedOrder memory order, uint currentPrice, uint currentRoundId, uint takerFee, uint makerFee ) internal { // handle the fees and refunds according to the mechanism rules // // note: commitDeposit will always be 0 as we no longer charge a commitDeposit on submit. however, // during upgrade there may be pending orders for execution with a commitDeposit. uint toRefund = order.commitDeposit; // refund the commitment deposit // refund keeperFee to margin if it's the account holder if (messageSender == account) { toRefund += order.keeperDeposit; } else { _manager().issueSUSD(messageSender, order.keeperDeposit); } Position memory position = marketState.positions(account); uint fundingIndex = _recomputeFunding(currentPrice); // we need to grab the fillPrice for events and margin updates. uint fillPrice = _fillPrice(order.sizeDelta, 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 if (toRefund > 0) { _updatePositionMargin(account, position, order.sizeDelta, fillPrice, int(toRefund)); // emit event for modifying the position (refunding fee/s) emitPositionModified( position.id, account, position.margin, position.size, 0, fillPrice, fundingIndex, 0, marketState.marketSkew() ); } // execute or revert _trade( account, TradeParams({ sizeDelta: order.sizeDelta, // using the pastPrice from the target roundId oraclePrice: currentPrice, // the funding is applied only from order confirmation time fillPrice: fillPrice, takerFee: takerFee, //_takerFeeDelayedOrder(_marketKey()), makerFee: makerFee, //_makerFeeDelayedOrder(_marketKey()), desiredFillPrice: order.desiredFillPrice, trackingCode: order.trackingCode }) ); // remove stored order marketState.deleteDelayedOrder(account); // emit event emitDelayedOrderRemoved(account, currentRoundId, order); } event DelayedOrderRemoved( address indexed account, bool isOffchain, uint currentRoundId, int sizeDelta, uint targetRoundId, uint commitDeposit, uint keeperDeposit, bytes32 trackingCode ); bytes32 internal constant DELAYEDORDERREMOVED_SIG = keccak256("DelayedOrderRemoved(address,bool,uint256,int256,uint256,uint256,uint256,bytes32)"); function emitDelayedOrderRemoved( address account, uint currentRoundId, DelayedOrder memory order ) internal { proxy._emit( abi.encode( order.isOffchain, currentRoundId, order.sizeDelta, order.targetRoundId, order.commitDeposit, order.keeperDeposit, order.trackingCode ), 2, DELAYEDORDERREMOVED_SIG, addressToBytes32(account), 0, 0 ); } }
[{"inputs":[{"internalType":"address payable","name":"_proxy","type":"address"},{"internalType":"address","name":"_marketState","type":"address"},{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_resolver","type":"address"}],"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":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"bool","name":"isOffchain","type":"bool"},{"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":"DelayedOrderRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"int256","name":"funding","type":"int256"},{"indexed":false,"internalType":"int256","name":"fundingRate","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":false,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":false,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnerChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnerNominated","type":"event"},{"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":"PerpsTracking","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"},{"indexed":false,"internalType":"int256","name":"skew","type":"int256"}],"name":"PositionModified","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"proxyAddress","type":"address"}],"name":"ProxyUpdated","type":"event"},{"constant":false,"inputs":[],"name":"acceptOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"cancelDelayedOrder","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"cancelOffchainDelayedOrder","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"executeDelayedOrder","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bytes[]","name":"priceUpdateData","type":"bytes[]"}],"name":"executeOffchainDelayedOrder","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[],"name":"isResolverCached","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"marketState","outputs":[{"internalType":"contract IPerpsV2MarketState","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"messageSender","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"nominateNewOwner","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"nominatedOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"proxy","outputs":[{"internalType":"contract Proxy","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"rebuildCache","outputs":[],"payable":false,"stateMutability":"nonpayable","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":"address","name":"sender","type":"address"}],"name":"setMessageSender","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address payable","name":"_proxy","type":"address"}],"name":"setProxy","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}]
Deployed Bytecode
0x6080604052600436106100fe5760003560e01c80638da5cb5b11610095578063c70b41e911610064578063c70b41e91461026a578063d67bdd251461028a578063dcce58061461029f578063dfa723cc146102bf578063ec556889146102d2576100fe565b80638da5cb5b146101f557806397107d6d1461020a578063a8300afb1461022a578063bc67f8321461024a576100fe565b806353a47bb7116100d157806353a47bb71461018757806374185360146101a957806379ba5097146101be578063899ffef4146101d3576100fe565b806304f3bcec1461010357806308fb1b771461012e5780631627540c146101435780632af64bd314610165575b600080fd5b34801561010f57600080fd5b506101186102e7565b60405161012591906157c3565b60405180910390f35b34801561013a57600080fd5b506101186102f6565b34801561014f57600080fd5b5061016361015e3660046148e7565b610305565b005b34801561017157600080fd5b5061017a610363565b60405161012591906155ec565b34801561019357600080fd5b5061019c61047b565b6040516101259190615519565b3480156101b557600080fd5b5061016361048a565b3480156101ca57600080fd5b506101636105e0565b3480156101df57600080fd5b506101e8610685565b60405161012591906155db565b34801561020157600080fd5b5061019c6107f7565b34801561021657600080fd5b506101636102253660046148e7565b610806565b34801561023657600080fd5b506101636102453660046148e7565b610859565b34801561025657600080fd5b506101636102653660046148e7565b610a6f565b34801561027657600080fd5b506101636102853660046148e7565b610a99565b34801561029657600080fd5b5061019c610b7c565b3480156102ab57600080fd5b506101636102ba3660046148e7565b610b8b565b6101636102cd366004614923565b610c63565b3480156102de57600080fd5b50610118610e84565b6002546001600160a01b031681565b6004546001600160a01b031681565b61030d610e93565b600180546001600160a01b0319166001600160a01b0383161790556040517f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce2290610358908390615519565b60405180910390a150565b6000606061036f610685565b905060005b815181101561047157600082828151811061038b57fe5b602090810291909101810151600081815260039092526040918290205460025492516321f8a72160e01b81529193506001600160a01b039081169216906321f8a721906103dc908590600401615662565b60206040518083038186803b1580156103f457600080fd5b505afa158015610408573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061042c9190810190614905565b6001600160a01b031614158061045757506000818152600360205260409020546001600160a01b0316155b156104685760009350505050610478565b50600101610374565b5060019150505b90565b6001546001600160a01b031681565b6060610494610685565b905060005b81518110156105dc5760008282815181106104b057fe5b602002602001015190506000600260009054906101000a90046001600160a01b03166001600160a01b031663dacb2d0183846040516020016104f291906154ed565b6040516020818303038152906040526040518363ffffffff1660e01b815260040161051e9291906156ce565b60206040518083038186803b15801561053657600080fd5b505afa15801561054a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061056e9190810190614905565b6000838152600360205260409081902080546001600160a01b0319166001600160a01b038416179055519091507f88a93678a3692f6789d9546fc621bf7234b101ddb7d4fe479455112831b8aa68906105ca9084908490615670565b60405180910390a15050600101610499565b5050565b6001546001600160a01b031633146106135760405162461bcd60e51b815260040161060a90615801565b60405180910390fd5b6000546001546040517fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c92610656926001600160a01b0391821692911690615535565b60405180910390a160018054600080546001600160a01b03199081166001600160a01b03841617909155169055565b606080610690610ebf565b6040805160078082526101008201909252919250606091906020820160e0803883390190505090506822bc31b430b733b2b960b91b816000815181106106d257fe5b6020026020010181815250506c45786368616e6765526174657360981b816001815181106106fc57fe5b6020026020010181815250506b53797374656d53746174757360a01b8160028151811061072557fe5b60200260200101818152505073233aba3ab932b9a6b0b935b2ba26b0b730b3b2b960611b8160038151811061075657fe5b602002602001018181525050600080516020615b998339815191528160048151811061077e57fe5b602002602001018181525050725065727073563245786368616e67655261746560681b816005815181106107ae57fe5b6020026020010181815250506e466c657869626c6553746f7261676560881b816006815181106107da57fe5b6020026020010181815250506107f08282610f10565b9250505090565b6000546001600160a01b031681565b61080e610e93565b600680546001600160a01b0319166001600160a01b0383161790556040517ffc80377ca9c49cc11ae6982f390a42db976d5530af7c43889264b13fbbd7c57e90610358908390615527565b610861610fcc565b610869614673565b6004805460405163645c04d560e11b81526001600160a01b039091169163c8b809aa9161089891869101615519565b6101206040518083038186803b1580156108b157600080fd5b505afa1580156108c5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506108e991908101906149d2565b90508060200151600f0b600014156109135760405162461bcd60e51b815260040161060a906158d1565b8051156109325760405162461bcd60e51b815260040161060a90615851565b600061093c610ff6565b6001600160a01b0316637a018a1e610952611016565b6040518263ffffffff1660e01b815260040161096e9190615662565b60206040518083038186803b15801561098657600080fd5b505afa15801561099a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506109be9190810190614996565b90508160c00151421015806109e057508082606001516001600160801b031611155b6109fc5760405162461bcd60e51b815260040161060a906158c1565b610a188260c001518284606001516001600160801b031661109e565b15610a355760405162461bcd60e51b815260040161060a90615811565b610a6a8383610a4460006110ec565b84610a55610a506112ee565b61133e565b610a65610a606112ee565b611367565b61138a565b505050565b610a77610fcc565b600780546001600160a01b0319166001600160a01b0392909216919091179055565b610aa1610fcc565b610aa9614673565b6004805460405163645c04d560e11b81526001600160a01b039091169163c8b809aa91610ad891869101615519565b6101206040518083038186803b158015610af157600080fd5b505afa158015610b05573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610b2991908101906149d2565b90508060200151600f0b60001415610b535760405162461bcd60e51b815260040161060a906158d1565b805115610b725760405162461bcd60e51b815260040161060a90615851565b6105dc8282611684565b6007546001600160a01b031681565b610b93610fcc565b610b9b614673565b6004805460405163645c04d560e11b81526001600160a01b039091169163c8b809aa91610bca91869101615519565b6101206040518083038186803b158015610be357600080fd5b505afa158015610bf7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610c1b91908101906149d2565b90508060200151600f0b60001415610c455760405162461bcd60e51b815260040161060a906158d1565b8051610b725760405162461bcd60e51b815260040161060a90615951565b610c6b610fcc565b610c73614673565b6004805460405163645c04d560e11b81526001600160a01b039091169163c8b809aa91610ca291889101615519565b6101206040518083038186803b158015610cbb57600080fd5b505afa158015610ccf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610cf391908101906149d2565b90508060200151600f0b60001415610d1d5760405162461bcd60e51b815260040161060a906158d1565b8051610d3b5760405162461bcd60e51b815260040161060a90615951565b610d436119d5565b60075460405163047ce44760e11b81526001600160a01b03928316926308f9c88e923492610d7b929091169088908890600401615550565b6000604051808303818588803b158015610d9457600080fd5b505af1158015610da8573d6000803e3d6000fd5b50505050506000610dbf610dba6112ee565b6119f6565b90506000610dd3610dce6112ee565b611a22565b9050600080610de184611a4e565b915091508460e001518111610e085760405162461bcd60e51b815260040161060a90615841565b828560e00151820311610e2d5760405162461bcd60e51b815260040161060a906158c1565b838560e00151420310610e525760405162461bcd60e51b815260040161060a90615811565b610e7a8886846000610e6a610e656112ee565b611c89565b610a65610e756112ee565b611cb5565b5050505050505050565b6006546001600160a01b031681565b6000546001600160a01b03163314610ebd5760405162461bcd60e51b815260040161060a906158a1565b565b604080516001808252818301909252606091602080830190803883390190505090506e466c657869626c6553746f7261676560881b81600081518110610f0157fe5b60200260200101818152505090565b60608151835101604051908082528060200260200182016040528015610f40578160200160208202803883390190505b50905060005b8351811015610f8257838181518110610f5b57fe5b6020026020010151828281518110610f6f57fe5b6020908102919091010152600101610f46565b5060005b8251811015610fc557828181518110610f9b57fe5b6020026020010151828286510181518110610fb257fe5b6020908102919091010152600101610f86565b5092915050565b6006546001600160a01b03163314610ebd5760405162461bcd60e51b815260040161060a90615931565b60006110116c45786368616e6765526174657360981b611ce1565b905090565b6000600460009054906101000a90046001600160a01b03166001600160a01b031663cdf456e16040518163ffffffff1660e01b815260040160206040518083038186803b15801561106657600080fd5b505afa15801561107a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506110119190810190614996565b6000806110a96112ee565b905084421180156110c357506110be81611d3e565b854203115b806110e1575082841180156110e157506110dc81611d6a565b838503115b9150505b9392505050565b60006110f6611d8f565b6001600160a01b031663856aae6c61110c6112ee565b6040518263ffffffff1660e01b81526004016111289190615662565b60006040518083038186803b15801561114057600080fd5b505afa158015611154573d6000803e3d6000fd5b50505050611160611d8f565b6001600160a01b03166342a28e21611176611016565b6040518263ffffffff1660e01b81526004016111929190615662565b60006040518083038186803b1580156111aa57600080fd5b505afa1580156111be573d6000803e3d6000fd5b50505050811561123b576111d0611d8f565b6001600160a01b031663856aae6c6111ee6111e96112ee565b611da9565b6040518263ffffffff1660e01b815260040161120a9190615662565b60006040518083038186803b15801561122257600080fd5b505afa158015611236573d6000803e3d6000fd5b505050505b6000806000611248610ff6565b6001600160a01b031663045056f861125e611016565b6040518263ffffffff1660e01b815260040161127a9190615662565b606060405180830381600087803b15801561129457600080fd5b505af11580156112a8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506112cc9190810190614a67565b9250925092506112e582806112de5750815b6001611e7a565b50909392505050565b6000600460009054906101000a90046001600160a01b03166001600160a01b031663d7103a466040518163ffffffff1660e01b815260040160206040518083038186803b15801561106657600080fd5b600061136182733a30b5b2b92332b2a232b630bcb2b227b93232b960611b611ebd565b92915050565b6000611361827336b0b5b2b92332b2a232b630bcb2b227b93232b960611b611ebd565b60808501516007546001600160801b03909116906001600160a01b03888116911614156113c55760a08601516001600160801b031601611436565b6113cd611f7a565b60075460a088015160405163a7b5833f60e01b81526001600160a01b039384169363a7b5833f9361140393911691600401615571565b600060405180830381600087803b15801561141d57600080fd5b505af1158015611431573d6000803e3d6000fd5b505050505b61143e6146bf565b6004805460405163055f575160e41b81526001600160a01b03909116916355f575109161146d918c9101615519565b60a06040518083038186803b15801561148557600080fd5b505afa158015611499573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506114bd91908101906149f1565b905060006114ca87611f9c565b905060006114df8960200151600f0b896121e1565b905083156115b7576114fb8a848b60200151600f0b848861231f565b6115b783600001516001600160401b03168b85604001516001600160801b03168660800151600f0b600086886000600460009054906101000a90046001600160a01b03166001600160a01b0316632b58ecef6040518163ffffffff1660e01b815260040160206040518083038186803b15801561157757600080fd5b505afa15801561158b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506115af91908101906149b4565b600f0b6125d1565b61160c8a6040518060e001604052808c60200151600f0b81526020018b81526020018481526020018c604001516001600160801b031681526020018981526020018881526020018c6101000151815250612656565b60048054604051634c33238d60e11b81526001600160a01b0390911691639866471a9161163b918e9101615519565b600060405180830381600087803b15801561165557600080fd5b505af1158015611669573d6000803e3d6000fd5b505050506116788a888b612d5d565b50505050505050505050565b600061168e610ff6565b6001600160a01b0316637a018a1e6116a4611016565b6040518263ffffffff1660e01b81526004016116c09190615662565b60206040518083038186803b1580156116d857600080fd5b505afa1580156116ec573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506117109190810190614996565b905061171c8282612e33565b6007546001600160a01b038481169116141561187b5761173a6146bf565b6004805460405163055f575160e41b81526001600160a01b03909116916355f575109161176991889101615519565b60a06040518083038186803b15801561178157600080fd5b505afa158015611795573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506117b991908101906149f1565b905060006117c760006110ec565b905060006117d482611f9c565b90506117f786848760200151600f0b858960a001516001600160801b031661231f565b61187383600001516001600160401b03168785604001516001600160801b03168660800151600f0b600087876000600460009054906101000a90046001600160a01b03166001600160a01b0316632b58ecef6040518163ffffffff1660e01b815260040160206040518083038186803b15801561157757600080fd5b5050506118ec565b611883611f7a565b60075460a084015160405163a7b5833f60e01b81526001600160a01b039384169363a7b5833f936118b993911691600401615571565b600060405180830381600087803b1580156118d357600080fd5b505af11580156118e7573d6000803e3d6000fd5b505050505b60808201516001600160801b03161561196957611907611f7a565b6001600160a01b031663d289ade283608001516040518263ffffffff1660e01b8152600401611936919061596f565b600060405180830381600087803b15801561195057600080fd5b505af1158015611964573d6000803e3d6000fd5b505050505b60048054604051634c33238d60e11b81526001600160a01b0390911691639866471a9161199891879101615519565b600060405180830381600087803b1580156119b257600080fd5b505af11580156119c6573d6000803e3d6000fd5b50505050610a6a838284612d5d565b6000611011725065727073563245786368616e67655261746560681b611ce1565b6000611361827f6f6666636861696e44656c617965644f726465724d6178416765000000000000611ebd565b6000611361827f6f6666636861696e44656c617965644f726465724d696e416765000000000000611ebd565b6000806000611a5d60016110ec565b9050611a676119d5565b6001600160a01b031663368bde96611a7d611016565b866040518363ffffffff1660e01b8152600401611a9b92919061567e565b604080518083038186803b158015611ab257600080fd5b505afa158015611ac6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611aea9190810190614ab4565b90935091508015801590611afe5750600083115b611b1a5760405162461bcd60e51b815260040161060a90615821565b6000838211611bc357611bbe732ad7ccaac0eeb396c3a5fc2b73a885435688c0d563907af6c06040518163ffffffff1660e01b815260040160206040518083038186803b158015611b6a57600080fd5b505af4158015611b7e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611ba29190810190614996565b611bb2868563ffffffff612ea616565b9063ffffffff612ed016565b611c52565b611c52732ad7ccaac0eeb396c3a5fc2b73a885435688c0d563907af6c06040518163ffffffff1660e01b815260040160206040518083038186803b158015611c0a57600080fd5b505af4158015611c1e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611c429190810190614996565b611bb2848763ffffffff612ea616565b905080611c65611c606112ee565b612ef8565b11611c825760405162461bcd60e51b815260040161060a90615901565b5050915091565b6000611361827f74616b65724665654f6666636861696e44656c617965644f7264657200000000611ebd565b6000611361827f6d616b65724665654f6666636861696e44656c617965644f7264657200000000611ebd565b60008181526003602090815260408083205490516001600160a01b039091169182151591611d11918691016154c2565b60405160208183030381529060405290610fc55760405162461bcd60e51b815260040161060a91906157df565b6000611361827f64656c617965644f72646572436f6e6669726d57696e646f7700000000000000611ebd565b600061136182756e6578745072696365436f6e6669726d57696e646f7760501b611ebd565b60006110116b53797374656d53746174757360a01b611ce1565b6000611db3612f24565b6001600160a01b031663f7833c5d600080516020615b9983398151915284706f6666636861696e4d61726b65744b657960781b604051602001611df792919061549c565b604051602081830303815290604052805190602001206040518363ffffffff1660e01b8152600401611e2a92919061567e565b60206040518083038186803b158015611e4257600080fd5b505afa158015611e56573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506113619190810190614996565b81156105dc576005600082600f811115611e9057fe5b60ff1660ff16815260200190815260200160002060405162461bcd60e51b815260040161060a91906157f0565b6000611ec7612f24565b6001600160a01b03166323257c2b600080516020615b998339815191528585604051602001611ef792919061549c565b604051602081830303815290604052805190602001206040518363ffffffff1660e01b8152600401611f2a92919061567e565b60206040518083038186803b158015611f4257600080fd5b505afa158015611f56573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506110e59190810190614996565b600061101173233aba3ab932b9a6b0b935b2ba26b0b730b3b2b960611b611ce1565b60048054604080516366f6867560e11b8152905160009384936001600160a01b03169263cded0cea9281830192602092829003018186803b158015611fe057600080fd5b505afa158015611ff4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506120189190810190614996565b90506000612024612f41565b9050600061203185612fa7565b60048054604051637e35d8f960e11b81529293506001600160a01b03169163fc6bb1f291612061918591016157d1565b600060405180830381600087803b15801561207b57600080fd5b505af115801561208f573d6000803e3d6000fd5b505060048054604051634af3b2b160e11b81526001600160a01b0390911693506395e7656292506120c2914291016159cc565b600060405180830381600087803b1580156120dc57600080fd5b505af11580156120f0573d6000803e3d6000fd5b5050600480546040516315e88f9160e11b81526001600160a01b039091169350632bd11f229250612123918691016157d1565b600060405180830381600087803b15801561213d57600080fd5b505af1158015612151573d6000803e3d6000fd5b505060048054604080516313dcd11b60e11b815290516112e59550869450879389936001600160a01b0316926327b9a2369281830192602092829003018186803b15801561219e57600080fd5b505afa1580156121b2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506121d69190810190614ae4565b63ffffffff16613002565b6004805460408051632b58ecef60e01b8152905160009384936001600160a01b031692632b58ecef9281830192602092829003018186803b15801561222557600080fd5b505afa158015612239573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061225d91908101906149b4565b600f0b9050600061227461226f6112ee565b6130a8565b90506000612288838363ffffffff6130c016565b905060006122ac836122a0868a63ffffffff6130ea16565b9063ffffffff6130c016565b905060006122d06122c3888563ffffffff61313016565b889063ffffffff6130ea16565b905060006122f46122e7898563ffffffff61313016565b899063ffffffff6130ea16565b9050612312671bc16d674ec800006122a0848463ffffffff6130ea16565b9998505050505050505050565b6123276146bf565b508360008061233783868661315a565b91509150612344816131e1565b600061234e613202565b905061240a6040518060a0016040528060006001600160401b03168152602001836001600160401b03168152602001856001600160801b03168152602001886001600160801b031681526020018a60800151600f0b8152506040518060a0016040528060006001600160401b031681526020018b602001516001600160401b031681526020018b604001516001600160801b031681526020018b606001516001600160801b031681526020018b60800151600f0b81525061328f565b6001600160801b03831660408901526080880151600f0b15612535576001600160801b03861660608901526001600160401b038116602089015260008512156125355760006124608960800151600f0b8861331d565b90506000612485826124798c60800151600f0b8b61338b565b9063ffffffff6133d516565b9050612495818611156008611e7a565b60808a01516000906124b090600f0b8b63ffffffff6130ea16565b905060006124c58c60800151600f0b836133fa565b80156124e757506124dc8c60800151600f0b613419565b6124e583613419565b105b9050806125305761251c6125046124ff8e8d8b613424565b613419565b61251461250f6112ee565b613448565b106007611e7a565b612530612527613462565b88106008611e7a565b505050505b600460009054906101000a90046001600160a01b03166001600160a01b0316635af0d81f8a8a600001518b602001518c604001518d606001518e608001516040518763ffffffff1660e01b81526004016125949695949392919061558c565b600060405180830381600087803b1580156125ae57600080fd5b505af11580156125c2573d6000803e3d6000fd5b50505050505050505050505050565b6006546040516001600160a01b039091169063907dff9790612603908a908a908a908a908a908a908a9060200161597d565b6040516020818303038152906040526003604051612620906154f8565b6040519081900390208d6126338e6134df565b60006040518763ffffffff1660e01b81526004016125949695949392919061577c565b6004805460405163fef48a9960e01b815284926001600160a01b039092169163fef48a999161268791859101615519565b60206040518083038186803b15801561269f57600080fd5b505afa1580156126b3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506126d79190810190614978565b156126e75760056000600e611e90565b6126ef6146bf565b6004805460405163055f575160e41b81526001600160a01b03909116916355f575109161271e91889101615519565b60a06040518083038186803b15801561273657600080fd5b505afa15801561274a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061276e91908101906149f1565b90506127786146bf565b6040518060a0016040528083600001516001600160401b0316815260200183602001516001600160401b0316815260200183604001516001600160801b0316815260200183606001516001600160801b031681526020018360800151600f0b81525090506127e46146bf565b6000806127f184886134eb565b925092509250612800816131e1565b6128178760400151886060015189600001516137d3565b50600480546080808701519086015160408051632b58ecef60e01b815290516001600160a01b039094169463b545f712946128d094600f90810b946128c494910b928892632b58ecef928083019260209291829003018186803b15801561287d57600080fd5b505afa158015612891573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506128b591908101906149b4565b600f0b9063ffffffff6130ea16565b9063ffffffff6137fb16565b6040518263ffffffff1660e01b81526004016128ec91906157d1565b600060405180830381600087803b15801561290657600080fd5b505af115801561291a573d6000803e3d6000fd5b505060045460808701516001600160a01b03909116925063460af7a691506129ea9061294890600f0b613419565b611bb261295b8860800151600f0b613419565b600480546040805163eb56105d60e01b815290516001600160a01b039092169263eb56105d928282019260209290829003018186803b15801561299d57600080fd5b505afa1580156129b1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506129d59190810190614a0f565b6001600160801b03169063ffffffff6133d516565b6040518263ffffffff1660e01b8152600401612a069190615961565b600060405180830381600087803b158015612a2057600080fd5b505af1158015612a34573d6000803e3d6000fd5b505050508160001015612aa757612a49611f7a565b6001600160a01b031663d289ade2836040518263ffffffff1660e01b8152600401612a749190615662565b600060405180830381600087803b158015612a8e57600080fd5b505af1158015612aa2573d6000803e3d6000fd5b505050505b60c087015115612ad157612ad18760c00151612ac1611016565b612ac96112ee565b8a5186613841565b6040808401516001600160801b031690860152612aee838561328f565b83516000612afa613202565b90508460800151600f0b60001415612b2957600080885260808801819052606088018190526020880152612c4e565b6080860151600f0b612c1557600480546040805163899346c760e01b815290516001600160a01b039092169263899346c7928282019260209290829003018186803b158015612b7757600080fd5b505afa158015612b8b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612baf9190810190614b02565b60048054604051631dbad55160e31b81529294506001600160a01b03169163edd6aa8891612be2916001870191016159e8565b600060405180830381600087803b158015612bfc57600080fd5b505af1158015612c10573d6000803e3d6000fd5b505050505b6001600160401b038083168852608080870151600f90810b900b9089015260408a01516001600160801b03166060890152811660208801525b600460009054906101000a90046001600160a01b03166001600160a01b0316635af0d81f8b89600001518a602001518b604001518c606001518d608001516040518763ffffffff1660e01b8152600401612cad9695949392919061558c565b600060405180830381600087803b158015612cc757600080fd5b505af1158015612cdb573d6000803e3d6000fd5b50505050611678826001600160401b03168b87604001516001600160801b03168860800151600f0b8d600001518e60400151878b600460009054906101000a90046001600160a01b03166001600160a01b0316632b58ecef6040518163ffffffff1660e01b815260040160206040518083038186803b15801561157757600080fd5b60065481516020808401516060850151608086015160a08701516101008801516040516001600160a01b039098169763907dff9797612daa9790968c9690959094909390929091016155fa565b6040516020818303038152906040526002604051612dc7906154e2565b6040518091039020612dd8886134df565b6000806040518763ffffffff1660e01b8152600401612dfc96959493929190615742565b600060405180830381600087803b158015612e1657600080fd5b505af1158015612e2a573d6000803e3d6000fd5b50505050505050565b815115612e6e57612e45610dba6112ee565b8260e00151420311612e695760405162461bcd60e51b815260040161060a90615911565b6105dc565b612e8a8260c001518284606001516001600160801b031661109e565b6105dc5760405162461bcd60e51b815260040161060a906158f1565b60006110e582612ec485670de0b6b3a764000063ffffffff6138f216565b9063ffffffff61392c16565b600082821115612ef25760405162461bcd60e51b815260040161060a90615871565b50900390565b6000611361827f6f6666636861696e5072696365446976657267656e6365000000000000000000611ebd565b60006110116e466c657869626c6553746f7261676560881b611ce1565b6000611011612f65612f51613961565b612f59613a08565b9063ffffffff61313016565b6004805460408051637226426160e11b815290516001600160a01b039092169263e44c84c2928282019260209290829003018186803b15801561287d57600080fd5b6000611361612fb583613a2f565b6004546001600160a01b03166341108cf2612fce613202565b6040518263ffffffff1660e01b8152600401612fea9190615662565b60206040518083038186803b15801561287d57600080fd5b6006546040516001600160a01b039091169063907dff979061302e908790879087908790602001615699565b604051602081830303815290604052600160405161304b9061550e565b6040519081900381206001600160e01b031960e086901b16825261307a939291600090819081906004016156ee565b600060405180830381600087803b15801561309457600080fd5b505af1158015610e7a573d6000803e3d6000fd5b60006113618268736b65775363616c6560b81b611ebd565b60006110e5826130de85670de0b6b3a764000063ffffffff613abc16565b9063ffffffff613b2716565b60008282018183128015906130ff5750838112155b80613114575060008312801561311457508381125b6110e55760405162461bcd60e51b815260040161060a90615861565b6000670de0b6b3a764000061314b848463ffffffff613abc16565b8161315257fe5b059392505050565b60008060006131798461316d8888613b8b565b9063ffffffff6130ea16565b90506000811215613192575060009150600890506131d9565b60808601518190600f0b60006131a8828961338b565b905081158015906131b95750808311155b156131ce5782600495509550505050506131d9565b509093506000925050505b935093915050565b6131ea81613bc5565b156131ff576005600082600f811115611e9057fe5b50565b60006110116001600460009054906101000a90046001600160a01b03166001600160a01b031663cded0cea6040518163ffffffff1660e01b815260040160206040518083038186803b15801561325757600080fd5b505afa15801561326b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611bb29190810190614996565b600061329a83613bdc565b905060006132a783613bdc565b6004805460408051631169848560e11b815290519394506001600160a01b039091169263104d46f7926133019286926128c492899288926322d3090a92828101926020929190829003018186803b15801561287d57600080fd5b6040518263ffffffff1660e01b815260040161307a91906157d1565b60008261332c57506000611361565b600061333b6124ff8585613cb9565b905061338361335061334b6112ee565b613ccb565b6133778361337761336261226f6112ee565b61336b8a613419565b9063ffffffff612ea616565b9063ffffffff613cf716565b949350505050565b6000806133ae6133a161339c6112ee565b613d21565b6133778561337788613419565b90506133836133bb613d46565b6124796133c88787613da2565b849063ffffffff6133d516565b6000828201838110156110e55760405162461bcd60e51b815260040161060a90615831565b6000821580613407575081155b806110e5575050600090811291131490565b600061136182613df7565b600081613433575060006110e5565b613383826122a08660800151600f0b86613cb9565b6000611361826a6d61784c6576657261676560a81b611ebd565b600061346c612f24565b6001600160a01b03166323257c2b600080516020615b998339815191527f706572707356324d696e496e697469616c4d617267696e0000000000000000006040518363ffffffff1660e01b81526004016134c792919061567e565b60206040518083038186803b15801561106657600080fd5b6001600160a01b031690565b6134f36146bf565b8151600090819061350d575083915060009050600a6137cc565b61351b858560200151613e0d565b1561352f57508391506000905060046137cc565b60008061353a613e4b565b915091508015613557575085935060009250600c91506137cc9050565b6135618683613ee8565b93506000806135788989604001518860000361315a565b9150915061358581613bc5565b1561359c578896506000955093506137cc92505050565b6135a46146bf565b6040518060a001604052808b600001516001600160401b031681526020016135ca613202565b6001600160401b03168152602001846001600160801b031681526020018a604001516001600160801b031681526020016136188b600001518d60800151600f0b6130ea90919063ffffffff16565b600f0b8152509050600061363a8b60800151600f0b8360800151600f0b6133fa565b801561366357506136518b60800151600f0b613419565b6136618360800151600f0b613419565b105b9050806136ad57613672613462565b6040830151613690906001600160801b03168a63ffffffff6133d516565b10156136ad575089975060009650600895506137cc945050505050565b60006136c48360800151600f0b8c6020015161331d565b905060006136e1826124798660800151600f0b8f6020015161338b565b9050808611613702575091985060009750600496506137cc95505050505050565b6000613735613717888d63ffffffff6133d516565b6122a08f604001518860800151600f0b61313090919063ffffffff16565b905061374081613419565b613756662386f26fc1000061247961250f6112ee565b101561377657508c9a5060009950600798506137cc975050505050505050565b5061379f61378a6137856112ee565b6140e2565b8e60800151600f0b8660800151600f0b6140ff565b156137bd57508b995060009850600697506137cc9650505050505050565b50919850600096505050505050505b9250925092565b60006137f3600083136137e8578385106137ec565b8385115b600d611e7a565b509192915050565b60008183038183128015906138105750838113155b80613825575060008312801561382557508381135b6110e55760405162461bcd60e51b815260040161060a90615921565b6006546040516001600160a01b039091169063907dff979061386d908790879087908790602001615699565b604051602081830303815290604052600260405161388a90615503565b6040519081900381206001600160e01b031960e086901b1682526138b99392918b906000908190600401615742565b600060405180830381600087803b1580156138d357600080fd5b505af11580156138e7573d6000803e3d6000fd5b505050505050505050565b60008261390157506000611361565b8282028284828161390e57fe5b04146110e55760405162461bcd60e51b815260040161060a906158b1565b600080821161394d5760405162461bcd60e51b815260040161060a90615881565b600082848161395857fe5b04949350505050565b6000611011620151806122a0600460009054906101000a90046001600160a01b03166001600160a01b03166327b9a2366040518163ffffffff1660e01b815260040160206040518083038186803b1580156139bb57600080fd5b505afa1580156139cf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506139f39190810190614ae4565b63ffffffff1642612ed090919063ffffffff16565b600080613a1b613a166112ee565b6142e9565b9050613a2981612f5961430a565b91505090565b600080613a3a612f41565b90506000613a9b6012600a0a6002026122a084600460009054906101000a90046001600160a01b03166001600160a01b031663e44c84c26040518163ffffffff1660e01b815260040160206040518083038186803b15801561287d57600080fd5b600003905061338384612f59613aaf613961565b849063ffffffff61313016565b600082613acb57506000611361565b82600019148015613adf5750600160ff1b82145b15613afc5760405162461bcd60e51b815260040161060a906158e1565b82820282848281613b0957fe5b05146110e55760405162461bcd60e51b815260040161060a906158e1565b600081613b465760405162461bcd60e51b815260040161060a90615941565b81600019148015613b5a5750600160ff1b83145b15613b775760405162461bcd60e51b815260040161060a90615891565b6000828481613b8257fe5b05949350505050565b600080613b9884846143ca565b90506133838161316d613bab878761441a565b60408801516001600160801b03169063ffffffff6130ea16565b60008082600f811115613bd457fe5b141592915050565b600480546020830151604051632088467960e11b815260009361136193613c9f93613c8b936001600160a01b03909216926341108cf292613c1e9291016159da565b60206040518083038186803b158015613c3657600080fd5b505afa158015613c4a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613c6e91908101906149b4565b60608601516001600160801b031690600f0b63ffffffff6130ea16565b6080850151600f0b9063ffffffff61313016565b60408401516001600160801b03169063ffffffff6137fb16565b60006110e5838363ffffffff61313016565b6000611361827f6c69717569646174696f6e5072656d69756d4d756c7469706c69657200000000611ebd565b6000670de0b6b3a7640000613d12848463ffffffff6138f216565b81613d1957fe5b049392505050565b600061136182756c69717569646174696f6e427566666572526174696f60501b611ebd565b6000613d50612f24565b6001600160a01b03166323257c2b600080516020615b99833981519152736b65657065724c69717569646174696f6e46656560601b6040518363ffffffff1660e01b81526004016134c792919061567e565b600080613db06133a1614458565b90506000613dbc6144bd565b90506000818311613dcd5782613dcf565b815b90506000613ddb614518565b9050808211613dea5780613dec565b815b979650505050505050565b6000808212613e065781611361565b5060000390565b60008260800151600f0b60001415613e2757506000611361565b613e388360800151600f0b8361338b565b613e428484614573565b11159392505050565b600080613e566145a1565b6001600160a01b031663c39def0b631cd554d160e21b613e74611016565b6040518363ffffffff1660e01b8152600401613e9192919061567e565b604080518083038186803b158015613ea857600080fd5b505afa158015613ebc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613ee09190810190614a2d565b915091509091565b6004805460408051632b58ecef60e01b8152905160009384936001600160a01b031692632b58ecef9281830192602092829003018186803b158015613f2c57600080fd5b505afa158015613f40573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613f6491908101906149b4565b600f0b90506000613f868560400151866000015161313090919063ffffffff16565b90506000613f978561337784613419565b9050613fa986600001518401846133fa565b1561407857600061404283600460009054906101000a90046001600160a01b03166001600160a01b0316632b58ecef6040518163ffffffff1660e01b815260040160206040518083038186803b15801561400257600080fd5b505afa158015614016573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061403a91908101906149b4565b600f0b6133fa565b614050578660a00151614056565b86608001515b905061406b6124ff848363ffffffff61313016565b8201945050505050611361565b8551600090614094906124ff908681019063ffffffff6130c016565b90506000816012600a0a03905060006140b889608001516133778561337789613419565b905060006140d18a60a00151613377856133778a613419565b919094010198975050505050505050565b6000611361826d6d61784d61726b657456616c756560901b611ebd565b600061410b83836133fa565b8015614127575061411b83613419565b61412483613419565b11155b15614134575060006110e5565b60006141d38361316d86600460009054906101000a90046001600160a01b03166001600160a01b0316632b58ecef6040518163ffffffff1660e01b815260040160206040518083038186803b15801561418c57600080fd5b505afa1580156141a0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506141c491908101906149b4565b600f0b9063ffffffff6137fb16565b9050600061427e6141e385613df7565b61316d6141ef88613df7565b600480546040805163eb56105d60e01b815290516001600160a01b039092169263eb56105d928282019260209290829003018186803b15801561423157600080fd5b505afa158015614245573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506142699190810190614a0f565b6001600160801b03169063ffffffff6137fb16565b9050600084600012156142a25761429b828463ffffffff6130ea16565b90506142b5565b6142b2828463ffffffff6137fb16565b90505b6142c96124ff82600263ffffffff613b2716565b8710156142dc57600193505050506110e5565b5060009695505050505050565b600061136182716d617846756e64696e6756656c6f6369747960701b611ebd565b6000806143a461431b61226f6112ee565b6004805460408051632b58ecef60e01b815290516001600160a01b0390921692632b58ecef928282019260209290829003018186803b15801561435d57600080fd5b505afa158015614371573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061439591908101906149b4565b600f0b9063ffffffff6130c016565b9050613a296143bc670de0b6b3a763ffff19836145b8565b670de0b6b3a76400006145cd565b60208201516000906001600160401b0316806143ea576000915050611361565b60006143f682856145e3565b608086015190915061441190600f0b8263ffffffff61313016565b95945050505050565b60008061443d84606001516001600160801b0316846137fb90919063ffffffff16565b608085015190915061338390600f0b8263ffffffff61313016565b6000614462612f24565b6001600160a01b03166323257c2b600080516020615b998339815191527f706572707356324c69717569646174696f6e466565526174696f0000000000006040518363ffffffff1660e01b81526004016134c792919061567e565b60006144c7612f24565b6001600160a01b03166323257c2b600080516020615b9983398151915272706572707356324d61784b656570657246656560681b6040518363ffffffff1660e01b81526004016134c792919061567e565b6000614522612f24565b6001600160a01b03166323257c2b600080516020615b9983398151915272706572707356324d696e4b656570657246656560681b6040518363ffffffff1660e01b81526004016134c792919061567e565b60008061459461458a8560800151600f0b8561331d565b6128c48686613b8b565b90506133836000826145b8565b60006110116822bc31b430b733b2b960b91b611ce1565b60008183126145c757826110e5565b50919050565b60008183126145dc57816110e5565b5090919050565b60048054604051632088467960e11b81526000926110e5926001600160a01b0316916341108cf29161461791889101615662565b60206040518083038186803b15801561462f57600080fd5b505afa158015614643573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061466791908101906149b4565b600f0b6128c484612fa7565b6040805161012081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e0810182905261010081019190915290565b6040805160a08101825260008082526020820181905291810182905260608101829052608081019190915290565b803561136181615b4e565b805161136181615b4e565b60008083601f84011261471557600080fd5b5081356001600160401b0381111561472c57600080fd5b60208301915083602082028301111561474457600080fd5b9250929050565b805161136181615b62565b805161136181615b6b565b805161136181615b74565b6000610120828403121561477f57600080fd5b61478a6101206159f6565b90506000614798848461474b565b82525060206147a984848301614761565b60208301525060406147bd848285016148c6565b60408301525060606147d1848285016148c6565b60608301525060806147e5848285016148c6565b60808301525060a06147f9848285016148c6565b60a08301525060c061480d84828501614756565b60c08301525060e061482184828501614756565b60e08301525061010061483684828501614756565b6101008301525092915050565b600060a0828403121561485557600080fd5b61485f60a06159f6565b9050600061486d84846148dc565b825250602061487e848483016148dc565b6020830152506040614892848285016148c6565b60408301525060606148a6848285016148c6565b60608301525060806148ba84828501614761565b60808301525092915050565b805161136181615b7d565b805161136181615b86565b805161136181615b8f565b6000602082840312156148f957600080fd5b600061338384846146ed565b60006020828403121561491757600080fd5b600061338384846146f8565b60008060006040848603121561493857600080fd5b600061494486866146ed565b93505060208401356001600160401b0381111561496057600080fd5b61496c86828701614703565b92509250509250925092565b60006020828403121561498a57600080fd5b6000613383848461474b565b6000602082840312156149a857600080fd5b60006133838484614756565b6000602082840312156149c657600080fd5b60006133838484614761565b600061012082840312156149e557600080fd5b6000613383848461476c565b600060a08284031215614a0357600080fd5b60006133838484614843565b600060208284031215614a2157600080fd5b600061338384846148c6565b60008060408385031215614a4057600080fd5b6000614a4c8585614756565b9250506020614a5d8582860161474b565b9150509250929050565b600080600060608486031215614a7c57600080fd5b6000614a888686614756565b9350506020614a998682870161474b565b9250506040614aaa8682870161474b565b9150509250925092565b60008060408385031215614ac757600080fd5b6000614ad38585614756565b9250506020614a5d85828601614756565b600060208284031215614af657600080fd5b600061338384846148d1565b600060208284031215614b1457600080fd5b600061338384846148dc565b6000614b2c8383614c2a565b505060200190565b6000613383848484614c44565b614b4a81615ac7565b82525050565b614b4a81615a90565b6000614b6482615a2e565b614b6e8185615a32565b9350614b7983615a1c565b8060005b83811015614ba7578151614b918882614b20565b9750614b9c83615a1c565b925050600101614b7d565b509495945050505050565b6000614bbe8385615a32565b935083602084028501614bd084610478565b8060005b87811015614c14578484038952614beb8284615a40565b614bf6868284614b34565b9550614c0184615a1c565b60209b909b019a93505050600101614bd4565b5091979650505050505050565b614b4a81615a9b565b614b4a81610478565b614b4a614c3f82610478565b610478565b6000614c508385615a32565b9350614c5d838584615b08565b614c6683615b44565b9093019392505050565b6000614c7b82615a2e565b614c858185615a32565b9350614c95818560208601615b14565b614c6681615b44565b614b4a81615ace565b614b4a81615aa0565b614b4a81615ad9565b614b4a81615ae7565b600081546001811660008114614cdf5760018114614d0557614d44565b607f6002830416614cf08187615a32565b60ff1984168152955050602085019250614d44565b60028204614d138187615a32565b9550614d1e85615a22565b60005b82811015614d3d57815488820152600190910190602001614d21565b8701945050505b505092915050565b6000614d59603583615a32565b7f596f75206d757374206265206e6f6d696e61746564206265666f726520796f7581527402063616e20616363657074206f776e65727368697605c1b602082015260400192915050565b6000614db0601983615a32565b7f6f7264657220746f6f206f6c642c207573652063616e63656c00000000000000815260200192915050565b6000614de9601383615a32565b720696e76616c69642c207072696365206973203606c1b815260200192915050565b6000614e18601b83615a32565b7f536166654d6174683a206164646974696f6e206f766572666c6f770000000000815260200192915050565b6000614e51601183615a32565b701c1c9a58d9481b9bdd081d5c19185d1959607a1b815260200192915050565b6000614e7e601383615a32565b721d5cd9481bd99998da185a5b881b595d1a1bd9606a1b815260200192915050565b6000614ead602183615a32565b7f5369676e6564536166654d6174683a206164646974696f6e206f766572666c6f8152607760f81b602082015260400192915050565b6000614ef0601e83615a32565b7f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815260200192915050565b6000614f29601a83615a32565b7f536166654d6174683a206469766973696f6e206279207a65726f000000000000815260200192915050565b6000614f62601183615a3b565b70026b4b9b9b4b7339030b2323932b9b99d1607d1b815260110192915050565b6000614f8f602183615a32565b7f5369676e6564536166654d6174683a206469766973696f6e206f766572666c6f8152607760f81b602082015260400192915050565b6000614fd2602f83615a32565b7f4f6e6c792074686520636f6e7472616374206f776e6572206d6179207065726681526e37b936903a3434b99030b1ba34b7b760891b602082015260400192915050565b6000615023602183615a32565b7f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f8152607760f81b602082015260400192915050565b6000615066601983615a32565b7f6578656375746162696c697479206e6f74207265616368656400000000000000815260200192915050565b600061509f601183615a32565b70373790383932bb34b7bab99037b93232b960791b815260200192915050565b60006150cc602783615a32565b7f5369676e6564536166654d6174683a206d756c7469706c69636174696f6e206f815266766572666c6f7760c81b602082015260400192915050565b6000615115602183615a32565b7f63616e6e6f742062652063616e63656c6c6564206279206b65657065722079658152601d60fa1b602082015260400192915050565b6000615158601983615a32565b7f707269636520646976657267656e636520746f6f206869676800000000000000815260200192915050565b6000615191605083615a3b565b7f44656c617965644f7264657252656d6f76656428616464726573732c626f6f6c81527f2c75696e743235362c696e743235362c75696e743235362c75696e743235362c60208201526f75696e743235362c627974657333322960801b604082015260500192915050565b6000615209601983615a3b565b7f5265736f6c766572206d697373696e67207461726765743a2000000000000000815260190192915050565b6000615242601183615a32565b7018d85b9b9bdd0818d85b98d95b081e595d607a1b815260200192915050565b600061526f605683615a3b565b7f506f736974696f6e4d6f6469666965642875696e743235362c6164647265737381527f2c75696e743235362c696e743235362c696e743235362c75696e743235362c75602082015275696e743235362c75696e743235362c696e743235362960501b604082015260560192915050565b60006152ed603583615a3b565b7f5065727073547261636b696e6728627974657333322c627974657333322c627981527474657333322c696e743235362c75696e743235362960581b602082015260350192915050565b6000615344602483615a32565b7f5369676e6564536166654d6174683a207375627472616374696f6e206f766572815263666c6f7760e01b602082015260400192915050565b600061538a601783615a32565b7f4f6e6c79207468652070726f78792063616e2063616c6c000000000000000000815260200192915050565b60006153c3602083615a32565b7f5369676e6564536166654d6174683a206469766973696f6e206279207a65726f815260200192915050565b60006153fc601283615a32565b711d5cd9481bdb98da185a5b881b595d1a1bd960721b815260200192915050565b600061542a603083615a3b565b7f46756e64696e675265636f6d707574656428696e743235362c696e743235362c81526f75696e743235362c75696e743235362960801b602082015260300192915050565b614b4a81615aa6565b614b4a81615af2565b614b4a81615ab2565b614b4a81615afd565b614b4a81615abb565b60006154a88285614c33565b6020820191506154b88284614c33565b5060200192915050565b60006154cd82614f55565b91506154d98284614c33565b50602001919050565b600061136182615184565b60006154cd826151fc565b600061136182615262565b6000611361826152e0565b60006113618261541d565b602081016113618284614b50565b602081016113618284614b41565b604081016155438285614b50565b6110e56020830184614b50565b6040810161555e8286614b50565b8181036020830152614411818486614bb2565b6040810161557f8285614b50565b6110e56020830184615478565b60c0810161559a8289614b50565b6155a76020830188615493565b6155b46040830187615493565b6155c1606083018661546f565b6155ce608083018561546f565b613dec60a0830184614ca7565b602080825281016110e58184614b59565b602081016113618284614c21565b60e08101615608828a614c21565b6156156020830189614c2a565b6156226040830188614ca7565b61562f606083018761546f565b61563c608083018661546f565b61564960a083018561546f565b61565660c0830184614c2a565b98975050505050505050565b602081016113618284614c2a565b604081016155438285614c2a565b6040810161568c8285614c2a565b6110e56020830184614c2a565b608081016156a78287614c2a565b6156b46020830186614c2a565b6156c16040830185614c2a565b6144116060830184614c2a565b604081016156dc8285614c2a565b81810360208301526133838184614c70565b60c080825281016156ff8189614c70565b905061570e6020830188614cb9565b61571b6040830187614c2a565b6157286060830186614cb0565b6157356080830185614cb0565b613dec60a0830184614cb0565b60c080825281016157538189614c70565b90506157626020830188614cb9565b61576f6040830187614c2a565b6157286060830186614c2a565b60c0808252810161578d8189614c70565b905061579c6020830188614cb9565b6157a96040830187614c2a565b6157b66060830186614c2a565b6157356080830185614c2a565b602081016113618284614c9e565b602081016113618284614ca7565b602080825281016110e58184614c70565b602080825281016110e58184614cc2565b6020808252810161136181614d4c565b6020808252810161136181614da3565b6020808252810161136181614ddc565b6020808252810161136181614e0b565b6020808252810161136181614e44565b6020808252810161136181614e71565b6020808252810161136181614ea0565b6020808252810161136181614ee3565b6020808252810161136181614f1c565b6020808252810161136181614f82565b6020808252810161136181614fc5565b6020808252810161136181615016565b6020808252810161136181615059565b6020808252810161136181615092565b60208082528101611361816150bf565b6020808252810161136181615108565b602080825281016113618161514b565b6020808252810161136181615235565b6020808252810161136181615337565b602080825281016113618161537d565b60208082528101611361816153b6565b60208082528101611361816153ef565b60208101611361828461546f565b602081016113618284615478565b60e0810161598b828a614c2a565b6159986020830189614c2a565b6159a56040830188614c2a565b6159b26060830187614c2a565b6159bf6080830186614c2a565b61564960a0830185614c2a565b602081016113618284615481565b60208101611361828461548a565b602081016113618284615493565b6040518181016001600160401b0381118282101715615a1457600080fd5b604052919050565b60200190565b60009081526020902090565b5190565b90815260200190565b919050565b6000808335601e1936859003018112615a5857600080fd5b8381016020810193503591506001600160401b03821115615a7857600080fd5b36829003841315615a8857600080fd5b509250929050565b6000611361826134df565b151590565b600f0b90565b6001600160801b031690565b63ffffffff1690565b6001600160401b031690565b6000611361825b600061136182615a90565b6000611361614c3f83610478565b600061136182610478565b600061136182615aa6565b600061136182615abb565b82818337506000910152565b60005b83811015615b2f578181015183820152602001615b17565b83811115615b3e576000848401525b50505050565b601f01601f191690565b615b5781615a90565b81146131ff57600080fd5b615b5781615a9b565b615b5781610478565b615b5781615aa0565b615b5781615aa6565b615b5781615ab2565b615b5781615abb56fe506572707356324d61726b657453657474696e67730000000000000000000000a365627a7a7231582020f245fdc81cc3e7a23dd15c4be74ac4fa927b17add7120d007f34371ae3a0ba6c6578706572696d656e74616cf564736f6c63430005100040
Library Used
SafeDecimalMath : 0x2ad7ccaac0eeb396c3a5fc2b73a885435688c0d5SystemSettingsLib : 0x343b5efcbf331957d3f4236eb16c338d7256f62dSignedSafeDecimalMath : 0xc7dcc0929881530d3386de51d9ffdd35b8009c6eExchangeSettlementLib : 0x3f60ffaef1ebd84e3c2d0c9c0e12388365d5df12
Loading...
Loading
Loading...
Loading
[ 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.