Overview
ETH Balance
0 ETH
Eth Value
$0.00Token Holdings
More Info
Private Name Tags
ContractCreator
Latest 25 from a total of 6,434 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Transfer Positio... | 13772422 | 957 days ago | IN | 0 ETH | 0.00469719 | ||||
Add Liquidity | 13065213 | 1068 days ago | IN | 0 ETH | 0.01077701 | ||||
Add Liquidity | 12613331 | 1139 days ago | IN | 0 ETH | 0.00280963 | ||||
Add Liquidity | 12613330 | 1139 days ago | IN | 0 ETH | 0.00682063 | ||||
Add Liquidity | 12579381 | 1144 days ago | IN | 0 ETH | 0.00096872 | ||||
Remove Liquidity | 12541871 | 1150 days ago | IN | 0 ETH | 0.00101326 | ||||
Add Liquidity | 12534611 | 1151 days ago | IN | 0 ETH | 0.00097377 | ||||
Add Liquidity | 12527926 | 1152 days ago | IN | 0 ETH | 0.00194778 | ||||
Add Liquidity | 12515608 | 1154 days ago | IN | 0 ETH | 0.00106559 | ||||
Claim Balance | 12475134 | 1160 days ago | IN | 0 ETH | 0.00169788 | ||||
Add Liquidity | 12458691 | 1163 days ago | IN | 0 ETH | 0.00736227 | ||||
Remove Liquidity | 12457949 | 1163 days ago | IN | 0 ETH | 0.00283764 | ||||
Remove Liquidity | 12456514 | 1163 days ago | IN | 0 ETH | 0.00405378 | ||||
Add Liquidity | 12455423 | 1163 days ago | IN | 0 ETH | 0.00438358 | ||||
Add Liquidity | 12455277 | 1163 days ago | IN | 0 ETH | 0.00470592 | ||||
Add Liquidity | 12455215 | 1163 days ago | IN | 0 ETH | 0.00487005 | ||||
Remove Liquidity | 12454817 | 1163 days ago | IN | 0 ETH | 0.00378352 | ||||
Add Liquidity | 12454800 | 1163 days ago | IN | 1.82 ETH | 0.00597957 | ||||
Add Liquidity | 12454037 | 1163 days ago | IN | 0 ETH | 0.003692 | ||||
Remove Liquidity | 12453919 | 1163 days ago | IN | 0 ETH | 0.00668873 | ||||
Remove Liquidity | 12453795 | 1163 days ago | IN | 0 ETH | 0.00574285 | ||||
Remove Liquidity | 12453567 | 1163 days ago | IN | 0 ETH | 0.0072968 | ||||
Add Liquidity | 12453437 | 1163 days ago | IN | 0 ETH | 0.08926769 | ||||
Transfer Wallet ... | 12453416 | 1163 days ago | IN | 0 ETH | 0.00839498 | ||||
Transfer Store O... | 12453415 | 1163 days ago | IN | 0 ETH | 0.00839857 |
Latest 25 internal transactions (View All)
Advanced mode:
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
12453402 | 1163 days ago | 8.21900245 ETH | ||||
12453402 | 1163 days ago | 8.21900245 ETH | ||||
12452702 | 1164 days ago | 2.75184353 ETH | ||||
12452571 | 1164 days ago | 18 ETH | ||||
12450701 | 1164 days ago | 1 ETH | ||||
12449977 | 1164 days ago | 116.83325678 ETH | ||||
12449977 | 1164 days ago | 116.83325678 ETH | ||||
12449005 | 1164 days ago | 10 ETH | ||||
12448207 | 1164 days ago | 6.06385144 ETH | ||||
12448207 | 1164 days ago | 6.06385144 ETH | ||||
12447587 | 1164 days ago | 9.5 ETH | ||||
12447193 | 1164 days ago | 15.54936688 ETH | ||||
12447193 | 1164 days ago | 15.54936688 ETH | ||||
12445940 | 1165 days ago | 63.78993341 ETH | ||||
12445940 | 1165 days ago | 63.78993341 ETH | ||||
12445631 | 1165 days ago | 3 ETH | ||||
12443712 | 1165 days ago | 6.06215116 ETH | ||||
12443712 | 1165 days ago | 6.06215116 ETH | ||||
12443495 | 1165 days ago | 1.01415357 ETH | ||||
12443495 | 1165 days ago | 1.01415357 ETH | ||||
12443088 | 1165 days ago | 17.7 ETH | ||||
12442394 | 1165 days ago | 9.91 ETH | ||||
12442307 | 1165 days ago | 7.4 ETH | ||||
12441966 | 1165 days ago | 13.8112158 ETH | ||||
12441966 | 1165 days ago | 13.8112158 ETH |
Loading...
Loading
Contract Name:
LiquidityProtection
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 2021-04-22 */ // File: @openzeppelin/contracts/math/SafeMath.sol // SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; /** * @dev Wrappers over Solidity's arithmetic operations with added overflow * checks. * * Arithmetic operations in Solidity wrap on overflow. This can easily result * in bugs, because programmers usually assume that an overflow raises an * error, which is the standard behavior in high level programming languages. * `SafeMath` restores this intuition by reverting the transaction when an * operation overflows. * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. */ library SafeMath { /** * @dev Returns the addition of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { uint256 c = a + b; if (c < a) return (false, 0); return (true, c); } /** * @dev Returns the substraction of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { if (b > a) return (false, 0); return (true, a - b); } /** * @dev Returns the multiplication of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) return (true, 0); uint256 c = a * b; if (c / a != b) return (false, 0); return (true, c); } /** * @dev Returns the division of two unsigned integers, with a division by zero flag. * * _Available since v3.4._ */ function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { if (b == 0) return (false, 0); return (true, a / b); } /** * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. * * _Available since v3.4._ */ function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { if (b == 0) return (false, 0); return (true, a % b); } /** * @dev Returns the addition of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * * - Addition cannot overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, "SafeMath: addition overflow"); return c; } /** * @dev Returns the subtraction of two unsigned integers, reverting on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { require(b <= a, "SafeMath: subtraction overflow"); return a - b; } /** * @dev Returns the multiplication of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * * - Multiplication cannot overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { if (a == 0) return 0; uint256 c = a * b; require(c / a == b, "SafeMath: multiplication overflow"); return c; } /** * @dev Returns the integer division of two unsigned integers, reverting on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { require(b > 0, "SafeMath: division by zero"); return a / b; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * reverting when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { require(b > 0, "SafeMath: modulo by zero"); return a % b; } /** * @dev Returns the subtraction of two unsigned integers, reverting with custom message on * overflow (when the result is negative). * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {trySub}. * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b <= a, errorMessage); return a - b; } /** * @dev Returns the integer division of two unsigned integers, reverting with custom message on * division by zero. The result is rounded towards zero. * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {tryDiv}. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b > 0, errorMessage); return a / b; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * reverting with custom message when dividing by zero. * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {tryMod}. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b > 0, errorMessage); return a % b; } } // File: @openzeppelin/contracts/utils/Address.sol pragma solidity >=0.6.2 <0.8.0; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize, which returns 0 for contracts in // construction, since the code is only stored at the end of the // constructor execution. uint256 size; // solhint-disable-next-line no-inline-assembly assembly { size := extcodesize(account) } return size > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); // solhint-disable-next-line avoid-low-level-calls, avoid-call-value (bool success, ) = recipient.call{ value: amount }(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain`call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); require(isContract(target), "Address: call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.call{ value: value }(data); return _verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) { require(isContract(target), "Address: static call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.staticcall(data); return _verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { require(isContract(target), "Address: delegate call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.delegatecall(data); return _verifyCallResult(success, returndata, errorMessage); } function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) { if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly // solhint-disable-next-line no-inline-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } } // File: @openzeppelin/contracts/token/ERC20/IERC20.sol pragma solidity >=0.6.0 <0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `recipient`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address recipient, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `sender` to `recipient` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); } // File: @bancor/token-governance/contracts/IClaimable.sol pragma solidity 0.6.12; /// @title Claimable contract interface interface IClaimable { function owner() external view returns (address); function transferOwnership(address newOwner) external; function acceptOwnership() external; } // File: @bancor/token-governance/contracts/IMintableToken.sol pragma solidity 0.6.12; /// @title Mintable Token interface interface IMintableToken is IERC20, IClaimable { function issue(address to, uint256 amount) external; function destroy(address from, uint256 amount) external; } // File: @bancor/token-governance/contracts/ITokenGovernance.sol pragma solidity 0.6.12; /// @title The interface for mintable/burnable token governance. interface ITokenGovernance { // The address of the mintable ERC20 token. function token() external view returns (IMintableToken); /// @dev Mints new tokens. /// /// @param to Account to receive the new amount. /// @param amount Amount to increase the supply by. /// function mint(address to, uint256 amount) external; /// @dev Burns tokens from the caller. /// /// @param amount Amount to decrease the supply by. /// function burn(uint256 amount) external; } // File: solidity/contracts/utility/interfaces/ICheckpointStore.sol pragma solidity 0.6.12; /** * @dev Checkpoint store contract interface */ interface ICheckpointStore { function addCheckpoint(address _address) external; function addPastCheckpoint(address _address, uint256 _time) external; function addPastCheckpoints(address[] calldata _addresses, uint256[] calldata _times) external; function checkpoint(address _address) external view returns (uint256); } // File: solidity/contracts/utility/MathEx.sol pragma solidity 0.6.12; /** * @dev This library provides a set of complex math operations. */ library MathEx { uint256 private constant MAX_EXP_BIT_LEN = 4; uint256 private constant MAX_EXP = 2**MAX_EXP_BIT_LEN - 1; uint256 private constant MAX_UINT128 = 2**128 - 1; /** * @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 returns the smallest integer larger than or equal to the square root of a positive integer * * @param _num a positive integer * * @return the smallest integer larger than or equal to the square root of the positive integer */ function ceilSqrt(uint256 _num) internal pure returns (uint256) { uint256 x = floorSqrt(_num); return x * x == _num ? x : x + 1; } /** * @dev computes a powered ratio * * @param _n ratio numerator * @param _d ratio denominator * @param _exp ratio exponent * * @return powered ratio's numerator and denominator */ function poweredRatio( uint256 _n, uint256 _d, uint256 _exp ) internal pure returns (uint256, uint256) { require(_exp <= MAX_EXP, "ERR_EXP_TOO_LARGE"); uint256[MAX_EXP_BIT_LEN] memory ns; uint256[MAX_EXP_BIT_LEN] memory ds; (ns[0], ds[0]) = reducedRatio(_n, _d, MAX_UINT128); for (uint256 i = 0; (_exp >> i) > 1; i++) { (ns[i + 1], ds[i + 1]) = reducedRatio(ns[i] ** 2, ds[i] ** 2, MAX_UINT128); } uint256 n = 1; uint256 d = 1; for (uint256 i = 0; (_exp >> i) > 0; i++) { if (((_exp >> i) & 1) > 0) { (n, d) = reducedRatio(n * ns[i], d * ds[i], MAX_UINT128); } } return (n, d); } /** * @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) { (uint256 n, uint256 d) = (_n, _d); if (n > _max || d > _max) { (n, d) = normalizedRatio(n, d, _max); } if (n != d) { return (n, d); } return (1, 1); } /** * @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 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; // we can now safely compute `_a * _scale` _b /= c; } if (_a != _b) { uint256 n = _a * _scale; uint256 d = _a + _b; // can overflow if (d >= _a) { // no overflow in `_a + _b` uint256 x = roundDiv(n, d); // we can now safely compute `_scale - x` uint256 y = _scale - x; return (x, y); } if (n < _b - (_b - _a) / 2) { return (0, _scale); // `_a * _scale < (_a + _b) / 2 < MAX_UINT256 < _a + _b` } return (1, _scale - 1); // `(_a + _b) / 2 < _a * _scale < MAX_UINT256 < _a + _b` } return (_scale / 2, _scale / 2); // allow reduction to `(1, 1)` in the calling function } /** * @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; } /** * @dev returns the larger of two values * * @param _val1 the first value * @param _val2 the second value */ function max(uint256 _val1, uint256 _val2) internal pure returns (uint256) { return _val1 > _val2 ? _val1 : _val2; } } // File: solidity/contracts/utility/ReentrancyGuard.sol pragma solidity 0.6.12; /** * @dev This contract provides protection against 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/Types.sol pragma solidity 0.6.12; /** * @dev This contract provides types which can be used by various contracts. */ struct Fraction { uint256 n; // numerator uint256 d; // denominator } // File: solidity/contracts/utility/Time.sol pragma solidity 0.6.12; /* Time implementing contract */ contract Time { /** * @dev returns the current time */ function time() internal view virtual returns (uint256) { return block.timestamp; } } // File: solidity/contracts/utility/Utils.sol pragma solidity 0.6.12; /** * @dev Utilities & Common Modifiers */ contract Utils { uint32 internal constant PPM_RESOLUTION = 1000000; // 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"); } // ensures that the portion is valid modifier validPortion(uint32 _portion) { _validPortion(_portion); _; } // error message binary size optimization function _validPortion(uint32 _portion) internal pure { require(_portion > 0 && _portion <= PPM_RESOLUTION, "ERR_INVALID_PORTION"); } // validates an external address - currently only checks that it isn't null or this modifier validExternalAddress(address _address) { _validExternalAddress(_address); _; } // error message binary size optimization function _validExternalAddress(address _address) internal view { require(_address != address(0) && _address != address(this), "ERR_INVALID_EXTERNAL_ADDRESS"); } // ensures that the fee is valid modifier validFee(uint32 fee) { _validFee(fee); _; } // error message binary size optimization function _validFee(uint32 fee) internal pure { require(fee <= PPM_RESOLUTION, "ERR_INVALID_FEE"); } } // File: solidity/contracts/utility/interfaces/IOwned.sol 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/utility/Owned.sol pragma solidity 0.6.12; /** * @dev This contract 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() public override { require(msg.sender == newOwner, "ERR_ACCESS_DENIED"); emit OwnerUpdate(owner, newOwner); owner = newOwner; newOwner = address(0); } } // File: solidity/contracts/converter/interfaces/IConverterAnchor.sol pragma solidity 0.6.12; /* Converter Anchor interface */ interface IConverterAnchor is IOwned { } // File: solidity/contracts/token/interfaces/IDSToken.sol pragma solidity 0.6.12; /* DSToken interface */ interface IDSToken is IConverterAnchor, IERC20 { function issue(address _to, uint256 _amount) external; function destroy(address _from, uint256 _amount) external; } // File: solidity/contracts/token/interfaces/IReserveToken.sol pragma solidity 0.6.12; /** * @dev This contract is used to represent reserve tokens, which are tokens that can either be regular ERC20 tokens or * native ETH (represented by the NATIVE_TOKEN_ADDRESS address) * * Please note that this interface is intentionally doesn't inherit from IERC20, so that it'd be possible to effectively * override its balanceOf() function in the ReserveToken library */ interface IReserveToken { } // File: @openzeppelin/contracts/token/ERC20/SafeERC20.sol pragma solidity >=0.6.0 <0.8.0; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using SafeMath for uint256; using Address for address; function safeTransfer(IERC20 token, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove(IERC20 token, address spender, uint256 value) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' // solhint-disable-next-line max-line-length require((value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender).add(value); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero"); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional // solhint-disable-next-line max-line-length require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } } // File: solidity/contracts/token/SafeERC20Ex.sol pragma solidity 0.6.12; /** * @dev Extends the SafeERC20 library with additional operations */ library SafeERC20Ex { using SafeERC20 for IERC20; /** * @dev ensures that the spender has sufficient allowance * * @param token the address of the token to ensure * @param spender the address allowed to spend * @param amount the allowed amount to spend */ function ensureApprove( IERC20 token, address spender, uint256 amount ) internal { if (amount == 0) { return; } uint256 allowance = token.allowance(address(this), spender); if (allowance >= amount) { return; } if (allowance > 0) { token.safeApprove(spender, 0); } token.safeApprove(spender, amount); } } // File: solidity/contracts/token/ReserveToken.sol pragma solidity 0.6.12; /** * @dev This library implements ERC20 and SafeERC20 utilities for reserve tokens, which can be either ERC20 tokens or ETH */ library ReserveToken { using SafeERC20 for IERC20; using SafeERC20Ex for IERC20; // the address that represents an ETH reserve IReserveToken public constant NATIVE_TOKEN_ADDRESS = IReserveToken(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE); /** * @dev returns whether the provided token represents an ERC20 or ETH reserve * * @param reserveToken the address of the reserve token * * @return whether the provided token represents an ERC20 or ETH reserve */ function isNativeToken(IReserveToken reserveToken) internal pure returns (bool) { return reserveToken == NATIVE_TOKEN_ADDRESS; } /** * @dev returns the balance of the reserve token * * @param reserveToken the address of the reserve token * @param account the address of the account to check * * @return the balance of the reserve token */ function balanceOf(IReserveToken reserveToken, address account) internal view returns (uint256) { if (isNativeToken(reserveToken)) { return account.balance; } return toIERC20(reserveToken).balanceOf(account); } /** * @dev transfers a specific amount of the reserve token * * @param reserveToken the address of the reserve token * @param to the destination address to transfer the amount to * @param amount the amount to transfer */ function safeTransfer( IReserveToken reserveToken, address to, uint256 amount ) internal { if (amount == 0) { return; } if (isNativeToken(reserveToken)) { payable(to).transfer(amount); } else { toIERC20(reserveToken).safeTransfer(to, amount); } } /** * @dev transfers a specific amount of the reserve token from a specific holder using the allowance mechanism * this function ignores a reserve token which represents an ETH reserve * * @param reserveToken the address of the reserve token * @param from the source address to transfer the amount from * @param to the destination address to transfer the amount to * @param amount the amount to transfer */ function safeTransferFrom( IReserveToken reserveToken, address from, address to, uint256 amount ) internal { if (amount == 0 || isNativeToken(reserveToken)) { return; } toIERC20(reserveToken).safeTransferFrom(from, to, amount); } /** * @dev ensures that the spender has sufficient allowance * this function ignores a reserve token which represents an ETH reserve * * @param reserveToken the address of the reserve token * @param spender the address allowed to spend * @param amount the allowed amount to spend */ function ensureApprove( IReserveToken reserveToken, address spender, uint256 amount ) internal { if (isNativeToken(reserveToken)) { return; } toIERC20(reserveToken).ensureApprove(spender, amount); } /** * @dev utility function that converts an IReserveToken to an IERC20 * * @param reserveToken the address of the reserve token * * @return an IERC20 */ function toIERC20(IReserveToken reserveToken) private pure returns (IERC20) { return IERC20(address(reserveToken)); } } // 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( IReserveToken _sourceToken, IReserveToken _targetToken, uint256 _amount ) external view returns (uint256, uint256); function convert( IReserveToken _sourceToken, IReserveToken _targetToken, uint256 _amount, address _trader, address payable _beneficiary ) external payable returns (uint256); function conversionFee() external view returns (uint32); function maxConversionFee() external view returns (uint32); function reserveBalance(IReserveToken _reserveToken) external view returns (uint256); receive() external payable; function transferAnchorOwnership(address _newOwner) external; function acceptAnchorOwnership() external; function setConversionFee(uint32 _conversionFee) external; function addReserve(IReserveToken _token, uint32 _weight) external; function transferReservesOnUpgrade(address _newConverter) external; function onUpgradeComplete() external; // deprecated, backward compatibility function token() external view returns (IConverterAnchor); function transferTokenOwnership(address _newOwner) external; function acceptTokenOwnership() external; function connectors(IReserveToken _address) external view returns ( uint256, uint32, bool, bool, bool ); function getConnectorBalance(IReserveToken _connectorToken) external view returns (uint256); function connectorTokens(uint256 _index) external view returns (IReserveToken); function connectorTokenCount() external view returns (uint16); /** * @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 reserve token * @param _toToken target reserve token * @param _trader wallet that initiated the trade * @param _amount input amount in units of the source token * @param _return output amount minus conversion fee in units of the target token * @param _conversionFee conversion fee in units of the target token */ event Conversion( IReserveToken indexed _fromToken, IReserveToken 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 * * @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(address indexed _token1, address 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); } // File: solidity/contracts/converter/interfaces/IConverterRegistry.sol pragma solidity 0.6.12; interface IConverterRegistry { function getAnchorCount() external view returns (uint256); function getAnchors() external view returns (address[] memory); function getAnchor(uint256 _index) external view returns (IConverterAnchor); function isAnchor(address _value) external view returns (bool); function getLiquidityPoolCount() external view returns (uint256); function getLiquidityPools() external view returns (address[] memory); function getLiquidityPool(uint256 _index) external view returns (IConverterAnchor); function isLiquidityPool(address _value) external view returns (bool); function getConvertibleTokenCount() external view returns (uint256); function getConvertibleTokens() external view returns (address[] memory); function getConvertibleToken(uint256 _index) external view returns (IReserveToken); function isConvertibleToken(address _value) external view returns (bool); function getConvertibleTokenAnchorCount(IReserveToken _convertibleToken) external view returns (uint256); function getConvertibleTokenAnchors(IReserveToken _convertibleToken) external view returns (address[] memory); function getConvertibleTokenAnchor(IReserveToken _convertibleToken, uint256 _index) external view returns (IConverterAnchor); function isConvertibleTokenAnchor(IReserveToken _convertibleToken, address _value) external view returns (bool); function getLiquidityPoolByConfig( uint16 _type, IReserveToken[] memory _reserveTokens, uint32[] memory _reserveWeights ) external view returns (IConverterAnchor); } // File: solidity/contracts/liquidity-protection/interfaces/ILiquidityProtectionStore.sol pragma solidity 0.6.12; /* Liquidity Protection Store interface */ interface ILiquidityProtectionStore is IOwned { function withdrawTokens( IReserveToken _token, address _to, uint256 _amount ) external; function protectedLiquidity(uint256 _id) external view returns ( address, IDSToken, IReserveToken, uint256, uint256, uint256, uint256, uint256 ); function addProtectedLiquidity( address _provider, IDSToken _poolToken, IReserveToken _reserveToken, uint256 _poolAmount, uint256 _reserveAmount, uint256 _reserveRateN, uint256 _reserveRateD, uint256 _timestamp ) external returns (uint256); function updateProtectedLiquidityAmounts( uint256 _id, uint256 _poolNewAmount, uint256 _reserveNewAmount ) external; function removeProtectedLiquidity(uint256 _id) external; function lockedBalance(address _provider, uint256 _index) external view returns (uint256, uint256); function lockedBalanceRange( address _provider, uint256 _startIndex, uint256 _endIndex ) external view returns (uint256[] memory, uint256[] memory); function addLockedBalance( address _provider, uint256 _reserveAmount, uint256 _expirationTime ) external returns (uint256); function removeLockedBalance(address _provider, uint256 _index) external; function systemBalance(IReserveToken _poolToken) external view returns (uint256); function incSystemBalance(IReserveToken _poolToken, uint256 _poolAmount) external; function decSystemBalance(IReserveToken _poolToken, uint256 _poolAmount) external; } // File: solidity/contracts/liquidity-protection/interfaces/ILiquidityProtectionStats.sol pragma solidity 0.6.12; /* Liquidity Protection Stats interface */ interface ILiquidityProtectionStats { function increaseTotalAmounts( address provider, IDSToken poolToken, IReserveToken reserveToken, uint256 poolAmount, uint256 reserveAmount ) external; function decreaseTotalAmounts( address provider, IDSToken poolToken, IReserveToken reserveToken, uint256 poolAmount, uint256 reserveAmount ) external; function addProviderPool(address provider, IDSToken poolToken) external returns (bool); function removeProviderPool(address provider, IDSToken poolToken) external returns (bool); function totalPoolAmount(IDSToken poolToken) external view returns (uint256); function totalReserveAmount(IDSToken poolToken, IReserveToken reserveToken) external view returns (uint256); function totalProviderAmount( address provider, IDSToken poolToken, IReserveToken reserveToken ) external view returns (uint256); function providerPools(address provider) external view returns (IDSToken[] memory); } // File: solidity/contracts/liquidity-protection/interfaces/ILiquidityProtectionEventsSubscriber.sol pragma solidity 0.6.12; /** * @dev Liquidity protection events subscriber interface */ interface ILiquidityProtectionEventsSubscriber { function onAddingLiquidity( address provider, IConverterAnchor poolAnchor, IReserveToken reserveToken, uint256 poolAmount, uint256 reserveAmount ) external; function onRemovingLiquidity( uint256 id, address provider, IConverterAnchor poolAnchor, IReserveToken reserveToken, uint256 poolAmount, uint256 reserveAmount ) external; } // File: solidity/contracts/liquidity-protection/interfaces/ILiquidityProtectionSettings.sol pragma solidity 0.6.12; /* Liquidity Protection Store Settings interface */ interface ILiquidityProtectionSettings { function isPoolWhitelisted(IConverterAnchor poolAnchor) external view returns (bool); function poolWhitelist() external view returns (address[] memory); function subscribers() external view returns (address[] memory); function isPoolSupported(IConverterAnchor poolAnchor) external view returns (bool); function minNetworkTokenLiquidityForMinting() external view returns (uint256); function defaultNetworkTokenMintingLimit() external view returns (uint256); function networkTokenMintingLimits(IConverterAnchor poolAnchor) external view returns (uint256); function addLiquidityDisabled(IConverterAnchor poolAnchor, IReserveToken reserveToken) external view returns (bool); function minProtectionDelay() external view returns (uint256); function maxProtectionDelay() external view returns (uint256); function minNetworkCompensation() external view returns (uint256); function lockDuration() external view returns (uint256); function averageRateMaxDeviation() external view returns (uint32); } // File: solidity/contracts/liquidity-protection/interfaces/ILiquidityProtectionSystemStore.sol pragma solidity 0.6.12; /* Liquidity Protection System Store interface */ interface ILiquidityProtectionSystemStore { function systemBalance(IERC20 poolToken) external view returns (uint256); function incSystemBalance(IERC20 poolToken, uint256 poolAmount) external; function decSystemBalance(IERC20 poolToken, uint256 poolAmount) external; function networkTokensMinted(IConverterAnchor poolAnchor) external view returns (uint256); function incNetworkTokensMinted(IConverterAnchor poolAnchor, uint256 amount) external; function decNetworkTokensMinted(IConverterAnchor poolAnchor, uint256 amount) external; } // File: solidity/contracts/utility/interfaces/ITokenHolder.sol pragma solidity 0.6.12; /* Token Holder interface */ interface ITokenHolder is IOwned { receive() external payable; function withdrawTokens( IReserveToken reserveToken, address payable to, uint256 amount ) external; function withdrawTokensMultiple( IReserveToken[] calldata reserveTokens, address payable to, uint256[] calldata amounts ) external; } // File: solidity/contracts/liquidity-protection/interfaces/ILiquidityProtection.sol pragma solidity 0.6.12; /* Liquidity Protection interface */ interface ILiquidityProtection { function store() external view returns (ILiquidityProtectionStore); function stats() external view returns (ILiquidityProtectionStats); function settings() external view returns (ILiquidityProtectionSettings); function systemStore() external view returns (ILiquidityProtectionSystemStore); function wallet() external view returns (ITokenHolder); function addLiquidityFor( address owner, IConverterAnchor poolAnchor, IReserveToken reserveToken, uint256 amount ) external payable returns (uint256); function addLiquidity( IConverterAnchor poolAnchor, IReserveToken reserveToken, uint256 amount ) external payable returns (uint256); function removeLiquidity(uint256 id, uint32 portion) external; } // File: solidity/contracts/liquidity-protection/LiquidityProtection.sol pragma solidity 0.6.12; interface ILiquidityPoolConverter is IConverter { function addLiquidity( IReserveToken[] memory reserveTokens, uint256[] memory reserveAmounts, uint256 _minReturn ) external payable; function removeLiquidity( uint256 amount, IReserveToken[] memory reserveTokens, uint256[] memory _reserveMinReturnAmounts ) external; function recentAverageRate(IReserveToken reserveToken) external view returns (uint256, uint256); } /** * @dev This contract implements the liquidity protection mechanism. */ contract LiquidityProtection is ILiquidityProtection, Utils, Owned, ReentrancyGuard, Time { using SafeMath for uint256; using ReserveToken for IReserveToken; using SafeERC20 for IERC20; using SafeERC20 for IDSToken; using SafeERC20Ex for IERC20; using MathEx for *; struct Position { address provider; // liquidity provider IDSToken poolToken; // pool token address IReserveToken reserveToken; // reserve token address uint256 poolAmount; // pool token amount uint256 reserveAmount; // reserve token amount uint256 reserveRateN; // rate of 1 protected reserve token in units of the other reserve token (numerator) uint256 reserveRateD; // rate of 1 protected reserve token in units of the other reserve token (denominator) uint256 timestamp; // timestamp } // various rates between the two reserve tokens. the rate is of 1 unit of the protected reserve token in units of the other reserve token struct PackedRates { uint128 addSpotRateN; // spot rate of 1 A in units of B when liquidity was added (numerator) uint128 addSpotRateD; // spot rate of 1 A in units of B when liquidity was added (denominator) uint128 removeSpotRateN; // spot rate of 1 A in units of B when liquidity is removed (numerator) uint128 removeSpotRateD; // spot rate of 1 A in units of B when liquidity is removed (denominator) uint128 removeAverageRateN; // average rate of 1 A in units of B when liquidity is removed (numerator) uint128 removeAverageRateD; // average rate of 1 A in units of B when liquidity is removed (denominator) } uint256 internal constant MAX_UINT128 = 2**128 - 1; uint256 internal constant MAX_UINT256 = uint256(-1); uint8 private constant FUNC_SELECTOR_LENGTH = 4; ILiquidityProtectionSettings private immutable _settings; ILiquidityProtectionStore private immutable _store; ILiquidityProtectionStats private immutable _stats; ILiquidityProtectionSystemStore private immutable _systemStore; ITokenHolder private immutable _wallet; IERC20 private immutable _networkToken; ITokenGovernance private immutable _networkTokenGovernance; IERC20 private immutable _govToken; ITokenGovernance private immutable _govTokenGovernance; ICheckpointStore private immutable _lastRemoveCheckpointStore; /** * @dev initializes a new LiquidityProtection contract * * @param settings liquidity protection settings * @param store liquidity protection store * @param stats liquidity protection stats * @param systemStore liquidity protection system store * @param wallet liquidity protection wallet * @param networkTokenGovernance network token governance * @param govTokenGovernance governance token governance * @param lastRemoveCheckpointStore last liquidity removal/unprotection checkpoints store */ constructor( ILiquidityProtectionSettings settings, ILiquidityProtectionStore store, ILiquidityProtectionStats stats, ILiquidityProtectionSystemStore systemStore, ITokenHolder wallet, ITokenGovernance networkTokenGovernance, ITokenGovernance govTokenGovernance, ICheckpointStore lastRemoveCheckpointStore ) public validAddress(address(settings)) validAddress(address(store)) validAddress(address(stats)) validAddress(address(systemStore)) validAddress(address(wallet)) validAddress(address(lastRemoveCheckpointStore)) { _settings = settings; _store = store; _stats = stats; _systemStore = systemStore; _wallet = wallet; _networkTokenGovernance = networkTokenGovernance; _govTokenGovernance = govTokenGovernance; _lastRemoveCheckpointStore = lastRemoveCheckpointStore; _networkToken = networkTokenGovernance.token(); _govToken = govTokenGovernance.token(); } // ensures that the pool is supported and whitelisted modifier poolSupportedAndWhitelisted(IConverterAnchor poolAnchor) { _poolSupported(poolAnchor); _poolWhitelisted(poolAnchor); _; } // ensures that add liquidity is enabled modifier addLiquidityEnabled(IConverterAnchor poolAnchor, IReserveToken reserveToken) { _addLiquidityEnabled(poolAnchor, reserveToken); _; } // error message binary size optimization function _poolSupported(IConverterAnchor poolAnchor) internal view { require(_settings.isPoolSupported(poolAnchor), "ERR_POOL_NOT_SUPPORTED"); } // error message binary size optimization function _poolWhitelisted(IConverterAnchor poolAnchor) internal view { require(_settings.isPoolWhitelisted(poolAnchor), "ERR_POOL_NOT_WHITELISTED"); } // error message binary size optimization function _addLiquidityEnabled(IConverterAnchor poolAnchor, IReserveToken reserveToken) internal view { require(!_settings.addLiquidityDisabled(poolAnchor, reserveToken), "ERR_ADD_LIQUIDITY_DISABLED"); } // error message binary size optimization function verifyEthAmount(uint256 value) internal view { require(msg.value == value, "ERR_ETH_AMOUNT_MISMATCH"); } /** * @dev returns the LP store * * @return the LP store */ function store() external view override returns (ILiquidityProtectionStore) { return _store; } /** * @dev returns the LP stats * * @return the LP stats */ function stats() external view override returns (ILiquidityProtectionStats) { return _stats; } /** * @dev returns the LP settings * * @return the LP settings */ function settings() external view override returns (ILiquidityProtectionSettings) { return _settings; } /** * @dev returns the LP system store * * @return the LP system store */ function systemStore() external view override returns (ILiquidityProtectionSystemStore) { return _systemStore; } /** * @dev returns the LP wallet * * @return the LP wallet */ function wallet() external view override returns (ITokenHolder) { return _wallet; } /** * @dev accept ETH */ receive() external payable {} /** * @dev transfers the ownership of the store * can only be called by the contract owner * * @param newOwner the new owner of the store */ function transferStoreOwnership(address newOwner) external ownerOnly { _store.transferOwnership(newOwner); } /** * @dev accepts the ownership of the store * can only be called by the contract owner */ function acceptStoreOwnership() external ownerOnly { _store.acceptOwnership(); } /** * @dev transfers the ownership of the wallet * can only be called by the contract owner * * @param newOwner the new owner of the wallet */ function transferWalletOwnership(address newOwner) external ownerOnly { _wallet.transferOwnership(newOwner); } /** * @dev accepts the ownership of the wallet * can only be called by the contract owner */ function acceptWalletOwnership() external ownerOnly { _wallet.acceptOwnership(); } /** * @dev adds protected liquidity to a pool for a specific recipient * also mints new governance tokens for the caller if the caller adds network tokens * * @param owner position owner * @param poolAnchor anchor of the pool * @param reserveToken reserve token to add to the pool * @param amount amount of tokens to add to the pool * * @return new position id */ function addLiquidityFor( address owner, IConverterAnchor poolAnchor, IReserveToken reserveToken, uint256 amount ) external payable override protected validAddress(owner) poolSupportedAndWhitelisted(poolAnchor) addLiquidityEnabled(poolAnchor, reserveToken) greaterThanZero(amount) returns (uint256) { return addLiquidity(owner, poolAnchor, reserveToken, amount); } /** * @dev adds protected liquidity to a pool * also mints new governance tokens for the caller if the caller adds network tokens * * @param poolAnchor anchor of the pool * @param reserveToken reserve token to add to the pool * @param amount amount of tokens to add to the pool * * @return new position id */ function addLiquidity( IConverterAnchor poolAnchor, IReserveToken reserveToken, uint256 amount ) external payable override protected poolSupportedAndWhitelisted(poolAnchor) addLiquidityEnabled(poolAnchor, reserveToken) greaterThanZero(amount) returns (uint256) { return addLiquidity(msg.sender, poolAnchor, reserveToken, amount); } /** * @dev adds protected liquidity to a pool for a specific recipient * also mints new governance tokens for the caller if the caller adds network tokens * * @param owner position owner * @param poolAnchor anchor of the pool * @param reserveToken reserve token to add to the pool * @param amount amount of tokens to add to the pool * * @return new position id */ function addLiquidity( address owner, IConverterAnchor poolAnchor, IReserveToken reserveToken, uint256 amount ) private returns (uint256) { if (isNetworkToken(reserveToken)) { verifyEthAmount(0); return addNetworkTokenLiquidity(owner, poolAnchor, amount); } // verify that ETH was passed with the call if needed verifyEthAmount(reserveToken.isNativeToken() ? amount : 0); return addBaseTokenLiquidity(owner, poolAnchor, reserveToken, amount); } /** * @dev adds network token liquidity to a pool * also mints new governance tokens for the caller * * @param owner position owner * @param poolAnchor anchor of the pool * @param amount amount of tokens to add to the pool * * @return new position id */ function addNetworkTokenLiquidity( address owner, IConverterAnchor poolAnchor, uint256 amount ) internal returns (uint256) { IDSToken poolToken = IDSToken(address(poolAnchor)); IReserveToken networkToken = IReserveToken(address(_networkToken)); // get the rate between the pool token and the reserve Fraction memory poolRate = poolTokenRate(poolToken, networkToken); // calculate the amount of pool tokens based on the amount of reserve tokens uint256 poolTokenAmount = amount.mul(poolRate.d).div(poolRate.n); // remove the pool tokens from the system's ownership (will revert if not enough tokens are available) _systemStore.decSystemBalance(poolToken, poolTokenAmount); // add the position for the recipient uint256 id = addPosition(owner, poolToken, networkToken, poolTokenAmount, amount, time()); // burns the network tokens from the caller. we need to transfer the tokens to the contract itself, since only // token holders can burn their tokens _networkToken.safeTransferFrom(msg.sender, address(this), amount); burnNetworkTokens(poolAnchor, amount); // mint governance tokens to the recipient _govTokenGovernance.mint(owner, amount); return id; } /** * @dev adds base token liquidity to a pool * * @param owner position owner * @param poolAnchor anchor of the pool * @param baseToken the base reserve token of the pool * @param amount amount of tokens to add to the pool * * @return new position id */ function addBaseTokenLiquidity( address owner, IConverterAnchor poolAnchor, IReserveToken baseToken, uint256 amount ) internal returns (uint256) { IDSToken poolToken = IDSToken(address(poolAnchor)); IReserveToken networkToken = IReserveToken(address(_networkToken)); // get the reserve balances ILiquidityPoolConverter converter = ILiquidityPoolConverter(payable(ownedBy(poolAnchor))); (uint256 reserveBalanceBase, uint256 reserveBalanceNetwork) = converterReserveBalances(converter, baseToken, networkToken); require(reserveBalanceNetwork >= _settings.minNetworkTokenLiquidityForMinting(), "ERR_NOT_ENOUGH_LIQUIDITY"); // calculate and mint the required amount of network tokens for adding liquidity uint256 newNetworkLiquidityAmount = amount.mul(reserveBalanceNetwork).div(reserveBalanceBase); // verify network token minting limit uint256 mintingLimit = _settings.networkTokenMintingLimits(poolAnchor); if (mintingLimit == 0) { mintingLimit = _settings.defaultNetworkTokenMintingLimit(); } uint256 newNetworkTokensMinted = _systemStore.networkTokensMinted(poolAnchor).add(newNetworkLiquidityAmount); require(newNetworkTokensMinted <= mintingLimit, "ERR_MAX_AMOUNT_REACHED"); // issue new network tokens to the system mintNetworkTokens(address(this), poolAnchor, newNetworkLiquidityAmount); // transfer the base tokens from the caller and approve the converter networkToken.ensureApprove(address(converter), newNetworkLiquidityAmount); if (!baseToken.isNativeToken()) { baseToken.safeTransferFrom(msg.sender, address(this), amount); baseToken.ensureApprove(address(converter), amount); } // add the liquidity to the converter addLiquidity(converter, baseToken, networkToken, amount, newNetworkLiquidityAmount, msg.value); // transfer the new pool tokens to the wallet uint256 poolTokenAmount = poolToken.balanceOf(address(this)); poolToken.safeTransfer(address(_wallet), poolTokenAmount); // the system splits the pool tokens with the caller // increase the system's pool token balance and add the position for the caller _systemStore.incSystemBalance(poolToken, poolTokenAmount - poolTokenAmount / 2); // account for rounding errors return addPosition(owner, poolToken, baseToken, poolTokenAmount / 2, amount, time()); } /** * @dev returns the single-side staking limits of a given pool * * @param poolAnchor anchor of the pool * * @return maximum amount of base tokens that can be single-side staked in the pool * @return maximum amount of network tokens that can be single-side staked in the pool */ function poolAvailableSpace(IConverterAnchor poolAnchor) external view poolSupportedAndWhitelisted(poolAnchor) returns (uint256, uint256) { return (baseTokenAvailableSpace(poolAnchor), networkTokenAvailableSpace(poolAnchor)); } /** * @dev returns the base-token staking limits of a given pool * * @param poolAnchor anchor of the pool * * @return maximum amount of base tokens that can be single-side staked in the pool */ function baseTokenAvailableSpace(IConverterAnchor poolAnchor) internal view returns (uint256) { // get the pool converter ILiquidityPoolConverter converter = ILiquidityPoolConverter(payable(ownedBy(poolAnchor))); // get the base token IReserveToken networkToken = IReserveToken(address(_networkToken)); IReserveToken baseToken = converterOtherReserve(converter, networkToken); // get the reserve balances (uint256 reserveBalanceBase, uint256 reserveBalanceNetwork) = converterReserveBalances(converter, baseToken, networkToken); // get the network token minting limit uint256 mintingLimit = _settings.networkTokenMintingLimits(poolAnchor); if (mintingLimit == 0) { mintingLimit = _settings.defaultNetworkTokenMintingLimit(); } // get the amount of network tokens already minted for the pool uint256 networkTokensMinted = _systemStore.networkTokensMinted(poolAnchor); // get the amount of network tokens which can minted for the pool uint256 networkTokensCanBeMinted = MathEx.max(mintingLimit, networkTokensMinted) - networkTokensMinted; // return the maximum amount of base token liquidity that can be single-sided staked in the pool return networkTokensCanBeMinted.mul(reserveBalanceBase).div(reserveBalanceNetwork); } /** * @dev returns the network-token staking limits of a given pool * * @param poolAnchor anchor of the pool * * @return maximum amount of network tokens that can be single-side staked in the pool */ function networkTokenAvailableSpace(IConverterAnchor poolAnchor) internal view returns (uint256) { // get the pool token IDSToken poolToken = IDSToken(address(poolAnchor)); IReserveToken networkToken = IReserveToken(address(_networkToken)); // get the pool token rate Fraction memory poolRate = poolTokenRate(poolToken, networkToken); // return the maximum amount of network token liquidity that can be single-sided staked in the pool return _systemStore.systemBalance(poolToken).mul(poolRate.n).add(poolRate.n).sub(1).div(poolRate.d); } /** * @dev returns the expected/actual amounts the provider will receive for removing liquidity * it's also possible to provide the remove liquidity time to get an estimation * for the return at that given point * * @param id position id * @param portion portion of liquidity to remove, in PPM * @param removeTimestamp time at which the liquidity is removed * * @return expected return amount in the reserve token * @return actual return amount in the reserve token * @return compensation in the network token */ function removeLiquidityReturn( uint256 id, uint32 portion, uint256 removeTimestamp ) external view validPortion(portion) returns ( uint256, uint256, uint256 ) { Position memory pos = position(id); // verify input require(pos.provider != address(0), "ERR_INVALID_ID"); require(removeTimestamp >= pos.timestamp, "ERR_INVALID_TIMESTAMP"); // calculate the portion of the liquidity to remove if (portion != PPM_RESOLUTION) { pos.poolAmount = pos.poolAmount.mul(portion) / PPM_RESOLUTION; pos.reserveAmount = pos.reserveAmount.mul(portion) / PPM_RESOLUTION; } // get the various rates between the reserves upon adding liquidity and now PackedRates memory packedRates = packRates(pos.poolToken, pos.reserveToken, pos.reserveRateN, pos.reserveRateD); uint256 targetAmount = removeLiquidityTargetAmount( pos.poolToken, pos.reserveToken, pos.poolAmount, pos.reserveAmount, packedRates, pos.timestamp, removeTimestamp ); // for network token, the return amount is identical to the target amount if (isNetworkToken(pos.reserveToken)) { return (targetAmount, targetAmount, 0); } // handle base token return // calculate the amount of pool tokens required for liquidation // note that the amount is doubled since it's not possible to liquidate one reserve only Fraction memory poolRate = poolTokenRate(pos.poolToken, pos.reserveToken); uint256 poolAmount = targetAmount.mul(poolRate.d).div(poolRate.n / 2); // limit the amount of pool tokens by the amount the system/caller holds uint256 availableBalance = _systemStore.systemBalance(pos.poolToken).add(pos.poolAmount); poolAmount = poolAmount > availableBalance ? availableBalance : poolAmount; // calculate the base token amount received by liquidating the pool tokens // note that the amount is divided by 2 since the pool amount represents both reserves uint256 baseAmount = poolAmount.mul(poolRate.n / 2).div(poolRate.d); uint256 networkAmount = networkCompensation(targetAmount, baseAmount, packedRates); return (targetAmount, baseAmount, networkAmount); } /** * @dev removes protected liquidity from a pool * also burns governance tokens from the caller if the caller removes network tokens * * @param id position id * @param portion portion of liquidity to remove, in PPM */ function removeLiquidity(uint256 id, uint32 portion) external override protected validPortion(portion) { removeLiquidity(msg.sender, id, portion); } /** * @dev removes a position from a pool * also burns governance tokens from the caller if the caller removes network tokens * * @param provider liquidity provider * @param id position id * @param portion portion of liquidity to remove, in PPM */ function removeLiquidity( address payable provider, uint256 id, uint32 portion ) internal { // remove the position from the store and update the stats and the last removal checkpoint Position memory removedPos = removePosition(provider, id, portion); // add the pool tokens to the system _systemStore.incSystemBalance(removedPos.poolToken, removedPos.poolAmount); // if removing network token liquidity, burn the governance tokens from the caller. we need to transfer the // tokens to the contract itself, since only token holders can burn their tokens if (isNetworkToken(removedPos.reserveToken)) { _govToken.safeTransferFrom(provider, address(this), removedPos.reserveAmount); _govTokenGovernance.burn(removedPos.reserveAmount); } // get the various rates between the reserves upon adding liquidity and now PackedRates memory packedRates = packRates(removedPos.poolToken, removedPos.reserveToken, removedPos.reserveRateN, removedPos.reserveRateD); // verify rate deviation as early as possible in order to reduce gas-cost for failing transactions verifyRateDeviation( packedRates.removeSpotRateN, packedRates.removeSpotRateD, packedRates.removeAverageRateN, packedRates.removeAverageRateD ); // get the target token amount uint256 targetAmount = removeLiquidityTargetAmount( removedPos.poolToken, removedPos.reserveToken, removedPos.poolAmount, removedPos.reserveAmount, packedRates, removedPos.timestamp, time() ); // remove network token liquidity if (isNetworkToken(removedPos.reserveToken)) { // mint network tokens for the caller and lock them mintNetworkTokens(address(_wallet), removedPos.poolToken, targetAmount); lockTokens(provider, targetAmount); return; } // remove base token liquidity // calculate the amount of pool tokens required for liquidation // note that the amount is doubled since it's not possible to liquidate one reserve only Fraction memory poolRate = poolTokenRate(removedPos.poolToken, removedPos.reserveToken); uint256 poolAmount = targetAmount.mul(poolRate.d).div(poolRate.n / 2); // limit the amount of pool tokens by the amount the system holds uint256 systemBalance = _systemStore.systemBalance(removedPos.poolToken); poolAmount = poolAmount > systemBalance ? systemBalance : poolAmount; // withdraw the pool tokens from the wallet IReserveToken poolToken = IReserveToken(address(removedPos.poolToken)); _systemStore.decSystemBalance(removedPos.poolToken, poolAmount); _wallet.withdrawTokens(poolToken, address(this), poolAmount); // remove liquidity removeLiquidity( removedPos.poolToken, poolAmount, removedPos.reserveToken, IReserveToken(address(_networkToken)) ); // transfer the base tokens to the caller uint256 baseBalance = removedPos.reserveToken.balanceOf(address(this)); removedPos.reserveToken.safeTransfer(provider, baseBalance); // compensate the caller with network tokens if still needed uint256 delta = networkCompensation(targetAmount, baseBalance, packedRates); if (delta > 0) { // check if there's enough network token balance, otherwise mint more uint256 networkBalance = _networkToken.balanceOf(address(this)); if (networkBalance < delta) { _networkTokenGovernance.mint(address(this), delta - networkBalance); } // lock network tokens for the caller _networkToken.safeTransfer(address(_wallet), delta); lockTokens(provider, delta); } // if the contract still holds network tokens, burn them uint256 networkBalance = _networkToken.balanceOf(address(this)); if (networkBalance > 0) { burnNetworkTokens(removedPos.poolToken, networkBalance); } } /** * @dev returns the amount the provider will receive for removing liquidity * it's also possible to provide the remove liquidity rate & time to get an estimation * for the return at that given point * * @param poolToken pool token * @param reserveToken reserve token * @param poolAmount pool token amount when the liquidity was added * @param reserveAmount reserve token amount that was added * @param packedRates see `struct PackedRates` * @param addTimestamp time at which the liquidity was added * @param removeTimestamp time at which the liquidity is removed * * @return amount received for removing liquidity */ function removeLiquidityTargetAmount( IDSToken poolToken, IReserveToken reserveToken, uint256 poolAmount, uint256 reserveAmount, PackedRates memory packedRates, uint256 addTimestamp, uint256 removeTimestamp ) internal view returns (uint256) { // get the rate between the pool token and the reserve token Fraction memory poolRate = poolTokenRate(poolToken, reserveToken); // get the rate between the reserves upon adding liquidity and now Fraction memory addSpotRate = Fraction({ n: packedRates.addSpotRateN, d: packedRates.addSpotRateD }); Fraction memory removeSpotRate = Fraction({ n: packedRates.removeSpotRateN, d: packedRates.removeSpotRateD }); Fraction memory removeAverageRate = Fraction({ n: packedRates.removeAverageRateN, d: packedRates.removeAverageRateD }); // calculate the protected amount of reserve tokens plus accumulated fee before compensation uint256 total = protectedAmountPlusFee(poolAmount, poolRate, addSpotRate, removeSpotRate); // calculate the impermanent loss Fraction memory loss = impLoss(addSpotRate, removeAverageRate); // calculate the protection level Fraction memory level = protectionLevel(addTimestamp, removeTimestamp); // calculate the compensation amount return compensationAmount(reserveAmount, MathEx.max(reserveAmount, total), loss, level); } /** * @dev transfers a position to a new provider * * @param id position id * @param newProvider the new provider * * @return new position id */ function transferPosition(uint256 id, address newProvider) external protected validAddress(newProvider) returns (uint256) { return transferPosition(msg.sender, id, newProvider); } /** * @dev transfers a position to a new provider and optionally notifies another contract * * @param id position id * @param newProvider the new provider * @param target the contract to notify * @param data the data to call the contract with * * @return new position id */ function transferPositionAndCall( uint256 id, address newProvider, address target, bytes memory data ) external protected validAddress(newProvider) validAddress(target) returns (uint256) { // make sure that we're not trying to call into the zero address or a fallback function require(data.length >= FUNC_SELECTOR_LENGTH, "ERR_INVALID_CALL_DATA"); uint256 newId = transferPosition(msg.sender, id, newProvider); Address.functionCall(target, data, "ERR_CALL_FAILED"); return newId; } /** * @dev transfers a position to a new provider * * @param provider the existing provider * @param id position id * @param newProvider the new provider * * @return new position id */ function transferPosition( address provider, uint256 id, address newProvider ) internal returns (uint256) { // remove the position from the store and update the stats and the last removal checkpoint Position memory removedPos = removePosition(provider, id, PPM_RESOLUTION); // add the position to the store, update the stats, and return the new id return addPosition( newProvider, removedPos.poolToken, removedPos.reserveToken, removedPos.poolAmount, removedPos.reserveAmount, removedPos.timestamp ); } /** * @dev allows the caller to claim network token balance that is no longer locked * note that the function can revert if the range is too large * * @param startIndex start index in the caller's list of locked balances * @param endIndex end index in the caller's list of locked balances (exclusive) */ function claimBalance(uint256 startIndex, uint256 endIndex) external protected { // get the locked balances from the store (uint256[] memory amounts, uint256[] memory expirationTimes) = _store.lockedBalanceRange(msg.sender, startIndex, endIndex); uint256 totalAmount = 0; uint256 length = amounts.length; assert(length == expirationTimes.length); // reverse iteration since we're removing from the list for (uint256 i = length; i > 0; i--) { uint256 index = i - 1; if (expirationTimes[index] > time()) { continue; } // remove the locked balance item _store.removeLockedBalance(msg.sender, startIndex + index); totalAmount = totalAmount.add(amounts[index]); } if (totalAmount > 0) { // transfer the tokens to the caller in a single call _wallet.withdrawTokens(IReserveToken(address(_networkToken)), msg.sender, totalAmount); } } /** * @dev returns the ROI for removing liquidity in the current state after providing liquidity with the given args * the function assumes full protection is in effect * return value is in PPM and can be larger than PPM_RESOLUTION for positive ROI, 1M = 0% ROI * * @param poolToken pool token * @param reserveToken reserve token * @param reserveAmount reserve token amount that was added * @param poolRateN rate of 1 pool token in reserve token units when the liquidity was added (numerator) * @param poolRateD rate of 1 pool token in reserve token units when the liquidity was added (denominator) * @param reserveRateN rate of 1 reserve token in the other reserve token units when the liquidity was added (numerator) * @param reserveRateD rate of 1 reserve token in the other reserve token units when the liquidity was added (denominator) * * @return ROI in PPM */ function poolROI( IDSToken poolToken, IReserveToken reserveToken, uint256 reserveAmount, uint256 poolRateN, uint256 poolRateD, uint256 reserveRateN, uint256 reserveRateD ) external view returns (uint256) { // calculate the amount of pool tokens based on the amount of reserve tokens uint256 poolAmount = reserveAmount.mul(poolRateD).div(poolRateN); // get the various rates between the reserves upon adding liquidity and now PackedRates memory packedRates = packRates(poolToken, reserveToken, reserveRateN, reserveRateD); // get the current return uint256 protectedReturn = removeLiquidityTargetAmount( poolToken, reserveToken, poolAmount, reserveAmount, packedRates, time().sub(_settings.maxProtectionDelay()), time() ); // calculate the ROI as the ratio between the current fully protected return and the initial amount return protectedReturn.mul(PPM_RESOLUTION).div(reserveAmount); } /** * @dev adds the position to the store and updates the stats * * @param provider the provider * @param poolToken pool token * @param reserveToken reserve token * @param poolAmount amount of pool tokens to protect * @param reserveAmount amount of reserve tokens to protect * @param timestamp the timestamp of the position * * @return new position id */ function addPosition( address provider, IDSToken poolToken, IReserveToken reserveToken, uint256 poolAmount, uint256 reserveAmount, uint256 timestamp ) internal returns (uint256) { // verify rate deviation as early as possible in order to reduce gas-cost for failing transactions (Fraction memory spotRate, Fraction memory averageRate) = reserveTokenRates(poolToken, reserveToken); verifyRateDeviation(spotRate.n, spotRate.d, averageRate.n, averageRate.d); notifyEventSubscribersOnAddingLiquidity(provider, poolToken, reserveToken, poolAmount, reserveAmount); _stats.increaseTotalAmounts(provider, poolToken, reserveToken, poolAmount, reserveAmount); _stats.addProviderPool(provider, poolToken); return _store.addProtectedLiquidity( provider, poolToken, reserveToken, poolAmount, reserveAmount, spotRate.n, spotRate.d, timestamp ); } /** * @dev removes the position from the store and updates the stats and the last removal checkpoint * * @param provider the provider * @param id position id * @param portion portion of the position to remove, in PPM * * @return a Position struct representing the removed liquidity */ function removePosition( address provider, uint256 id, uint32 portion ) private returns (Position memory) { Position memory pos = providerPosition(id, provider); // verify that the pool is whitelisted _poolWhitelisted(pos.poolToken); // verify that the position is not removed on the same block in which it was added require(pos.timestamp < time(), "ERR_TOO_EARLY"); if (portion == PPM_RESOLUTION) { notifyEventSubscribersOnRemovingLiquidity( id, pos.provider, pos.poolToken, pos.reserveToken, pos.poolAmount, pos.reserveAmount ); // remove the position from the provider _store.removeProtectedLiquidity(id); } else { // remove a portion of the position from the provider uint256 fullPoolAmount = pos.poolAmount; uint256 fullReserveAmount = pos.reserveAmount; pos.poolAmount = pos.poolAmount.mul(portion) / PPM_RESOLUTION; pos.reserveAmount = pos.reserveAmount.mul(portion) / PPM_RESOLUTION; notifyEventSubscribersOnRemovingLiquidity( id, pos.provider, pos.poolToken, pos.reserveToken, pos.poolAmount, pos.reserveAmount ); _store.updateProtectedLiquidityAmounts( id, fullPoolAmount - pos.poolAmount, fullReserveAmount - pos.reserveAmount ); } // update the statistics _stats.decreaseTotalAmounts(pos.provider, pos.poolToken, pos.reserveToken, pos.poolAmount, pos.reserveAmount); // update last liquidity removal checkpoint _lastRemoveCheckpointStore.addCheckpoint(provider); return pos; } /** * @dev locks network tokens for the provider and emits the tokens locked event * * @param provider tokens provider * @param amount amount of network tokens */ function lockTokens(address provider, uint256 amount) internal { uint256 expirationTime = time().add(_settings.lockDuration()); _store.addLockedBalance(provider, amount, expirationTime); } /** * @dev returns the rate of 1 pool token in reserve token units * * @param poolToken pool token * @param reserveToken reserve token */ function poolTokenRate(IDSToken poolToken, IReserveToken reserveToken) internal view virtual returns (Fraction memory) { // get the pool token supply uint256 poolTokenSupply = poolToken.totalSupply(); // get the reserve balance IConverter converter = IConverter(payable(ownedBy(poolToken))); uint256 reserveBalance = converter.getConnectorBalance(reserveToken); // for standard pools, 50% of the pool supply value equals the value of each reserve return Fraction({ n: reserveBalance.mul(2), d: poolTokenSupply }); } /** * @dev returns the spot rate and average rate of 1 reserve token in the other reserve token units * * @param poolToken pool token * @param reserveToken reserve token * * @return spot rate * @return average rate */ function reserveTokenRates(IDSToken poolToken, IReserveToken reserveToken) internal view returns (Fraction memory, Fraction memory) { ILiquidityPoolConverter converter = ILiquidityPoolConverter(payable(ownedBy(poolToken))); IReserveToken otherReserve = converterOtherReserve(converter, reserveToken); (uint256 spotRateN, uint256 spotRateD) = converterReserveBalances(converter, otherReserve, reserveToken); (uint256 averageRateN, uint256 averageRateD) = converter.recentAverageRate(reserveToken); return (Fraction({ n: spotRateN, d: spotRateD }), Fraction({ n: averageRateN, d: averageRateD })); } /** * @dev returns the various rates between the reserves * * @param poolToken pool token * @param reserveToken reserve token * @param addSpotRateN add spot rate numerator * @param addSpotRateD add spot rate denominator * * @return see `struct PackedRates` */ function packRates( IDSToken poolToken, IReserveToken reserveToken, uint256 addSpotRateN, uint256 addSpotRateD ) internal view returns (PackedRates memory) { (Fraction memory removeSpotRate, Fraction memory removeAverageRate) = reserveTokenRates(poolToken, reserveToken); assert( addSpotRateN <= MAX_UINT128 && addSpotRateD <= MAX_UINT128 && removeSpotRate.n <= MAX_UINT128 && removeSpotRate.d <= MAX_UINT128 && removeAverageRate.n <= MAX_UINT128 && removeAverageRate.d <= MAX_UINT128 ); return PackedRates({ addSpotRateN: uint128(addSpotRateN), addSpotRateD: uint128(addSpotRateD), removeSpotRateN: uint128(removeSpotRate.n), removeSpotRateD: uint128(removeSpotRate.d), removeAverageRateN: uint128(removeAverageRate.n), removeAverageRateD: uint128(removeAverageRate.d) }); } /** * @dev verifies that the deviation of the average rate from the spot rate is within the permitted range * for example, if the maximum permitted deviation is 5%, then verify `95/100 <= average/spot <= 100/95` * * @param spotRateN spot rate numerator * @param spotRateD spot rate denominator * @param averageRateN average rate numerator * @param averageRateD average rate denominator */ function verifyRateDeviation( uint256 spotRateN, uint256 spotRateD, uint256 averageRateN, uint256 averageRateD ) internal view { uint256 ppmDelta = PPM_RESOLUTION - _settings.averageRateMaxDeviation(); uint256 min = spotRateN.mul(averageRateD).mul(ppmDelta).mul(ppmDelta); uint256 mid = spotRateD.mul(averageRateN).mul(ppmDelta).mul(PPM_RESOLUTION); uint256 max = spotRateN.mul(averageRateD).mul(PPM_RESOLUTION).mul(PPM_RESOLUTION); require(min <= mid && mid <= max, "ERR_INVALID_RATE"); } /** * @dev utility to add liquidity to a converter * * @param converter converter * @param reserveToken1 reserve token 1 * @param reserveToken2 reserve token 2 * @param reserveAmount1 reserve amount 1 * @param reserveAmount2 reserve amount 2 * @param value ETH amount to add */ function addLiquidity( ILiquidityPoolConverter converter, IReserveToken reserveToken1, IReserveToken reserveToken2, uint256 reserveAmount1, uint256 reserveAmount2, uint256 value ) internal { IReserveToken[] memory reserveTokens = new IReserveToken[](2); uint256[] memory amounts = new uint256[](2); reserveTokens[0] = reserveToken1; reserveTokens[1] = reserveToken2; amounts[0] = reserveAmount1; amounts[1] = reserveAmount2; converter.addLiquidity{ value: value }(reserveTokens, amounts, 1); } /** * @dev utility to remove liquidity from a converter * * @param poolToken pool token of the converter * @param poolAmount amount of pool tokens to remove * @param reserveToken1 reserve token 1 * @param reserveToken2 reserve token 2 */ function removeLiquidity( IDSToken poolToken, uint256 poolAmount, IReserveToken reserveToken1, IReserveToken reserveToken2 ) internal { ILiquidityPoolConverter converter = ILiquidityPoolConverter(payable(ownedBy(poolToken))); IReserveToken[] memory reserveTokens = new IReserveToken[](2); uint256[] memory minReturns = new uint256[](2); reserveTokens[0] = reserveToken1; reserveTokens[1] = reserveToken2; minReturns[0] = 1; minReturns[1] = 1; converter.removeLiquidity(poolAmount, reserveTokens, minReturns); } /** * @dev returns a position from the store * * @param id position id * * @return a position */ function position(uint256 id) internal view returns (Position memory) { Position memory pos; ( pos.provider, pos.poolToken, pos.reserveToken, pos.poolAmount, pos.reserveAmount, pos.reserveRateN, pos.reserveRateD, pos.timestamp ) = _store.protectedLiquidity(id); return pos; } /** * @dev returns a position from the store * * @param id position id * @param provider authorized provider * * @return a position */ function providerPosition(uint256 id, address provider) internal view returns (Position memory) { Position memory pos = position(id); require(pos.provider == provider, "ERR_ACCESS_DENIED"); return pos; } /** * @dev returns the protected amount of reserve tokens plus accumulated fee before compensation * * @param poolAmount pool token amount when the liquidity was added * @param poolRate rate of 1 pool token in the related reserve token units * @param addRate rate of 1 reserve token in the other reserve token units when the liquidity was added * @param removeRate rate of 1 reserve token in the other reserve token units when the liquidity is removed * * @return protected amount of reserve tokens plus accumulated fee = sqrt(removeRate / addRate) * poolRate * poolAmount */ function protectedAmountPlusFee( uint256 poolAmount, Fraction memory poolRate, Fraction memory addRate, Fraction memory removeRate ) internal pure returns (uint256) { uint256 n = MathEx.ceilSqrt(addRate.d.mul(removeRate.n)).mul(poolRate.n); uint256 d = MathEx.floorSqrt(addRate.n.mul(removeRate.d)).mul(poolRate.d); uint256 x = n * poolAmount; if (x / n == poolAmount) { return x / d; } (uint256 hi, uint256 lo) = n > poolAmount ? (n, poolAmount) : (poolAmount, n); (uint256 p, uint256 q) = MathEx.reducedRatio(hi, d, MAX_UINT256 / lo); uint256 min = (hi / d).mul(lo); if (q > 0) { return MathEx.max(min, (p * lo) / q); } return min; } /** * @dev returns the impermanent loss incurred due to the change in rates between the reserve tokens * * @param prevRate previous rate between the reserves * @param newRate new rate between the reserves * * @return impermanent loss (as a ratio) */ function impLoss(Fraction memory prevRate, Fraction memory newRate) internal pure returns (Fraction memory) { uint256 ratioN = newRate.n.mul(prevRate.d); uint256 ratioD = newRate.d.mul(prevRate.n); uint256 prod = ratioN * ratioD; uint256 root = prod / ratioN == ratioD ? MathEx.floorSqrt(prod) : MathEx.floorSqrt(ratioN) * MathEx.floorSqrt(ratioD); uint256 sum = ratioN.add(ratioD); // the arithmetic below is safe because `x + y >= sqrt(x * y) * 2` if (sum % 2 == 0) { sum /= 2; return Fraction({ n: sum - root, d: sum }); } return Fraction({ n: sum - root * 2, d: sum }); } /** * @dev returns the protection level based on the timestamp and protection delays * * @param addTimestamp time at which the liquidity was added * @param removeTimestamp time at which the liquidity is removed * * @return protection level (as a ratio) */ function protectionLevel(uint256 addTimestamp, uint256 removeTimestamp) internal view returns (Fraction memory) { uint256 timeElapsed = removeTimestamp.sub(addTimestamp); uint256 minProtectionDelay = _settings.minProtectionDelay(); uint256 maxProtectionDelay = _settings.maxProtectionDelay(); if (timeElapsed < minProtectionDelay) { return Fraction({ n: 0, d: 1 }); } if (timeElapsed >= maxProtectionDelay) { return Fraction({ n: 1, d: 1 }); } return Fraction({ n: timeElapsed, d: maxProtectionDelay }); } /** * @dev returns the compensation amount based on the impermanent loss and the protection level * * @param amount protected amount in units of the reserve token * @param total amount plus fee in units of the reserve token * @param loss protection level (as a ratio between 0 and 1) * @param level impermanent loss (as a ratio between 0 and 1) * * @return compensation amount */ function compensationAmount( uint256 amount, uint256 total, Fraction memory loss, Fraction memory level ) internal pure returns (uint256) { uint256 levelN = level.n.mul(amount); uint256 levelD = level.d; uint256 maxVal = MathEx.max(MathEx.max(levelN, levelD), total); (uint256 lossN, uint256 lossD) = MathEx.reducedRatio(loss.n, loss.d, MAX_UINT256 / maxVal); return total.mul(lossD.sub(lossN)).div(lossD).add(lossN.mul(levelN).div(lossD.mul(levelD))); } function networkCompensation( uint256 targetAmount, uint256 baseAmount, PackedRates memory packedRates ) internal view returns (uint256) { if (targetAmount <= baseAmount) { return 0; } // calculate the delta in network tokens uint256 delta = (targetAmount - baseAmount).mul(packedRates.removeAverageRateN).div(packedRates.removeAverageRateD); // the delta might be very small due to precision loss // in which case no compensation will take place (gas optimization) if (delta >= _settings.minNetworkCompensation()) { return delta; } return 0; } // utility to mint network tokens function mintNetworkTokens( address owner, IConverterAnchor poolAnchor, uint256 amount ) private { _systemStore.incNetworkTokensMinted(poolAnchor, amount); _networkTokenGovernance.mint(owner, amount); } // utility to burn network tokens function burnNetworkTokens(IConverterAnchor poolAnchor, uint256 amount) private { _systemStore.decNetworkTokensMinted(poolAnchor, amount); _networkTokenGovernance.burn(amount); } /** * @dev notify event subscribers on adding liquidity * * @param provider liquidity provider * @param poolToken pool token * @param reserveToken reserve token * @param poolAmount amount of pool tokens to protect * @param reserveAmount amount of reserve tokens to protect */ function notifyEventSubscribersOnAddingLiquidity( address provider, IDSToken poolToken, IReserveToken reserveToken, uint256 poolAmount, uint256 reserveAmount ) private { address[] memory subscribers = _settings.subscribers(); uint256 length = subscribers.length; for (uint256 i = 0; i < length; i++) { ILiquidityProtectionEventsSubscriber(subscribers[i]).onAddingLiquidity( provider, poolToken, reserveToken, poolAmount, reserveAmount ); } } /** * @dev notify event subscribers on removing liquidity * * @param id position id * @param provider liquidity provider * @param poolToken pool token * @param reserveToken reserve token * @param poolAmount amount of pool tokens to protect * @param reserveAmount amount of reserve tokens to protect */ function notifyEventSubscribersOnRemovingLiquidity( uint256 id, address provider, IDSToken poolToken, IReserveToken reserveToken, uint256 poolAmount, uint256 reserveAmount ) private { address[] memory subscribers = _settings.subscribers(); uint256 length = subscribers.length; for (uint256 i = 0; i < length; i++) { ILiquidityProtectionEventsSubscriber(subscribers[i]).onRemovingLiquidity( id, provider, poolToken, reserveToken, poolAmount, reserveAmount ); } } // utility to get the reserve balances function converterReserveBalances( IConverter converter, IReserveToken reserveToken1, IReserveToken reserveToken2 ) private view returns (uint256, uint256) { return (converter.getConnectorBalance(reserveToken1), converter.getConnectorBalance(reserveToken2)); } // utility to get the other reserve function converterOtherReserve(IConverter converter, IReserveToken thisReserve) private view returns (IReserveToken) { IReserveToken otherReserve = converter.connectorTokens(0); return otherReserve != thisReserve ? otherReserve : converter.connectorTokens(1); } // utility to get the owner function ownedBy(IOwned owned) private view returns (address) { return owned.owner(); } /** * @dev returns whether the provided reserve token is the network token * * @return whether the provided reserve token is the network token */ function isNetworkToken(IReserveToken reserveToken) private view returns (bool) { return address(reserveToken) == address(_networkToken); } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"contract ILiquidityProtectionSettings","name":"settings","type":"address"},{"internalType":"contract ILiquidityProtectionStore","name":"store","type":"address"},{"internalType":"contract ILiquidityProtectionStats","name":"stats","type":"address"},{"internalType":"contract ILiquidityProtectionSystemStore","name":"systemStore","type":"address"},{"internalType":"contract ITokenHolder","name":"wallet","type":"address"},{"internalType":"contract ITokenGovernance","name":"networkTokenGovernance","type":"address"},{"internalType":"contract ITokenGovernance","name":"govTokenGovernance","type":"address"},{"internalType":"contract ICheckpointStore","name":"lastRemoveCheckpointStore","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_prevOwner","type":"address"},{"indexed":true,"internalType":"address","name":"_newOwner","type":"address"}],"name":"OwnerUpdate","type":"event"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"acceptStoreOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"acceptWalletOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IConverterAnchor","name":"poolAnchor","type":"address"},{"internalType":"contract IReserveToken","name":"reserveToken","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"addLiquidity","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"contract IConverterAnchor","name":"poolAnchor","type":"address"},{"internalType":"contract IReserveToken","name":"reserveToken","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"addLiquidityFor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"startIndex","type":"uint256"},{"internalType":"uint256","name":"endIndex","type":"uint256"}],"name":"claimBalance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"newOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IConverterAnchor","name":"poolAnchor","type":"address"}],"name":"poolAvailableSpace","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IDSToken","name":"poolToken","type":"address"},{"internalType":"contract IReserveToken","name":"reserveToken","type":"address"},{"internalType":"uint256","name":"reserveAmount","type":"uint256"},{"internalType":"uint256","name":"poolRateN","type":"uint256"},{"internalType":"uint256","name":"poolRateD","type":"uint256"},{"internalType":"uint256","name":"reserveRateN","type":"uint256"},{"internalType":"uint256","name":"reserveRateD","type":"uint256"}],"name":"poolROI","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint32","name":"portion","type":"uint32"}],"name":"removeLiquidity","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint32","name":"portion","type":"uint32"},{"internalType":"uint256","name":"removeTimestamp","type":"uint256"}],"name":"removeLiquidityReturn","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"settings","outputs":[{"internalType":"contract ILiquidityProtectionSettings","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stats","outputs":[{"internalType":"contract ILiquidityProtectionStats","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"store","outputs":[{"internalType":"contract ILiquidityProtectionStore","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"systemStore","outputs":[{"internalType":"contract ILiquidityProtectionSystemStore","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"address","name":"newProvider","type":"address"}],"name":"transferPosition","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"address","name":"newProvider","type":"address"},{"internalType":"address","name":"target","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"transferPositionAndCall","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferStoreOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferWalletOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"wallet","outputs":[{"internalType":"contract ITokenHolder","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]
Contract Creation Code
6101c060405260016002553480156200001757600080fd5b50604051620059c7380380620059c783398181016040526101008110156200003e57600080fd5b508051602082015160408301516060840151608085015160a086015160c087015160e090970151600080546001600160a01b03191633179055959694959394929391929091876200008f816200022e565b876200009b816200022e565b87620000a7816200022e565b87620000b3816200022e565b87620000bf816200022e565b85620000cb816200022e565b6001600160601b031960608f811b82166080528e811b821660a0528d811b821660c0528c811b821660e0528b811b8216610100528a811b82166101405289811b82166101805288901b166101a05260408051637e062a3560e11b815290516001600160a01b038b169163fc0c546a916004808301926020929190829003018186803b1580156200015a57600080fd5b505afa1580156200016f573d6000803e3d6000fd5b505050506040513d60208110156200018657600080fd5b505160601b6001600160601b0319166101205260408051637e062a3560e11b815290516001600160a01b038a169163fc0c546a916004808301926020929190829003018186803b158015620001da57600080fd5b505afa158015620001ef573d6000803e3d6000fd5b505050506040513d60208110156200020657600080fd5b505160601b6001600160601b03191661016052506200028d9c50505050505050505050505050565b6001600160a01b0381166200028a576040805162461bcd60e51b815260206004820152601360248201527f4552525f494e56414c49445f4144445245535300000000000000000000000000604482015290519081900360640190fd5b50565b60805160601c60a05160601c60c05160601c60e05160601c6101005160601c6101205160601c6101405160601c6101605160601c6101805160601c6101a05160601c6155836200044460003980612f7b52508061219c52806142a452508061217052508061266b52806139915280613f305250806109d4528061152c52806117635280611dfe528061252e52806125a252806126d8528061272f5280614165528061427052806143685250806105ff528061067452806109a55280610f0952806122c3528061248052806126fa5280614775525080610c625280610e47528061169952806117b552806120b6528061233452806123ed52806139025280613ea152806141bd52806145c252806147a45250806112345280612f17528061309252806130f6525080610705528061090a5280610e735280610edd528061105e5280611a755280612d305280612e6552806131fe5280613ab95250806111035280611258528061134e5280611434528061156f528061160f5280612004528061285a5280613563528061360652806137c55280613a2a52806143a75280614494528061453452806148d75280614ad752506155836000f3fe6080604052600436106101395760003560e01c8063975057e7116100ab578063caee4c8f1161006f578063caee4c8f146104df578063d4ee1d901461051b578063d80528ae14610530578063e06174e414610545578063e4a767261461055a578063f2fde38b1461059057610140565b8063975057e714610338578063bf3b11011461034d578063c1f85f3d14610380578063c2250a9914610451578063c83df6631461048457610140565b80636d533e9b116100fd5780636d533e9b14610254578063782ed90c146102ae57806379ba5097146102e4578063879015e8146102f957806389d94b461461030e5780638da5cb5b1461032357610140565b806324afe2d91461014557806328790b5a14610191578063521eb273146101a857806355bd513f146101d9578063630d8c631461022457610140565b3661014057005b600080fd5b34801561015157600080fd5b506101786004803603602081101561016857600080fd5b50356001600160a01b03166105c3565b6040805192835260208301919091528051918290030190f35b34801561019d57600080fd5b506101a66105f5565b005b3480156101b457600080fd5b506101bd610672565b604080516001600160a01b039092168252519081900360200190f35b3480156101e557600080fd5b50610212600480360360408110156101fc57600080fd5b50803590602001356001600160a01b0316610696565b60408051918252519081900360200190f35b34801561023057600080fd5b506101a66004803603604081101561024757600080fd5b50803590602001356106c6565b34801561026057600080fd5b506102906004803603606081101561027757600080fd5b5080359063ffffffff6020820135169060400135610a71565b60408051938452602084019290925282820152519081900360600190f35b3480156102ba57600080fd5b506101a6600480360360408110156102d157600080fd5b508035906020013563ffffffff16610d63565b3480156102f057600080fd5b506101a6610d8e565b34801561030557600080fd5b506101bd610e45565b34801561031a57600080fd5b506101a6610e69565b34801561032f57600080fd5b506101bd610ecc565b34801561034457600080fd5b506101bd610edb565b34801561035957600080fd5b506101a66004803603602081101561037057600080fd5b50356001600160a01b0316610eff565b34801561038c57600080fd5b50610212600480360360808110156103a357600080fd5b8135916001600160a01b03602082013581169260408301359091169190810190608081016060820135600160201b8111156103dd57600080fd5b8201836020820111156103ef57600080fd5b803590602001918460018302840111600160201b8311171561041057600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550610f91945050505050565b34801561045d57600080fd5b506101a66004803603602081101561047457600080fd5b50356001600160a01b0316611054565b34801561049057600080fd5b50610212600480360360e08110156104a757600080fd5b506001600160a01b03813581169160208101359091169060408101359060608101359060808101359060a08101359060c001356110cb565b610212600480360360808110156104f557600080fd5b506001600160a01b038135811691602081013582169160408201351690606001356111c3565b34801561052757600080fd5b506101bd611223565b34801561053c57600080fd5b506101bd611232565b34801561055157600080fd5b506101bd611256565b6102126004803603606081101561057057600080fd5b506001600160a01b0381358116916020810135909116906040013561127a565b34801561059c57600080fd5b506101a6600480360360208110156105b357600080fd5b50356001600160a01b03166112ce565b600080826105d08161134c565b6105d981611432565b6105e28461151c565b6105eb8561175e565b9250925050915091565b6105fd611861565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166379ba50976040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561065857600080fd5b505af115801561066c573d6000803e3d6000fd5b50505050565b7f000000000000000000000000000000000000000000000000000000000000000090565b60006106a06118b6565b60028055816106ae816118fe565b6106b933858561194f565b6001600255949350505050565b6106ce6118b6565b6002805560408051637a1036f560e11b81523360048201526024810184905260448101839052905160609182916001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169163f4206dea916064808301926000929190829003018186803b15801561074b57600080fd5b505afa15801561075f573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604090815281101561078857600080fd5b8101908080516040519392919084600160201b8211156107a757600080fd5b9083019060208201858111156107bc57600080fd5b82518660208202830111600160201b821117156107d857600080fd5b82525081516020918201928201910280838360005b838110156108055781810151838201526020016107ed565b5050505090500160405260200180516040519392919084600160201b82111561082d57600080fd5b90830190602082018581111561084257600080fd5b82518660208202830111600160201b8211171561085e57600080fd5b82525081516020918201928201910280838360005b8381101561088b578181015183820152602001610873565b505050509050016040525050509150915060008083519050825181146108ad57fe5b805b801561099c5760001981016108c2611996565b8582815181106108ce57fe5b602002602001015111156108e25750610993565b604080516390e0661b60e01b8152336004820152898301602482015290516001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016916390e0661b91604480830192600092919082900301818387803b15801561095157600080fd5b505af1158015610965573d6000803e3d6000fd5b5050505061098f86828151811061097857fe5b60200260200101518561199a90919063ffffffff16565b9350505b600019016108af565b508115610a64577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316635e35359e7f000000000000000000000000000000000000000000000000000000000000000033856040518463ffffffff1660e01b815260040180846001600160a01b03168152602001836001600160a01b031681526020018281526020019350505050600060405180830381600087803b158015610a4b57600080fd5b505af1158015610a5f573d6000803e3d6000fd5b505050505b5050600160025550505050565b600080600084610a80816119fd565b610a886153f7565b610a9188611a63565b80519091506001600160a01b0316610ae1576040805162461bcd60e51b815260206004820152600e60248201526d11549497d253959053125117d25160921b604482015290519081900360640190fd5b8060e00151861015610b32576040805162461bcd60e51b815260206004820152601560248201527404552525f494e56414c49445f54494d455354414d5605c1b604482015290519081900360640190fd5b63ffffffff8716620f424014610b97576060810151620f424090610b5f9063ffffffff8a811690611b6d16565b81610b6657fe5b0460608201526080810151620f424090610b899063ffffffff8a811690611b6d16565b81610b9057fe5b0460808201525b610b9f615457565b610bbb826020015183604001518460a001518560c00151611bc6565b90506000610be28360200151846040015185606001518660800151868860e001518e611ce6565b9050610bf18360400151611dfc565b15610c0757955085945060009350610d59915050565b610c0f61548c565b610c2184602001518560400151611e2e565b90506000610c546002836000015181610c3657fe5b04610c4e846020015186611b6d90919063ffffffff16565b90611f53565b90506000610d0386606001517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316635121220c89602001516040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015610cd157600080fd5b505afa158015610ce5573d6000803e3d6000fd5b505050506040513d6020811015610cfb57600080fd5b50519061199a565b9050808211610d125781610d14565b805b91506000610d3a8460200151610c4e6002876000015181610d3157fe5b87919004611b6d565b90506000610d49868389611fba565b959b509099509397505050505050505b5093509350939050565b610d6b6118b6565b6002805580610d79816119fd565b610d8433848461209f565b5050600160025550565b6001546001600160a01b03163314610de1576040805162461bcd60e51b815260206004820152601160248201527011549497d050d0d154d4d7d11153925151607a1b604482015290519081900360640190fd5b600154600080546040516001600160a01b0393841693909116917f343765429aea5a34b3ff6a3785a98a5abb2597aca87bfbb58632c173d585373a91a360018054600080546001600160a01b03199081166001600160a01b03841617909155169055565b7f000000000000000000000000000000000000000000000000000000000000000090565b610e71611861565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166379ba50976040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561065857600080fd5b6000546001600160a01b031681565b7f000000000000000000000000000000000000000000000000000000000000000090565b610f07611861565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663f2fde38b826040518263ffffffff1660e01b815260040180826001600160a01b03168152602001915050600060405180830381600087803b158015610f7657600080fd5b505af1158015610f8a573d6000803e3d6000fd5b5050505050565b6000610f9b6118b6565b6002805583610fa9816118fe565b83610fb3816118fe565b835160041115611002576040805162461bcd60e51b81526020600482015260156024820152744552525f494e56414c49445f43414c4c5f4441544160581b604482015290519081900360640190fd5b600061100f33898961194f565b905061104386866040518060400160405280600f81526020016e11549497d0d0531317d19052531151608a1b8152506127ec565b506001600255979650505050505050565b61105c611861565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663f2fde38b826040518263ffffffff1660e01b815260040180826001600160a01b03168152602001915050600060405180830381600087803b158015610f7657600080fd5b6000806110dc86610c4e8988611b6d565b90506110e6615457565b6110f28a8a8787611bc6565b905060006111a18b8b858c866111947f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663ce3f3adb6040518163ffffffff1660e01b815260040160206040518083038186803b15801561115a57600080fd5b505afa15801561116e573d6000803e3d6000fd5b505050506040513d602081101561118457600080fd5b505161118e611996565b906127fb565b61119c611996565b611ce6565b90506111b489610c4e83620f4240611b6d565b9b9a5050505050505050505050565b60006111cd6118b6565b60028055846111db816118fe565b846111e58161134c565b6111ee81611432565b85856111fa8282612858565b8561120481612958565b6112108a8a8a8a61299e565b60016002559a9950505050505050505050565b6001546001600160a01b031681565b7f000000000000000000000000000000000000000000000000000000000000000090565b7f000000000000000000000000000000000000000000000000000000000000000090565b60006112846118b6565b60028055836112928161134c565b61129b81611432565b84846112a78282612858565b846112b181612958565b6112bd3389898961299e565b600160025598975050505050505050565b6112d6611861565b6000546001600160a01b038281169116141561132a576040805162461bcd60e51b815260206004820152600e60248201526d22a9292fa9a0a6a2afa7aba722a960911b604482015290519081900360640190fd5b600180546001600160a01b0319166001600160a01b0392909216919091179055565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d4f63148826040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156113b957600080fd5b505afa1580156113cd573d6000803e3d6000fd5b505050506040513d60208110156113e357600080fd5b505161142f576040805162461bcd60e51b815260206004820152601660248201527511549497d413d3d317d393d517d4d5541413d495115160521b604482015290519081900360640190fd5b50565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316632b26a982826040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561149f57600080fd5b505afa1580156114b3573d6000803e3d6000fd5b505050506040513d60208110156114c957600080fd5b505161142f576040805162461bcd60e51b815260206004820152601860248201527f4552525f504f4f4c5f4e4f545f57484954454c49535445440000000000000000604482015290519081900360640190fd5b600080611528836129fd565b90507f000000000000000000000000000000000000000000000000000000000000000060006115578383612a6a565b9050600080611567858486612b6d565b9150915060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663943fd08a896040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156115da57600080fd5b505afa1580156115ee573d6000803e3d6000fd5b505050506040513d602081101561160457600080fd5b5051905080611695577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663b97b55ce6040518163ffffffff1660e01b815260040160206040518083038186803b15801561166657600080fd5b505afa15801561167a573d6000803e3d6000fd5b505050506040513d602081101561169057600080fd5b505190505b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663350ed8e78a6040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561170457600080fd5b505afa158015611718573d6000803e3d6000fd5b505050506040513d602081101561172e57600080fd5b5051905060008161173f8482612c6d565b03905061175084610c4e8388611b6d565b9a9950505050505050505050565b6000817f000000000000000000000000000000000000000000000000000000000000000061178a61548c565b6117948383611e2e565b90506118588160200151610c4e600161118e856000015161185287600001517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316635121220c8c6040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561182057600080fd5b505afa158015611834573d6000803e3d6000fd5b505050506040513d602081101561184a57600080fd5b505190611b6d565b9061199a565b95945050505050565b6000546001600160a01b031633146118b4576040805162461bcd60e51b815260206004820152601160248201527011549497d050d0d154d4d7d11153925151607a1b604482015290519081900360640190fd5b565b6001600254146118b4576040805162461bcd60e51b815260206004820152600e60248201526d4552525f5245454e5452414e435960901b604482015290519081900360640190fd5b6001600160a01b03811661142f576040805162461bcd60e51b81526020600482015260136024820152724552525f494e56414c49445f4144445245535360681b604482015290519081900360640190fd5b60006119596153f7565b6119678585620f4240612c83565b905061198b8382602001518360400151846060015185608001518660e00151613009565b9150505b9392505050565b4290565b6000828201838110156119f4576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b90505b92915050565b60008163ffffffff16118015611a1c5750620f424063ffffffff821611155b61142f576040805162461bcd60e51b815260206004820152601360248201527222a9292fa4a72b20a624a22fa827a92a24a7a760691b604482015290519081900360640190fd5b611a6b6153f7565b611a736153f7565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316635290ffbb846040518263ffffffff1660e01b8152600401808281526020019150506101006040518083038186803b158015611ad857600080fd5b505afa158015611aec573d6000803e3d6000fd5b505050506040513d610100811015611b0357600080fd5b50805160208083015160408085015160608087015160808089015160a0808b015160c0808d015160e09d8e01519d8f019d909d528d019b909b528b01999099528901979097528701959095526001600160a01b039485169086015283169084015216815292915050565b600082611b7c575060006119f7565b82820282848281611b8957fe5b04146119f45760405162461bcd60e51b81526004018080602001828103825260218152602001806154cd6021913960400191505060405180910390fd5b611bce615457565b611bd661548c565b611bde61548c565b611be88787613280565b915091506001600160801b038511158015611c0a57506001600160801b038411155b8015611c1e575081516001600160801b0310155b8015611c3557506001600160801b03826020015111155b8015611c49575080516001600160801b0310155b8015611c6057506001600160801b03816020015111155b611c6657fe5b6040518060c00160405280866001600160801b03168152602001856001600160801b0316815260200183600001516001600160801b0316815260200183602001516001600160801b0316815260200182600001516001600160801b0316815260200182602001516001600160801b0316815250925050505b949350505050565b6000611cf061548c565b611cfa8989611e2e565b9050611d0461548c565b604051806040016040528087600001516001600160801b0316815260200187602001516001600160801b03168152509050611d3d61548c565b50604080518082018252908701516001600160801b0390811682526060880151166020820152611d6b61548c565b604051806040016040528089608001516001600160801b031681526020018960a001516001600160801b031681525090506000611daa8b868686613370565b9050611db461548c565b611dbe858461347b565b9050611dc861548c565b611dd28a8a613549565b9050611de98c611de28e86612c6d565b84846136e5565b9f9e505050505050505050505050505050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0390811691161490565b611e3661548c565b6000836001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b158015611e7157600080fd5b505afa158015611e85573d6000803e3d6000fd5b505050506040513d6020811015611e9b57600080fd5b505190506000611eaa856129fd565b90506000816001600160a01b031663d8959512866040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015611efb57600080fd5b505afa158015611f0f573d6000803e3d6000fd5b505050506040513d6020811015611f2557600080fd5b50516040805180820190915290915080611f40836002611b6d565b8152602001939093525090949350505050565b6000808211611fa9576040805162461bcd60e51b815260206004820152601a60248201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604482015290519081900360640190fd5b818381611fb257fe5b049392505050565b6000828411611fcb5750600061198f565b60006120008360a001516001600160801b0316610c4e85608001516001600160801b0316878903611b6d90919063ffffffff16565b90507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663a80c76ff6040518163ffffffff1660e01b815260040160206040518083038186803b15801561205b57600080fd5b505afa15801561206f573d6000803e3d6000fd5b505050506040513d602081101561208557600080fd5b5051811061209457905061198f565b506000949350505050565b6120a76153f7565b6120b2848484612c83565b90507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663332100fa826020015183606001516040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050600060405180830381600087803b15801561213357600080fd5b505af1158015612147573d6000803e3d6000fd5b505050506121588160400151611dfc565b1561221d57608081015161219a906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169086903090613767565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166342966c6882608001516040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b15801561220457600080fd5b505af1158015612218573d6000803e3d6000fd5b505050505b612225615457565b612241826020015183604001518460a001518560c00151611bc6565b905061228381604001516001600160801b031682606001516001600160801b031683608001516001600160801b03168460a001516001600160801b03166137c1565b60006122aa8360200151846040015185606001518660800151868860e0015161119c611996565b90506122b98360400151611dfc565b156122ff576122ed7f0000000000000000000000000000000000000000000000000000000000000000846020015183613900565b6122f78682613a23565b5050506127e7565b61230761548c565b61231984602001518560400151611e2e565b9050600061232e6002836000015181610c3657fe5b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316635121220c87602001516040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156123a357600080fd5b505afa1580156123b7573d6000803e3d6000fd5b505050506040513d60208110156123cd57600080fd5b505190508082116123de57816123e0565b805b91506000866020015190507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166319c6a5e48860200151856040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050600060405180830381600087803b15801561246657600080fd5b505af115801561247a573d6000803e3d6000fd5b505050507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316635e35359e8230866040518463ffffffff1660e01b815260040180846001600160a01b03168152602001836001600160a01b031681526020018281526020019350505050600060405180830381600087803b15801561250657600080fd5b505af115801561251a573d6000803e3d6000fd5b5050505061255287602001518489604001517f0000000000000000000000000000000000000000000000000000000000000000613b60565b604087015160009061256d906001600160a01b031630613d36565b6040890151909150612589906001600160a01b03168c83613ddf565b600061259687838a611fba565b9050801561272b5760007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561260d57600080fd5b505afa158015612621573d6000803e3d6000fd5b505050506040513d602081101561263757600080fd5b50519050818110156126cb57604080516340c10f1960e01b8152306004820152828403602482015290516001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016916340c10f1991604480830192600092919082900301818387803b1580156126b257600080fd5b505af11580156126c6573d6000803e3d6000fd5b505050505b61271f6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000167f000000000000000000000000000000000000000000000000000000000000000084613e4d565b6127298d83613a23565b505b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561279a57600080fd5b505afa1580156127ae573d6000803e3d6000fd5b505050506040513d60208110156127c457600080fd5b5051905080156127dc576127dc8a6020015182613e9f565b505050505050505050505b505050565b6060611cde8484600085613fb0565b600082821115612852576040805162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b50900390565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316639e571a6a83836040518363ffffffff1660e01b815260040180836001600160a01b03168152602001826001600160a01b031681526020019250505060206040518083038186803b1580156128d657600080fd5b505afa1580156128ea573d6000803e3d6000fd5b505050506040513d602081101561290057600080fd5b505115612954576040805162461bcd60e51b815260206004820152601a60248201527f4552525f4144445f4c49515549444954595f44495341424c4544000000000000604482015290519081900360640190fd5b5050565b6000811161142f576040805162461bcd60e51b815260206004820152600e60248201526d4552525f5a45524f5f56414c554560901b604482015290519081900360640190fd5b60006129a983611dfc565b156129ca576129b8600061410c565b6129c3858584614160565b9050611cde565b6129f16129df846001600160a01b031661433e565b6129ea5760006129ec565b825b61410c565b61185885858585614363565b6000816001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b158015612a3857600080fd5b505afa158015612a4c573d6000803e3d6000fd5b505050506040513d6020811015612a6257600080fd5b505192915050565b600080836001600160a01b03166319b6401560006040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b158015612ab257600080fd5b505afa158015612ac6573d6000803e3d6000fd5b505050506040513d6020811015612adc57600080fd5b505190506001600160a01b0380821690841614156119f457836001600160a01b03166319b6401560016040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b158015612b3957600080fd5b505afa158015612b4d573d6000803e3d6000fd5b505050506040513d6020811015612b6357600080fd5b5051949350505050565b600080846001600160a01b031663d8959512856040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015612bbd57600080fd5b505afa158015612bd1573d6000803e3d6000fd5b505050506040513d6020811015612be757600080fd5b505160408051636c4aca8960e11b81526001600160a01b03868116600483015291519188169163d895951291602480820192602092909190829003018186803b158015612c3357600080fd5b505afa158015612c47573d6000803e3d6000fd5b505050506040513d6020811015612c5d57600080fd5b505190925090505b935093915050565b6000818311612c7c57816119f4565b5090919050565b612c8b6153f7565b612c936153f7565b612c9d848661485a565b9050612cac8160200151611432565b612cb4611996565b8160e0015110612cfb576040805162461bcd60e51b815260206004820152600d60248201526c4552525f544f4f5f4541524c5960981b604482015290519081900360640190fd5b63ffffffff8316620f42401415612db157612d2e84826000015183602001518460400151856060015186608001516148d3565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316636f366b71856040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b158015612d9457600080fd5b505af1158015612da8573d6000803e3d6000fd5b50505050612ec7565b60608101516080820151620f4240612dd28363ffffffff88811690611b6d16565b81612dd957fe5b0460608401526080830151620f424090612dfc9063ffffffff88811690611b6d16565b81612e0357fe5b04608084018190528351602085015160408601516060870151612e2b948b94939291906148d3565b606083015160808401516040805163161139bd60e31b8152600481018a905292850360248401529083036044830152516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169163b089cde891606480830192600092919082900301818387803b158015612eac57600080fd5b505af1158015612ec0573d6000803e3d6000fd5b5050505050505b805160208201516040808401516060850151608086015183516327396b6d60e01b81526001600160a01b0396871660048201529486166024860152918516604485015260648401526084830152517f0000000000000000000000000000000000000000000000000000000000000000909216916327396b6d9160a48082019260009290919082900301818387803b158015612f6157600080fd5b505af1158015612f75573d6000803e3d6000fd5b505050507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316631d092adf866040518263ffffffff1660e01b815260040180826001600160a01b03168152602001915050600060405180830381600087803b158015612fe857600080fd5b505af1158015612ffc573d6000803e3d6000fd5b5092979650505050505050565b600061301361548c565b61301b61548c565b6130258888613280565b9150915061304582600001518360200151836000015184602001516137c1565b6130528989898989614ad3565b60408051630aa558ef60e41b81526001600160a01b038b811660048301528a811660248301528981166044830152606482018990526084820188905291517f00000000000000000000000000000000000000000000000000000000000000009092169163aa558ef09160a48082019260009290919082900301818387803b1580156130dc57600080fd5b505af11580156130f0573d6000803e3d6000fd5b505050507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663fd4bc1e68a8a6040518363ffffffff1660e01b815260040180836001600160a01b03168152602001826001600160a01b0316815260200192505050602060405180830381600087803b15801561317457600080fd5b505af1158015613188573d6000803e3d6000fd5b505050506040513d602081101561319e57600080fd5b50508151602080840151604080516361d5f08760e01b81526001600160a01b038e811660048301528d811660248301528c81166044830152606482018c9052608482018b905260a482019590955260c481019290925260e48201889052517f0000000000000000000000000000000000000000000000000000000000000000909316926361d5f08792610104808401939192918290030181600087803b15801561324757600080fd5b505af115801561325b573d6000803e3d6000fd5b505050506040513d602081101561327157600080fd5b50519998505050505050505050565b61328861548c565b61329061548c565b600061329b856129fd565b905060006132a98286612a6a565b90506000806132b9848489612b6d565b91509150600080856001600160a01b0316631f0181bc8a6040518263ffffffff1660e01b815260040180826001600160a01b03168152602001915050604080518083038186803b15801561330c57600080fd5b505afa158015613320573d6000803e3d6000fd5b505050506040513d604081101561333657600080fd5b5080516020918201516040805180820182529788528784019690965285518087019096529085529084015250919890975095505050505050565b825181516020840151600092839261339b92613395916133909190611b6d565b614cc0565b90611b6d565b905060006133ca86602001516133956133c587602001518960000151611b6d90919063ffffffff16565b614ce0565b9050818702878382816133d957fe5b0414156133f4578181816133e957fe5b049350505050611cde565b600080898511613405578985613408565b848a5b915091506000806134258487856000198161341f57fe5b04614d36565b91509150600061343f8488878161343857fe5b0490611b6d565b9050811561346b5761345c81838686028161345657fe5b04612c6d565b98505050505050505050611cde565b9c9b505050505050505050505050565b61348361548c565b602083015182516000916134979190611b6d565b845160208501519192506000916134ad91611b6d565b90508181026000828483816134be57fe5b04146134dc576134cd83614ce0565b6134d685614ce0565b026134e5565b6134e582614ce0565b905060006134f3858561199a565b905060028106613526576002810490506040518060400160405280838303815260200182815250955050505050506119f7565b604080518082019091526002909202810382526020820152935050505092915050565b61355161548c565b600061355d83856127fb565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316632c560f896040518163ffffffff1660e01b815260040160206040518083038186803b1580156135ba57600080fd5b505afa1580156135ce573d6000803e3d6000fd5b505050506040513d60208110156135e457600080fd5b50516040805163ce3f3adb60e01b815290519192506000916001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169163ce3f3adb916004808301926020929190829003018186803b15801561364c57600080fd5b505afa158015613660573d6000803e3d6000fd5b505050506040513d602081101561367657600080fd5b50519050818310156136a357604051806040016040528060008152602001600181525093505050506119f7565b8083106136cb57604051806040016040528060018152602001600181525093505050506119f7565b604080518082019091529283526020830152509392505050565b805160009081906136f69087611b6d565b6020840151909150600061371361370d8484612c6d565b88612c6d565b905060008061373088600001518960200151856000198161341f57fe5b909250905061175061374f6137458387611b6d565b610c4e8589611b6d565b61185283610c4e61376082886127fb565b8e90611b6d565b604080516001600160a01b0380861660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b03166323b872dd60e01b17905261066c908590614d7e565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166324a088686040518163ffffffff1660e01b815260040160206040518083038186803b15801561381c57600080fd5b505afa158015613830573d6000803e3d6000fd5b505050506040513d602081101561384657600080fd5b5051620f42400363ffffffff908116915060009061387290839061339590829082908b908990611b6d16565b90506000613889620f424061339585818a8a611b6d565b905060006138a0620f424061339581818c8a611b6d565b90508183111580156138b25750808211155b6138f6576040805162461bcd60e51b815260206004820152601060248201526f4552525f494e56414c49445f5241544560801b604482015290519081900360640190fd5b5050505050505050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663deacd84e83836040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050600060405180830381600087803b15801561397757600080fd5b505af115801561398b573d6000803e3d6000fd5b505050507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166340c10f1984836040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050600060405180830381600087803b158015613a0657600080fd5b505af1158015613a1a573d6000803e3d6000fd5b50505050505050565b6000613ab57f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663045544436040518163ffffffff1660e01b815260040160206040518083038186803b158015613a8157600080fd5b505afa158015613a95573d6000803e3d6000fd5b505050506040513d6020811015613aab57600080fd5b5051611852611996565b90507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663dbae3a5d8484846040518463ffffffff1660e01b815260040180846001600160a01b031681526020018381526020018281526020019350505050602060405180830381600087803b158015613b3657600080fd5b505af1158015613b4a573d6000803e3d6000fd5b505050506040513d6020811015610f8a57600080fd5b6000613b6b856129fd565b604080516002808252606080830184529394509091602083019080368337505060408051600280825260608083018452949550909250906020830190803683370190505090508482600081518110613bbf57fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508382600181518110613bed57fe5b60200260200101906001600160a01b031690816001600160a01b031681525050600181600081518110613c1c57fe5b602002602001018181525050600181600181518110613c3757fe5b602002602001018181525050826001600160a01b031663b127c0a58784846040518463ffffffff1660e01b8152600401808481526020018060200180602001838103835285818151815260200191508051906020019060200280838360005b83811015613cae578181015183820152602001613c96565b50505050905001838103825284818151815260200191508051906020019060200280838360005b83811015613ced578181015183820152602001613cd5565b5050505090500195505050505050600060405180830381600087803b158015613d1557600080fd5b505af1158015613d29573d6000803e3d6000fd5b5050505050505050505050565b6000613d418361433e565b15613d5757506001600160a01b038116316119f7565b613d6083614e2f565b6001600160a01b03166370a08231836040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015613dac57600080fd5b505afa158015613dc0573d6000803e3d6000fd5b505050506040513d6020811015613dd657600080fd5b50519392505050565b80613de9576127e7565b613df28361433e565b15613e33576040516001600160a01b0383169082156108fc029083906000818181858888f19350505050158015613e2d573d6000803e3d6000fd5b506127e7565b6127e78282613e4186614e2f565b6001600160a01b031691905b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b1790526127e7908490614d7e565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663802fa3ba83836040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050600060405180830381600087803b158015613f1657600080fd5b505af1158015613f2a573d6000803e3d6000fd5b505050507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166342966c68826040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b158015613f9457600080fd5b505af1158015613fa8573d6000803e3d6000fd5b505050505050565b606082471015613ff15760405162461bcd60e51b81526004018080602001828103825260268152602001806154a76026913960400191505060405180910390fd5b613ffa85614e32565b61404b576040805162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015290519081900360640190fd5b60006060866001600160a01b031685876040518082805190602001908083835b6020831061408a5780518252601f19909201916020918201910161406b565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d80600081146140ec576040519150601f19603f3d011682016040523d82523d6000602084013e6140f1565b606091505b5091509150614101828286614e38565b979650505050505050565b80341461142f576040805162461bcd60e51b815260206004820152601760248201527f4552525f4554485f414d4f554e545f4d49534d41544348000000000000000000604482015290519081900360640190fd5b6000827f000000000000000000000000000000000000000000000000000000000000000061418c61548c565b6141968383611e2e565b905060006141b98260000151610c4e846020015189611b6d90919063ffffffff16565b90507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166319c6a5e485836040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050600060405180830381600087803b15801561423257600080fd5b505af1158015614246573d6000803e3d6000fd5b505050506000614261898686858b61425c611996565b613009565b90506142986001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001633308a613767565b6142a28888613e9f565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166340c10f198a896040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050600060405180830381600087803b15801561431957600080fd5b505af115801561432d573d6000803e3d6000fd5b50929b9a5050505050505050505050565b6001600160a01b03811673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee14919050565b6000837f000000000000000000000000000000000000000000000000000000000000000082614391836129fd565b90506000806143a1838986612b6d565b915091507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166312588d0e6040518163ffffffff1660e01b815260040160206040518083038186803b1580156143fe57600080fd5b505afa158015614412573d6000803e3d6000fd5b505050506040513d602081101561442857600080fd5b505181101561447e576040805162461bcd60e51b815260206004820152601860248201527f4552525f4e4f545f454e4f5547485f4c49515549444954590000000000000000604482015290519081900360640190fd5b600061448e83610c4e8a85611b6d565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663943fd08a8c6040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156144ff57600080fd5b505afa158015614513573d6000803e3d6000fd5b505050506040513d602081101561452957600080fd5b50519050806145ba577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663b97b55ce6040518163ffffffff1660e01b815260040160206040518083038186803b15801561458b57600080fd5b505afa15801561459f573d6000803e3d6000fd5b505050506040513d60208110156145b557600080fd5b505190505b600061462d837f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663350ed8e78f6040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015610cd157600080fd5b90508181111561467d576040805162461bcd60e51b815260206004820152601660248201527511549497d3505617d05353d5539517d4915050d2115160521b604482015290519081900360640190fd5b614688308d85613900565b61469c6001600160a01b0388168785614edc565b6146ae8b6001600160a01b031661433e565b6146db576146c76001600160a01b038c1633308d614f0d565b6146db6001600160a01b038c16878c614edc565b6146e9868c898d8734614f48565b6000886001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561473857600080fd5b505afa15801561474c573d6000803e3d6000fd5b505050506040513d602081101561476257600080fd5b5051905061479a6001600160a01b038a167f000000000000000000000000000000000000000000000000000000000000000083613e4d565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001663332100fa8a6002840484036040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050600060405180830381600087803b15801561481657600080fd5b505af115801561482a573d6000803e3d6000fd5b505050506148488e8a8e6002858161483e57fe5b048f61425c611996565b9e9d5050505050505050505050505050565b6148626153f7565b61486a6153f7565b61487384611a63565b9050826001600160a01b031681600001516001600160a01b0316146119f4576040805162461bcd60e51b815260206004820152601160248201527011549497d050d0d154d4d7d11153925151607a1b604482015290519081900360640190fd5b60607f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316639e2ce9d26040518163ffffffff1660e01b815260040160006040518083038186803b15801561492e57600080fd5b505afa158015614942573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052602081101561496b57600080fd5b8101908080516040519392919084600160201b82111561498a57600080fd5b90830190602082018581111561499f57600080fd5b82518660208202830111600160201b821117156149bb57600080fd5b82525081516020918201928201910280838360005b838110156149e85781810151838201526020016149d0565b50505050905001604052505050905060008151905060005b81811015614ac857828181518110614a1457fe5b60200260200101516001600160a01b031663b8128fe68a8a8a8a8a8a6040518763ffffffff1660e01b815260040180878152602001866001600160a01b03168152602001856001600160a01b03168152602001846001600160a01b031681526020018381526020018281526020019650505050505050600060405180830381600087803b158015614aa457600080fd5b505af1158015614ab8573d6000803e3d6000fd5b505060019092019150614a009050565b505050505050505050565b60607f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316639e2ce9d26040518163ffffffff1660e01b815260040160006040518083038186803b158015614b2e57600080fd5b505afa158015614b42573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526020811015614b6b57600080fd5b8101908080516040519392919084600160201b821115614b8a57600080fd5b908301906020820185811115614b9f57600080fd5b82518660208202830111600160201b82111715614bbb57600080fd5b82525081516020918201928201910280838360005b83811015614be8578181015183820152602001614bd0565b50505050905001604052505050905060008151905060005b818110156138f657828181518110614c1457fe5b60200260200101516001600160a01b031663139c22ea89898989896040518663ffffffff1660e01b815260040180866001600160a01b03168152602001856001600160a01b03168152602001846001600160a01b0316815260200183815260200182815260200195505050505050600060405180830381600087803b158015614c9c57600080fd5b505af1158015614cb0573d6000803e3d6000fd5b505060019092019150614c009050565b600080614ccc83614ce0565b905082818202146119f7578060010161198f565b60008060028304600101905060006002828581614cf957fe5b04830181614d0357fe5b0490505b80821115614d2f578091506002828581614d1d57fe5b04830181614d2757fe5b049050614d07565b5092915050565b600080848484821180614d4857508481115b15614d5e57614d58828287615101565b90925090505b808214614d6f579092509050612c65565b50600196879650945050505050565b6060614dd3826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166127ec9092919063ffffffff16565b8051909150156127e757808060200190516020811015614df257600080fd5b50516127e75760405162461bcd60e51b815260040180806020018281038252602a8152602001806154ee602a913960400191505060405180910390fd5b90565b3b151590565b60608315614e4757508161198f565b825115614e575782518084602001fd5b8160405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b83811015614ea1578181015183820152602001614e89565b50505050905090810190601f168015614ece5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b614ee58361433e565b15614eef576127e7565b6127e78282614efd86614e2f565b6001600160a01b0316919061513a565b801580614f1e5750614f1e8461433e565b15614f285761066c565b61066c838383614f3788614e2f565b6001600160a01b0316929190613767565b6040805160028082526060808301845292602083019080368337505060408051600280825260608083018452949550909250906020830190803683370190505090508682600081518110614f9857fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508582600181518110614fc657fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508481600081518110614ff457fe5b602002602001018181525050838160018151811061500e57fe5b602002602001018181525050876001600160a01b0316637d8916bd84848460016040518563ffffffff1660e01b8152600401808060200180602001848152602001838103835286818151815260200191508051906020019060200280838360005b8381101561508757818101518382015260200161506f565b50505050905001838103825285818151815260200191508051906020019060200280838360005b838110156150c65781810151838201526020016150ae565b50505050905001955050505050506000604051808303818588803b1580156150ed57600080fd5b505af11580156127dc573d6000803e3d6000fd5b60008083851161511f576151168585856151ff565b91509150612c65565b60008061512d8688876151ff565b9890975095505050505050565b80615144576127e7565b60408051636eb1769f60e11b81523060048201526001600160a01b038481166024830152915160009286169163dd62ed3e916044808301926020929190829003018186803b15801561519557600080fd5b505afa1580156151a9573d6000803e3d6000fd5b505050506040513d60208110156151bf57600080fd5b505190508181106151d057506127e7565b80156151eb576151eb6001600160a01b0385168460006152b8565b61066c6001600160a01b03851684846152b8565b6000806000836000198161520f57fe5b0490508086111561524857600081600101878161522857fe5b04600101905080878161523757fe5b04965080868161524357fe5b049550505b8486146152a85785840285870187811061527957600061526883836153cb565b955050508385039250612c65915050565b60028888030487038210156152975760008694509450505050612c65565b600180870394509450505050612c65565b5050600290910493849350915050565b80158061533e575060408051636eb1769f60e11b81523060048201526001600160a01b03848116602483015291519185169163dd62ed3e91604480820192602092909190829003018186803b15801561531057600080fd5b505afa158015615324573d6000803e3d6000fd5b505050506040513d602081101561533a57600080fd5b5051155b6153795760405162461bcd60e51b81526004018080602001828103825260368152602001806155186036913960400191505060405180910390fd5b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b1790526127e7908490614d7e565b60006002820482038284816153dc57fe5b06816153e457fe5b048284816153ee57fe5b04019392505050565b60405180610100016040528060006001600160a01b0316815260200160006001600160a01b0316815260200160006001600160a01b0316815260200160008152602001600081526020016000815260200160008152602001600081525090565b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a081019190915290565b60405180604001604052806000815260200160008152509056fe416464726573733a20696e73756666696369656e742062616c616e636520666f722063616c6c536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f775361666545524332303a204552433230206f7065726174696f6e20646964206e6f7420737563636565645361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f20746f206e6f6e2d7a65726f20616c6c6f77616e6365a2646970667358221220c8b6271b30b2c24905ee56b1c37d0c40d8938c9618b1b8f1a1d621e472ed259664736f6c634300060c0033000000000000000000000000f7d28faa1fe9ea53279fe6e3cde75175859bdf46000000000000000000000000f5fab5dbd2f3bf675de4cb76517d4767013cfb550000000000000000000000009712bb50dc6efb8a3d7d12cea500a50967d2d471000000000000000000000000c4c5634de585d43daec8fa2a6fb6286cd9b87131000000000000000000000000d1d846312b819743974786050848d9b3d06b9b55000000000000000000000000a489c2b5b36835a327851ab917a80562b5afc2440000000000000000000000000887ae1251e180d7d453aedebee26e1639f20113000000000000000000000000f8a2fb650e25a26ce839d64be8a0abbcb0b87b32
Deployed Bytecode
0x6080604052600436106101395760003560e01c8063975057e7116100ab578063caee4c8f1161006f578063caee4c8f146104df578063d4ee1d901461051b578063d80528ae14610530578063e06174e414610545578063e4a767261461055a578063f2fde38b1461059057610140565b8063975057e714610338578063bf3b11011461034d578063c1f85f3d14610380578063c2250a9914610451578063c83df6631461048457610140565b80636d533e9b116100fd5780636d533e9b14610254578063782ed90c146102ae57806379ba5097146102e4578063879015e8146102f957806389d94b461461030e5780638da5cb5b1461032357610140565b806324afe2d91461014557806328790b5a14610191578063521eb273146101a857806355bd513f146101d9578063630d8c631461022457610140565b3661014057005b600080fd5b34801561015157600080fd5b506101786004803603602081101561016857600080fd5b50356001600160a01b03166105c3565b6040805192835260208301919091528051918290030190f35b34801561019d57600080fd5b506101a66105f5565b005b3480156101b457600080fd5b506101bd610672565b604080516001600160a01b039092168252519081900360200190f35b3480156101e557600080fd5b50610212600480360360408110156101fc57600080fd5b50803590602001356001600160a01b0316610696565b60408051918252519081900360200190f35b34801561023057600080fd5b506101a66004803603604081101561024757600080fd5b50803590602001356106c6565b34801561026057600080fd5b506102906004803603606081101561027757600080fd5b5080359063ffffffff6020820135169060400135610a71565b60408051938452602084019290925282820152519081900360600190f35b3480156102ba57600080fd5b506101a6600480360360408110156102d157600080fd5b508035906020013563ffffffff16610d63565b3480156102f057600080fd5b506101a6610d8e565b34801561030557600080fd5b506101bd610e45565b34801561031a57600080fd5b506101a6610e69565b34801561032f57600080fd5b506101bd610ecc565b34801561034457600080fd5b506101bd610edb565b34801561035957600080fd5b506101a66004803603602081101561037057600080fd5b50356001600160a01b0316610eff565b34801561038c57600080fd5b50610212600480360360808110156103a357600080fd5b8135916001600160a01b03602082013581169260408301359091169190810190608081016060820135600160201b8111156103dd57600080fd5b8201836020820111156103ef57600080fd5b803590602001918460018302840111600160201b8311171561041057600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550610f91945050505050565b34801561045d57600080fd5b506101a66004803603602081101561047457600080fd5b50356001600160a01b0316611054565b34801561049057600080fd5b50610212600480360360e08110156104a757600080fd5b506001600160a01b03813581169160208101359091169060408101359060608101359060808101359060a08101359060c001356110cb565b610212600480360360808110156104f557600080fd5b506001600160a01b038135811691602081013582169160408201351690606001356111c3565b34801561052757600080fd5b506101bd611223565b34801561053c57600080fd5b506101bd611232565b34801561055157600080fd5b506101bd611256565b6102126004803603606081101561057057600080fd5b506001600160a01b0381358116916020810135909116906040013561127a565b34801561059c57600080fd5b506101a6600480360360208110156105b357600080fd5b50356001600160a01b03166112ce565b600080826105d08161134c565b6105d981611432565b6105e28461151c565b6105eb8561175e565b9250925050915091565b6105fd611861565b7f000000000000000000000000d1d846312b819743974786050848d9b3d06b9b556001600160a01b03166379ba50976040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561065857600080fd5b505af115801561066c573d6000803e3d6000fd5b50505050565b7f000000000000000000000000d1d846312b819743974786050848d9b3d06b9b5590565b60006106a06118b6565b60028055816106ae816118fe565b6106b933858561194f565b6001600255949350505050565b6106ce6118b6565b6002805560408051637a1036f560e11b81523360048201526024810184905260448101839052905160609182916001600160a01b037f000000000000000000000000f5fab5dbd2f3bf675de4cb76517d4767013cfb55169163f4206dea916064808301926000929190829003018186803b15801561074b57600080fd5b505afa15801561075f573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604090815281101561078857600080fd5b8101908080516040519392919084600160201b8211156107a757600080fd5b9083019060208201858111156107bc57600080fd5b82518660208202830111600160201b821117156107d857600080fd5b82525081516020918201928201910280838360005b838110156108055781810151838201526020016107ed565b5050505090500160405260200180516040519392919084600160201b82111561082d57600080fd5b90830190602082018581111561084257600080fd5b82518660208202830111600160201b8211171561085e57600080fd5b82525081516020918201928201910280838360005b8381101561088b578181015183820152602001610873565b505050509050016040525050509150915060008083519050825181146108ad57fe5b805b801561099c5760001981016108c2611996565b8582815181106108ce57fe5b602002602001015111156108e25750610993565b604080516390e0661b60e01b8152336004820152898301602482015290516001600160a01b037f000000000000000000000000f5fab5dbd2f3bf675de4cb76517d4767013cfb5516916390e0661b91604480830192600092919082900301818387803b15801561095157600080fd5b505af1158015610965573d6000803e3d6000fd5b5050505061098f86828151811061097857fe5b60200260200101518561199a90919063ffffffff16565b9350505b600019016108af565b508115610a64577f000000000000000000000000d1d846312b819743974786050848d9b3d06b9b556001600160a01b0316635e35359e7f0000000000000000000000001f573d6fb3f13d689ff844b4ce37794d79a7ff1c33856040518463ffffffff1660e01b815260040180846001600160a01b03168152602001836001600160a01b031681526020018281526020019350505050600060405180830381600087803b158015610a4b57600080fd5b505af1158015610a5f573d6000803e3d6000fd5b505050505b5050600160025550505050565b600080600084610a80816119fd565b610a886153f7565b610a9188611a63565b80519091506001600160a01b0316610ae1576040805162461bcd60e51b815260206004820152600e60248201526d11549497d253959053125117d25160921b604482015290519081900360640190fd5b8060e00151861015610b32576040805162461bcd60e51b815260206004820152601560248201527404552525f494e56414c49445f54494d455354414d5605c1b604482015290519081900360640190fd5b63ffffffff8716620f424014610b97576060810151620f424090610b5f9063ffffffff8a811690611b6d16565b81610b6657fe5b0460608201526080810151620f424090610b899063ffffffff8a811690611b6d16565b81610b9057fe5b0460808201525b610b9f615457565b610bbb826020015183604001518460a001518560c00151611bc6565b90506000610be28360200151846040015185606001518660800151868860e001518e611ce6565b9050610bf18360400151611dfc565b15610c0757955085945060009350610d59915050565b610c0f61548c565b610c2184602001518560400151611e2e565b90506000610c546002836000015181610c3657fe5b04610c4e846020015186611b6d90919063ffffffff16565b90611f53565b90506000610d0386606001517f000000000000000000000000c4c5634de585d43daec8fa2a6fb6286cd9b871316001600160a01b0316635121220c89602001516040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015610cd157600080fd5b505afa158015610ce5573d6000803e3d6000fd5b505050506040513d6020811015610cfb57600080fd5b50519061199a565b9050808211610d125781610d14565b805b91506000610d3a8460200151610c4e6002876000015181610d3157fe5b87919004611b6d565b90506000610d49868389611fba565b959b509099509397505050505050505b5093509350939050565b610d6b6118b6565b6002805580610d79816119fd565b610d8433848461209f565b5050600160025550565b6001546001600160a01b03163314610de1576040805162461bcd60e51b815260206004820152601160248201527011549497d050d0d154d4d7d11153925151607a1b604482015290519081900360640190fd5b600154600080546040516001600160a01b0393841693909116917f343765429aea5a34b3ff6a3785a98a5abb2597aca87bfbb58632c173d585373a91a360018054600080546001600160a01b03199081166001600160a01b03841617909155169055565b7f000000000000000000000000c4c5634de585d43daec8fa2a6fb6286cd9b8713190565b610e71611861565b7f000000000000000000000000f5fab5dbd2f3bf675de4cb76517d4767013cfb556001600160a01b03166379ba50976040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561065857600080fd5b6000546001600160a01b031681565b7f000000000000000000000000f5fab5dbd2f3bf675de4cb76517d4767013cfb5590565b610f07611861565b7f000000000000000000000000d1d846312b819743974786050848d9b3d06b9b556001600160a01b031663f2fde38b826040518263ffffffff1660e01b815260040180826001600160a01b03168152602001915050600060405180830381600087803b158015610f7657600080fd5b505af1158015610f8a573d6000803e3d6000fd5b5050505050565b6000610f9b6118b6565b6002805583610fa9816118fe565b83610fb3816118fe565b835160041115611002576040805162461bcd60e51b81526020600482015260156024820152744552525f494e56414c49445f43414c4c5f4441544160581b604482015290519081900360640190fd5b600061100f33898961194f565b905061104386866040518060400160405280600f81526020016e11549497d0d0531317d19052531151608a1b8152506127ec565b506001600255979650505050505050565b61105c611861565b7f000000000000000000000000f5fab5dbd2f3bf675de4cb76517d4767013cfb556001600160a01b031663f2fde38b826040518263ffffffff1660e01b815260040180826001600160a01b03168152602001915050600060405180830381600087803b158015610f7657600080fd5b6000806110dc86610c4e8988611b6d565b90506110e6615457565b6110f28a8a8787611bc6565b905060006111a18b8b858c866111947f000000000000000000000000f7d28faa1fe9ea53279fe6e3cde75175859bdf466001600160a01b031663ce3f3adb6040518163ffffffff1660e01b815260040160206040518083038186803b15801561115a57600080fd5b505afa15801561116e573d6000803e3d6000fd5b505050506040513d602081101561118457600080fd5b505161118e611996565b906127fb565b61119c611996565b611ce6565b90506111b489610c4e83620f4240611b6d565b9b9a5050505050505050505050565b60006111cd6118b6565b60028055846111db816118fe565b846111e58161134c565b6111ee81611432565b85856111fa8282612858565b8561120481612958565b6112108a8a8a8a61299e565b60016002559a9950505050505050505050565b6001546001600160a01b031681565b7f0000000000000000000000009712bb50dc6efb8a3d7d12cea500a50967d2d47190565b7f000000000000000000000000f7d28faa1fe9ea53279fe6e3cde75175859bdf4690565b60006112846118b6565b60028055836112928161134c565b61129b81611432565b84846112a78282612858565b846112b181612958565b6112bd3389898961299e565b600160025598975050505050505050565b6112d6611861565b6000546001600160a01b038281169116141561132a576040805162461bcd60e51b815260206004820152600e60248201526d22a9292fa9a0a6a2afa7aba722a960911b604482015290519081900360640190fd5b600180546001600160a01b0319166001600160a01b0392909216919091179055565b7f000000000000000000000000f7d28faa1fe9ea53279fe6e3cde75175859bdf466001600160a01b031663d4f63148826040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156113b957600080fd5b505afa1580156113cd573d6000803e3d6000fd5b505050506040513d60208110156113e357600080fd5b505161142f576040805162461bcd60e51b815260206004820152601660248201527511549497d413d3d317d393d517d4d5541413d495115160521b604482015290519081900360640190fd5b50565b7f000000000000000000000000f7d28faa1fe9ea53279fe6e3cde75175859bdf466001600160a01b0316632b26a982826040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561149f57600080fd5b505afa1580156114b3573d6000803e3d6000fd5b505050506040513d60208110156114c957600080fd5b505161142f576040805162461bcd60e51b815260206004820152601860248201527f4552525f504f4f4c5f4e4f545f57484954454c49535445440000000000000000604482015290519081900360640190fd5b600080611528836129fd565b90507f0000000000000000000000001f573d6fb3f13d689ff844b4ce37794d79a7ff1c60006115578383612a6a565b9050600080611567858486612b6d565b9150915060007f000000000000000000000000f7d28faa1fe9ea53279fe6e3cde75175859bdf466001600160a01b031663943fd08a896040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156115da57600080fd5b505afa1580156115ee573d6000803e3d6000fd5b505050506040513d602081101561160457600080fd5b5051905080611695577f000000000000000000000000f7d28faa1fe9ea53279fe6e3cde75175859bdf466001600160a01b031663b97b55ce6040518163ffffffff1660e01b815260040160206040518083038186803b15801561166657600080fd5b505afa15801561167a573d6000803e3d6000fd5b505050506040513d602081101561169057600080fd5b505190505b60007f000000000000000000000000c4c5634de585d43daec8fa2a6fb6286cd9b871316001600160a01b031663350ed8e78a6040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561170457600080fd5b505afa158015611718573d6000803e3d6000fd5b505050506040513d602081101561172e57600080fd5b5051905060008161173f8482612c6d565b03905061175084610c4e8388611b6d565b9a9950505050505050505050565b6000817f0000000000000000000000001f573d6fb3f13d689ff844b4ce37794d79a7ff1c61178a61548c565b6117948383611e2e565b90506118588160200151610c4e600161118e856000015161185287600001517f000000000000000000000000c4c5634de585d43daec8fa2a6fb6286cd9b871316001600160a01b0316635121220c8c6040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561182057600080fd5b505afa158015611834573d6000803e3d6000fd5b505050506040513d602081101561184a57600080fd5b505190611b6d565b9061199a565b95945050505050565b6000546001600160a01b031633146118b4576040805162461bcd60e51b815260206004820152601160248201527011549497d050d0d154d4d7d11153925151607a1b604482015290519081900360640190fd5b565b6001600254146118b4576040805162461bcd60e51b815260206004820152600e60248201526d4552525f5245454e5452414e435960901b604482015290519081900360640190fd5b6001600160a01b03811661142f576040805162461bcd60e51b81526020600482015260136024820152724552525f494e56414c49445f4144445245535360681b604482015290519081900360640190fd5b60006119596153f7565b6119678585620f4240612c83565b905061198b8382602001518360400151846060015185608001518660e00151613009565b9150505b9392505050565b4290565b6000828201838110156119f4576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b90505b92915050565b60008163ffffffff16118015611a1c5750620f424063ffffffff821611155b61142f576040805162461bcd60e51b815260206004820152601360248201527222a9292fa4a72b20a624a22fa827a92a24a7a760691b604482015290519081900360640190fd5b611a6b6153f7565b611a736153f7565b7f000000000000000000000000f5fab5dbd2f3bf675de4cb76517d4767013cfb556001600160a01b0316635290ffbb846040518263ffffffff1660e01b8152600401808281526020019150506101006040518083038186803b158015611ad857600080fd5b505afa158015611aec573d6000803e3d6000fd5b505050506040513d610100811015611b0357600080fd5b50805160208083015160408085015160608087015160808089015160a0808b015160c0808d015160e09d8e01519d8f019d909d528d019b909b528b01999099528901979097528701959095526001600160a01b039485169086015283169084015216815292915050565b600082611b7c575060006119f7565b82820282848281611b8957fe5b04146119f45760405162461bcd60e51b81526004018080602001828103825260218152602001806154cd6021913960400191505060405180910390fd5b611bce615457565b611bd661548c565b611bde61548c565b611be88787613280565b915091506001600160801b038511158015611c0a57506001600160801b038411155b8015611c1e575081516001600160801b0310155b8015611c3557506001600160801b03826020015111155b8015611c49575080516001600160801b0310155b8015611c6057506001600160801b03816020015111155b611c6657fe5b6040518060c00160405280866001600160801b03168152602001856001600160801b0316815260200183600001516001600160801b0316815260200183602001516001600160801b0316815260200182600001516001600160801b0316815260200182602001516001600160801b0316815250925050505b949350505050565b6000611cf061548c565b611cfa8989611e2e565b9050611d0461548c565b604051806040016040528087600001516001600160801b0316815260200187602001516001600160801b03168152509050611d3d61548c565b50604080518082018252908701516001600160801b0390811682526060880151166020820152611d6b61548c565b604051806040016040528089608001516001600160801b031681526020018960a001516001600160801b031681525090506000611daa8b868686613370565b9050611db461548c565b611dbe858461347b565b9050611dc861548c565b611dd28a8a613549565b9050611de98c611de28e86612c6d565b84846136e5565b9f9e505050505050505050505050505050565b7f0000000000000000000000001f573d6fb3f13d689ff844b4ce37794d79a7ff1c6001600160a01b0390811691161490565b611e3661548c565b6000836001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b158015611e7157600080fd5b505afa158015611e85573d6000803e3d6000fd5b505050506040513d6020811015611e9b57600080fd5b505190506000611eaa856129fd565b90506000816001600160a01b031663d8959512866040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015611efb57600080fd5b505afa158015611f0f573d6000803e3d6000fd5b505050506040513d6020811015611f2557600080fd5b50516040805180820190915290915080611f40836002611b6d565b8152602001939093525090949350505050565b6000808211611fa9576040805162461bcd60e51b815260206004820152601a60248201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604482015290519081900360640190fd5b818381611fb257fe5b049392505050565b6000828411611fcb5750600061198f565b60006120008360a001516001600160801b0316610c4e85608001516001600160801b0316878903611b6d90919063ffffffff16565b90507f000000000000000000000000f7d28faa1fe9ea53279fe6e3cde75175859bdf466001600160a01b031663a80c76ff6040518163ffffffff1660e01b815260040160206040518083038186803b15801561205b57600080fd5b505afa15801561206f573d6000803e3d6000fd5b505050506040513d602081101561208557600080fd5b5051811061209457905061198f565b506000949350505050565b6120a76153f7565b6120b2848484612c83565b90507f000000000000000000000000c4c5634de585d43daec8fa2a6fb6286cd9b871316001600160a01b031663332100fa826020015183606001516040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050600060405180830381600087803b15801561213357600080fd5b505af1158015612147573d6000803e3d6000fd5b505050506121588160400151611dfc565b1561221d57608081015161219a906001600160a01b037f00000000000000000000000048fb253446873234f2febbf9bdeaa72d9d387f94169086903090613767565b7f0000000000000000000000000887ae1251e180d7d453aedebee26e1639f201136001600160a01b03166342966c6882608001516040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b15801561220457600080fd5b505af1158015612218573d6000803e3d6000fd5b505050505b612225615457565b612241826020015183604001518460a001518560c00151611bc6565b905061228381604001516001600160801b031682606001516001600160801b031683608001516001600160801b03168460a001516001600160801b03166137c1565b60006122aa8360200151846040015185606001518660800151868860e0015161119c611996565b90506122b98360400151611dfc565b156122ff576122ed7f000000000000000000000000d1d846312b819743974786050848d9b3d06b9b55846020015183613900565b6122f78682613a23565b5050506127e7565b61230761548c565b61231984602001518560400151611e2e565b9050600061232e6002836000015181610c3657fe5b905060007f000000000000000000000000c4c5634de585d43daec8fa2a6fb6286cd9b871316001600160a01b0316635121220c87602001516040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156123a357600080fd5b505afa1580156123b7573d6000803e3d6000fd5b505050506040513d60208110156123cd57600080fd5b505190508082116123de57816123e0565b805b91506000866020015190507f000000000000000000000000c4c5634de585d43daec8fa2a6fb6286cd9b871316001600160a01b03166319c6a5e48860200151856040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050600060405180830381600087803b15801561246657600080fd5b505af115801561247a573d6000803e3d6000fd5b505050507f000000000000000000000000d1d846312b819743974786050848d9b3d06b9b556001600160a01b0316635e35359e8230866040518463ffffffff1660e01b815260040180846001600160a01b03168152602001836001600160a01b031681526020018281526020019350505050600060405180830381600087803b15801561250657600080fd5b505af115801561251a573d6000803e3d6000fd5b5050505061255287602001518489604001517f0000000000000000000000001f573d6fb3f13d689ff844b4ce37794d79a7ff1c613b60565b604087015160009061256d906001600160a01b031630613d36565b6040890151909150612589906001600160a01b03168c83613ddf565b600061259687838a611fba565b9050801561272b5760007f0000000000000000000000001f573d6fb3f13d689ff844b4ce37794d79a7ff1c6001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561260d57600080fd5b505afa158015612621573d6000803e3d6000fd5b505050506040513d602081101561263757600080fd5b50519050818110156126cb57604080516340c10f1960e01b8152306004820152828403602482015290516001600160a01b037f000000000000000000000000a489c2b5b36835a327851ab917a80562b5afc24416916340c10f1991604480830192600092919082900301818387803b1580156126b257600080fd5b505af11580156126c6573d6000803e3d6000fd5b505050505b61271f6001600160a01b037f0000000000000000000000001f573d6fb3f13d689ff844b4ce37794d79a7ff1c167f000000000000000000000000d1d846312b819743974786050848d9b3d06b9b5584613e4d565b6127298d83613a23565b505b60007f0000000000000000000000001f573d6fb3f13d689ff844b4ce37794d79a7ff1c6001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561279a57600080fd5b505afa1580156127ae573d6000803e3d6000fd5b505050506040513d60208110156127c457600080fd5b5051905080156127dc576127dc8a6020015182613e9f565b505050505050505050505b505050565b6060611cde8484600085613fb0565b600082821115612852576040805162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b50900390565b7f000000000000000000000000f7d28faa1fe9ea53279fe6e3cde75175859bdf466001600160a01b0316639e571a6a83836040518363ffffffff1660e01b815260040180836001600160a01b03168152602001826001600160a01b031681526020019250505060206040518083038186803b1580156128d657600080fd5b505afa1580156128ea573d6000803e3d6000fd5b505050506040513d602081101561290057600080fd5b505115612954576040805162461bcd60e51b815260206004820152601a60248201527f4552525f4144445f4c49515549444954595f44495341424c4544000000000000604482015290519081900360640190fd5b5050565b6000811161142f576040805162461bcd60e51b815260206004820152600e60248201526d4552525f5a45524f5f56414c554560901b604482015290519081900360640190fd5b60006129a983611dfc565b156129ca576129b8600061410c565b6129c3858584614160565b9050611cde565b6129f16129df846001600160a01b031661433e565b6129ea5760006129ec565b825b61410c565b61185885858585614363565b6000816001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b158015612a3857600080fd5b505afa158015612a4c573d6000803e3d6000fd5b505050506040513d6020811015612a6257600080fd5b505192915050565b600080836001600160a01b03166319b6401560006040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b158015612ab257600080fd5b505afa158015612ac6573d6000803e3d6000fd5b505050506040513d6020811015612adc57600080fd5b505190506001600160a01b0380821690841614156119f457836001600160a01b03166319b6401560016040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b158015612b3957600080fd5b505afa158015612b4d573d6000803e3d6000fd5b505050506040513d6020811015612b6357600080fd5b5051949350505050565b600080846001600160a01b031663d8959512856040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015612bbd57600080fd5b505afa158015612bd1573d6000803e3d6000fd5b505050506040513d6020811015612be757600080fd5b505160408051636c4aca8960e11b81526001600160a01b03868116600483015291519188169163d895951291602480820192602092909190829003018186803b158015612c3357600080fd5b505afa158015612c47573d6000803e3d6000fd5b505050506040513d6020811015612c5d57600080fd5b505190925090505b935093915050565b6000818311612c7c57816119f4565b5090919050565b612c8b6153f7565b612c936153f7565b612c9d848661485a565b9050612cac8160200151611432565b612cb4611996565b8160e0015110612cfb576040805162461bcd60e51b815260206004820152600d60248201526c4552525f544f4f5f4541524c5960981b604482015290519081900360640190fd5b63ffffffff8316620f42401415612db157612d2e84826000015183602001518460400151856060015186608001516148d3565b7f000000000000000000000000f5fab5dbd2f3bf675de4cb76517d4767013cfb556001600160a01b0316636f366b71856040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b158015612d9457600080fd5b505af1158015612da8573d6000803e3d6000fd5b50505050612ec7565b60608101516080820151620f4240612dd28363ffffffff88811690611b6d16565b81612dd957fe5b0460608401526080830151620f424090612dfc9063ffffffff88811690611b6d16565b81612e0357fe5b04608084018190528351602085015160408601516060870151612e2b948b94939291906148d3565b606083015160808401516040805163161139bd60e31b8152600481018a905292850360248401529083036044830152516001600160a01b037f000000000000000000000000f5fab5dbd2f3bf675de4cb76517d4767013cfb55169163b089cde891606480830192600092919082900301818387803b158015612eac57600080fd5b505af1158015612ec0573d6000803e3d6000fd5b5050505050505b805160208201516040808401516060850151608086015183516327396b6d60e01b81526001600160a01b0396871660048201529486166024860152918516604485015260648401526084830152517f0000000000000000000000009712bb50dc6efb8a3d7d12cea500a50967d2d471909216916327396b6d9160a48082019260009290919082900301818387803b158015612f6157600080fd5b505af1158015612f75573d6000803e3d6000fd5b505050507f000000000000000000000000f8a2fb650e25a26ce839d64be8a0abbcb0b87b326001600160a01b0316631d092adf866040518263ffffffff1660e01b815260040180826001600160a01b03168152602001915050600060405180830381600087803b158015612fe857600080fd5b505af1158015612ffc573d6000803e3d6000fd5b5092979650505050505050565b600061301361548c565b61301b61548c565b6130258888613280565b9150915061304582600001518360200151836000015184602001516137c1565b6130528989898989614ad3565b60408051630aa558ef60e41b81526001600160a01b038b811660048301528a811660248301528981166044830152606482018990526084820188905291517f0000000000000000000000009712bb50dc6efb8a3d7d12cea500a50967d2d4719092169163aa558ef09160a48082019260009290919082900301818387803b1580156130dc57600080fd5b505af11580156130f0573d6000803e3d6000fd5b505050507f0000000000000000000000009712bb50dc6efb8a3d7d12cea500a50967d2d4716001600160a01b031663fd4bc1e68a8a6040518363ffffffff1660e01b815260040180836001600160a01b03168152602001826001600160a01b0316815260200192505050602060405180830381600087803b15801561317457600080fd5b505af1158015613188573d6000803e3d6000fd5b505050506040513d602081101561319e57600080fd5b50508151602080840151604080516361d5f08760e01b81526001600160a01b038e811660048301528d811660248301528c81166044830152606482018c9052608482018b905260a482019590955260c481019290925260e48201889052517f000000000000000000000000f5fab5dbd2f3bf675de4cb76517d4767013cfb55909316926361d5f08792610104808401939192918290030181600087803b15801561324757600080fd5b505af115801561325b573d6000803e3d6000fd5b505050506040513d602081101561327157600080fd5b50519998505050505050505050565b61328861548c565b61329061548c565b600061329b856129fd565b905060006132a98286612a6a565b90506000806132b9848489612b6d565b91509150600080856001600160a01b0316631f0181bc8a6040518263ffffffff1660e01b815260040180826001600160a01b03168152602001915050604080518083038186803b15801561330c57600080fd5b505afa158015613320573d6000803e3d6000fd5b505050506040513d604081101561333657600080fd5b5080516020918201516040805180820182529788528784019690965285518087019096529085529084015250919890975095505050505050565b825181516020840151600092839261339b92613395916133909190611b6d565b614cc0565b90611b6d565b905060006133ca86602001516133956133c587602001518960000151611b6d90919063ffffffff16565b614ce0565b9050818702878382816133d957fe5b0414156133f4578181816133e957fe5b049350505050611cde565b600080898511613405578985613408565b848a5b915091506000806134258487856000198161341f57fe5b04614d36565b91509150600061343f8488878161343857fe5b0490611b6d565b9050811561346b5761345c81838686028161345657fe5b04612c6d565b98505050505050505050611cde565b9c9b505050505050505050505050565b61348361548c565b602083015182516000916134979190611b6d565b845160208501519192506000916134ad91611b6d565b90508181026000828483816134be57fe5b04146134dc576134cd83614ce0565b6134d685614ce0565b026134e5565b6134e582614ce0565b905060006134f3858561199a565b905060028106613526576002810490506040518060400160405280838303815260200182815250955050505050506119f7565b604080518082019091526002909202810382526020820152935050505092915050565b61355161548c565b600061355d83856127fb565b905060007f000000000000000000000000f7d28faa1fe9ea53279fe6e3cde75175859bdf466001600160a01b0316632c560f896040518163ffffffff1660e01b815260040160206040518083038186803b1580156135ba57600080fd5b505afa1580156135ce573d6000803e3d6000fd5b505050506040513d60208110156135e457600080fd5b50516040805163ce3f3adb60e01b815290519192506000916001600160a01b037f000000000000000000000000f7d28faa1fe9ea53279fe6e3cde75175859bdf46169163ce3f3adb916004808301926020929190829003018186803b15801561364c57600080fd5b505afa158015613660573d6000803e3d6000fd5b505050506040513d602081101561367657600080fd5b50519050818310156136a357604051806040016040528060008152602001600181525093505050506119f7565b8083106136cb57604051806040016040528060018152602001600181525093505050506119f7565b604080518082019091529283526020830152509392505050565b805160009081906136f69087611b6d565b6020840151909150600061371361370d8484612c6d565b88612c6d565b905060008061373088600001518960200151856000198161341f57fe5b909250905061175061374f6137458387611b6d565b610c4e8589611b6d565b61185283610c4e61376082886127fb565b8e90611b6d565b604080516001600160a01b0380861660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b03166323b872dd60e01b17905261066c908590614d7e565b60007f000000000000000000000000f7d28faa1fe9ea53279fe6e3cde75175859bdf466001600160a01b03166324a088686040518163ffffffff1660e01b815260040160206040518083038186803b15801561381c57600080fd5b505afa158015613830573d6000803e3d6000fd5b505050506040513d602081101561384657600080fd5b5051620f42400363ffffffff908116915060009061387290839061339590829082908b908990611b6d16565b90506000613889620f424061339585818a8a611b6d565b905060006138a0620f424061339581818c8a611b6d565b90508183111580156138b25750808211155b6138f6576040805162461bcd60e51b815260206004820152601060248201526f4552525f494e56414c49445f5241544560801b604482015290519081900360640190fd5b5050505050505050565b7f000000000000000000000000c4c5634de585d43daec8fa2a6fb6286cd9b871316001600160a01b031663deacd84e83836040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050600060405180830381600087803b15801561397757600080fd5b505af115801561398b573d6000803e3d6000fd5b505050507f000000000000000000000000a489c2b5b36835a327851ab917a80562b5afc2446001600160a01b03166340c10f1984836040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050600060405180830381600087803b158015613a0657600080fd5b505af1158015613a1a573d6000803e3d6000fd5b50505050505050565b6000613ab57f000000000000000000000000f7d28faa1fe9ea53279fe6e3cde75175859bdf466001600160a01b031663045544436040518163ffffffff1660e01b815260040160206040518083038186803b158015613a8157600080fd5b505afa158015613a95573d6000803e3d6000fd5b505050506040513d6020811015613aab57600080fd5b5051611852611996565b90507f000000000000000000000000f5fab5dbd2f3bf675de4cb76517d4767013cfb556001600160a01b031663dbae3a5d8484846040518463ffffffff1660e01b815260040180846001600160a01b031681526020018381526020018281526020019350505050602060405180830381600087803b158015613b3657600080fd5b505af1158015613b4a573d6000803e3d6000fd5b505050506040513d6020811015610f8a57600080fd5b6000613b6b856129fd565b604080516002808252606080830184529394509091602083019080368337505060408051600280825260608083018452949550909250906020830190803683370190505090508482600081518110613bbf57fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508382600181518110613bed57fe5b60200260200101906001600160a01b031690816001600160a01b031681525050600181600081518110613c1c57fe5b602002602001018181525050600181600181518110613c3757fe5b602002602001018181525050826001600160a01b031663b127c0a58784846040518463ffffffff1660e01b8152600401808481526020018060200180602001838103835285818151815260200191508051906020019060200280838360005b83811015613cae578181015183820152602001613c96565b50505050905001838103825284818151815260200191508051906020019060200280838360005b83811015613ced578181015183820152602001613cd5565b5050505090500195505050505050600060405180830381600087803b158015613d1557600080fd5b505af1158015613d29573d6000803e3d6000fd5b5050505050505050505050565b6000613d418361433e565b15613d5757506001600160a01b038116316119f7565b613d6083614e2f565b6001600160a01b03166370a08231836040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015613dac57600080fd5b505afa158015613dc0573d6000803e3d6000fd5b505050506040513d6020811015613dd657600080fd5b50519392505050565b80613de9576127e7565b613df28361433e565b15613e33576040516001600160a01b0383169082156108fc029083906000818181858888f19350505050158015613e2d573d6000803e3d6000fd5b506127e7565b6127e78282613e4186614e2f565b6001600160a01b031691905b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b1790526127e7908490614d7e565b7f000000000000000000000000c4c5634de585d43daec8fa2a6fb6286cd9b871316001600160a01b031663802fa3ba83836040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050600060405180830381600087803b158015613f1657600080fd5b505af1158015613f2a573d6000803e3d6000fd5b505050507f000000000000000000000000a489c2b5b36835a327851ab917a80562b5afc2446001600160a01b03166342966c68826040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b158015613f9457600080fd5b505af1158015613fa8573d6000803e3d6000fd5b505050505050565b606082471015613ff15760405162461bcd60e51b81526004018080602001828103825260268152602001806154a76026913960400191505060405180910390fd5b613ffa85614e32565b61404b576040805162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015290519081900360640190fd5b60006060866001600160a01b031685876040518082805190602001908083835b6020831061408a5780518252601f19909201916020918201910161406b565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d80600081146140ec576040519150601f19603f3d011682016040523d82523d6000602084013e6140f1565b606091505b5091509150614101828286614e38565b979650505050505050565b80341461142f576040805162461bcd60e51b815260206004820152601760248201527f4552525f4554485f414d4f554e545f4d49534d41544348000000000000000000604482015290519081900360640190fd5b6000827f0000000000000000000000001f573d6fb3f13d689ff844b4ce37794d79a7ff1c61418c61548c565b6141968383611e2e565b905060006141b98260000151610c4e846020015189611b6d90919063ffffffff16565b90507f000000000000000000000000c4c5634de585d43daec8fa2a6fb6286cd9b871316001600160a01b03166319c6a5e485836040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050600060405180830381600087803b15801561423257600080fd5b505af1158015614246573d6000803e3d6000fd5b505050506000614261898686858b61425c611996565b613009565b90506142986001600160a01b037f0000000000000000000000001f573d6fb3f13d689ff844b4ce37794d79a7ff1c1633308a613767565b6142a28888613e9f565b7f0000000000000000000000000887ae1251e180d7d453aedebee26e1639f201136001600160a01b03166340c10f198a896040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050600060405180830381600087803b15801561431957600080fd5b505af115801561432d573d6000803e3d6000fd5b50929b9a5050505050505050505050565b6001600160a01b03811673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee14919050565b6000837f0000000000000000000000001f573d6fb3f13d689ff844b4ce37794d79a7ff1c82614391836129fd565b90506000806143a1838986612b6d565b915091507f000000000000000000000000f7d28faa1fe9ea53279fe6e3cde75175859bdf466001600160a01b03166312588d0e6040518163ffffffff1660e01b815260040160206040518083038186803b1580156143fe57600080fd5b505afa158015614412573d6000803e3d6000fd5b505050506040513d602081101561442857600080fd5b505181101561447e576040805162461bcd60e51b815260206004820152601860248201527f4552525f4e4f545f454e4f5547485f4c49515549444954590000000000000000604482015290519081900360640190fd5b600061448e83610c4e8a85611b6d565b905060007f000000000000000000000000f7d28faa1fe9ea53279fe6e3cde75175859bdf466001600160a01b031663943fd08a8c6040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156144ff57600080fd5b505afa158015614513573d6000803e3d6000fd5b505050506040513d602081101561452957600080fd5b50519050806145ba577f000000000000000000000000f7d28faa1fe9ea53279fe6e3cde75175859bdf466001600160a01b031663b97b55ce6040518163ffffffff1660e01b815260040160206040518083038186803b15801561458b57600080fd5b505afa15801561459f573d6000803e3d6000fd5b505050506040513d60208110156145b557600080fd5b505190505b600061462d837f000000000000000000000000c4c5634de585d43daec8fa2a6fb6286cd9b871316001600160a01b031663350ed8e78f6040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015610cd157600080fd5b90508181111561467d576040805162461bcd60e51b815260206004820152601660248201527511549497d3505617d05353d5539517d4915050d2115160521b604482015290519081900360640190fd5b614688308d85613900565b61469c6001600160a01b0388168785614edc565b6146ae8b6001600160a01b031661433e565b6146db576146c76001600160a01b038c1633308d614f0d565b6146db6001600160a01b038c16878c614edc565b6146e9868c898d8734614f48565b6000886001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561473857600080fd5b505afa15801561474c573d6000803e3d6000fd5b505050506040513d602081101561476257600080fd5b5051905061479a6001600160a01b038a167f000000000000000000000000d1d846312b819743974786050848d9b3d06b9b5583613e4d565b6001600160a01b037f000000000000000000000000c4c5634de585d43daec8fa2a6fb6286cd9b871311663332100fa8a6002840484036040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050600060405180830381600087803b15801561481657600080fd5b505af115801561482a573d6000803e3d6000fd5b505050506148488e8a8e6002858161483e57fe5b048f61425c611996565b9e9d5050505050505050505050505050565b6148626153f7565b61486a6153f7565b61487384611a63565b9050826001600160a01b031681600001516001600160a01b0316146119f4576040805162461bcd60e51b815260206004820152601160248201527011549497d050d0d154d4d7d11153925151607a1b604482015290519081900360640190fd5b60607f000000000000000000000000f7d28faa1fe9ea53279fe6e3cde75175859bdf466001600160a01b0316639e2ce9d26040518163ffffffff1660e01b815260040160006040518083038186803b15801561492e57600080fd5b505afa158015614942573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052602081101561496b57600080fd5b8101908080516040519392919084600160201b82111561498a57600080fd5b90830190602082018581111561499f57600080fd5b82518660208202830111600160201b821117156149bb57600080fd5b82525081516020918201928201910280838360005b838110156149e85781810151838201526020016149d0565b50505050905001604052505050905060008151905060005b81811015614ac857828181518110614a1457fe5b60200260200101516001600160a01b031663b8128fe68a8a8a8a8a8a6040518763ffffffff1660e01b815260040180878152602001866001600160a01b03168152602001856001600160a01b03168152602001846001600160a01b031681526020018381526020018281526020019650505050505050600060405180830381600087803b158015614aa457600080fd5b505af1158015614ab8573d6000803e3d6000fd5b505060019092019150614a009050565b505050505050505050565b60607f000000000000000000000000f7d28faa1fe9ea53279fe6e3cde75175859bdf466001600160a01b0316639e2ce9d26040518163ffffffff1660e01b815260040160006040518083038186803b158015614b2e57600080fd5b505afa158015614b42573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526020811015614b6b57600080fd5b8101908080516040519392919084600160201b821115614b8a57600080fd5b908301906020820185811115614b9f57600080fd5b82518660208202830111600160201b82111715614bbb57600080fd5b82525081516020918201928201910280838360005b83811015614be8578181015183820152602001614bd0565b50505050905001604052505050905060008151905060005b818110156138f657828181518110614c1457fe5b60200260200101516001600160a01b031663139c22ea89898989896040518663ffffffff1660e01b815260040180866001600160a01b03168152602001856001600160a01b03168152602001846001600160a01b0316815260200183815260200182815260200195505050505050600060405180830381600087803b158015614c9c57600080fd5b505af1158015614cb0573d6000803e3d6000fd5b505060019092019150614c009050565b600080614ccc83614ce0565b905082818202146119f7578060010161198f565b60008060028304600101905060006002828581614cf957fe5b04830181614d0357fe5b0490505b80821115614d2f578091506002828581614d1d57fe5b04830181614d2757fe5b049050614d07565b5092915050565b600080848484821180614d4857508481115b15614d5e57614d58828287615101565b90925090505b808214614d6f579092509050612c65565b50600196879650945050505050565b6060614dd3826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166127ec9092919063ffffffff16565b8051909150156127e757808060200190516020811015614df257600080fd5b50516127e75760405162461bcd60e51b815260040180806020018281038252602a8152602001806154ee602a913960400191505060405180910390fd5b90565b3b151590565b60608315614e4757508161198f565b825115614e575782518084602001fd5b8160405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b83811015614ea1578181015183820152602001614e89565b50505050905090810190601f168015614ece5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b614ee58361433e565b15614eef576127e7565b6127e78282614efd86614e2f565b6001600160a01b0316919061513a565b801580614f1e5750614f1e8461433e565b15614f285761066c565b61066c838383614f3788614e2f565b6001600160a01b0316929190613767565b6040805160028082526060808301845292602083019080368337505060408051600280825260608083018452949550909250906020830190803683370190505090508682600081518110614f9857fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508582600181518110614fc657fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508481600081518110614ff457fe5b602002602001018181525050838160018151811061500e57fe5b602002602001018181525050876001600160a01b0316637d8916bd84848460016040518563ffffffff1660e01b8152600401808060200180602001848152602001838103835286818151815260200191508051906020019060200280838360005b8381101561508757818101518382015260200161506f565b50505050905001838103825285818151815260200191508051906020019060200280838360005b838110156150c65781810151838201526020016150ae565b50505050905001955050505050506000604051808303818588803b1580156150ed57600080fd5b505af11580156127dc573d6000803e3d6000fd5b60008083851161511f576151168585856151ff565b91509150612c65565b60008061512d8688876151ff565b9890975095505050505050565b80615144576127e7565b60408051636eb1769f60e11b81523060048201526001600160a01b038481166024830152915160009286169163dd62ed3e916044808301926020929190829003018186803b15801561519557600080fd5b505afa1580156151a9573d6000803e3d6000fd5b505050506040513d60208110156151bf57600080fd5b505190508181106151d057506127e7565b80156151eb576151eb6001600160a01b0385168460006152b8565b61066c6001600160a01b03851684846152b8565b6000806000836000198161520f57fe5b0490508086111561524857600081600101878161522857fe5b04600101905080878161523757fe5b04965080868161524357fe5b049550505b8486146152a85785840285870187811061527957600061526883836153cb565b955050508385039250612c65915050565b60028888030487038210156152975760008694509450505050612c65565b600180870394509450505050612c65565b5050600290910493849350915050565b80158061533e575060408051636eb1769f60e11b81523060048201526001600160a01b03848116602483015291519185169163dd62ed3e91604480820192602092909190829003018186803b15801561531057600080fd5b505afa158015615324573d6000803e3d6000fd5b505050506040513d602081101561533a57600080fd5b5051155b6153795760405162461bcd60e51b81526004018080602001828103825260368152602001806155186036913960400191505060405180910390fd5b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b1790526127e7908490614d7e565b60006002820482038284816153dc57fe5b06816153e457fe5b048284816153ee57fe5b04019392505050565b60405180610100016040528060006001600160a01b0316815260200160006001600160a01b0316815260200160006001600160a01b0316815260200160008152602001600081526020016000815260200160008152602001600081525090565b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a081019190915290565b60405180604001604052806000815260200160008152509056fe416464726573733a20696e73756666696369656e742062616c616e636520666f722063616c6c536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f775361666545524332303a204552433230206f7065726174696f6e20646964206e6f7420737563636565645361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f20746f206e6f6e2d7a65726f20616c6c6f77616e6365a2646970667358221220c8b6271b30b2c24905ee56b1c37d0c40d8938c9618b1b8f1a1d621e472ed259664736f6c634300060c0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000f7d28faa1fe9ea53279fe6e3cde75175859bdf46000000000000000000000000f5fab5dbd2f3bf675de4cb76517d4767013cfb550000000000000000000000009712bb50dc6efb8a3d7d12cea500a50967d2d471000000000000000000000000c4c5634de585d43daec8fa2a6fb6286cd9b87131000000000000000000000000d1d846312b819743974786050848d9b3d06b9b55000000000000000000000000a489c2b5b36835a327851ab917a80562b5afc2440000000000000000000000000887ae1251e180d7d453aedebee26e1639f20113000000000000000000000000f8a2fb650e25a26ce839d64be8a0abbcb0b87b32
-----Decoded View---------------
Arg [0] : settings (address): 0xF7D28FaA1FE9Ea53279fE6e3Cde75175859bdF46
Arg [1] : store (address): 0xf5FAB5DBD2f3bf675dE4cB76517d4767013cfB55
Arg [2] : stats (address): 0x9712Bb50DC6Efb8a3d7D12cEA500a50967d2d471
Arg [3] : systemStore (address): 0xc4C5634De585d43DaEC8fA2a6Fb6286cd9B87131
Arg [4] : wallet (address): 0xD1D846312B819743974786050848D9B3d06b9b55
Arg [5] : networkTokenGovernance (address): 0xa489C2b5b36835A327851Ab917A80562B5AFC244
Arg [6] : govTokenGovernance (address): 0x0887ae1251E180d7D453aeDEBee26e1639f20113
Arg [7] : lastRemoveCheckpointStore (address): 0xF8a2FB650e25a26CE839D64bE8a0aBbCb0b87B32
-----Encoded View---------------
8 Constructor Arguments found :
Arg [0] : 000000000000000000000000f7d28faa1fe9ea53279fe6e3cde75175859bdf46
Arg [1] : 000000000000000000000000f5fab5dbd2f3bf675de4cb76517d4767013cfb55
Arg [2] : 0000000000000000000000009712bb50dc6efb8a3d7d12cea500a50967d2d471
Arg [3] : 000000000000000000000000c4c5634de585d43daec8fa2a6fb6286cd9b87131
Arg [4] : 000000000000000000000000d1d846312b819743974786050848d9b3d06b9b55
Arg [5] : 000000000000000000000000a489c2b5b36835a327851ab917a80562b5afc244
Arg [6] : 0000000000000000000000000887ae1251e180d7d453aedebee26e1639f20113
Arg [7] : 000000000000000000000000f8a2fb650e25a26ce839d64be8a0abbcb0b87b32
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.