Feature Tip: Add private address tag to any address under My Name Tag !
Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
Latest 1 from a total of 1 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
0x60806040 | 11036280 | 1381 days ago | IN | Create: LiquidityPoolV1ConverterFactory | 0 ETH | 0.12704831 |
Latest 25 internal transactions (View All)
Advanced mode:
Loading...
Loading
Contract Name:
LiquidityPoolV1ConverterFactory
Compiler Version
v0.6.12+commit.27d51765
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity)
/** *Submitted for verification at Etherscan.io on 2020-10-11 */ // File: solidity/contracts/utility/interfaces/IOwned.sol // SPDX-License-Identifier: SEE LICENSE IN LICENSE pragma solidity 0.6.12; /* Owned contract interface */ interface IOwned { // this function isn't since the compiler emits automatically generated getter functions as external function owner() external view returns (address); function transferOwnership(address _newOwner) external; function acceptOwnership() external; } // File: solidity/contracts/converter/interfaces/IConverterAnchor.sol pragma solidity 0.6.12; /* Converter Anchor interface */ interface IConverterAnchor is IOwned { } // File: solidity/contracts/token/interfaces/IERC20Token.sol pragma solidity 0.6.12; /* ERC20 Standard Token interface */ interface IERC20Token { function name() external view returns (string memory); function symbol() external view returns (string memory); function decimals() external view returns (uint8); function totalSupply() external view returns (uint256); function balanceOf(address _owner) external view returns (uint256); function allowance(address _owner, address _spender) external view returns (uint256); function transfer(address _to, uint256 _value) external returns (bool); function transferFrom(address _from, address _to, uint256 _value) external returns (bool); function approve(address _spender, uint256 _value) external returns (bool); } // File: solidity/contracts/utility/interfaces/IWhitelist.sol pragma solidity 0.6.12; /* Whitelist interface */ interface IWhitelist { function isWhitelisted(address _address) external view returns (bool); } // File: solidity/contracts/converter/interfaces/IConverter.sol pragma solidity 0.6.12; /* Converter interface */ interface IConverter is IOwned { function converterType() external pure returns (uint16); function anchor() external view returns (IConverterAnchor); function isActive() external view returns (bool); function targetAmountAndFee(IERC20Token _sourceToken, IERC20Token _targetToken, uint256 _amount) external view returns (uint256, uint256); function convert(IERC20Token _sourceToken, IERC20Token _targetToken, uint256 _amount, address _trader, address payable _beneficiary) external payable returns (uint256); function conversionWhitelist() external view returns (IWhitelist); function conversionFee() external view returns (uint32); function maxConversionFee() external view returns (uint32); function reserveBalance(IERC20Token _reserveToken) external view returns (uint256); receive() external payable; function transferAnchorOwnership(address _newOwner) external; function acceptAnchorOwnership() external; function setConversionFee(uint32 _conversionFee) external; function setConversionWhitelist(IWhitelist _whitelist) external; function withdrawTokens(IERC20Token _token, address _to, uint256 _amount) external; function withdrawETH(address payable _to) external; function addReserve(IERC20Token _token, uint32 _ratio) external; // deprecated, backward compatibility function token() external view returns (IConverterAnchor); function transferTokenOwnership(address _newOwner) external; function acceptTokenOwnership() external; function connectors(IERC20Token _address) external view returns (uint256, uint32, bool, bool, bool); function getConnectorBalance(IERC20Token _connectorToken) external view returns (uint256); function connectorTokens(uint256 _index) external view returns (IERC20Token); function connectorTokenCount() external view returns (uint16); } // File: solidity/contracts/converter/interfaces/IConverterUpgrader.sol pragma solidity 0.6.12; /* Converter Upgrader interface */ interface IConverterUpgrader { function upgrade(bytes32 _version) external; function upgrade(uint16 _version) external; } // File: solidity/contracts/converter/interfaces/IBancorFormula.sol pragma solidity 0.6.12; /* Bancor Formula interface */ interface IBancorFormula { function purchaseTargetAmount(uint256 _supply, uint256 _reserveBalance, uint32 _reserveWeight, uint256 _amount) external view returns (uint256); function saleTargetAmount(uint256 _supply, uint256 _reserveBalance, uint32 _reserveWeight, uint256 _amount) external view returns (uint256); function crossReserveTargetAmount(uint256 _sourceReserveBalance, uint32 _sourceReserveWeight, uint256 _targetReserveBalance, uint32 _targetReserveWeight, uint256 _amount) external view returns (uint256); function fundCost(uint256 _supply, uint256 _reserveBalance, uint32 _reserveRatio, uint256 _amount) external view returns (uint256); function fundSupplyAmount(uint256 _supply, uint256 _reserveBalance, uint32 _reserveRatio, uint256 _amount) external view returns (uint256); function liquidateReserveAmount(uint256 _supply, uint256 _reserveBalance, uint32 _reserveRatio, uint256 _amount) external view returns (uint256); function balancedWeights(uint256 _primaryReserveStakedBalance, uint256 _primaryReserveBalance, uint256 _secondaryReserveBalance, uint256 _reserveRateNumerator, uint256 _reserveRateDenominator) external view returns (uint32, uint32); } // File: solidity/contracts/utility/Owned.sol pragma solidity 0.6.12; /** * @dev Provides support and utilities for contract ownership */ contract Owned is IOwned { address public override owner; address public newOwner; /** * @dev triggered when the owner is updated * * @param _prevOwner previous owner * @param _newOwner new owner */ event OwnerUpdate(address indexed _prevOwner, address indexed _newOwner); /** * @dev initializes a new Owned instance */ constructor() public { owner = msg.sender; } // allows execution by the owner only modifier ownerOnly { _ownerOnly(); _; } // error message binary size optimization function _ownerOnly() internal view { require(msg.sender == owner, "ERR_ACCESS_DENIED"); } /** * @dev allows transferring the contract ownership * the new owner still needs to accept the transfer * can only be called by the contract owner * * @param _newOwner new contract owner */ function transferOwnership(address _newOwner) public override ownerOnly { require(_newOwner != owner, "ERR_SAME_OWNER"); newOwner = _newOwner; } /** * @dev used by a new owner to accept an ownership transfer */ function acceptOwnership() override public { require(msg.sender == newOwner, "ERR_ACCESS_DENIED"); emit OwnerUpdate(owner, newOwner); owner = newOwner; newOwner = address(0); } } // File: solidity/contracts/utility/Utils.sol pragma solidity 0.6.12; /** * @dev Utilities & Common Modifiers */ contract Utils { // verifies that a value is greater than zero modifier greaterThanZero(uint256 _value) { _greaterThanZero(_value); _; } // error message binary size optimization function _greaterThanZero(uint256 _value) internal pure { require(_value > 0, "ERR_ZERO_VALUE"); } // validates an address - currently only checks that it isn't null modifier validAddress(address _address) { _validAddress(_address); _; } // error message binary size optimization function _validAddress(address _address) internal pure { require(_address != address(0), "ERR_INVALID_ADDRESS"); } // verifies that the address is different than this contract address modifier notThis(address _address) { _notThis(_address); _; } // error message binary size optimization function _notThis(address _address) internal view { require(_address != address(this), "ERR_ADDRESS_IS_SELF"); } } // File: solidity/contracts/utility/interfaces/IContractRegistry.sol pragma solidity 0.6.12; /* Contract Registry interface */ interface IContractRegistry { function addressOf(bytes32 _contractName) external view returns (address); } // File: solidity/contracts/utility/ContractRegistryClient.sol pragma solidity 0.6.12; /** * @dev Base contract for ContractRegistry clients */ contract ContractRegistryClient is Owned, Utils { bytes32 internal constant CONTRACT_REGISTRY = "ContractRegistry"; bytes32 internal constant BANCOR_NETWORK = "BancorNetwork"; bytes32 internal constant BANCOR_FORMULA = "BancorFormula"; bytes32 internal constant CONVERTER_FACTORY = "ConverterFactory"; bytes32 internal constant CONVERSION_PATH_FINDER = "ConversionPathFinder"; bytes32 internal constant CONVERTER_UPGRADER = "BancorConverterUpgrader"; bytes32 internal constant CONVERTER_REGISTRY = "BancorConverterRegistry"; bytes32 internal constant CONVERTER_REGISTRY_DATA = "BancorConverterRegistryData"; bytes32 internal constant BNT_TOKEN = "BNTToken"; bytes32 internal constant BANCOR_X = "BancorX"; bytes32 internal constant BANCOR_X_UPGRADER = "BancorXUpgrader"; bytes32 internal constant CHAINLINK_ORACLE_WHITELIST = "ChainlinkOracleWhitelist"; IContractRegistry public registry; // address of the current contract-registry IContractRegistry public prevRegistry; // address of the previous contract-registry bool public onlyOwnerCanUpdateRegistry; // only an owner can update the contract-registry /** * @dev verifies that the caller is mapped to the given contract name * * @param _contractName contract name */ modifier only(bytes32 _contractName) { _only(_contractName); _; } // error message binary size optimization function _only(bytes32 _contractName) internal view { require(msg.sender == addressOf(_contractName), "ERR_ACCESS_DENIED"); } /** * @dev initializes a new ContractRegistryClient instance * * @param _registry address of a contract-registry contract */ constructor(IContractRegistry _registry) internal validAddress(address(_registry)) { registry = IContractRegistry(_registry); prevRegistry = IContractRegistry(_registry); } /** * @dev updates to the new contract-registry */ function updateRegistry() public { // verify that this function is permitted require(msg.sender == owner || !onlyOwnerCanUpdateRegistry, "ERR_ACCESS_DENIED"); // get the new contract-registry IContractRegistry newRegistry = IContractRegistry(addressOf(CONTRACT_REGISTRY)); // verify that the new contract-registry is different and not zero require(newRegistry != registry && address(newRegistry) != address(0), "ERR_INVALID_REGISTRY"); // verify that the new contract-registry is pointing to a non-zero contract-registry require(newRegistry.addressOf(CONTRACT_REGISTRY) != address(0), "ERR_INVALID_REGISTRY"); // save a backup of the current contract-registry before replacing it prevRegistry = registry; // replace the current contract-registry with the new contract-registry registry = newRegistry; } /** * @dev restores the previous contract-registry */ function restoreRegistry() public ownerOnly { // restore the previous contract-registry registry = prevRegistry; } /** * @dev restricts the permission to update the contract-registry * * @param _onlyOwnerCanUpdateRegistry indicates whether or not permission is restricted to owner only */ function restrictRegistryUpdate(bool _onlyOwnerCanUpdateRegistry) public ownerOnly { // change the permission to update the contract-registry onlyOwnerCanUpdateRegistry = _onlyOwnerCanUpdateRegistry; } /** * @dev returns the address associated with the given contract name * * @param _contractName contract name * * @return contract address */ function addressOf(bytes32 _contractName) internal view returns (address) { return registry.addressOf(_contractName); } } // File: solidity/contracts/utility/ReentrancyGuard.sol pragma solidity 0.6.12; /** * @dev ReentrancyGuard * * The contract provides protection against re-entrancy - calling a function (directly or * indirectly) from within itself. */ contract ReentrancyGuard { uint256 private constant UNLOCKED = 1; uint256 private constant LOCKED = 2; // LOCKED while protected code is being executed, UNLOCKED otherwise uint256 private state = UNLOCKED; /** * @dev ensures instantiation only by sub-contracts */ constructor() internal {} // protects a function against reentrancy attacks modifier protected() { _protected(); state = LOCKED; _; state = UNLOCKED; } // error message binary size optimization function _protected() internal view { require(state == UNLOCKED, "ERR_REENTRANCY"); } } // File: solidity/contracts/utility/SafeMath.sol pragma solidity 0.6.12; /** * @dev Library for basic math operations with overflow/underflow protection */ library SafeMath { /** * @dev returns the sum of _x and _y, reverts if the calculation overflows * * @param _x value 1 * @param _y value 2 * * @return sum */ function add(uint256 _x, uint256 _y) internal pure returns (uint256) { uint256 z = _x + _y; require(z >= _x, "ERR_OVERFLOW"); return z; } /** * @dev returns the difference of _x minus _y, reverts if the calculation underflows * * @param _x minuend * @param _y subtrahend * * @return difference */ function sub(uint256 _x, uint256 _y) internal pure returns (uint256) { require(_x >= _y, "ERR_UNDERFLOW"); return _x - _y; } /** * @dev returns the product of multiplying _x by _y, reverts if the calculation overflows * * @param _x factor 1 * @param _y factor 2 * * @return product */ function mul(uint256 _x, uint256 _y) internal pure returns (uint256) { // gas optimization if (_x == 0) return 0; uint256 z = _x * _y; require(z / _x == _y, "ERR_OVERFLOW"); return z; } /** * @dev Integer division of two numbers truncating the quotient, reverts on division by zero. * * @param _x dividend * @param _y divisor * * @return quotient */ function div(uint256 _x, uint256 _y) internal pure returns (uint256) { require(_y > 0, "ERR_DIVIDE_BY_ZERO"); uint256 c = _x / _y; return c; } } // File: solidity/contracts/utility/TokenHandler.sol pragma solidity 0.6.12; contract TokenHandler { bytes4 private constant APPROVE_FUNC_SELECTOR = bytes4(keccak256("approve(address,uint256)")); bytes4 private constant TRANSFER_FUNC_SELECTOR = bytes4(keccak256("transfer(address,uint256)")); bytes4 private constant TRANSFER_FROM_FUNC_SELECTOR = bytes4(keccak256("transferFrom(address,address,uint256)")); /** * @dev executes the ERC20 token's `approve` function and reverts upon failure * the main purpose of this function is to prevent a non standard ERC20 token * from failing silently * * @param _token ERC20 token address * @param _spender approved address * @param _value allowance amount */ function safeApprove(IERC20Token _token, address _spender, uint256 _value) internal { (bool success, bytes memory data) = address(_token).call(abi.encodeWithSelector(APPROVE_FUNC_SELECTOR, _spender, _value)); require(success && (data.length == 0 || abi.decode(data, (bool))), 'ERR_APPROVE_FAILED'); } /** * @dev executes the ERC20 token's `transfer` function and reverts upon failure * the main purpose of this function is to prevent a non standard ERC20 token * from failing silently * * @param _token ERC20 token address * @param _to target address * @param _value transfer amount */ function safeTransfer(IERC20Token _token, address _to, uint256 _value) internal { (bool success, bytes memory data) = address(_token).call(abi.encodeWithSelector(TRANSFER_FUNC_SELECTOR, _to, _value)); require(success && (data.length == 0 || abi.decode(data, (bool))), 'ERR_TRANSFER_FAILED'); } /** * @dev executes the ERC20 token's `transferFrom` function and reverts upon failure * the main purpose of this function is to prevent a non standard ERC20 token * from failing silently * * @param _token ERC20 token address * @param _from source address * @param _to target address * @param _value transfer amount */ function safeTransferFrom(IERC20Token _token, address _from, address _to, uint256 _value) internal { (bool success, bytes memory data) = address(_token).call(abi.encodeWithSelector(TRANSFER_FROM_FUNC_SELECTOR, _from, _to, _value)); require(success && (data.length == 0 || abi.decode(data, (bool))), 'ERR_TRANSFER_FROM_FAILED'); } } // File: solidity/contracts/utility/interfaces/ITokenHolder.sol pragma solidity 0.6.12; /* Token Holder interface */ interface ITokenHolder is IOwned { function withdrawTokens(IERC20Token _token, address _to, uint256 _amount) external; } // File: solidity/contracts/utility/TokenHolder.sol pragma solidity 0.6.12; /** * @dev We consider every contract to be a 'token holder' since it's currently not possible * for a contract to deny receiving tokens. * * The TokenHolder's contract sole purpose is to provide a safety mechanism that allows * the owner to send tokens that were sent to the contract by mistake back to their sender. * * Note that we use the non standard ERC-20 interface which has no return value for transfer * in order to support both non standard as well as standard token contracts. * see https://github.com/ethereum/solidity/issues/4116 */ contract TokenHolder is ITokenHolder, TokenHandler, Owned, Utils { /** * @dev withdraws tokens held by the contract and sends them to an account * can only be called by the owner * * @param _token ERC20 token contract address * @param _to account to receive the new amount * @param _amount amount to withdraw */ function withdrawTokens(IERC20Token _token, address _to, uint256 _amount) public virtual override ownerOnly validAddress(address(_token)) validAddress(_to) notThis(_to) { safeTransfer(_token, _to, _amount); } } // File: solidity/contracts/token/interfaces/IEtherToken.sol pragma solidity 0.6.12; /* Ether Token interface */ interface IEtherToken is IERC20Token { function deposit() external payable; function withdraw(uint256 _amount) external; function depositTo(address _to) external payable; function withdrawTo(address payable _to, uint256 _amount) external; } // File: solidity/contracts/bancorx/interfaces/IBancorX.sol pragma solidity 0.6.12; interface IBancorX { function token() external view returns (IERC20Token); function xTransfer(bytes32 _toBlockchain, bytes32 _to, uint256 _amount, uint256 _id) external; function getXTransferAmount(uint256 _xTransferId, address _for) external view returns (uint256); } // File: solidity/contracts/converter/ConverterBase.sol pragma solidity 0.6.12; /** * @dev ConverterBase * * The converter contains the main logic for conversions between different ERC20 tokens. * * It is also the upgradable part of the mechanism (note that upgrades are opt-in). * * The anchor must be set on construction and cannot be changed afterwards. * Wrappers are provided for some of the anchor's functions, for easier access. * * Once the converter accepts ownership of the anchor, it becomes the anchor's sole controller * and can execute any of its functions. * * To upgrade the converter, anchor ownership must be transferred to a new converter, along with * any relevant data. * * Note that the converter can transfer anchor ownership to a new converter that * doesn't allow upgrades anymore, for finalizing the relationship between the converter * and the anchor. * * Converter types (defined as uint16 type) - * 0 = liquid token converter * 1 = liquidity pool v1 converter * 2 = liquidity pool v2 converter * * Note that converters don't currently support tokens with transfer fees. */ abstract contract ConverterBase is IConverter, TokenHandler, TokenHolder, ContractRegistryClient, ReentrancyGuard { using SafeMath for uint256; uint32 internal constant PPM_RESOLUTION = 1000000; IERC20Token internal constant ETH_RESERVE_ADDRESS = IERC20Token(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE); struct Reserve { uint256 balance; // reserve balance uint32 weight; // reserve weight, represented in ppm, 1-1000000 bool deprecated1; // deprecated bool deprecated2; // deprecated bool isSet; // true if the reserve is valid, false otherwise } /** * @dev version number */ uint16 public constant version = 41; IConverterAnchor public override anchor; // converter anchor contract IWhitelist public override conversionWhitelist; // whitelist contract with list of addresses that are allowed to use the converter IERC20Token[] public reserveTokens; // ERC20 standard token addresses (prior version 17, use 'connectorTokens' instead) mapping (IERC20Token => Reserve) public reserves; // reserve token addresses -> reserve data (prior version 17, use 'connectors' instead) uint32 public reserveRatio = 0; // ratio between the reserves and the market cap, equal to the total reserve weights uint32 public override maxConversionFee = 0; // maximum conversion fee for the lifetime of the contract, // represented in ppm, 0...1000000 (0 = no fee, 100 = 0.01%, 1000000 = 100%) uint32 public override conversionFee = 0; // current conversion fee, represented in ppm, 0...maxConversionFee bool public constant conversionsEnabled = true; // deprecated, backward compatibility /** * @dev triggered when the converter is activated * * @param _type converter type * @param _anchor converter anchor * @param _activated true if the converter was activated, false if it was deactivated */ event Activation(uint16 indexed _type, IConverterAnchor indexed _anchor, bool indexed _activated); /** * @dev triggered when a conversion between two tokens occurs * * @param _fromToken source ERC20 token * @param _toToken target ERC20 token * @param _trader wallet that initiated the trade * @param _amount amount converted, in the source token * @param _return amount returned, minus conversion fee * @param _conversionFee conversion fee */ event Conversion( IERC20Token indexed _fromToken, IERC20Token indexed _toToken, address indexed _trader, uint256 _amount, uint256 _return, int256 _conversionFee ); /** * @dev triggered when the rate between two tokens in the converter changes * note that the event might be dispatched for rate updates between any two tokens in the converter * note that prior to version 28, you should use the 'PriceDataUpdate' event instead * * @param _token1 address of the first token * @param _token2 address of the second token * @param _rateN rate of 1 unit of `_token1` in `_token2` (numerator) * @param _rateD rate of 1 unit of `_token1` in `_token2` (denominator) */ event TokenRateUpdate( IERC20Token indexed _token1, IERC20Token indexed _token2, uint256 _rateN, uint256 _rateD ); /** * @dev triggered when the conversion fee is updated * * @param _prevFee previous fee percentage, represented in ppm * @param _newFee new fee percentage, represented in ppm */ event ConversionFeeUpdate(uint32 _prevFee, uint32 _newFee); /** * @dev used by sub-contracts to initialize a new converter * * @param _anchor anchor governed by the converter * @param _registry address of a contract registry contract * @param _maxConversionFee maximum conversion fee, represented in ppm */ constructor( IConverterAnchor _anchor, IContractRegistry _registry, uint32 _maxConversionFee ) validAddress(address(_anchor)) ContractRegistryClient(_registry) internal validConversionFee(_maxConversionFee) { anchor = _anchor; maxConversionFee = _maxConversionFee; } // ensures that the converter is active modifier active() { _active(); _; } // error message binary size optimization function _active() internal view { require(isActive(), "ERR_INACTIVE"); } // ensures that the converter is not active modifier inactive() { _inactive(); _; } // error message binary size optimization function _inactive() internal view { require(!isActive(), "ERR_ACTIVE"); } // validates a reserve token address - verifies that the address belongs to one of the reserve tokens modifier validReserve(IERC20Token _address) { _validReserve(_address); _; } // error message binary size optimization function _validReserve(IERC20Token _address) internal view { require(reserves[_address].isSet, "ERR_INVALID_RESERVE"); } // validates conversion fee modifier validConversionFee(uint32 _conversionFee) { _validConversionFee(_conversionFee); _; } // error message binary size optimization function _validConversionFee(uint32 _conversionFee) internal pure { require(_conversionFee <= PPM_RESOLUTION, "ERR_INVALID_CONVERSION_FEE"); } // validates reserve weight modifier validReserveWeight(uint32 _weight) { _validReserveWeight(_weight); _; } // error message binary size optimization function _validReserveWeight(uint32 _weight) internal pure { require(_weight > 0 && _weight <= PPM_RESOLUTION, "ERR_INVALID_RESERVE_WEIGHT"); } // overrides interface declaration function converterType() public pure virtual override returns (uint16); // overrides interface declaration function targetAmountAndFee(IERC20Token _sourceToken, IERC20Token _targetToken, uint256 _amount) public view virtual override returns (uint256, uint256); /** * @dev deposits ether * can only be called if the converter has an ETH reserve */ receive() external override payable { require(reserves[ETH_RESERVE_ADDRESS].isSet, "ERR_INVALID_RESERVE"); // require(hasETHReserve(), "ERR_INVALID_RESERVE"); // a workaround for a problem when running solidity-coverage // see https://github.com/sc-forks/solidity-coverage/issues/487 } /** * @dev withdraws ether * can only be called by the owner if the converter is inactive or by upgrader contract * can only be called after the upgrader contract has accepted the ownership of this contract * can only be called if the converter has an ETH reserve * * @param _to address to send the ETH to */ function withdrawETH(address payable _to) public override protected ownerOnly validReserve(ETH_RESERVE_ADDRESS) { address converterUpgrader = addressOf(CONVERTER_UPGRADER); // verify that the converter is inactive or that the owner is the upgrader contract require(!isActive() || owner == converterUpgrader, "ERR_ACCESS_DENIED"); _to.transfer(address(this).balance); // sync the ETH reserve balance syncReserveBalance(ETH_RESERVE_ADDRESS); } /** * @dev checks whether or not the converter version is 28 or higher * * @return true, since the converter version is 28 or higher */ function isV28OrHigher() public pure returns (bool) { return true; } /** * @dev allows the owner to update & enable the conversion whitelist contract address * when set, only addresses that are whitelisted are actually allowed to use the converter * note that the whitelist check is actually done by the BancorNetwork contract * * @param _whitelist address of a whitelist contract */ function setConversionWhitelist(IWhitelist _whitelist) public override ownerOnly notThis(address(_whitelist)) { conversionWhitelist = _whitelist; } /** * @dev returns true if the converter is active, false otherwise * * @return true if the converter is active, false otherwise */ function isActive() public view virtual override returns (bool) { return anchor.owner() == address(this); } /** * @dev transfers the anchor ownership * the new owner needs to accept the transfer * can only be called by the converter upgrder while the upgrader is the owner * note that prior to version 28, you should use 'transferAnchorOwnership' instead * * @param _newOwner new token owner */ function transferAnchorOwnership(address _newOwner) public override ownerOnly only(CONVERTER_UPGRADER) { anchor.transferOwnership(_newOwner); } /** * @dev accepts ownership of the anchor after an ownership transfer * most converters are also activated as soon as they accept the anchor ownership * can only be called by the contract owner * note that prior to version 28, you should use 'acceptTokenOwnership' instead */ function acceptAnchorOwnership() public virtual override ownerOnly { // verify the the converter has at least one reserve require(reserveTokenCount() > 0, "ERR_INVALID_RESERVE_COUNT"); anchor.acceptOwnership(); syncReserveBalances(); } /** * @dev updates the current conversion fee * can only be called by the contract owner * * @param _conversionFee new conversion fee, represented in ppm */ function setConversionFee(uint32 _conversionFee) public override ownerOnly { require(_conversionFee <= maxConversionFee, "ERR_INVALID_CONVERSION_FEE"); emit ConversionFeeUpdate(conversionFee, _conversionFee); conversionFee = _conversionFee; } /** * @dev withdraws tokens held by the converter and sends them to an account * can only be called by the owner * note that reserve tokens can only be withdrawn by the owner while the converter is inactive * unless the owner is the converter upgrader contract * * @param _token ERC20 token contract address * @param _to account to receive the new amount * @param _amount amount to withdraw */ function withdrawTokens(IERC20Token _token, address _to, uint256 _amount) public override(IConverter, TokenHolder) protected ownerOnly { address converterUpgrader = addressOf(CONVERTER_UPGRADER); // if the token is not a reserve token, allow withdrawal // otherwise verify that the converter is inactive or that the owner is the upgrader contract require(!reserves[_token].isSet || !isActive() || owner == converterUpgrader, "ERR_ACCESS_DENIED"); super.withdrawTokens(_token, _to, _amount); // if the token is a reserve token, sync the reserve balance if (reserves[_token].isSet) syncReserveBalance(_token); } /** * @dev upgrades the converter to the latest version * can only be called by the owner * note that the owner needs to call acceptOwnership on the new converter after the upgrade */ function upgrade() public ownerOnly { IConverterUpgrader converterUpgrader = IConverterUpgrader(addressOf(CONVERTER_UPGRADER)); // trigger de-activation event emit Activation(converterType(), anchor, false); transferOwnership(address(converterUpgrader)); converterUpgrader.upgrade(version); acceptOwnership(); } /** * @dev returns the number of reserve tokens defined * note that prior to version 17, you should use 'connectorTokenCount' instead * * @return number of reserve tokens */ function reserveTokenCount() public view returns (uint16) { return uint16(reserveTokens.length); } /** * @dev defines a new reserve token for the converter * can only be called by the owner while the converter is inactive * * @param _token address of the reserve token * @param _weight reserve weight, represented in ppm, 1-1000000 */ function addReserve(IERC20Token _token, uint32 _weight) public virtual override ownerOnly inactive validAddress(address(_token)) notThis(address(_token)) validReserveWeight(_weight) { // validate input require(address(_token) != address(anchor) && !reserves[_token].isSet, "ERR_INVALID_RESERVE"); require(_weight <= PPM_RESOLUTION - reserveRatio, "ERR_INVALID_RESERVE_WEIGHT"); require(reserveTokenCount() < uint16(-1), "ERR_INVALID_RESERVE_COUNT"); Reserve storage newReserve = reserves[_token]; newReserve.balance = 0; newReserve.weight = _weight; newReserve.isSet = true; reserveTokens.push(_token); reserveRatio += _weight; } /** * @dev returns the reserve's weight * added in version 28 * * @param _reserveToken reserve token contract address * * @return reserve weight */ function reserveWeight(IERC20Token _reserveToken) public view validReserve(_reserveToken) returns (uint32) { return reserves[_reserveToken].weight; } /** * @dev returns the reserve's balance * note that prior to version 17, you should use 'getConnectorBalance' instead * * @param _reserveToken reserve token contract address * * @return reserve balance */ function reserveBalance(IERC20Token _reserveToken) public override view validReserve(_reserveToken) returns (uint256) { return reserves[_reserveToken].balance; } /** * @dev checks whether or not the converter has an ETH reserve * * @return true if the converter has an ETH reserve, false otherwise */ function hasETHReserve() public view returns (bool) { return reserves[ETH_RESERVE_ADDRESS].isSet; } /** * @dev converts a specific amount of source tokens to target tokens * can only be called by the bancor network contract * * @param _sourceToken source ERC20 token * @param _targetToken target ERC20 token * @param _amount amount of tokens to convert (in units of the source token) * @param _trader address of the caller who executed the conversion * @param _beneficiary wallet to receive the conversion result * * @return amount of tokens received (in units of the target token) */ function convert(IERC20Token _sourceToken, IERC20Token _targetToken, uint256 _amount, address _trader, address payable _beneficiary) public override payable protected only(BANCOR_NETWORK) returns (uint256) { // validate input require(_sourceToken != _targetToken, "ERR_SAME_SOURCE_TARGET"); // if a whitelist is set, verify that both and trader and the beneficiary are whitelisted require(address(conversionWhitelist) == address(0) || (conversionWhitelist.isWhitelisted(_trader) && conversionWhitelist.isWhitelisted(_beneficiary)), "ERR_NOT_WHITELISTED"); return doConvert(_sourceToken, _targetToken, _amount, _trader, _beneficiary); } /** * @dev converts a specific amount of source tokens to target tokens * called by ConverterBase and allows the inherited contracts to implement custom conversion logic * * @param _sourceToken source ERC20 token * @param _targetToken target ERC20 token * @param _amount amount of tokens to convert (in units of the source token) * @param _trader address of the caller who executed the conversion * @param _beneficiary wallet to receive the conversion result * * @return amount of tokens received (in units of the target token) */ function doConvert( IERC20Token _sourceToken, IERC20Token _targetToken, uint256 _amount, address _trader, address payable _beneficiary) internal virtual returns (uint256); /** * @dev returns the conversion fee for a given target amount * * @param _targetAmount target amount * * @return conversion fee */ function calculateFee(uint256 _targetAmount) internal view returns (uint256) { return _targetAmount.mul(conversionFee).div(PPM_RESOLUTION); } /** * @dev syncs the stored reserve balance for a given reserve with the real reserve balance * * @param _reserveToken address of the reserve token */ function syncReserveBalance(IERC20Token _reserveToken) internal validReserve(_reserveToken) { if (_reserveToken == ETH_RESERVE_ADDRESS) reserves[_reserveToken].balance = address(this).balance; else reserves[_reserveToken].balance = _reserveToken.balanceOf(address(this)); } /** * @dev syncs all stored reserve balances */ function syncReserveBalances() internal { uint256 reserveCount = reserveTokens.length; for (uint256 i = 0; i < reserveCount; i++) syncReserveBalance(reserveTokens[i]); } /** * @dev helper, dispatches the Conversion event * * @param _sourceToken source ERC20 token * @param _targetToken target ERC20 token * @param _trader address of the caller who executed the conversion * @param _amount amount purchased/sold (in the source token) * @param _returnAmount amount returned (in the target token) */ function dispatchConversionEvent( IERC20Token _sourceToken, IERC20Token _targetToken, address _trader, uint256 _amount, uint256 _returnAmount, uint256 _feeAmount) internal { // fee amount is converted to 255 bits - // negative amount means the fee is taken from the source token, positive amount means its taken from the target token // currently the fee is always taken from the target token // since we convert it to a signed number, we first ensure that it's capped at 255 bits to prevent overflow assert(_feeAmount < 2 ** 255); emit Conversion(_sourceToken, _targetToken, _trader, _amount, _returnAmount, int256(_feeAmount)); } /** * @dev deprecated since version 28, backward compatibility - use only for earlier versions */ function token() public view override returns (IConverterAnchor) { return anchor; } /** * @dev deprecated, backward compatibility */ function transferTokenOwnership(address _newOwner) public override ownerOnly { transferAnchorOwnership(_newOwner); } /** * @dev deprecated, backward compatibility */ function acceptTokenOwnership() public override ownerOnly { acceptAnchorOwnership(); } /** * @dev deprecated, backward compatibility */ function connectors(IERC20Token _address) public view override returns (uint256, uint32, bool, bool, bool) { Reserve memory reserve = reserves[_address]; return(reserve.balance, reserve.weight, false, false, reserve.isSet); } /** * @dev deprecated, backward compatibility */ function connectorTokens(uint256 _index) public view override returns (IERC20Token) { return ConverterBase.reserveTokens[_index]; } /** * @dev deprecated, backward compatibility */ function connectorTokenCount() public view override returns (uint16) { return reserveTokenCount(); } /** * @dev deprecated, backward compatibility */ function getConnectorBalance(IERC20Token _connectorToken) public view override returns (uint256) { return reserveBalance(_connectorToken); } /** * @dev deprecated, backward compatibility */ function getReturn(IERC20Token _sourceToken, IERC20Token _targetToken, uint256 _amount) public view returns (uint256, uint256) { return targetAmountAndFee(_sourceToken, _targetToken, _amount); } } // File: solidity/contracts/converter/LiquidityPoolConverter.sol pragma solidity 0.6.12; /** * @dev Liquidity Pool Converter * * The liquidity pool converter is the base contract for specific types of converters that * manage liquidity pools. * * Liquidity pools have 2 reserves or more and they allow converting between them. * * Note that TokenRateUpdate events are dispatched for pool tokens as well. * The pool token is the first token in the event in that case. */ abstract contract LiquidityPoolConverter is ConverterBase { /** * @dev triggered after liquidity is added * * @param _provider liquidity provider * @param _reserveToken reserve token address * @param _amount reserve token amount * @param _newBalance reserve token new balance * @param _newSupply pool token new supply */ event LiquidityAdded( address indexed _provider, IERC20Token indexed _reserveToken, uint256 _amount, uint256 _newBalance, uint256 _newSupply ); /** * @dev triggered after liquidity is removed * * @param _provider liquidity provider * @param _reserveToken reserve token address * @param _amount reserve token amount * @param _newBalance reserve token new balance * @param _newSupply pool token new supply */ event LiquidityRemoved( address indexed _provider, IERC20Token indexed _reserveToken, uint256 _amount, uint256 _newBalance, uint256 _newSupply ); /** * @dev initializes a new LiquidityPoolConverter instance * * @param _anchor anchor governed by the converter * @param _registry address of a contract registry contract * @param _maxConversionFee maximum conversion fee, represented in ppm */ constructor( IConverterAnchor _anchor, IContractRegistry _registry, uint32 _maxConversionFee ) ConverterBase(_anchor, _registry, _maxConversionFee) internal { } /** * @dev accepts ownership of the anchor after an ownership transfer * also activates the converter * can only be called by the contract owner * note that prior to version 28, you should use 'acceptTokenOwnership' instead */ function acceptAnchorOwnership() public virtual override { // verify that the converter has at least 2 reserves require(reserveTokenCount() > 1, "ERR_INVALID_RESERVE_COUNT"); super.acceptAnchorOwnership(); } } // File: solidity/contracts/token/interfaces/IDSToken.sol pragma solidity 0.6.12; /* DSToken interface */ interface IDSToken is IConverterAnchor, IERC20Token { function issue(address _to, uint256 _amount) external; function destroy(address _from, uint256 _amount) external; } // File: solidity/contracts/utility/Math.sol pragma solidity 0.6.12; /** * @dev Library for complex math operations */ library Math { using SafeMath for uint256; /** * @dev returns the largest integer smaller than or equal to the square root of a positive integer * * @param _num a positive integer * * @return the largest integer smaller than or equal to the square root of the positive integer */ function floorSqrt(uint256 _num) internal pure returns (uint256) { uint256 x = _num / 2 + 1; uint256 y = (x + _num / x) / 2; while (x > y) { x = y; y = (x + _num / x) / 2; } return x; } /** * @dev computes a reduced-scalar ratio * * @param _n ratio numerator * @param _d ratio denominator * @param _max maximum desired scalar * * @return ratio's numerator and denominator */ function reducedRatio(uint256 _n, uint256 _d, uint256 _max) internal pure returns (uint256, uint256) { if (_n > _max || _d > _max) return normalizedRatio(_n, _d, _max); return (_n, _d); } /** * @dev computes "scale * a / (a + b)" and "scale * b / (a + b)". */ function normalizedRatio(uint256 _a, uint256 _b, uint256 _scale) internal pure returns (uint256, uint256) { if (_a == _b) return (_scale / 2, _scale / 2); if (_a < _b) return accurateRatio(_a, _b, _scale); (uint256 y, uint256 x) = accurateRatio(_b, _a, _scale); return (x, y); } /** * @dev computes "scale * a / (a + b)" and "scale * b / (a + b)", assuming that "a < b". */ function accurateRatio(uint256 _a, uint256 _b, uint256 _scale) internal pure returns (uint256, uint256) { uint256 maxVal = uint256(-1) / _scale; if (_a > maxVal) { uint256 c = _a / (maxVal + 1) + 1; _a /= c; _b /= c; } uint256 x = roundDiv(_a * _scale, _a.add(_b)); uint256 y = _scale - x; return (x, y); } /** * @dev computes the nearest integer to a given quotient without overflowing or underflowing. */ function roundDiv(uint256 _n, uint256 _d) internal pure returns (uint256) { return _n / _d + _n % _d / (_d - _d / 2); } /** * @dev returns the average number of decimal digits in a given list of positive integers * * @param _values list of positive integers * * @return the average number of decimal digits in the given list of positive integers */ function geometricMean(uint256[] memory _values) internal pure returns (uint256) { uint256 numOfDigits = 0; uint256 length = _values.length; for (uint256 i = 0; i < length; i++) numOfDigits += decimalLength(_values[i]); return uint256(10) ** (roundDivUnsafe(numOfDigits, length) - 1); } /** * @dev returns the number of decimal digits in a given positive integer * * @param _x positive integer * * @return the number of decimal digits in the given positive integer */ function decimalLength(uint256 _x) internal pure returns (uint256) { uint256 y = 0; for (uint256 x = _x; x > 0; x /= 10) y++; return y; } /** * @dev returns the nearest integer to a given quotient * the computation is overflow-safe assuming that the input is sufficiently small * * @param _n quotient numerator * @param _d quotient denominator * * @return the nearest integer to the given quotient */ function roundDivUnsafe(uint256 _n, uint256 _d) internal pure returns (uint256) { return (_n + _d / 2) / _d; } } // File: solidity/contracts/utility/Types.sol pragma solidity 0.6.12; /** * @dev Provides types that can be used by various contracts */ struct Fraction { uint256 n; // numerator uint256 d; // denominator } // File: solidity/contracts/converter/types/liquidity-pool-v1/LiquidityPoolV1Converter.sol pragma solidity 0.6.12; /** * @dev Liquidity Pool v1 Converter * * The liquidity pool v1 converter is a specialized version of a converter that manages * a classic bancor liquidity pool. * * Even though pools can have many reserves, the standard pool configuration * is 2 reserves with 50%/50% weights. */ contract LiquidityPoolV1Converter is LiquidityPoolConverter { using Math for *; IEtherToken internal etherToken = IEtherToken(0xc0829421C1d260BD3cB3E0F06cfE2D52db2cE315); uint256 internal constant MAX_RATE_FACTOR_LOWER_BOUND = 1e30; // the period of time taken into account when calculating the recent averate rate uint256 private constant AVERAGE_RATE_PERIOD = 10 minutes; // true if the pool is a 2 reserves / 50%/50% weights pool, false otherwise bool public isStandardPool = false; // only used in standard pools Fraction public prevAverageRate; // average rate after the previous conversion (1 reserve token 0 in reserve token 1 units) uint256 public prevAverageRateUpdateTime; // last time when the previous rate was updated (in seconds) /** * @dev triggered after a conversion with new price data * deprecated, use `TokenRateUpdate` from version 28 and up * * @param _connectorToken reserve token * @param _tokenSupply pool token supply * @param _connectorBalance reserve balance * @param _connectorWeight reserve weight */ event PriceDataUpdate( IERC20Token indexed _connectorToken, uint256 _tokenSupply, uint256 _connectorBalance, uint32 _connectorWeight ); /** * @dev initializes a new LiquidityPoolV1Converter instance * * @param _token pool token governed by the converter * @param _registry address of a contract registry contract * @param _maxConversionFee maximum conversion fee, represented in ppm */ constructor( IDSToken _token, IContractRegistry _registry, uint32 _maxConversionFee ) LiquidityPoolConverter(_token, _registry, _maxConversionFee) public { } /** * @dev returns the converter type * * @return see the converter types in the the main contract doc */ function converterType() public pure override returns (uint16) { return 1; } /** * @dev accepts ownership of the anchor after an ownership transfer * also activates the converter * can only be called by the contract owner * note that prior to version 28, you should use 'acceptTokenOwnership' instead */ function acceptAnchorOwnership() public override ownerOnly { super.acceptAnchorOwnership(); emit Activation(converterType(), anchor, true); } /** * @dev defines a new reserve token for the converter * can only be called by the owner while the converter is inactive * * @param _token address of the reserve token * @param _weight reserve weight, represented in ppm, 1-1000000 */ function addReserve(IERC20Token _token, uint32 _weight) public override ownerOnly { super.addReserve(_token, _weight); isStandardPool = reserveTokens.length == 2 && reserves[reserveTokens[0]].weight == PPM_RESOLUTION / 2 && reserves[reserveTokens[1]].weight == PPM_RESOLUTION / 2; } /** * @dev returns the expected target amount of converting one reserve to another along with the fee * * @param _sourceToken contract address of the source reserve token * @param _targetToken contract address of the target reserve token * @param _amount amount of tokens received from the user * * @return expected target amount * @return expected fee */ function targetAmountAndFee(IERC20Token _sourceToken, IERC20Token _targetToken, uint256 _amount) public view override active validReserve(_sourceToken) validReserve(_targetToken) returns (uint256, uint256) { // validate input require(_sourceToken != _targetToken, "ERR_SAME_SOURCE_TARGET"); uint256 amount = IBancorFormula(addressOf(BANCOR_FORMULA)).crossReserveTargetAmount( reserveBalance(_sourceToken), reserves[_sourceToken].weight, reserveBalance(_targetToken), reserves[_targetToken].weight, _amount ); // return the amount minus the conversion fee and the conversion fee uint256 fee = calculateFee(amount); return (amount - fee, fee); } /** * @dev converts a specific amount of source tokens to target tokens * can only be called by the bancor network contract * * @param _sourceToken source ERC20 token * @param _targetToken target ERC20 token * @param _amount amount of tokens to convert (in units of the source token) * @param _trader address of the caller who executed the conversion * @param _beneficiary wallet to receive the conversion result * * @return amount of tokens received (in units of the target token) */ function doConvert(IERC20Token _sourceToken, IERC20Token _targetToken, uint256 _amount, address _trader, address payable _beneficiary) internal override returns (uint256) { // update the recent average rate if (isStandardPool && prevAverageRateUpdateTime < time()) { prevAverageRate = recentAverageRate(); prevAverageRateUpdateTime = time(); } // get expected target amount and fee (uint256 amount, uint256 fee) = targetAmountAndFee(_sourceToken, _targetToken, _amount); // ensure that the trade gives something in return require(amount != 0, "ERR_ZERO_TARGET_AMOUNT"); // ensure that the trade won't deplete the reserve balance assert(amount < reserveBalance(_targetToken)); // ensure that the input amount was already deposited if (_sourceToken == ETH_RESERVE_ADDRESS) require(msg.value == _amount, "ERR_ETH_AMOUNT_MISMATCH"); else require(msg.value == 0 && _sourceToken.balanceOf(address(this)).sub(reserveBalance(_sourceToken)) >= _amount, "ERR_INVALID_AMOUNT"); // sync the reserve balances syncReserveBalance(_sourceToken); reserves[_targetToken].balance = reserves[_targetToken].balance.sub(amount); // transfer funds to the beneficiary in the to reserve token if (_targetToken == ETH_RESERVE_ADDRESS) _beneficiary.transfer(amount); else safeTransfer(_targetToken, _beneficiary, amount); // dispatch the conversion event dispatchConversionEvent(_sourceToken, _targetToken, _trader, _amount, amount, fee); // dispatch rate updates dispatchTokenRateUpdateEvents(_sourceToken, _targetToken); return amount; } /** * @dev returns the recent average rate of 1 `_token` in the other reserve token units * note that the rate can only be queried for reserves in a standard pool * * @param _token token to get the rate for * @return recent average rate between the reserves (numerator) * @return recent average rate between the reserves (denominator) */ function recentAverageRate(IERC20Token _token) external view returns (uint256, uint256) { // verify that the pool is standard require(isStandardPool, "ERR_NON_STANDARD_POOL"); // get the recent average rate of reserve 0 Fraction memory rate = recentAverageRate(); if (_token == reserveTokens[0]) { return (rate.n, rate.d); } return (rate.d, rate.n); } /** * @dev returns the recent average rate of 1 reserve token 0 in reserve token 1 units * * @return recent average rate between the reserves */ function recentAverageRate() internal view returns (Fraction memory) { // get the elapsed time since the previous average rate was calculated uint256 timeElapsed = time() - prevAverageRateUpdateTime; // if the previous average rate was calculated in the current block, return it if (timeElapsed == 0) { return prevAverageRate; } // get the current rate between the reserves uint256 currentRateN = reserves[reserveTokens[1]].balance; uint256 currentRateD = reserves[reserveTokens[0]].balance; // if the previous average rate was calculated a while ago, the average rate is equal to the current rate if (timeElapsed >= AVERAGE_RATE_PERIOD) { return Fraction({ n: currentRateN, d: currentRateD }); } // given N as the sampling window, the new rate is calculated according to the following formula: // newRate = prevAverageRate + timeElapsed * [currentRate - prevAverageRate] / N // calculate the numerator and the denumerator of the new rate Fraction memory prevAverage = prevAverageRate; uint256 x = prevAverage.d.mul(currentRateN); uint256 y = prevAverage.n.mul(currentRateD); // since we know that timeElapsed < AVERAGE_RATE_PERIOD, we can avoid using SafeMath: uint256 newRateN = y.mul(AVERAGE_RATE_PERIOD - timeElapsed).add(x.mul(timeElapsed)); uint256 newRateD = prevAverage.d.mul(currentRateD).mul(AVERAGE_RATE_PERIOD); (newRateN, newRateD) = Math.reducedRatio(newRateN, newRateD, MAX_RATE_FACTOR_LOWER_BOUND); return Fraction({ n: newRateN, d: newRateD }); } /** * @dev increases the pool's liquidity and mints new shares in the pool to the caller * note that prior to version 28, you should use 'fund' instead * * @param _reserveTokens address of each reserve token * @param _reserveAmounts amount of each reserve token * @param _minReturn token minimum return-amount * * @return amount of pool tokens issued */ function addLiquidity(IERC20Token[] memory _reserveTokens, uint256[] memory _reserveAmounts, uint256 _minReturn) public payable protected active returns (uint256) { // verify the user input verifyLiquidityInput(_reserveTokens, _reserveAmounts, _minReturn); // if one of the reserves is ETH, then verify that the input amount of ETH is equal to the input value of ETH for (uint256 i = 0; i < _reserveTokens.length; i++) if (_reserveTokens[i] == ETH_RESERVE_ADDRESS) require(_reserveAmounts[i] == msg.value, "ERR_ETH_AMOUNT_MISMATCH"); // if the input value of ETH is larger than zero, then verify that one of the reserves is ETH if (msg.value > 0) { require(reserves[ETH_RESERVE_ADDRESS].isSet, "ERR_NO_ETH_RESERVE"); } // get the total supply uint256 totalSupply = IDSToken(address(anchor)).totalSupply(); // transfer from the user an equally-worth amount of each one of the reserve tokens uint256 amount = addLiquidityToPool(_reserveTokens, _reserveAmounts, totalSupply); // verify that the equivalent amount of tokens is equal to or larger than the user's expectation require(amount >= _minReturn, "ERR_RETURN_TOO_LOW"); // issue the tokens to the user IDSToken(address(anchor)).issue(msg.sender, amount); // return the amount of pool tokens issued return amount; } /** * @dev decreases the pool's liquidity and burns the caller's shares in the pool * note that prior to version 28, you should use 'liquidate' instead * * @param _amount token amount * @param _reserveTokens address of each reserve token * @param _reserveMinReturnAmounts minimum return-amount of each reserve token * * @return the amount of each reserve token granted for the given amount of pool tokens */ function removeLiquidity(uint256 _amount, IERC20Token[] memory _reserveTokens, uint256[] memory _reserveMinReturnAmounts) public protected active returns (uint256[] memory) { // verify the user input verifyLiquidityInput(_reserveTokens, _reserveMinReturnAmounts, _amount); // get the total supply BEFORE destroying the user tokens uint256 totalSupply = IDSToken(address(anchor)).totalSupply(); // destroy the user tokens IDSToken(address(anchor)).destroy(msg.sender, _amount); // transfer to the user an equivalent amount of each one of the reserve tokens return removeLiquidityFromPool(_reserveTokens, _reserveMinReturnAmounts, totalSupply, _amount); } /** * @dev increases the pool's liquidity and mints new shares in the pool to the caller * for example, if the caller increases the supply by 10%, * then it will cost an amount equal to 10% of each reserve token balance * note that starting from version 28, you should use 'addLiquidity' instead * * @param _amount amount to increase the supply by (in the pool token) * * @return amount of pool tokens issued */ function fund(uint256 _amount) public payable protected returns (uint256) { syncReserveBalances(); reserves[ETH_RESERVE_ADDRESS].balance = reserves[ETH_RESERVE_ADDRESS].balance.sub(msg.value); uint256 supply = IDSToken(address(anchor)).totalSupply(); IBancorFormula formula = IBancorFormula(addressOf(BANCOR_FORMULA)); // iterate through the reserve tokens and transfer a percentage equal to the weight between // _amount and the total supply in each reserve from the caller to the converter uint256 reserveCount = reserveTokens.length; for (uint256 i = 0; i < reserveCount; i++) { IERC20Token reserveToken = reserveTokens[i]; uint256 rsvBalance = reserves[reserveToken].balance; uint256 reserveAmount = formula.fundCost(supply, rsvBalance, reserveRatio, _amount); // transfer funds from the caller in the reserve token if (reserveToken == ETH_RESERVE_ADDRESS) { if (msg.value > reserveAmount) { msg.sender.transfer(msg.value - reserveAmount); } else if (msg.value < reserveAmount) { require(msg.value == 0, "ERR_INVALID_ETH_VALUE"); safeTransferFrom(etherToken, msg.sender, address(this), reserveAmount); etherToken.withdraw(reserveAmount); } } else { safeTransferFrom(reserveToken, msg.sender, address(this), reserveAmount); } // sync the reserve balance uint256 newReserveBalance = rsvBalance.add(reserveAmount); reserves[reserveToken].balance = newReserveBalance; uint256 newPoolTokenSupply = supply.add(_amount); // dispatch liquidity update for the pool token/reserve emit LiquidityAdded(msg.sender, reserveToken, reserveAmount, newReserveBalance, newPoolTokenSupply); // dispatch the `TokenRateUpdate` event for the pool token dispatchPoolTokenRateUpdateEvent(newPoolTokenSupply, reserveToken, newReserveBalance, reserves[reserveToken].weight); } // issue new funds to the caller in the pool token IDSToken(address(anchor)).issue(msg.sender, _amount); // return the amount of pool tokens issued return _amount; } /** * @dev decreases the pool's liquidity and burns the caller's shares in the pool * for example, if the holder sells 10% of the supply, * then they will receive 10% of each reserve token balance in return * note that starting from version 28, you should use 'removeLiquidity' instead * * @param _amount amount to liquidate (in the pool token) * * @return the amount of each reserve token granted for the given amount of pool tokens */ function liquidate(uint256 _amount) public protected returns (uint256[] memory) { require(_amount > 0, "ERR_ZERO_AMOUNT"); uint256 totalSupply = IDSToken(address(anchor)).totalSupply(); IDSToken(address(anchor)).destroy(msg.sender, _amount); uint256[] memory reserveMinReturnAmounts = new uint256[](reserveTokens.length); for (uint256 i = 0; i < reserveMinReturnAmounts.length; i++) reserveMinReturnAmounts[i] = 1; return removeLiquidityFromPool(reserveTokens, reserveMinReturnAmounts, totalSupply, _amount); } /** * @dev given the amount of one of the reserve tokens to add liquidity of, * returns the required amount of each one of the other reserve tokens * since an empty pool can be funded with any list of non-zero input amounts, * this function assumes that the pool is not empty (has already been funded) * * @param _reserveTokens address of each reserve token * @param _reserveTokenIndex index of the relevant reserve token * @param _reserveAmount amount of the relevant reserve token * * @return the required amount of each one of the reserve tokens */ function addLiquidityCost(IERC20Token[] memory _reserveTokens, uint256 _reserveTokenIndex, uint256 _reserveAmount) public view returns (uint256[] memory) { uint256[] memory reserveAmounts = new uint256[](_reserveTokens.length); uint256 totalSupply = IDSToken(address(anchor)).totalSupply(); IBancorFormula formula = IBancorFormula(addressOf(BANCOR_FORMULA)); uint256 amount = formula.fundSupplyAmount(totalSupply, reserves[_reserveTokens[_reserveTokenIndex]].balance, reserveRatio, _reserveAmount); for (uint256 i = 0; i < reserveAmounts.length; i++) reserveAmounts[i] = formula.fundCost(totalSupply, reserves[_reserveTokens[i]].balance, reserveRatio, amount); return reserveAmounts; } /** * @dev given the amount of one of the reserve tokens to add liquidity of, * returns the amount of pool tokens entitled for it * since an empty pool can be funded with any list of non-zero input amounts, * this function assumes that the pool is not empty (has already been funded) * * @param _reserveToken address of the reserve token * @param _reserveAmount amount of the reserve token * * @return the amount of pool tokens entitled */ function addLiquidityReturn(IERC20Token _reserveToken, uint256 _reserveAmount) public view returns (uint256) { uint256 totalSupply = IDSToken(address(anchor)).totalSupply(); IBancorFormula formula = IBancorFormula(addressOf(BANCOR_FORMULA)); return formula.fundSupplyAmount(totalSupply, reserves[_reserveToken].balance, reserveRatio, _reserveAmount); } /** * @dev returns the amount of each reserve token entitled for a given amount of pool tokens * * @param _amount amount of pool tokens * @param _reserveTokens address of each reserve token * * @return the amount of each reserve token entitled for the given amount of pool tokens */ function removeLiquidityReturn(uint256 _amount, IERC20Token[] memory _reserveTokens) public view returns (uint256[] memory) { uint256 totalSupply = IDSToken(address(anchor)).totalSupply(); IBancorFormula formula = IBancorFormula(addressOf(BANCOR_FORMULA)); return removeLiquidityReserveAmounts(_amount, _reserveTokens, totalSupply, formula); } /** * @dev verifies that a given array of tokens is identical to the converter's array of reserve tokens * we take this input in order to allow specifying the corresponding reserve amounts in any order * * @param _reserveTokens array of reserve tokens * @param _reserveAmounts array of reserve amounts * @param _amount token amount */ function verifyLiquidityInput(IERC20Token[] memory _reserveTokens, uint256[] memory _reserveAmounts, uint256 _amount) private view { uint256 i; uint256 j; uint256 length = reserveTokens.length; require(length == _reserveTokens.length, "ERR_INVALID_RESERVE"); require(length == _reserveAmounts.length, "ERR_INVALID_AMOUNT"); for (i = 0; i < length; i++) { // verify that every input reserve token is included in the reserve tokens require(reserves[_reserveTokens[i]].isSet, "ERR_INVALID_RESERVE"); for (j = 0; j < length; j++) { if (reserveTokens[i] == _reserveTokens[j]) break; } // verify that every reserve token is included in the input reserve tokens require(j < length, "ERR_INVALID_RESERVE"); // verify that every input reserve token amount is larger than zero require(_reserveAmounts[i] > 0, "ERR_INVALID_AMOUNT"); } // verify that the input token amount is larger than zero require(_amount > 0, "ERR_ZERO_AMOUNT"); } /** * @dev adds liquidity (reserve) to the pool * * @param _reserveTokens address of each reserve token * @param _reserveAmounts amount of each reserve token * @param _totalSupply token total supply * * @return amount of pool tokens issued */ function addLiquidityToPool(IERC20Token[] memory _reserveTokens, uint256[] memory _reserveAmounts, uint256 _totalSupply) private returns (uint256) { if (_totalSupply == 0) return addLiquidityToEmptyPool(_reserveTokens, _reserveAmounts); return addLiquidityToNonEmptyPool(_reserveTokens, _reserveAmounts, _totalSupply); } /** * @dev adds liquidity (reserve) to the pool when it's empty * * @param _reserveTokens address of each reserve token * @param _reserveAmounts amount of each reserve token * * @return amount of pool tokens issued */ function addLiquidityToEmptyPool(IERC20Token[] memory _reserveTokens, uint256[] memory _reserveAmounts) private returns (uint256) { // calculate the geometric-mean of the reserve amounts approved by the user uint256 amount = Math.geometricMean(_reserveAmounts); // transfer each one of the reserve amounts from the user to the pool for (uint256 i = 0; i < _reserveTokens.length; i++) { IERC20Token reserveToken = _reserveTokens[i]; uint256 reserveAmount = _reserveAmounts[i]; if (reserveToken != ETH_RESERVE_ADDRESS) // ETH has already been transferred as part of the transaction safeTransferFrom(reserveToken, msg.sender, address(this), reserveAmount); reserves[reserveToken].balance = reserveAmount; emit LiquidityAdded(msg.sender, reserveToken, reserveAmount, reserveAmount, amount); // dispatch the `TokenRateUpdate` event for the pool token dispatchPoolTokenRateUpdateEvent(amount, reserveToken, reserveAmount, reserves[reserveToken].weight); } // return the amount of pool tokens issued return amount; } /** * @dev adds liquidity (reserve) to the pool when it's not empty * * @param _reserveTokens address of each reserve token * @param _reserveAmounts amount of each reserve token * @param _totalSupply token total supply * * @return amount of pool tokens issued */ function addLiquidityToNonEmptyPool(IERC20Token[] memory _reserveTokens, uint256[] memory _reserveAmounts, uint256 _totalSupply) private returns (uint256) { syncReserveBalances(); reserves[ETH_RESERVE_ADDRESS].balance = reserves[ETH_RESERVE_ADDRESS].balance.sub(msg.value); IBancorFormula formula = IBancorFormula(addressOf(BANCOR_FORMULA)); uint256 amount = getMinShare(formula, _totalSupply, _reserveTokens, _reserveAmounts); uint256 newPoolTokenSupply = _totalSupply.add(amount); for (uint256 i = 0; i < _reserveTokens.length; i++) { IERC20Token reserveToken = _reserveTokens[i]; uint256 rsvBalance = reserves[reserveToken].balance; uint256 reserveAmount = formula.fundCost(_totalSupply, rsvBalance, reserveRatio, amount); require(reserveAmount > 0, "ERR_ZERO_TARGET_AMOUNT"); assert(reserveAmount <= _reserveAmounts[i]); // transfer each one of the reserve amounts from the user to the pool if (reserveToken != ETH_RESERVE_ADDRESS) // ETH has already been transferred as part of the transaction safeTransferFrom(reserveToken, msg.sender, address(this), reserveAmount); else if (_reserveAmounts[i] > reserveAmount) // transfer the extra amount of ETH back to the user msg.sender.transfer(_reserveAmounts[i] - reserveAmount); uint256 newReserveBalance = rsvBalance.add(reserveAmount); reserves[reserveToken].balance = newReserveBalance; emit LiquidityAdded(msg.sender, reserveToken, reserveAmount, newReserveBalance, newPoolTokenSupply); // dispatch the `TokenRateUpdate` event for the pool token dispatchPoolTokenRateUpdateEvent(newPoolTokenSupply, reserveToken, newReserveBalance, reserves[reserveToken].weight); } // return the amount of pool tokens issued return amount; } /** * @dev returns the amount of each reserve token entitled for a given amount of pool tokens * * @param _amount amount of pool tokens * @param _reserveTokens address of each reserve token * @param _totalSupply token total supply * @param _formula formula contract * * @return the amount of each reserve token entitled for the given amount of pool tokens */ function removeLiquidityReserveAmounts(uint256 _amount, IERC20Token[] memory _reserveTokens, uint256 _totalSupply, IBancorFormula _formula) private view returns (uint256[] memory) { uint256[] memory reserveAmounts = new uint256[](_reserveTokens.length); for (uint256 i = 0; i < reserveAmounts.length; i++) reserveAmounts[i] = _formula.liquidateReserveAmount(_totalSupply, reserves[_reserveTokens[i]].balance, reserveRatio, _amount); return reserveAmounts; } /** * @dev removes liquidity (reserve) from the pool * * @param _reserveTokens address of each reserve token * @param _reserveMinReturnAmounts minimum return-amount of each reserve token * @param _totalSupply token total supply * @param _amount token amount * * @return the amount of each reserve token granted for the given amount of pool tokens */ function removeLiquidityFromPool(IERC20Token[] memory _reserveTokens, uint256[] memory _reserveMinReturnAmounts, uint256 _totalSupply, uint256 _amount) private returns (uint256[] memory) { syncReserveBalances(); IBancorFormula formula = IBancorFormula(addressOf(BANCOR_FORMULA)); uint256 newPoolTokenSupply = _totalSupply.sub(_amount); uint256[] memory reserveAmounts = removeLiquidityReserveAmounts(_amount, _reserveTokens, _totalSupply, formula); for (uint256 i = 0; i < _reserveTokens.length; i++) { IERC20Token reserveToken = _reserveTokens[i]; uint256 reserveAmount = reserveAmounts[i]; require(reserveAmount >= _reserveMinReturnAmounts[i], "ERR_ZERO_TARGET_AMOUNT"); uint256 newReserveBalance = reserves[reserveToken].balance.sub(reserveAmount); reserves[reserveToken].balance = newReserveBalance; // transfer each one of the reserve amounts from the pool to the user if (reserveToken == ETH_RESERVE_ADDRESS) msg.sender.transfer(reserveAmount); else safeTransfer(reserveToken, msg.sender, reserveAmount); emit LiquidityRemoved(msg.sender, reserveToken, reserveAmount, newReserveBalance, newPoolTokenSupply); // dispatch the `TokenRateUpdate` event for the pool token dispatchPoolTokenRateUpdateEvent(newPoolTokenSupply, reserveToken, newReserveBalance, reserves[reserveToken].weight); } // return the amount of each reserve token granted for the given amount of pool tokens return reserveAmounts; } function getMinShare(IBancorFormula formula, uint256 _totalSupply, IERC20Token[] memory _reserveTokens, uint256[] memory _reserveAmounts) private view returns (uint256) { uint256 minIndex = 0; for (uint256 i = 1; i < _reserveTokens.length; i++) { if (_reserveAmounts[i].mul(reserves[_reserveTokens[minIndex]].balance) < _reserveAmounts[minIndex].mul(reserves[_reserveTokens[i]].balance)) minIndex = i; } return formula.fundSupplyAmount(_totalSupply, reserves[_reserveTokens[minIndex]].balance, reserveRatio, _reserveAmounts[minIndex]); } /** * @dev dispatches token rate update events for the reserve tokens and the pool token * * @param _sourceToken address of the source reserve token * @param _targetToken address of the target reserve token */ function dispatchTokenRateUpdateEvents(IERC20Token _sourceToken, IERC20Token _targetToken) private { uint256 poolTokenSupply = IDSToken(address(anchor)).totalSupply(); uint256 sourceReserveBalance = reserveBalance(_sourceToken); uint256 targetReserveBalance = reserveBalance(_targetToken); uint32 sourceReserveWeight = reserves[_sourceToken].weight; uint32 targetReserveWeight = reserves[_targetToken].weight; // dispatch token rate update event for the reserve tokens uint256 rateN = targetReserveBalance.mul(sourceReserveWeight); uint256 rateD = sourceReserveBalance.mul(targetReserveWeight); emit TokenRateUpdate(_sourceToken, _targetToken, rateN, rateD); // dispatch token rate update events for the pool token dispatchPoolTokenRateUpdateEvent(poolTokenSupply, _sourceToken, sourceReserveBalance, sourceReserveWeight); dispatchPoolTokenRateUpdateEvent(poolTokenSupply, _targetToken, targetReserveBalance, targetReserveWeight); // dispatch price data update events (deprecated events) emit PriceDataUpdate(_sourceToken, poolTokenSupply, sourceReserveBalance, sourceReserveWeight); emit PriceDataUpdate(_targetToken, poolTokenSupply, targetReserveBalance, targetReserveWeight); } /** * @dev dispatches token rate update event for the pool token * * @param _poolTokenSupply total pool token supply * @param _reserveToken address of the reserve token * @param _reserveBalance reserve balance * @param _reserveWeight reserve weight */ function dispatchPoolTokenRateUpdateEvent(uint256 _poolTokenSupply, IERC20Token _reserveToken, uint256 _reserveBalance, uint32 _reserveWeight) private { emit TokenRateUpdate(IDSToken(address(anchor)), _reserveToken, _reserveBalance.mul(PPM_RESOLUTION), _poolTokenSupply.mul(_reserveWeight)); } /** * @dev returns the current time * utility to allow overrides for tests */ function time() internal view virtual returns (uint256) { return now; } } // File: solidity/contracts/converter/interfaces/ITypedConverterFactory.sol pragma solidity 0.6.12; /* Typed Converter Factory interface */ interface ITypedConverterFactory { function converterType() external pure returns (uint16); function createConverter(IConverterAnchor _anchor, IContractRegistry _registry, uint32 _maxConversionFee) external returns (IConverter); } // File: solidity/contracts/converter/types/liquidity-pool-v1/LiquidityPoolV1ConverterFactory.sol pragma solidity 0.6.12; /* LiquidityPoolV1Converter Factory */ contract LiquidityPoolV1ConverterFactory is ITypedConverterFactory { /** * @dev returns the converter type the factory is associated with * * @return converter type */ function converterType() external pure override returns (uint16) { return 1; } /** * @dev creates a new converter with the given arguments and transfers * the ownership to the caller * * @param _anchor anchor governed by the converter * @param _registry address of a contract registry contract * @param _maxConversionFee maximum conversion fee, represented in ppm * * @return a new converter */ function createConverter(IConverterAnchor _anchor, IContractRegistry _registry, uint32 _maxConversionFee) external virtual override returns (IConverter) { IConverter converter = new LiquidityPoolV1Converter(IDSToken(address(_anchor)), _registry, _maxConversionFee); converter.transferOwnership(msg.sender); return converter; } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[],"name":"converterType","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"contract IConverterAnchor","name":"_anchor","type":"address"},{"internalType":"contract IContractRegistry","name":"_registry","type":"address"},{"internalType":"uint32","name":"_maxConversionFee","type":"uint32"}],"name":"createConverter","outputs":[{"internalType":"contract IConverter","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
608060405234801561001057600080fd5b5061561c806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c8063114139581461003b5780633e8ff43f14610094575b600080fd5b6100786004803603606081101561005157600080fd5b5080356001600160a01b03908116916020810135909116906040013563ffffffff166100b3565b604080516001600160a01b039092168252519081900360200190f35b61009c610184565b6040805161ffff9092168252519081900360200190f35b6000808484846040516100c590610189565b80846001600160a01b03168152602001836001600160a01b031681526020018263ffffffff1681526020019350505050604051809103906000f080158015610111573d6000803e3d6000fd5b509050806001600160a01b031663f2fde38b336040518263ffffffff1660e01b815260040180826001600160a01b03168152602001915050600060405180830381600087803b15801561016357600080fd5b505af1158015610177573d6000803e3d6000fd5b5092979650505050505050565b600190565b615450806101978339019056fe608060405260016004557fc0829421c1d260bd3cb3e0f06cfe2d52db2ce315000000000000000000000000600955600a805460ff191690553480156200004457600080fd5b506040516200545038038062005450833981810160405260608110156200006a57600080fd5b5080516020820151604090920151600080546001600160a01b031916331790559091908282828282828180620000a0816200013a565b50600280546001600160a01b039092166001600160a01b031992831681179091556003805490921617905582620000d7816200013a565b81620000e38162000199565b5050600580546001600160a01b039094166001600160a01b031990941693909317909255506009805463ffffffff9092166401000000000263ffffffff60201b1990921691909117905550620001f8945050505050565b6001600160a01b03811662000196576040805162461bcd60e51b815260206004820152601360248201527f4552525f494e56414c49445f4144445245535300000000000000000000000000604482015290519081900360640190fd5b50565b620f424063ffffffff8216111562000196576040805162461bcd60e51b815260206004820152601a60248201527f4552525f494e56414c49445f434f4e56455253494f4e5f464545000000000000604482015290519081900360640190fd5b61524880620002086000396000f3fe6080604052600436106103545760003560e01c806371f52bf3116101c6578063cdc91c69116100f7578063d895951211610095578063e8dc12ff1161006f578063e8dc12ff14610efa578063ecbca55d14610f40578063f2fde38b14610f70578063fc0c546a14610fa3576103e5565b8063d895951214610e7f578063dc8de37914610eb2578063e2c5246814610ee5576103e5565b8063d3fb73b4116100d1578063d3fb73b414610e0d578063d4ee1d9014610e22578063d55ec69714610e37578063d66bd52414610e4c576103e5565b8063cdc91c6914610db9578063d031370b14610dce578063d260529c14610df8576103e5565b80639b99a8e211610164578063b4a176d31161013e578063b4a176d314610d5d578063bf75455814610d72578063c45d3d9214610d87578063ca1d209d14610d9c576103e5565b80639b99a8e214610bce578063af94b8d814610be3578063b127c0a514610c26576103e5565b80637d8916bd116101a05780637d8916bd146109cc57806380d9416d14610af15780638da5cb5b14610ba457806394c275ad14610bb9576103e5565b806371f52bf31461098d57806379ba5097146109a25780637b103999146109b7576103e5565b806338a5e016116102a057806354fd4d501161023e57806361cd756e1161021857806361cd756e146108d357806367b6d57c146108e8578063690d83201461091b5780636a49d2c41461094e576103e5565b806354fd4d5014610866578063579cd3ca1461087b5780635e35359e14610890576103e5565b8063415f12401161027a578063415f1240146107bb57806349d10b64146107e55780634af80f0e146107fa5780634e40c2601461082d576103e5565b806338a5e0161461076557806338e9f27a1461077a5780633e8ff43f1461078f576103e5565b80631cfab2901161030d5780631f0181bc116102e75780631f0181bc146106d557806321e6b53d1461070857806322f3e2d41461073b5780632fe8a6ad14610750576103e5565b80631cfab2901461061f5780631d4db791146106525780631e1401f814610679576103e5565b8063024c7ec7146103ea5780630c7d5cd8146104165780630e53aae91461044457806312c2aca4146104ab57806315458837146104d457806319b64015146105d9576103e5565b366103e5576000805160206151f383398151915260005260086020527f353c2eb9e53a4a4a6d45d72082ff2e9dc829d1125618772a83eb0e7f86632c4354600160301b900460ff166103e3576040805162461bcd60e51b81526020600482015260136024820152724552525f494e56414c49445f5245534552564560681b604482015290519081900360640190fd5b005b600080fd5b3480156103f657600080fd5b506103e36004803603602081101561040d57600080fd5b50351515610fb8565b34801561042257600080fd5b5061042b610fde565b6040805163ffffffff9092168252519081900360200190f35b34801561045057600080fd5b506104776004803603602081101561046757600080fd5b50356001600160a01b0316610fea565b6040805195865263ffffffff9094166020860152911515848401521515606084015215156080830152519081900360a00190f35b3480156104b757600080fd5b506104c0611081565b604080519115158252519081900360200190f35b3480156104e057600080fd5b50610589600480360360408110156104f757600080fd5b81359190810190604081016020820135600160201b81111561051857600080fd5b82018360208201111561052a57600080fd5b803590602001918460208302840111600160201b8311171561054b57600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295506110c8945050505050565b60408051602080825283518183015283519192839290830191858101910280838360005b838110156105c55781810151838201526020016105ad565b505050509050019250505060405180910390f35b3480156105e557600080fd5b50610603600480360360208110156105fc57600080fd5b503561117c565b604080516001600160a01b039092168252519081900360200190f35b34801561062b57600080fd5b5061042b6004803603602081101561064257600080fd5b50356001600160a01b03166111a6565b34801561065e57600080fd5b506106676111d8565b60408051918252519081900360200190f35b34801561068557600080fd5b506106bc6004803603606081101561069c57600080fd5b506001600160a01b038135811691602081013590911690604001356111de565b6040805192835260208301919091528051918290030190f35b3480156106e157600080fd5b506106bc600480360360208110156106f857600080fd5b50356001600160a01b03166111f9565b34801561071457600080fd5b506103e36004803603602081101561072b57600080fd5b50356001600160a01b03166112b0565b34801561074757600080fd5b506104c06112c4565b34801561075c57600080fd5b506104c0611359565b34801561077157600080fd5b506103e3611369565b34801561078657600080fd5b506104c061137b565b34801561079b57600080fd5b506107a4611384565b6040805161ffff9092168252519081900360200190f35b3480156107c757600080fd5b50610589600480360360208110156107de57600080fd5b5035611389565b3480156107f157600080fd5b506103e36115a6565b34801561080657600080fd5b506103e36004803603602081101561081d57600080fd5b50356001600160a01b03166117ae565b34801561083957600080fd5b506106676004803603604081101561085057600080fd5b506001600160a01b0381351690602001356117e3565b34801561087257600080fd5b506107a461192c565b34801561088757600080fd5b5061042b611931565b34801561089c57600080fd5b506103e3600480360360608110156108b357600080fd5b506001600160a01b03813581169160208101359091169060400135611944565b3480156108df57600080fd5b50610603611a5f565b3480156108f457600080fd5b506103e36004803603602081101561090b57600080fd5b50356001600160a01b0316611a6e565b34801561092757600080fd5b506103e36004803603602081101561093e57600080fd5b50356001600160a01b0316611b04565b34801561095a57600080fd5b506103e36004803603604081101561097157600080fd5b5080356001600160a01b0316906020013563ffffffff16611c15565b34801561099957600080fd5b506107a4611cee565b3480156109ae57600080fd5b506103e3611cfd565b3480156109c357600080fd5b50610603611db4565b610667600480360360608110156109e257600080fd5b810190602081018135600160201b8111156109fc57600080fd5b820183602082011115610a0e57600080fd5b803590602001918460208302840111600160201b83111715610a2f57600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b811115610a7e57600080fd5b820183602082011115610a9057600080fd5b803590602001918460208302840111600160201b83111715610ab157600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295505091359250611dc3915050565b348015610afd57600080fd5b5061058960048036036060811015610b1457600080fd5b810190602081018135600160201b811115610b2e57600080fd5b820183602082011115610b4057600080fd5b803590602001918460208302840111600160201b83111715610b6157600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550508235935050506020013561206b565b348015610bb057600080fd5b5061060361231a565b348015610bc557600080fd5b5061042b612329565b348015610bda57600080fd5b506107a461233c565b348015610bef57600080fd5b506106bc60048036036060811015610c0657600080fd5b506001600160a01b03813581169160208101359091169060400135612342565b348015610c3257600080fd5b5061058960048036036060811015610c4957600080fd5b81359190810190604081016020820135600160201b811115610c6a57600080fd5b820183602082011115610c7c57600080fd5b803590602001918460208302840111600160201b83111715610c9d57600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b811115610cec57600080fd5b820183602082011115610cfe57600080fd5b803590602001918460208302840111600160201b83111715610d1f57600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295506124e5945050505050565b348015610d6957600080fd5b506103e3612600565b348015610d7e57600080fd5b506104c061262c565b348015610d9357600080fd5b50610603612631565b61066760048036036020811015610db257600080fd5b5035612640565b348015610dc557600080fd5b506103e3612aa7565b348015610dda57600080fd5b5061060360048036036020811015610df157600080fd5b5035612b00565b348015610e0457600080fd5b506104c0611384565b348015610e1957600080fd5b50610603612b27565b348015610e2e57600080fd5b50610603612b36565b348015610e4357600080fd5b506103e3612b45565b348015610e5857600080fd5b5061047760048036036020811015610e6f57600080fd5b50356001600160a01b0316612c2d565b348015610e8b57600080fd5b5061066760048036036020811015610ea257600080fd5b50356001600160a01b0316612c6f565b348015610ebe57600080fd5b5061066760048036036020811015610ed557600080fd5b50356001600160a01b0316612c76565b348015610ef157600080fd5b506106bc612c9f565b610667600480360360a0811015610f1057600080fd5b506001600160a01b0381358116916020810135821691604082013591606081013582169160809091013516612ca8565b348015610f4c57600080fd5b506103e360048036036020811015610f6357600080fd5b503563ffffffff16612ea2565b348015610f7c57600080fd5b506103e360048036036020811015610f9357600080fd5b50356001600160a01b0316612f89565b348015610faf57600080fd5b50610603613007565b610fc0613016565b60038054911515600160a01b0260ff60a01b19909216919091179055565b60095463ffffffff1681565b6000806000806000610ffa61518a565b505050506001600160a01b03929092166000908152600860209081526040808320815160a081018352815480825260019092015463ffffffff811694820185905260ff600160201b82048116151594830194909452650100000000008104841615156060830152600160301b90049092161515608090920182905295919450919250829190565b6000805160206151f383398151915260005260086020527f353c2eb9e53a4a4a6d45d72082ff2e9dc829d1125618772a83eb0e7f86632c4354600160301b900460ff165b90565b60606000600560009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561111a57600080fd5b505afa15801561112e573d6000803e3d6000fd5b505050506040513d602081101561114457600080fd5b5051905060006111636c42616e636f72466f726d756c6160981b613069565b9050611171858584846130e7565b925050505b92915050565b60006007828154811061118b57fe5b6000918252602090912001546001600160a01b031692915050565b6000816111b28161322c565b50506001600160a01b031660009081526008602052604090206001015463ffffffff1690565b600d5481565b6000806111ec858585612342565b915091505b935093915050565b600a54600090819060ff1661124d576040805162461bcd60e51b815260206004820152601560248201527411549497d393d397d4d510539110549117d413d3d3605a1b604482015290519081900360640190fd5b6112556151b8565b61125d613299565b9050600760008154811061126d57fe5b6000918252602090912001546001600160a01b038581169116141561129e57805160209091015190925090506112ab565b6020810151905190925090505b915091565b6112b8613016565b6112c181611a6e565b50565b6000306001600160a01b0316600560009054906101000a90046001600160a01b03166001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b15801561131e57600080fd5b505afa158015611332573d6000803e3d6000fd5b505050506040513d602081101561134857600080fd5b50516001600160a01b031614905090565b600354600160a01b900460ff1681565b611371613016565b611379612aa7565b565b600a5460ff1681565b600190565b6060611393613434565b6002600455816113dc576040805162461bcd60e51b815260206004820152600f60248201526e11549497d6915493d7d05353d55395608a1b604482015290519081900360640190fd5b600554604080516318160ddd60e01b815290516000926001600160a01b0316916318160ddd916004808301926020929190829003018186803b15801561142157600080fd5b505afa158015611435573d6000803e3d6000fd5b505050506040513d602081101561144b57600080fd5b50516005546040805163a24835d160e01b81523360048201526024810187905290519293506001600160a01b039091169163a24835d19160448082019260009290919082900301818387803b1580156114a357600080fd5b505af11580156114b7573d6000803e3d6000fd5b505060075460609250905067ffffffffffffffff811180156114d857600080fd5b50604051908082528060200260200182016040528015611502578160200160208202803683370190505b50905060005b815181101561153257600182828151811061151f57fe5b6020908102919091010152600101611508565b50611599600780548060200260200160405190810160405280929190818152602001828054801561158c57602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831161156e575b505050505082848761347c565b6001600455949350505050565b6000546001600160a01b03163314806115c95750600354600160a01b900460ff16155b61160e576040805162461bcd60e51b815260206004820152601160248201527011549497d050d0d154d4d7d11153925151607a1b604482015290519081900360640190fd5b600061162c6f436f6e7472616374526567697374727960801b613069565b6002549091506001600160a01b0380831691161480159061165557506001600160a01b03811615155b61169d576040805162461bcd60e51b81526020600482015260146024820152734552525f494e56414c49445f524547495354525960601b604482015290519081900360640190fd5b60006001600160a01b0316816001600160a01b031663bb34534c6f436f6e7472616374526567697374727960801b6040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b1580156116ff57600080fd5b505afa158015611713573d6000803e3d6000fd5b505050506040513d602081101561172957600080fd5b50516001600160a01b0316141561177e576040805162461bcd60e51b81526020600482015260146024820152734552525f494e56414c49445f524547495354525960601b604482015290519081900360640190fd5b60028054600380546001600160a01b038084166001600160a01b0319928316179092559091169216919091179055565b6117b6613016565b806117c081613687565b50600680546001600160a01b0319166001600160a01b0392909216919091179055565b600080600560009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561183457600080fd5b505afa158015611848573d6000803e3d6000fd5b505050506040513d602081101561185e57600080fd5b50519050600061187d6c42616e636f72466f726d756c6160981b613069565b6001600160a01b03868116600090815260086020908152604091829020546009548351632f55bdb560e01b815260048101899052602481019290925263ffffffff16604482015260648101899052915193945091841692632f55bdb592608480840193919291829003018186803b1580156118f757600080fd5b505afa15801561190b573d6000803e3d6000fd5b505050506040513d602081101561192157600080fd5b505195945050505050565b602981565b600954600160401b900463ffffffff1681565b61194c613434565b6002600455611959613016565b600061197e762130b731b7b921b7b73b32b93a32b92ab833b930b232b960491b613069565b6001600160a01b038516600090815260086020526040902060010154909150600160301b900460ff1615806119b857506119b66112c4565b155b806119d057506000546001600160a01b038281169116145b611a15576040805162461bcd60e51b815260206004820152601160248201527011549497d050d0d154d4d7d11153925151607a1b604482015290519081900360640190fd5b611a208484846136db565b6001600160a01b038416600090815260086020526040902060010154600160301b900460ff1615611a5457611a548461370c565b505060016004555050565b6003546001600160a01b031681565b611a76613016565b762130b731b7b921b7b73b32b93a32b92ab833b930b232b960491b611a9a816137e5565b6005546040805163f2fde38b60e01b81526001600160a01b0385811660048301529151919092169163f2fde38b91602480830192600092919082900301818387803b158015611ae857600080fd5b505af1158015611afc573d6000803e3d6000fd5b505050505050565b611b0c613434565b6002600455611b19613016565b6000805160206151f3833981519152611b318161322c565b6000611b56762130b731b7b921b7b73b32b93a32b92ab833b930b232b960491b613069565b9050611b606112c4565b1580611b7957506000546001600160a01b038281169116145b611bbe576040805162461bcd60e51b815260206004820152601160248201527011549497d050d0d154d4d7d11153925151607a1b604482015290519081900360640190fd5b6040516001600160a01b038416904780156108fc02916000818181858888f19350505050158015611bf3573d6000803e3d6000fd5b50611c0b6000805160206151f383398151915261370c565b5050600160045550565b611c1d613016565b611c278282613847565b6007546002148015611c8357506002620f42400463ffffffff16600860006007600081548110611c5357fe5b60009182526020808320909101546001600160a01b0316835282019290925260400190206001015463ffffffff16145b8015611cd957506002620f42400463ffffffff16600860006007600181548110611ca957fe5b60009182526020808320909101546001600160a01b0316835282019290925260400190206001015463ffffffff16145b600a805460ff19169115159190911790555050565b6000611cf861233c565b905090565b6001546001600160a01b03163314611d50576040805162461bcd60e51b815260206004820152601160248201527011549497d050d0d154d4d7d11153925151607a1b604482015290519081900360640190fd5b600154600080546040516001600160a01b0393841693909116917f343765429aea5a34b3ff6a3785a98a5abb2597aca87bfbb58632c173d585373a91a360018054600080546001600160a01b03199081166001600160a01b03841617909155169055565b6002546001600160a01b031681565b6000611dcd613434565b6002600455611dda613a69565b611de5848484613ab1565b60005b8451811015611e95576000805160206151f38339815191526001600160a01b0316858281518110611e1557fe5b60200260200101516001600160a01b03161415611e8d5734848281518110611e3957fe5b602002602001015114611e8d576040805162461bcd60e51b815260206004820152601760248201527608aa4a4be8aa890be829a9eaa9ca8be9a92a69a82a8869604b1b604482015290519081900360640190fd5b600101611de8565b503415611f25576000805160206151f383398151915260005260086020527f353c2eb9e53a4a4a6d45d72082ff2e9dc829d1125618772a83eb0e7f86632c4354600160301b900460ff16611f25576040805162461bcd60e51b81526020600482015260126024820152714552525f4e4f5f4554485f5245534552564560701b604482015290519081900360640190fd5b600554604080516318160ddd60e01b815290516000926001600160a01b0316916318160ddd916004808301926020929190829003018186803b158015611f6a57600080fd5b505afa158015611f7e573d6000803e3d6000fd5b505050506040513d6020811015611f9457600080fd5b505190506000611fa5868684613d46565b905083811015611ff1576040805162461bcd60e51b81526020600482015260126024820152714552525f52455455524e5f544f4f5f4c4f5760701b604482015290519081900360640190fd5b6005546040805163219e412d60e21b81523360048201526024810184905290516001600160a01b039092169163867904b49160448082019260009290919082900301818387803b15801561204457600080fd5b505af1158015612058573d6000803e3d6000fd5b5050600160045550909695505050505050565b606080845167ffffffffffffffff8111801561208657600080fd5b506040519080825280602002602001820160405280156120b0578160200160208202803683370190505b5090506000600560009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561210357600080fd5b505afa158015612117573d6000803e3d6000fd5b505050506040513d602081101561212d57600080fd5b50519050600061214c6c42616e636f72466f726d756c6160981b613069565b90506000816001600160a01b0316632f55bdb584600860008c8c8151811061217057fe5b6020908102919091018101516001600160a01b031682528181019290925260409081016000205460095482516001600160e01b031960e088901b1681526004810195909552602485019190915263ffffffff166044840152606483018b905251608480840193829003018186803b1580156121ea57600080fd5b505afa1580156121fe573d6000803e3d6000fd5b505050506040513d602081101561221457600080fd5b5051905060005b845181101561230b57826001600160a01b031663ebbb215885600860008d868151811061224457fe5b6020908102919091018101516001600160a01b031682528181019290925260409081016000205460095482516001600160e01b031960e088901b1681526004810195909552602485019190915263ffffffff1660448401526064830187905251608480840193829003018186803b1580156122be57600080fd5b505afa1580156122d2573d6000803e3d6000fd5b505050506040513d60208110156122e857600080fd5b505185518690839081106122f857fe5b602090810291909101015260010161221b565b509293505050505b9392505050565b6000546001600160a01b031681565b600954600160201b900463ffffffff1681565b60075490565b60008061234d613a69565b846123578161322c565b846123618161322c565b856001600160a01b0316876001600160a01b031614156123c1576040805162461bcd60e51b815260206004820152601660248201527511549497d4d0535157d4d3d55490d157d5105491d15560521b604482015290519081900360640190fd5b60006123dc6c42616e636f72466f726d756c6160981b613069565b6001600160a01b03166394491fab6123f38a612c76565b6001600160a01b038b1660009081526008602052604090206001015463ffffffff1661241e8b612c76565b6001600160a01b038c166000908152600860209081526040918290206001015482516001600160e01b031960e089901b168152600481019690965263ffffffff94851660248701526044860193909352929091166064840152608483018b90525160a480840193829003018186803b15801561249957600080fd5b505afa1580156124ad573d6000803e3d6000fd5b505050506040513d60208110156124c357600080fd5b5051905060006124d282613d71565b9182900399919850909650505050505050565b60606124ef613434565b60026004556124fc613a69565b612507838386613ab1565b600554604080516318160ddd60e01b815290516000926001600160a01b0316916318160ddd916004808301926020929190829003018186803b15801561254c57600080fd5b505afa158015612560573d6000803e3d6000fd5b505050506040513d602081101561257657600080fd5b50516005546040805163a24835d160e01b81523360048201526024810189905290519293506001600160a01b039091169163a24835d19160448082019260009290919082900301818387803b1580156125ce57600080fd5b505af11580156125e2573d6000803e3d6000fd5b505050506125f28484838861347c565b600160045595945050505050565b612608613016565b600354600280546001600160a01b0319166001600160a01b03909216919091179055565b600181565b6006546001600160a01b031681565b600061264a613434565b6002600455612657613da2565b6000805160206151f383398151915260005260086020526000805160206151d3833981519152546126889034613de2565b6000805160206151f38339815191526000908152600860209081526000805160206151d383398151915292909255600554604080516318160ddd60e01b8152905192936001600160a01b03909216926318160ddd92600480840193919291829003018186803b1580156126fa57600080fd5b505afa15801561270e573d6000803e3d6000fd5b505050506040513d602081101561272457600080fd5b5051905060006127436c42616e636f72466f726d756c6160981b613069565b60075490915060005b81811015612a2d5760006007828154811061276357fe5b60009182526020808320909101546001600160a01b03908116808452600883526040808520546009548251631d77642b60e31b8152600481018d90526024810183905263ffffffff9091166044820152606481018e9052915192965094939289169263ebbb215892608480840193829003018186803b1580156127e557600080fd5b505afa1580156127f9573d6000803e3d6000fd5b505050506040513d602081101561280f57600080fd5b505190506001600160a01b0383166000805160206151f3833981519152141561295a578034111561286f5760405133903483900380156108fc02916000818181858888f19350505050158015612869573d6000803e3d6000fd5b50612955565b803410156129555734156128c2576040805162461bcd60e51b81526020600482015260156024820152744552525f494e56414c49445f4554485f56414c554560581b604482015290519081900360640190fd5b6009546128e190600160601b90046001600160a01b0316333084613e2f565b6009600c9054906101000a90046001600160a01b03166001600160a01b0316632e1a7d4d826040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b15801561293c57600080fd5b505af1158015612950573d6000803e3d6000fd5b505050505b612966565b61296683333084613e2f565b60006129728383613f9a565b6001600160a01b038516600090815260086020526040812082905590915061299a898c613f9a565b604080518581526020810185905280820183905290519192506001600160a01b0387169133917f4a1a2a6176e9646d9e3157f7c2ab3c499f18337c0b0828cfb28e0a61de4a11f7919081900360600190a36001600160a01b038516600090815260086020526040902060010154612a1c9082908790859063ffffffff16613fe3565b50506001909301925061274c915050565b506005546040805163219e412d60e21b81523360048201526024810188905290516001600160a01b039092169163867904b49160448082019260009290919082900301818387803b158015612a8157600080fd5b505af1158015612a95573d6000803e3d6000fd5b50506001600455509495945050505050565b612aaf613016565b612ab7614052565b6005546001906001600160a01b0316612ace611384565b61ffff167f6b08c2e2c9969e55a647a764db9b554d64dc42f1a704da11a6d5b129ad163f2c60405160405180910390a4565b60078181548110612b0d57fe5b6000918252602090912001546001600160a01b0316905081565b6005546001600160a01b031681565b6001546001600160a01b031681565b612b4d613016565b6000612b72762130b731b7b921b7b73b32b93a32b92ab833b930b232b960491b613069565b6005549091506000906001600160a01b0316612b8c611384565b61ffff167f6b08c2e2c9969e55a647a764db9b554d64dc42f1a704da11a6d5b129ad163f2c60405160405180910390a4612bc581612f89565b6040805163487ac64b60e11b81526029600482015290516001600160a01b038316916390f58c9691602480830192600092919082900301818387803b158015612c0d57600080fd5b505af1158015612c21573d6000803e3d6000fd5b505050506112c1611cfd565b6008602052600090815260409020805460019091015463ffffffff81169060ff600160201b8204811691650100000000008104821691600160301b9091041685565b6000611176825b600081612c828161322c565b50506001600160a01b031660009081526008602052604090205490565b600b54600c5482565b6000612cb2613434565b60026004556c42616e636f724e6574776f726b60981b612cd1816137e5565b856001600160a01b0316876001600160a01b03161415612d31576040805162461bcd60e51b815260206004820152601660248201527511549497d4d0535157d4d3d55490d157d5105491d15560521b604482015290519081900360640190fd5b6006546001600160a01b03161580612e3e575060065460408051633af32abf60e01b81526001600160a01b03878116600483015291519190921691633af32abf916024808301926020929190829003018186803b158015612d9157600080fd5b505afa158015612da5573d6000803e3d6000fd5b505050506040513d6020811015612dbb57600080fd5b50518015612e3e575060065460408051633af32abf60e01b81526001600160a01b03868116600483015291519190921691633af32abf916024808301926020929190829003018186803b158015612e1157600080fd5b505afa158015612e25573d6000803e3d6000fd5b505050506040513d6020811015612e3b57600080fd5b50515b612e85576040805162461bcd60e51b815260206004820152601360248201527211549497d393d517d5d2125511531254d51151606a1b604482015290519081900360640190fd5b612e9287878787876140b6565b6001600455979650505050505050565b612eaa613016565b60095463ffffffff600160201b90910481169082161115612f12576040805162461bcd60e51b815260206004820152601a60248201527f4552525f494e56414c49445f434f4e56455253494f4e5f464545000000000000604482015290519081900360640190fd5b6009546040805163ffffffff600160401b90930483168152918316602083015280517f81cd2ffb37dd237c0e4e2a3de5265fcf9deb43d3e7801e80db9f1ccfba7ee6009281900390910190a16009805463ffffffff909216600160401b026bffffffff000000000000000019909216919091179055565b612f91613016565b6000546001600160a01b0382811691161415612fe5576040805162461bcd60e51b815260206004820152600e60248201526d22a9292fa9a0a6a2afa7aba722a960911b604482015290519081900360640190fd5b600180546001600160a01b0319166001600160a01b0392909216919091179055565b6005546001600160a01b031690565b6000546001600160a01b03163314611379576040805162461bcd60e51b815260206004820152601160248201527011549497d050d0d154d4d7d11153925151607a1b604482015290519081900360640190fd5b60025460408051632ecd14d360e21b81526004810184905290516000926001600160a01b03169163bb34534c916024808301926020929190829003018186803b1580156130b557600080fd5b505afa1580156130c9573d6000803e3d6000fd5b505050506040513d60208110156130df57600080fd5b505192915050565b606080845167ffffffffffffffff8111801561310257600080fd5b5060405190808252806020026020018201604052801561312c578160200160208202803683370190505b50905060005b815181101561322257836001600160a01b0316638074590a86600860008a868151811061315b57fe5b6020908102919091018101516001600160a01b031682528181019290925260409081016000205460095482516001600160e01b031960e088901b1681526004810195909552602485019190915263ffffffff166044840152606483018c905251608480840193829003018186803b1580156131d557600080fd5b505afa1580156131e9573d6000803e3d6000fd5b505050506040513d60208110156131ff57600080fd5b5051825183908390811061320f57fe5b6020908102919091010152600101613132565b5095945050505050565b6001600160a01b038116600090815260086020526040902060010154600160301b900460ff166112c1576040805162461bcd60e51b81526020600482015260136024820152724552525f494e56414c49445f5245534552564560681b604482015290519081900360640190fd5b6132a16151b8565b6000600d546132ae61437a565b039050806132d457505060408051808201909152600b548152600c5460208201526110c5565b60006008600060076001815481106132e857fe5b60009182526020808320909101546001600160a01b0316835282019290925260400181205460078054919350600891839190829061332257fe5b60009182526020808320909101546001600160a01b031683528201929092526040019020549050610258831061336d5760408051808201909152918252602082015291506110c59050565b6133756151b8565b5060408051808201909152600b548152600c546020820181905260009061339c908561437e565b82519091506000906133ae908561437e565b905060006133d46133bf848961437e565b6133ce846102588b900361437e565b90613f9a565b905060006133fb6102586133f588886020015161437e90919063ffffffff16565b9061437e565b905061341582826c0c9f2c9cd04674edea400000006143dc565b6040805180820190915291825260208201529850505050505050505090565b600160045414611379576040805162461bcd60e51b815260206004820152600e60248201526d4552525f5245454e5452414e435960901b604482015290519081900360640190fd5b6060613486613da2565b60006134a16c42616e636f72466f726d756c6160981b613069565b905060006134af8585613de2565b905060606134bf858988866130e7565b905060005b885181101561367b5760008982815181106134db57fe5b6020026020010151905060008383815181106134f357fe5b6020026020010151905089838151811061350957fe5b602002602001015181101561355e576040805162461bcd60e51b815260206004820152601660248201527511549497d6915493d7d5105491d15517d05353d5539560521b604482015290519081900360640190fd5b6001600160a01b0382166000908152600860205260408120546135819083613de2565b6001600160a01b03841660008181526008602052604090208290559091506000805160206151f383398151915214156135e757604051339083156108fc029084906000818181858888f193505050501580156135e1573d6000803e3d6000fd5b506135f2565b6135f283338461440e565b604080518381526020810183905280820188905290516001600160a01b0385169133917fbc7d19d505c7ec4db83f3b51f19fb98c4c8a99922e7839d1ee608dfbee29501b9181900360600190a36001600160a01b0383166000908152600860205260409020600101546136709087908590849063ffffffff16613fe3565b5050506001016134c4565b50979650505050505050565b6001600160a01b0381163014156112c1576040805162461bcd60e51b815260206004820152601360248201527222a9292fa0a2222922a9a9afa4a9afa9a2a62360691b604482015290519081900360640190fd5b6136e3613016565b826136ed8161456e565b826136f78161456e565b8361370181613687565b611afc86868661440e565b806137168161322c565b6001600160a01b0382166000805160206151f38339815191521415613755576001600160a01b03821660009081526008602052604090204790556137e1565b604080516370a0823160e01b815230600482015290516001600160a01b038416916370a08231916024808301926020929190829003018186803b15801561379b57600080fd5b505afa1580156137af573d6000803e3d6000fd5b505050506040513d60208110156137c557600080fd5b50516001600160a01b0383166000908152600860205260409020555b5050565b6137ee81613069565b6001600160a01b0316336001600160a01b0316146112c1576040805162461bcd60e51b815260206004820152601160248201527011549497d050d0d154d4d7d11153925151607a1b604482015290519081900360640190fd5b61384f613016565b6138576145bf565b816138618161456e565b8261386b81613687565b8261387581614606565b6005546001600160a01b038681169116148015906138b657506001600160a01b038516600090815260086020526040902060010154600160301b900460ff16155b6138fd576040805162461bcd60e51b81526020600482015260136024820152724552525f494e56414c49445f5245534552564560681b604482015290519081900360640190fd5b60095463ffffffff908116620f42400381169085161115613965576040805162461bcd60e51b815260206004820152601a60248201527f4552525f494e56414c49445f524553455256455f574549474854000000000000604482015290519081900360640190fd5b61ffff61397061233c565b61ffff16106139c2576040805162461bcd60e51b815260206004820152601960248201527811549497d253959053125117d49154d154959157d0d3d55395603a1b604482015290519081900360640190fd5b5050506001600160a01b0390911660008181526008602052604081208181556001908101805466ff0000000000001963ffffffff80881663ffffffff199384161791909116600160301b179092556007805493840181559093527fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c68890910180546001600160a01b031916909317909255600980548084169094019092169216919091179055565b613a716112c4565b611379576040805162461bcd60e51b815260206004820152600c60248201526b4552525f494e41435449564560a01b604482015290519081900360640190fd5b600754835160009182918114613b04576040805162461bcd60e51b81526020600482015260136024820152724552525f494e56414c49445f5245534552564560681b604482015290519081900360640190fd5b84518114613b4e576040805162461bcd60e51b815260206004820152601260248201527111549497d253959053125117d05353d5539560721b604482015290519081900360640190fd5b600092505b80831015613cff5760086000878581518110613b6b57fe5b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060010160069054906101000a900460ff16613beb576040805162461bcd60e51b81526020600482015260136024820152724552525f494e56414c49445f5245534552564560681b604482015290519081900360640190fd5b600091505b80821015613c4d57858281518110613c0457fe5b60200260200101516001600160a01b031660078481548110613c2257fe5b6000918252602090912001546001600160a01b03161415613c4257613c4d565b600190910190613bf0565b808210613c97576040805162461bcd60e51b81526020600482015260136024820152724552525f494e56414c49445f5245534552564560681b604482015290519081900360640190fd5b6000858481518110613ca557fe5b602002602001015111613cf4576040805162461bcd60e51b815260206004820152601260248201527111549497d253959053125117d05353d5539560721b604482015290519081900360640190fd5b600190920191613b53565b60008411611afc576040805162461bcd60e51b815260206004820152600f60248201526e11549497d6915493d7d05353d55395608a1b604482015290519081900360640190fd5b600081613d5e57613d578484614676565b9050612313565b613d69848484614795565b949350505050565b60095460009061117690620f424090613d9c908590600160401b900463ffffffff9081169061437e16565b90614ac2565b60075460005b818110156137e157613dda60078281548110613dc057fe5b6000918252602090912001546001600160a01b031661370c565b600101613da8565b600081831015613e29576040805162461bcd60e51b815260206004820152600d60248201526c4552525f554e444552464c4f5760981b604482015290519081900360640190fd5b50900390565b604080516001600160a01b0385811660248301528481166044830152606480830185905283518084039091018152608490920183526020820180516001600160e01b03166323b872dd60e01b17815292518251600094606094938a169392918291908083835b60208310613eb45780518252601f199092019160209182019101613e95565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114613f16576040519150601f19603f3d011682016040523d82523d6000602084013e613f1b565b606091505b5091509150818015613f49575080511580613f495750808060200190516020811015613f4657600080fd5b50515b611afc576040805162461bcd60e51b815260206004820152601860248201527f4552525f5452414e534645525f46524f4d5f4641494c45440000000000000000604482015290519081900360640190fd5b600082820183811015612313576040805162461bcd60e51b815260206004820152600c60248201526b4552525f4f564552464c4f5760a01b604482015290519081900360640190fd5b6005546001600160a01b0380851691167f77f29993cf2c084e726f7e802da0719d6a0ade3e204badc7a3ffd57ecb768c2461402185620f424061437e565b6140348863ffffffff8088169061437e16565b6040805192835260208301919091528051918290030190a350505050565b600161405c61233c565b61ffff16116140ae576040805162461bcd60e51b815260206004820152601960248201527811549497d253959053125117d49154d154959157d0d3d55395603a1b604482015290519081900360640190fd5b611379614b21565b600a5460009060ff1680156140d357506140ce61437a565b600d54105b156140f8576140e0613299565b8051600b5560200151600c556140f461437a565b600d555b600080614106888888612342565b915091508160001415614159576040805162461bcd60e51b815260206004820152601660248201527511549497d6915493d7d5105491d15517d05353d5539560521b604482015290519081900360640190fd5b61416287612c76565b821061416a57fe5b6001600160a01b0388166000805160206151f383398151915214156141dc578534146141d7576040805162461bcd60e51b815260206004820152601760248201527608aa4a4be8aa890be829a9eaa9ca8be9a92a69a82a8869604b1b604482015290519081900360640190fd5b6142b3565b3415801561426d57508561426a6141f28a612c76565b604080516370a0823160e01b815230600482015290516001600160a01b038d16916370a08231916024808301926020929190829003018186803b15801561423857600080fd5b505afa15801561424c573d6000803e3d6000fd5b505050506040513d602081101561426257600080fd5b505190613de2565b10155b6142b3576040805162461bcd60e51b815260206004820152601260248201527111549497d253959053125117d05353d5539560721b604482015290519081900360640190fd5b6142bc8861370c565b6001600160a01b0387166000908152600860205260409020546142df9083613de2565b6001600160a01b0388166000818152600860205260409020919091556000805160206151f3833981519152141561434c576040516001600160a01b0385169083156108fc029084906000818181858888f19350505050158015614346573d6000803e3d6000fd5b50614357565b61435787858461440e565b614365888887898686614bf5565b61436f8888614c5e565b509695505050505050565b4290565b60008261438d57506000611176565b8282028284828161439a57fe5b0414612313576040805162461bcd60e51b815260206004820152600c60248201526b4552525f4f564552464c4f5760a01b604482015290519081900360640190fd5b600080828511806143ec57508284115b15614405576143fc858585614e57565b915091506111f1565b50929391925050565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b178152925182516000946060949389169392918291908083835b6020831061448b5780518252601f19909201916020918201910161446c565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d80600081146144ed576040519150601f19603f3d011682016040523d82523d6000602084013e6144f2565b606091505b5091509150818015614520575080511580614520575080806020019051602081101561451d57600080fd5b50515b614567576040805162461bcd60e51b815260206004820152601360248201527211549497d514905394d1915497d19052531151606a1b604482015290519081900360640190fd5b5050505050565b6001600160a01b0381166112c1576040805162461bcd60e51b81526020600482015260136024820152724552525f494e56414c49445f4144445245535360681b604482015290519081900360640190fd5b6145c76112c4565b15611379576040805162461bcd60e51b815260206004820152600a6024820152694552525f41435449564560b01b604482015290519081900360640190fd5b60008163ffffffff161180156146255750620f424063ffffffff821611155b6112c1576040805162461bcd60e51b815260206004820152601a60248201527f4552525f494e56414c49445f524553455256455f574549474854000000000000604482015290519081900360640190fd5b60008061468283614e9c565b905060005b845181101561478d57600085828151811061469e57fe5b6020026020010151905060008583815181106146b657fe5b602002602001015190506000805160206151f38339815191526001600160a01b0316826001600160a01b0316146146f3576146f382333084613e2f565b6001600160a01b0382166000818152600860209081526040918290208490558151848152908101849052808201879052905133917f4a1a2a6176e9646d9e3157f7c2ab3c499f18337c0b0828cfb28e0a61de4a11f7919081900360600190a36001600160a01b0382166000908152600860205260409020600101546147839085908490849063ffffffff16613fe3565b5050600101614687565b509392505050565b600061479f613da2565b6000805160206151f383398151915260005260086020526000805160206151d3833981519152546147d09034613de2565b6000805160206151f3833981519152600090815260086020526000805160206151d3833981519152919091556148156c42616e636f72466f726d756c6160981b613069565b9050600061482582858888614eee565b905060006148338583613f9a565b905060005b8751811015614ab657600088828151811061484f57fe5b6020908102919091018101516001600160a01b038082166000908152600884526040808220546009548251631d77642b60e31b8152600481018f90526024810183905263ffffffff9091166044820152606481018b90529151949650949193928a169263ebbb215892608480840193919291829003018186803b1580156148d557600080fd5b505afa1580156148e9573d6000803e3d6000fd5b505050506040513d60208110156148ff57600080fd5b505190508061494e576040805162461bcd60e51b815260206004820152601660248201527511549497d6915493d7d5105491d15517d05353d5539560521b604482015290519081900360640190fd5b89848151811061495a57fe5b602002602001015181111561496b57fe5b6001600160a01b0383166000805160206151f38339815191521461499a5761499583333084613e2f565b614a05565b808a85815181106149a757fe5b60200260200101511115614a0557336001600160a01b03166108fc828c87815181106149cf57fe5b6020026020010151039081150290604051600060405180830381858888f19350505050158015614a03573d6000803e3d6000fd5b505b6000614a118383613f9a565b6001600160a01b03851660008181526008602090815260409182902084905581518681529081018490528082018a90529051929350909133917f4a1a2a6176e9646d9e3157f7c2ab3c499f18337c0b0828cfb28e0a61de4a11f7919081900360600190a36001600160a01b038416600090815260086020526040902060010154614aa69087908690849063ffffffff16613fe3565b5050600190920191506148389050565b50909695505050505050565b6000808211614b0d576040805162461bcd60e51b81526020600482015260126024820152714552525f4449564944455f42595f5a45524f60701b604482015290519081900360640190fd5b6000828481614b1857fe5b04949350505050565b614b29613016565b6000614b3361233c565b61ffff1611614b85576040805162461bcd60e51b815260206004820152601960248201527811549497d253959053125117d49154d154959157d0d3d55395603a1b604482015290519081900360640190fd5b600560009054906101000a90046001600160a01b03166001600160a01b03166379ba50976040518163ffffffff1660e01b8152600401600060405180830381600087803b158015614bd557600080fd5b505af1158015614be9573d6000803e3d6000fd5b50505050611379613da2565b600160ff1b8110614c0257fe5b604080518481526020810184905280820183905290516001600160a01b038087169288821692918a16917f276856b36cbc45526a0ba64f44611557a2a8b68662c5388e9fe6d72e86e1c8cb9181900360600190a4505050505050565b600554604080516318160ddd60e01b815290516000926001600160a01b0316916318160ddd916004808301926020929190829003018186803b158015614ca357600080fd5b505afa158015614cb7573d6000803e3d6000fd5b505050506040513d6020811015614ccd57600080fd5b505190506000614cdc84612c76565b90506000614ce984612c76565b6001600160a01b038087166000908152600860205260408082206001908101549389168352908220015492935063ffffffff91821692821691614d30908590859061437e16565b90506000614d478663ffffffff8086169061437e16565b604080518481526020810183905281519293506001600160a01b03808c1693908d16927f77f29993cf2c084e726f7e802da0719d6a0ade3e204badc7a3ffd57ecb768c24928290030190a3614d9e878a8887613fe3565b614daa87898786613fe3565b604080518881526020810188905263ffffffff86168183015290516001600160a01b038b16917f8a6a7f53b3c8fa1dc4b83e3f1be668c1b251ff8d44cdcb83eb3acec3fec6a788919081900360600190a2604080518881526020810187905263ffffffff85168183015290516001600160a01b038a16917f8a6a7f53b3c8fa1dc4b83e3f1be668c1b251ff8d44cdcb83eb3acec3fec6a788919081900360600190a2505050505050505050565b60008083851415614e6e57505060028104806111f1565b83851015614e81576143fc8585856150b4565b600080614e8f8688876150b4565b9890975095505050505050565b80516000908190815b81811015614ed557614ec9858281518110614ebc57fe5b6020026020010151615124565b90920191600101614ea5565b506001614ee28383615146565b03600a0a949350505050565b60008060015b8451811015614fbb57614f5e60086000878481518110614f1057fe5b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060000154858481518110614f4857fe5b602002602001015161437e90919063ffffffff16565b614fa960086000888681518110614f7157fe5b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060000154868481518110614f4857fe5b1015614fb3578091505b600101614ef4565b50856001600160a01b0316632f55bdb58660086000888681518110614fdc57fe5b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060000154600960009054906101000a900463ffffffff1687868151811061502757fe5b60200260200101516040518563ffffffff1660e01b8152600401808581526020018481526020018363ffffffff16815260200182815260200194505050505060206040518083038186803b15801561507e57600080fd5b505afa158015615092573d6000803e3d6000fd5b505050506040513d60208110156150a857600080fd5b50519695505050505050565b600080600083600019816150c457fe5b049050808611156150fd5760008160010187816150dd57fe5b0460010190508087816150ec57fe5b0496508086816150f857fe5b049550505b600061511487860261510f8989613f9a565b61515e565b9794889003965093945050505050565b600080825b801561513f5760019190910190600a9004615129565b5092915050565b6000816002810484018161515657fe5b049392505050565b600060028204820382848161516f57fe5b068161517757fe5b0482848161518157fe5b04019392505050565b6040805160a08101825260008082526020820181905291810182905260608101829052608081019190915290565b60405180604001604052806000815260200160008152509056fe353c2eb9e53a4a4a6d45d72082ff2e9dc829d1125618772a83eb0e7f86632c42000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeea26469706673582212207d4ffe0c158efb1275dc65d8adc06d493a356eeba3de7beddbd999d8aeda058764736f6c634300060c0033a26469706673582212207838310a225ff25bc9c002d9464173bc5331417c8b84452d7569a34aed70b15164736f6c634300060c0033
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106100365760003560e01c8063114139581461003b5780633e8ff43f14610094575b600080fd5b6100786004803603606081101561005157600080fd5b5080356001600160a01b03908116916020810135909116906040013563ffffffff166100b3565b604080516001600160a01b039092168252519081900360200190f35b61009c610184565b6040805161ffff9092168252519081900360200190f35b6000808484846040516100c590610189565b80846001600160a01b03168152602001836001600160a01b031681526020018263ffffffff1681526020019350505050604051809103906000f080158015610111573d6000803e3d6000fd5b509050806001600160a01b031663f2fde38b336040518263ffffffff1660e01b815260040180826001600160a01b03168152602001915050600060405180830381600087803b15801561016357600080fd5b505af1158015610177573d6000803e3d6000fd5b5092979650505050505050565b600190565b615450806101978339019056fe608060405260016004557fc0829421c1d260bd3cb3e0f06cfe2d52db2ce315000000000000000000000000600955600a805460ff191690553480156200004457600080fd5b506040516200545038038062005450833981810160405260608110156200006a57600080fd5b5080516020820151604090920151600080546001600160a01b031916331790559091908282828282828180620000a0816200013a565b50600280546001600160a01b039092166001600160a01b031992831681179091556003805490921617905582620000d7816200013a565b81620000e38162000199565b5050600580546001600160a01b039094166001600160a01b031990941693909317909255506009805463ffffffff9092166401000000000263ffffffff60201b1990921691909117905550620001f8945050505050565b6001600160a01b03811662000196576040805162461bcd60e51b815260206004820152601360248201527f4552525f494e56414c49445f4144445245535300000000000000000000000000604482015290519081900360640190fd5b50565b620f424063ffffffff8216111562000196576040805162461bcd60e51b815260206004820152601a60248201527f4552525f494e56414c49445f434f4e56455253494f4e5f464545000000000000604482015290519081900360640190fd5b61524880620002086000396000f3fe6080604052600436106103545760003560e01c806371f52bf3116101c6578063cdc91c69116100f7578063d895951211610095578063e8dc12ff1161006f578063e8dc12ff14610efa578063ecbca55d14610f40578063f2fde38b14610f70578063fc0c546a14610fa3576103e5565b8063d895951214610e7f578063dc8de37914610eb2578063e2c5246814610ee5576103e5565b8063d3fb73b4116100d1578063d3fb73b414610e0d578063d4ee1d9014610e22578063d55ec69714610e37578063d66bd52414610e4c576103e5565b8063cdc91c6914610db9578063d031370b14610dce578063d260529c14610df8576103e5565b80639b99a8e211610164578063b4a176d31161013e578063b4a176d314610d5d578063bf75455814610d72578063c45d3d9214610d87578063ca1d209d14610d9c576103e5565b80639b99a8e214610bce578063af94b8d814610be3578063b127c0a514610c26576103e5565b80637d8916bd116101a05780637d8916bd146109cc57806380d9416d14610af15780638da5cb5b14610ba457806394c275ad14610bb9576103e5565b806371f52bf31461098d57806379ba5097146109a25780637b103999146109b7576103e5565b806338a5e016116102a057806354fd4d501161023e57806361cd756e1161021857806361cd756e146108d357806367b6d57c146108e8578063690d83201461091b5780636a49d2c41461094e576103e5565b806354fd4d5014610866578063579cd3ca1461087b5780635e35359e14610890576103e5565b8063415f12401161027a578063415f1240146107bb57806349d10b64146107e55780634af80f0e146107fa5780634e40c2601461082d576103e5565b806338a5e0161461076557806338e9f27a1461077a5780633e8ff43f1461078f576103e5565b80631cfab2901161030d5780631f0181bc116102e75780631f0181bc146106d557806321e6b53d1461070857806322f3e2d41461073b5780632fe8a6ad14610750576103e5565b80631cfab2901461061f5780631d4db791146106525780631e1401f814610679576103e5565b8063024c7ec7146103ea5780630c7d5cd8146104165780630e53aae91461044457806312c2aca4146104ab57806315458837146104d457806319b64015146105d9576103e5565b366103e5576000805160206151f383398151915260005260086020527f353c2eb9e53a4a4a6d45d72082ff2e9dc829d1125618772a83eb0e7f86632c4354600160301b900460ff166103e3576040805162461bcd60e51b81526020600482015260136024820152724552525f494e56414c49445f5245534552564560681b604482015290519081900360640190fd5b005b600080fd5b3480156103f657600080fd5b506103e36004803603602081101561040d57600080fd5b50351515610fb8565b34801561042257600080fd5b5061042b610fde565b6040805163ffffffff9092168252519081900360200190f35b34801561045057600080fd5b506104776004803603602081101561046757600080fd5b50356001600160a01b0316610fea565b6040805195865263ffffffff9094166020860152911515848401521515606084015215156080830152519081900360a00190f35b3480156104b757600080fd5b506104c0611081565b604080519115158252519081900360200190f35b3480156104e057600080fd5b50610589600480360360408110156104f757600080fd5b81359190810190604081016020820135600160201b81111561051857600080fd5b82018360208201111561052a57600080fd5b803590602001918460208302840111600160201b8311171561054b57600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295506110c8945050505050565b60408051602080825283518183015283519192839290830191858101910280838360005b838110156105c55781810151838201526020016105ad565b505050509050019250505060405180910390f35b3480156105e557600080fd5b50610603600480360360208110156105fc57600080fd5b503561117c565b604080516001600160a01b039092168252519081900360200190f35b34801561062b57600080fd5b5061042b6004803603602081101561064257600080fd5b50356001600160a01b03166111a6565b34801561065e57600080fd5b506106676111d8565b60408051918252519081900360200190f35b34801561068557600080fd5b506106bc6004803603606081101561069c57600080fd5b506001600160a01b038135811691602081013590911690604001356111de565b6040805192835260208301919091528051918290030190f35b3480156106e157600080fd5b506106bc600480360360208110156106f857600080fd5b50356001600160a01b03166111f9565b34801561071457600080fd5b506103e36004803603602081101561072b57600080fd5b50356001600160a01b03166112b0565b34801561074757600080fd5b506104c06112c4565b34801561075c57600080fd5b506104c0611359565b34801561077157600080fd5b506103e3611369565b34801561078657600080fd5b506104c061137b565b34801561079b57600080fd5b506107a4611384565b6040805161ffff9092168252519081900360200190f35b3480156107c757600080fd5b50610589600480360360208110156107de57600080fd5b5035611389565b3480156107f157600080fd5b506103e36115a6565b34801561080657600080fd5b506103e36004803603602081101561081d57600080fd5b50356001600160a01b03166117ae565b34801561083957600080fd5b506106676004803603604081101561085057600080fd5b506001600160a01b0381351690602001356117e3565b34801561087257600080fd5b506107a461192c565b34801561088757600080fd5b5061042b611931565b34801561089c57600080fd5b506103e3600480360360608110156108b357600080fd5b506001600160a01b03813581169160208101359091169060400135611944565b3480156108df57600080fd5b50610603611a5f565b3480156108f457600080fd5b506103e36004803603602081101561090b57600080fd5b50356001600160a01b0316611a6e565b34801561092757600080fd5b506103e36004803603602081101561093e57600080fd5b50356001600160a01b0316611b04565b34801561095a57600080fd5b506103e36004803603604081101561097157600080fd5b5080356001600160a01b0316906020013563ffffffff16611c15565b34801561099957600080fd5b506107a4611cee565b3480156109ae57600080fd5b506103e3611cfd565b3480156109c357600080fd5b50610603611db4565b610667600480360360608110156109e257600080fd5b810190602081018135600160201b8111156109fc57600080fd5b820183602082011115610a0e57600080fd5b803590602001918460208302840111600160201b83111715610a2f57600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b811115610a7e57600080fd5b820183602082011115610a9057600080fd5b803590602001918460208302840111600160201b83111715610ab157600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295505091359250611dc3915050565b348015610afd57600080fd5b5061058960048036036060811015610b1457600080fd5b810190602081018135600160201b811115610b2e57600080fd5b820183602082011115610b4057600080fd5b803590602001918460208302840111600160201b83111715610b6157600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550508235935050506020013561206b565b348015610bb057600080fd5b5061060361231a565b348015610bc557600080fd5b5061042b612329565b348015610bda57600080fd5b506107a461233c565b348015610bef57600080fd5b506106bc60048036036060811015610c0657600080fd5b506001600160a01b03813581169160208101359091169060400135612342565b348015610c3257600080fd5b5061058960048036036060811015610c4957600080fd5b81359190810190604081016020820135600160201b811115610c6a57600080fd5b820183602082011115610c7c57600080fd5b803590602001918460208302840111600160201b83111715610c9d57600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b811115610cec57600080fd5b820183602082011115610cfe57600080fd5b803590602001918460208302840111600160201b83111715610d1f57600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295506124e5945050505050565b348015610d6957600080fd5b506103e3612600565b348015610d7e57600080fd5b506104c061262c565b348015610d9357600080fd5b50610603612631565b61066760048036036020811015610db257600080fd5b5035612640565b348015610dc557600080fd5b506103e3612aa7565b348015610dda57600080fd5b5061060360048036036020811015610df157600080fd5b5035612b00565b348015610e0457600080fd5b506104c0611384565b348015610e1957600080fd5b50610603612b27565b348015610e2e57600080fd5b50610603612b36565b348015610e4357600080fd5b506103e3612b45565b348015610e5857600080fd5b5061047760048036036020811015610e6f57600080fd5b50356001600160a01b0316612c2d565b348015610e8b57600080fd5b5061066760048036036020811015610ea257600080fd5b50356001600160a01b0316612c6f565b348015610ebe57600080fd5b5061066760048036036020811015610ed557600080fd5b50356001600160a01b0316612c76565b348015610ef157600080fd5b506106bc612c9f565b610667600480360360a0811015610f1057600080fd5b506001600160a01b0381358116916020810135821691604082013591606081013582169160809091013516612ca8565b348015610f4c57600080fd5b506103e360048036036020811015610f6357600080fd5b503563ffffffff16612ea2565b348015610f7c57600080fd5b506103e360048036036020811015610f9357600080fd5b50356001600160a01b0316612f89565b348015610faf57600080fd5b50610603613007565b610fc0613016565b60038054911515600160a01b0260ff60a01b19909216919091179055565b60095463ffffffff1681565b6000806000806000610ffa61518a565b505050506001600160a01b03929092166000908152600860209081526040808320815160a081018352815480825260019092015463ffffffff811694820185905260ff600160201b82048116151594830194909452650100000000008104841615156060830152600160301b90049092161515608090920182905295919450919250829190565b6000805160206151f383398151915260005260086020527f353c2eb9e53a4a4a6d45d72082ff2e9dc829d1125618772a83eb0e7f86632c4354600160301b900460ff165b90565b60606000600560009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561111a57600080fd5b505afa15801561112e573d6000803e3d6000fd5b505050506040513d602081101561114457600080fd5b5051905060006111636c42616e636f72466f726d756c6160981b613069565b9050611171858584846130e7565b925050505b92915050565b60006007828154811061118b57fe5b6000918252602090912001546001600160a01b031692915050565b6000816111b28161322c565b50506001600160a01b031660009081526008602052604090206001015463ffffffff1690565b600d5481565b6000806111ec858585612342565b915091505b935093915050565b600a54600090819060ff1661124d576040805162461bcd60e51b815260206004820152601560248201527411549497d393d397d4d510539110549117d413d3d3605a1b604482015290519081900360640190fd5b6112556151b8565b61125d613299565b9050600760008154811061126d57fe5b6000918252602090912001546001600160a01b038581169116141561129e57805160209091015190925090506112ab565b6020810151905190925090505b915091565b6112b8613016565b6112c181611a6e565b50565b6000306001600160a01b0316600560009054906101000a90046001600160a01b03166001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b15801561131e57600080fd5b505afa158015611332573d6000803e3d6000fd5b505050506040513d602081101561134857600080fd5b50516001600160a01b031614905090565b600354600160a01b900460ff1681565b611371613016565b611379612aa7565b565b600a5460ff1681565b600190565b6060611393613434565b6002600455816113dc576040805162461bcd60e51b815260206004820152600f60248201526e11549497d6915493d7d05353d55395608a1b604482015290519081900360640190fd5b600554604080516318160ddd60e01b815290516000926001600160a01b0316916318160ddd916004808301926020929190829003018186803b15801561142157600080fd5b505afa158015611435573d6000803e3d6000fd5b505050506040513d602081101561144b57600080fd5b50516005546040805163a24835d160e01b81523360048201526024810187905290519293506001600160a01b039091169163a24835d19160448082019260009290919082900301818387803b1580156114a357600080fd5b505af11580156114b7573d6000803e3d6000fd5b505060075460609250905067ffffffffffffffff811180156114d857600080fd5b50604051908082528060200260200182016040528015611502578160200160208202803683370190505b50905060005b815181101561153257600182828151811061151f57fe5b6020908102919091010152600101611508565b50611599600780548060200260200160405190810160405280929190818152602001828054801561158c57602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831161156e575b505050505082848761347c565b6001600455949350505050565b6000546001600160a01b03163314806115c95750600354600160a01b900460ff16155b61160e576040805162461bcd60e51b815260206004820152601160248201527011549497d050d0d154d4d7d11153925151607a1b604482015290519081900360640190fd5b600061162c6f436f6e7472616374526567697374727960801b613069565b6002549091506001600160a01b0380831691161480159061165557506001600160a01b03811615155b61169d576040805162461bcd60e51b81526020600482015260146024820152734552525f494e56414c49445f524547495354525960601b604482015290519081900360640190fd5b60006001600160a01b0316816001600160a01b031663bb34534c6f436f6e7472616374526567697374727960801b6040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b1580156116ff57600080fd5b505afa158015611713573d6000803e3d6000fd5b505050506040513d602081101561172957600080fd5b50516001600160a01b0316141561177e576040805162461bcd60e51b81526020600482015260146024820152734552525f494e56414c49445f524547495354525960601b604482015290519081900360640190fd5b60028054600380546001600160a01b038084166001600160a01b0319928316179092559091169216919091179055565b6117b6613016565b806117c081613687565b50600680546001600160a01b0319166001600160a01b0392909216919091179055565b600080600560009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561183457600080fd5b505afa158015611848573d6000803e3d6000fd5b505050506040513d602081101561185e57600080fd5b50519050600061187d6c42616e636f72466f726d756c6160981b613069565b6001600160a01b03868116600090815260086020908152604091829020546009548351632f55bdb560e01b815260048101899052602481019290925263ffffffff16604482015260648101899052915193945091841692632f55bdb592608480840193919291829003018186803b1580156118f757600080fd5b505afa15801561190b573d6000803e3d6000fd5b505050506040513d602081101561192157600080fd5b505195945050505050565b602981565b600954600160401b900463ffffffff1681565b61194c613434565b6002600455611959613016565b600061197e762130b731b7b921b7b73b32b93a32b92ab833b930b232b960491b613069565b6001600160a01b038516600090815260086020526040902060010154909150600160301b900460ff1615806119b857506119b66112c4565b155b806119d057506000546001600160a01b038281169116145b611a15576040805162461bcd60e51b815260206004820152601160248201527011549497d050d0d154d4d7d11153925151607a1b604482015290519081900360640190fd5b611a208484846136db565b6001600160a01b038416600090815260086020526040902060010154600160301b900460ff1615611a5457611a548461370c565b505060016004555050565b6003546001600160a01b031681565b611a76613016565b762130b731b7b921b7b73b32b93a32b92ab833b930b232b960491b611a9a816137e5565b6005546040805163f2fde38b60e01b81526001600160a01b0385811660048301529151919092169163f2fde38b91602480830192600092919082900301818387803b158015611ae857600080fd5b505af1158015611afc573d6000803e3d6000fd5b505050505050565b611b0c613434565b6002600455611b19613016565b6000805160206151f3833981519152611b318161322c565b6000611b56762130b731b7b921b7b73b32b93a32b92ab833b930b232b960491b613069565b9050611b606112c4565b1580611b7957506000546001600160a01b038281169116145b611bbe576040805162461bcd60e51b815260206004820152601160248201527011549497d050d0d154d4d7d11153925151607a1b604482015290519081900360640190fd5b6040516001600160a01b038416904780156108fc02916000818181858888f19350505050158015611bf3573d6000803e3d6000fd5b50611c0b6000805160206151f383398151915261370c565b5050600160045550565b611c1d613016565b611c278282613847565b6007546002148015611c8357506002620f42400463ffffffff16600860006007600081548110611c5357fe5b60009182526020808320909101546001600160a01b0316835282019290925260400190206001015463ffffffff16145b8015611cd957506002620f42400463ffffffff16600860006007600181548110611ca957fe5b60009182526020808320909101546001600160a01b0316835282019290925260400190206001015463ffffffff16145b600a805460ff19169115159190911790555050565b6000611cf861233c565b905090565b6001546001600160a01b03163314611d50576040805162461bcd60e51b815260206004820152601160248201527011549497d050d0d154d4d7d11153925151607a1b604482015290519081900360640190fd5b600154600080546040516001600160a01b0393841693909116917f343765429aea5a34b3ff6a3785a98a5abb2597aca87bfbb58632c173d585373a91a360018054600080546001600160a01b03199081166001600160a01b03841617909155169055565b6002546001600160a01b031681565b6000611dcd613434565b6002600455611dda613a69565b611de5848484613ab1565b60005b8451811015611e95576000805160206151f38339815191526001600160a01b0316858281518110611e1557fe5b60200260200101516001600160a01b03161415611e8d5734848281518110611e3957fe5b602002602001015114611e8d576040805162461bcd60e51b815260206004820152601760248201527608aa4a4be8aa890be829a9eaa9ca8be9a92a69a82a8869604b1b604482015290519081900360640190fd5b600101611de8565b503415611f25576000805160206151f383398151915260005260086020527f353c2eb9e53a4a4a6d45d72082ff2e9dc829d1125618772a83eb0e7f86632c4354600160301b900460ff16611f25576040805162461bcd60e51b81526020600482015260126024820152714552525f4e4f5f4554485f5245534552564560701b604482015290519081900360640190fd5b600554604080516318160ddd60e01b815290516000926001600160a01b0316916318160ddd916004808301926020929190829003018186803b158015611f6a57600080fd5b505afa158015611f7e573d6000803e3d6000fd5b505050506040513d6020811015611f9457600080fd5b505190506000611fa5868684613d46565b905083811015611ff1576040805162461bcd60e51b81526020600482015260126024820152714552525f52455455524e5f544f4f5f4c4f5760701b604482015290519081900360640190fd5b6005546040805163219e412d60e21b81523360048201526024810184905290516001600160a01b039092169163867904b49160448082019260009290919082900301818387803b15801561204457600080fd5b505af1158015612058573d6000803e3d6000fd5b5050600160045550909695505050505050565b606080845167ffffffffffffffff8111801561208657600080fd5b506040519080825280602002602001820160405280156120b0578160200160208202803683370190505b5090506000600560009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561210357600080fd5b505afa158015612117573d6000803e3d6000fd5b505050506040513d602081101561212d57600080fd5b50519050600061214c6c42616e636f72466f726d756c6160981b613069565b90506000816001600160a01b0316632f55bdb584600860008c8c8151811061217057fe5b6020908102919091018101516001600160a01b031682528181019290925260409081016000205460095482516001600160e01b031960e088901b1681526004810195909552602485019190915263ffffffff166044840152606483018b905251608480840193829003018186803b1580156121ea57600080fd5b505afa1580156121fe573d6000803e3d6000fd5b505050506040513d602081101561221457600080fd5b5051905060005b845181101561230b57826001600160a01b031663ebbb215885600860008d868151811061224457fe5b6020908102919091018101516001600160a01b031682528181019290925260409081016000205460095482516001600160e01b031960e088901b1681526004810195909552602485019190915263ffffffff1660448401526064830187905251608480840193829003018186803b1580156122be57600080fd5b505afa1580156122d2573d6000803e3d6000fd5b505050506040513d60208110156122e857600080fd5b505185518690839081106122f857fe5b602090810291909101015260010161221b565b509293505050505b9392505050565b6000546001600160a01b031681565b600954600160201b900463ffffffff1681565b60075490565b60008061234d613a69565b846123578161322c565b846123618161322c565b856001600160a01b0316876001600160a01b031614156123c1576040805162461bcd60e51b815260206004820152601660248201527511549497d4d0535157d4d3d55490d157d5105491d15560521b604482015290519081900360640190fd5b60006123dc6c42616e636f72466f726d756c6160981b613069565b6001600160a01b03166394491fab6123f38a612c76565b6001600160a01b038b1660009081526008602052604090206001015463ffffffff1661241e8b612c76565b6001600160a01b038c166000908152600860209081526040918290206001015482516001600160e01b031960e089901b168152600481019690965263ffffffff94851660248701526044860193909352929091166064840152608483018b90525160a480840193829003018186803b15801561249957600080fd5b505afa1580156124ad573d6000803e3d6000fd5b505050506040513d60208110156124c357600080fd5b5051905060006124d282613d71565b9182900399919850909650505050505050565b60606124ef613434565b60026004556124fc613a69565b612507838386613ab1565b600554604080516318160ddd60e01b815290516000926001600160a01b0316916318160ddd916004808301926020929190829003018186803b15801561254c57600080fd5b505afa158015612560573d6000803e3d6000fd5b505050506040513d602081101561257657600080fd5b50516005546040805163a24835d160e01b81523360048201526024810189905290519293506001600160a01b039091169163a24835d19160448082019260009290919082900301818387803b1580156125ce57600080fd5b505af11580156125e2573d6000803e3d6000fd5b505050506125f28484838861347c565b600160045595945050505050565b612608613016565b600354600280546001600160a01b0319166001600160a01b03909216919091179055565b600181565b6006546001600160a01b031681565b600061264a613434565b6002600455612657613da2565b6000805160206151f383398151915260005260086020526000805160206151d3833981519152546126889034613de2565b6000805160206151f38339815191526000908152600860209081526000805160206151d383398151915292909255600554604080516318160ddd60e01b8152905192936001600160a01b03909216926318160ddd92600480840193919291829003018186803b1580156126fa57600080fd5b505afa15801561270e573d6000803e3d6000fd5b505050506040513d602081101561272457600080fd5b5051905060006127436c42616e636f72466f726d756c6160981b613069565b60075490915060005b81811015612a2d5760006007828154811061276357fe5b60009182526020808320909101546001600160a01b03908116808452600883526040808520546009548251631d77642b60e31b8152600481018d90526024810183905263ffffffff9091166044820152606481018e9052915192965094939289169263ebbb215892608480840193829003018186803b1580156127e557600080fd5b505afa1580156127f9573d6000803e3d6000fd5b505050506040513d602081101561280f57600080fd5b505190506001600160a01b0383166000805160206151f3833981519152141561295a578034111561286f5760405133903483900380156108fc02916000818181858888f19350505050158015612869573d6000803e3d6000fd5b50612955565b803410156129555734156128c2576040805162461bcd60e51b81526020600482015260156024820152744552525f494e56414c49445f4554485f56414c554560581b604482015290519081900360640190fd5b6009546128e190600160601b90046001600160a01b0316333084613e2f565b6009600c9054906101000a90046001600160a01b03166001600160a01b0316632e1a7d4d826040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b15801561293c57600080fd5b505af1158015612950573d6000803e3d6000fd5b505050505b612966565b61296683333084613e2f565b60006129728383613f9a565b6001600160a01b038516600090815260086020526040812082905590915061299a898c613f9a565b604080518581526020810185905280820183905290519192506001600160a01b0387169133917f4a1a2a6176e9646d9e3157f7c2ab3c499f18337c0b0828cfb28e0a61de4a11f7919081900360600190a36001600160a01b038516600090815260086020526040902060010154612a1c9082908790859063ffffffff16613fe3565b50506001909301925061274c915050565b506005546040805163219e412d60e21b81523360048201526024810188905290516001600160a01b039092169163867904b49160448082019260009290919082900301818387803b158015612a8157600080fd5b505af1158015612a95573d6000803e3d6000fd5b50506001600455509495945050505050565b612aaf613016565b612ab7614052565b6005546001906001600160a01b0316612ace611384565b61ffff167f6b08c2e2c9969e55a647a764db9b554d64dc42f1a704da11a6d5b129ad163f2c60405160405180910390a4565b60078181548110612b0d57fe5b6000918252602090912001546001600160a01b0316905081565b6005546001600160a01b031681565b6001546001600160a01b031681565b612b4d613016565b6000612b72762130b731b7b921b7b73b32b93a32b92ab833b930b232b960491b613069565b6005549091506000906001600160a01b0316612b8c611384565b61ffff167f6b08c2e2c9969e55a647a764db9b554d64dc42f1a704da11a6d5b129ad163f2c60405160405180910390a4612bc581612f89565b6040805163487ac64b60e11b81526029600482015290516001600160a01b038316916390f58c9691602480830192600092919082900301818387803b158015612c0d57600080fd5b505af1158015612c21573d6000803e3d6000fd5b505050506112c1611cfd565b6008602052600090815260409020805460019091015463ffffffff81169060ff600160201b8204811691650100000000008104821691600160301b9091041685565b6000611176825b600081612c828161322c565b50506001600160a01b031660009081526008602052604090205490565b600b54600c5482565b6000612cb2613434565b60026004556c42616e636f724e6574776f726b60981b612cd1816137e5565b856001600160a01b0316876001600160a01b03161415612d31576040805162461bcd60e51b815260206004820152601660248201527511549497d4d0535157d4d3d55490d157d5105491d15560521b604482015290519081900360640190fd5b6006546001600160a01b03161580612e3e575060065460408051633af32abf60e01b81526001600160a01b03878116600483015291519190921691633af32abf916024808301926020929190829003018186803b158015612d9157600080fd5b505afa158015612da5573d6000803e3d6000fd5b505050506040513d6020811015612dbb57600080fd5b50518015612e3e575060065460408051633af32abf60e01b81526001600160a01b03868116600483015291519190921691633af32abf916024808301926020929190829003018186803b158015612e1157600080fd5b505afa158015612e25573d6000803e3d6000fd5b505050506040513d6020811015612e3b57600080fd5b50515b612e85576040805162461bcd60e51b815260206004820152601360248201527211549497d393d517d5d2125511531254d51151606a1b604482015290519081900360640190fd5b612e9287878787876140b6565b6001600455979650505050505050565b612eaa613016565b60095463ffffffff600160201b90910481169082161115612f12576040805162461bcd60e51b815260206004820152601a60248201527f4552525f494e56414c49445f434f4e56455253494f4e5f464545000000000000604482015290519081900360640190fd5b6009546040805163ffffffff600160401b90930483168152918316602083015280517f81cd2ffb37dd237c0e4e2a3de5265fcf9deb43d3e7801e80db9f1ccfba7ee6009281900390910190a16009805463ffffffff909216600160401b026bffffffff000000000000000019909216919091179055565b612f91613016565b6000546001600160a01b0382811691161415612fe5576040805162461bcd60e51b815260206004820152600e60248201526d22a9292fa9a0a6a2afa7aba722a960911b604482015290519081900360640190fd5b600180546001600160a01b0319166001600160a01b0392909216919091179055565b6005546001600160a01b031690565b6000546001600160a01b03163314611379576040805162461bcd60e51b815260206004820152601160248201527011549497d050d0d154d4d7d11153925151607a1b604482015290519081900360640190fd5b60025460408051632ecd14d360e21b81526004810184905290516000926001600160a01b03169163bb34534c916024808301926020929190829003018186803b1580156130b557600080fd5b505afa1580156130c9573d6000803e3d6000fd5b505050506040513d60208110156130df57600080fd5b505192915050565b606080845167ffffffffffffffff8111801561310257600080fd5b5060405190808252806020026020018201604052801561312c578160200160208202803683370190505b50905060005b815181101561322257836001600160a01b0316638074590a86600860008a868151811061315b57fe5b6020908102919091018101516001600160a01b031682528181019290925260409081016000205460095482516001600160e01b031960e088901b1681526004810195909552602485019190915263ffffffff166044840152606483018c905251608480840193829003018186803b1580156131d557600080fd5b505afa1580156131e9573d6000803e3d6000fd5b505050506040513d60208110156131ff57600080fd5b5051825183908390811061320f57fe5b6020908102919091010152600101613132565b5095945050505050565b6001600160a01b038116600090815260086020526040902060010154600160301b900460ff166112c1576040805162461bcd60e51b81526020600482015260136024820152724552525f494e56414c49445f5245534552564560681b604482015290519081900360640190fd5b6132a16151b8565b6000600d546132ae61437a565b039050806132d457505060408051808201909152600b548152600c5460208201526110c5565b60006008600060076001815481106132e857fe5b60009182526020808320909101546001600160a01b0316835282019290925260400181205460078054919350600891839190829061332257fe5b60009182526020808320909101546001600160a01b031683528201929092526040019020549050610258831061336d5760408051808201909152918252602082015291506110c59050565b6133756151b8565b5060408051808201909152600b548152600c546020820181905260009061339c908561437e565b82519091506000906133ae908561437e565b905060006133d46133bf848961437e565b6133ce846102588b900361437e565b90613f9a565b905060006133fb6102586133f588886020015161437e90919063ffffffff16565b9061437e565b905061341582826c0c9f2c9cd04674edea400000006143dc565b6040805180820190915291825260208201529850505050505050505090565b600160045414611379576040805162461bcd60e51b815260206004820152600e60248201526d4552525f5245454e5452414e435960901b604482015290519081900360640190fd5b6060613486613da2565b60006134a16c42616e636f72466f726d756c6160981b613069565b905060006134af8585613de2565b905060606134bf858988866130e7565b905060005b885181101561367b5760008982815181106134db57fe5b6020026020010151905060008383815181106134f357fe5b6020026020010151905089838151811061350957fe5b602002602001015181101561355e576040805162461bcd60e51b815260206004820152601660248201527511549497d6915493d7d5105491d15517d05353d5539560521b604482015290519081900360640190fd5b6001600160a01b0382166000908152600860205260408120546135819083613de2565b6001600160a01b03841660008181526008602052604090208290559091506000805160206151f383398151915214156135e757604051339083156108fc029084906000818181858888f193505050501580156135e1573d6000803e3d6000fd5b506135f2565b6135f283338461440e565b604080518381526020810183905280820188905290516001600160a01b0385169133917fbc7d19d505c7ec4db83f3b51f19fb98c4c8a99922e7839d1ee608dfbee29501b9181900360600190a36001600160a01b0383166000908152600860205260409020600101546136709087908590849063ffffffff16613fe3565b5050506001016134c4565b50979650505050505050565b6001600160a01b0381163014156112c1576040805162461bcd60e51b815260206004820152601360248201527222a9292fa0a2222922a9a9afa4a9afa9a2a62360691b604482015290519081900360640190fd5b6136e3613016565b826136ed8161456e565b826136f78161456e565b8361370181613687565b611afc86868661440e565b806137168161322c565b6001600160a01b0382166000805160206151f38339815191521415613755576001600160a01b03821660009081526008602052604090204790556137e1565b604080516370a0823160e01b815230600482015290516001600160a01b038416916370a08231916024808301926020929190829003018186803b15801561379b57600080fd5b505afa1580156137af573d6000803e3d6000fd5b505050506040513d60208110156137c557600080fd5b50516001600160a01b0383166000908152600860205260409020555b5050565b6137ee81613069565b6001600160a01b0316336001600160a01b0316146112c1576040805162461bcd60e51b815260206004820152601160248201527011549497d050d0d154d4d7d11153925151607a1b604482015290519081900360640190fd5b61384f613016565b6138576145bf565b816138618161456e565b8261386b81613687565b8261387581614606565b6005546001600160a01b038681169116148015906138b657506001600160a01b038516600090815260086020526040902060010154600160301b900460ff16155b6138fd576040805162461bcd60e51b81526020600482015260136024820152724552525f494e56414c49445f5245534552564560681b604482015290519081900360640190fd5b60095463ffffffff908116620f42400381169085161115613965576040805162461bcd60e51b815260206004820152601a60248201527f4552525f494e56414c49445f524553455256455f574549474854000000000000604482015290519081900360640190fd5b61ffff61397061233c565b61ffff16106139c2576040805162461bcd60e51b815260206004820152601960248201527811549497d253959053125117d49154d154959157d0d3d55395603a1b604482015290519081900360640190fd5b5050506001600160a01b0390911660008181526008602052604081208181556001908101805466ff0000000000001963ffffffff80881663ffffffff199384161791909116600160301b179092556007805493840181559093527fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c68890910180546001600160a01b031916909317909255600980548084169094019092169216919091179055565b613a716112c4565b611379576040805162461bcd60e51b815260206004820152600c60248201526b4552525f494e41435449564560a01b604482015290519081900360640190fd5b600754835160009182918114613b04576040805162461bcd60e51b81526020600482015260136024820152724552525f494e56414c49445f5245534552564560681b604482015290519081900360640190fd5b84518114613b4e576040805162461bcd60e51b815260206004820152601260248201527111549497d253959053125117d05353d5539560721b604482015290519081900360640190fd5b600092505b80831015613cff5760086000878581518110613b6b57fe5b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060010160069054906101000a900460ff16613beb576040805162461bcd60e51b81526020600482015260136024820152724552525f494e56414c49445f5245534552564560681b604482015290519081900360640190fd5b600091505b80821015613c4d57858281518110613c0457fe5b60200260200101516001600160a01b031660078481548110613c2257fe5b6000918252602090912001546001600160a01b03161415613c4257613c4d565b600190910190613bf0565b808210613c97576040805162461bcd60e51b81526020600482015260136024820152724552525f494e56414c49445f5245534552564560681b604482015290519081900360640190fd5b6000858481518110613ca557fe5b602002602001015111613cf4576040805162461bcd60e51b815260206004820152601260248201527111549497d253959053125117d05353d5539560721b604482015290519081900360640190fd5b600190920191613b53565b60008411611afc576040805162461bcd60e51b815260206004820152600f60248201526e11549497d6915493d7d05353d55395608a1b604482015290519081900360640190fd5b600081613d5e57613d578484614676565b9050612313565b613d69848484614795565b949350505050565b60095460009061117690620f424090613d9c908590600160401b900463ffffffff9081169061437e16565b90614ac2565b60075460005b818110156137e157613dda60078281548110613dc057fe5b6000918252602090912001546001600160a01b031661370c565b600101613da8565b600081831015613e29576040805162461bcd60e51b815260206004820152600d60248201526c4552525f554e444552464c4f5760981b604482015290519081900360640190fd5b50900390565b604080516001600160a01b0385811660248301528481166044830152606480830185905283518084039091018152608490920183526020820180516001600160e01b03166323b872dd60e01b17815292518251600094606094938a169392918291908083835b60208310613eb45780518252601f199092019160209182019101613e95565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114613f16576040519150601f19603f3d011682016040523d82523d6000602084013e613f1b565b606091505b5091509150818015613f49575080511580613f495750808060200190516020811015613f4657600080fd5b50515b611afc576040805162461bcd60e51b815260206004820152601860248201527f4552525f5452414e534645525f46524f4d5f4641494c45440000000000000000604482015290519081900360640190fd5b600082820183811015612313576040805162461bcd60e51b815260206004820152600c60248201526b4552525f4f564552464c4f5760a01b604482015290519081900360640190fd5b6005546001600160a01b0380851691167f77f29993cf2c084e726f7e802da0719d6a0ade3e204badc7a3ffd57ecb768c2461402185620f424061437e565b6140348863ffffffff8088169061437e16565b6040805192835260208301919091528051918290030190a350505050565b600161405c61233c565b61ffff16116140ae576040805162461bcd60e51b815260206004820152601960248201527811549497d253959053125117d49154d154959157d0d3d55395603a1b604482015290519081900360640190fd5b611379614b21565b600a5460009060ff1680156140d357506140ce61437a565b600d54105b156140f8576140e0613299565b8051600b5560200151600c556140f461437a565b600d555b600080614106888888612342565b915091508160001415614159576040805162461bcd60e51b815260206004820152601660248201527511549497d6915493d7d5105491d15517d05353d5539560521b604482015290519081900360640190fd5b61416287612c76565b821061416a57fe5b6001600160a01b0388166000805160206151f383398151915214156141dc578534146141d7576040805162461bcd60e51b815260206004820152601760248201527608aa4a4be8aa890be829a9eaa9ca8be9a92a69a82a8869604b1b604482015290519081900360640190fd5b6142b3565b3415801561426d57508561426a6141f28a612c76565b604080516370a0823160e01b815230600482015290516001600160a01b038d16916370a08231916024808301926020929190829003018186803b15801561423857600080fd5b505afa15801561424c573d6000803e3d6000fd5b505050506040513d602081101561426257600080fd5b505190613de2565b10155b6142b3576040805162461bcd60e51b815260206004820152601260248201527111549497d253959053125117d05353d5539560721b604482015290519081900360640190fd5b6142bc8861370c565b6001600160a01b0387166000908152600860205260409020546142df9083613de2565b6001600160a01b0388166000818152600860205260409020919091556000805160206151f3833981519152141561434c576040516001600160a01b0385169083156108fc029084906000818181858888f19350505050158015614346573d6000803e3d6000fd5b50614357565b61435787858461440e565b614365888887898686614bf5565b61436f8888614c5e565b509695505050505050565b4290565b60008261438d57506000611176565b8282028284828161439a57fe5b0414612313576040805162461bcd60e51b815260206004820152600c60248201526b4552525f4f564552464c4f5760a01b604482015290519081900360640190fd5b600080828511806143ec57508284115b15614405576143fc858585614e57565b915091506111f1565b50929391925050565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b178152925182516000946060949389169392918291908083835b6020831061448b5780518252601f19909201916020918201910161446c565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d80600081146144ed576040519150601f19603f3d011682016040523d82523d6000602084013e6144f2565b606091505b5091509150818015614520575080511580614520575080806020019051602081101561451d57600080fd5b50515b614567576040805162461bcd60e51b815260206004820152601360248201527211549497d514905394d1915497d19052531151606a1b604482015290519081900360640190fd5b5050505050565b6001600160a01b0381166112c1576040805162461bcd60e51b81526020600482015260136024820152724552525f494e56414c49445f4144445245535360681b604482015290519081900360640190fd5b6145c76112c4565b15611379576040805162461bcd60e51b815260206004820152600a6024820152694552525f41435449564560b01b604482015290519081900360640190fd5b60008163ffffffff161180156146255750620f424063ffffffff821611155b6112c1576040805162461bcd60e51b815260206004820152601a60248201527f4552525f494e56414c49445f524553455256455f574549474854000000000000604482015290519081900360640190fd5b60008061468283614e9c565b905060005b845181101561478d57600085828151811061469e57fe5b6020026020010151905060008583815181106146b657fe5b602002602001015190506000805160206151f38339815191526001600160a01b0316826001600160a01b0316146146f3576146f382333084613e2f565b6001600160a01b0382166000818152600860209081526040918290208490558151848152908101849052808201879052905133917f4a1a2a6176e9646d9e3157f7c2ab3c499f18337c0b0828cfb28e0a61de4a11f7919081900360600190a36001600160a01b0382166000908152600860205260409020600101546147839085908490849063ffffffff16613fe3565b5050600101614687565b509392505050565b600061479f613da2565b6000805160206151f383398151915260005260086020526000805160206151d3833981519152546147d09034613de2565b6000805160206151f3833981519152600090815260086020526000805160206151d3833981519152919091556148156c42616e636f72466f726d756c6160981b613069565b9050600061482582858888614eee565b905060006148338583613f9a565b905060005b8751811015614ab657600088828151811061484f57fe5b6020908102919091018101516001600160a01b038082166000908152600884526040808220546009548251631d77642b60e31b8152600481018f90526024810183905263ffffffff9091166044820152606481018b90529151949650949193928a169263ebbb215892608480840193919291829003018186803b1580156148d557600080fd5b505afa1580156148e9573d6000803e3d6000fd5b505050506040513d60208110156148ff57600080fd5b505190508061494e576040805162461bcd60e51b815260206004820152601660248201527511549497d6915493d7d5105491d15517d05353d5539560521b604482015290519081900360640190fd5b89848151811061495a57fe5b602002602001015181111561496b57fe5b6001600160a01b0383166000805160206151f38339815191521461499a5761499583333084613e2f565b614a05565b808a85815181106149a757fe5b60200260200101511115614a0557336001600160a01b03166108fc828c87815181106149cf57fe5b6020026020010151039081150290604051600060405180830381858888f19350505050158015614a03573d6000803e3d6000fd5b505b6000614a118383613f9a565b6001600160a01b03851660008181526008602090815260409182902084905581518681529081018490528082018a90529051929350909133917f4a1a2a6176e9646d9e3157f7c2ab3c499f18337c0b0828cfb28e0a61de4a11f7919081900360600190a36001600160a01b038416600090815260086020526040902060010154614aa69087908690849063ffffffff16613fe3565b5050600190920191506148389050565b50909695505050505050565b6000808211614b0d576040805162461bcd60e51b81526020600482015260126024820152714552525f4449564944455f42595f5a45524f60701b604482015290519081900360640190fd5b6000828481614b1857fe5b04949350505050565b614b29613016565b6000614b3361233c565b61ffff1611614b85576040805162461bcd60e51b815260206004820152601960248201527811549497d253959053125117d49154d154959157d0d3d55395603a1b604482015290519081900360640190fd5b600560009054906101000a90046001600160a01b03166001600160a01b03166379ba50976040518163ffffffff1660e01b8152600401600060405180830381600087803b158015614bd557600080fd5b505af1158015614be9573d6000803e3d6000fd5b50505050611379613da2565b600160ff1b8110614c0257fe5b604080518481526020810184905280820183905290516001600160a01b038087169288821692918a16917f276856b36cbc45526a0ba64f44611557a2a8b68662c5388e9fe6d72e86e1c8cb9181900360600190a4505050505050565b600554604080516318160ddd60e01b815290516000926001600160a01b0316916318160ddd916004808301926020929190829003018186803b158015614ca357600080fd5b505afa158015614cb7573d6000803e3d6000fd5b505050506040513d6020811015614ccd57600080fd5b505190506000614cdc84612c76565b90506000614ce984612c76565b6001600160a01b038087166000908152600860205260408082206001908101549389168352908220015492935063ffffffff91821692821691614d30908590859061437e16565b90506000614d478663ffffffff8086169061437e16565b604080518481526020810183905281519293506001600160a01b03808c1693908d16927f77f29993cf2c084e726f7e802da0719d6a0ade3e204badc7a3ffd57ecb768c24928290030190a3614d9e878a8887613fe3565b614daa87898786613fe3565b604080518881526020810188905263ffffffff86168183015290516001600160a01b038b16917f8a6a7f53b3c8fa1dc4b83e3f1be668c1b251ff8d44cdcb83eb3acec3fec6a788919081900360600190a2604080518881526020810187905263ffffffff85168183015290516001600160a01b038a16917f8a6a7f53b3c8fa1dc4b83e3f1be668c1b251ff8d44cdcb83eb3acec3fec6a788919081900360600190a2505050505050505050565b60008083851415614e6e57505060028104806111f1565b83851015614e81576143fc8585856150b4565b600080614e8f8688876150b4565b9890975095505050505050565b80516000908190815b81811015614ed557614ec9858281518110614ebc57fe5b6020026020010151615124565b90920191600101614ea5565b506001614ee28383615146565b03600a0a949350505050565b60008060015b8451811015614fbb57614f5e60086000878481518110614f1057fe5b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060000154858481518110614f4857fe5b602002602001015161437e90919063ffffffff16565b614fa960086000888681518110614f7157fe5b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060000154868481518110614f4857fe5b1015614fb3578091505b600101614ef4565b50856001600160a01b0316632f55bdb58660086000888681518110614fdc57fe5b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060000154600960009054906101000a900463ffffffff1687868151811061502757fe5b60200260200101516040518563ffffffff1660e01b8152600401808581526020018481526020018363ffffffff16815260200182815260200194505050505060206040518083038186803b15801561507e57600080fd5b505afa158015615092573d6000803e3d6000fd5b505050506040513d60208110156150a857600080fd5b50519695505050505050565b600080600083600019816150c457fe5b049050808611156150fd5760008160010187816150dd57fe5b0460010190508087816150ec57fe5b0496508086816150f857fe5b049550505b600061511487860261510f8989613f9a565b61515e565b9794889003965093945050505050565b600080825b801561513f5760019190910190600a9004615129565b5092915050565b6000816002810484018161515657fe5b049392505050565b600060028204820382848161516f57fe5b068161517757fe5b0482848161518157fe5b04019392505050565b6040805160a08101825260008082526020820181905291810182905260608101829052608081019190915290565b60405180604001604052806000815260200160008152509056fe353c2eb9e53a4a4a6d45d72082ff2e9dc829d1125618772a83eb0e7f86632c42000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeea26469706673582212207d4ffe0c158efb1275dc65d8adc06d493a356eeba3de7beddbd999d8aeda058764736f6c634300060c0033a26469706673582212207838310a225ff25bc9c002d9464173bc5331417c8b84452d7569a34aed70b15164736f6c634300060c0033
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 26 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.