More Info
Private Name Tags
ContractCreator
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
0xcd7b070f723fd8ce8530368a5f1a81c3df84380413d657ca99ba1ebd545fab7b | Add Liquidity | (pending) | 2 days ago | IN | 0.00367 ETH | (Pending) | |||
Add Liquidity | 20495838 | 248 days ago | IN | 0 ETH | 0.00007296 | ||||
Add Liquidity | 20408356 | 260 days ago | IN | 0 ETH | 0.00010457 | ||||
Remove Liquidity | 20150708 | 296 days ago | IN | 0 ETH | 0.00010137 | ||||
Add Liquidity | 20149621 | 296 days ago | IN | 0 ETH | 0.0001673 | ||||
Add Liquidity | 19836109 | 340 days ago | IN | 0 ETH | 0.00031152 | ||||
Add Liquidity | 19750101 | 352 days ago | IN | 0 ETH | 0.01305058 | ||||
Add Liquidity | 19750101 | 352 days ago | IN | 0 ETH | 0.00034081 | ||||
Add Liquidity | 15738756 | 914 days ago | IN | 0 ETH | 0.00140786 | ||||
Add Liquidity | 15738756 | 914 days ago | IN | 0 ETH | 0.00140786 | ||||
Add Liquidity | 15738756 | 914 days ago | IN | 0 ETH | 0.00141609 | ||||
Add Liquidity | 15738756 | 914 days ago | IN | 0 ETH | 0.00140875 | ||||
Add Liquidity | 15738756 | 914 days ago | IN | 0 ETH | 0.00141609 | ||||
Remove Liquidity | 15559221 | 939 days ago | IN | 0 ETH | 0.00013517 | ||||
Add Liquidity | 15243416 | 989 days ago | IN | 0 ETH | 0.00029216 | ||||
Add Liquidity | 15066722 | 1017 days ago | IN | 0.035 ETH | 0.00056993 | ||||
Add Liquidity | 14865748 | 1051 days ago | IN | 0 ETH | 0.00083222 | ||||
Add Liquidity | 14817178 | 1059 days ago | IN | 0 ETH | 0.00100477 | ||||
Add Liquidity | 14817178 | 1059 days ago | IN | 0 ETH | 0.00100477 | ||||
Add Liquidity | 14817178 | 1059 days ago | IN | 0 ETH | 0.00096799 | ||||
Add Liquidity | 14812012 | 1060 days ago | IN | 0 ETH | 0.00440603 | ||||
Remove Liquidity | 14781434 | 1065 days ago | IN | 0 ETH | 0.00137731 | ||||
Remove Liquidity | 14780081 | 1065 days ago | IN | 0 ETH | 0.0019084 | ||||
Remove Liquidity | 14758312 | 1069 days ago | IN | 0 ETH | 0.00422382 | ||||
Remove Liquidity | 14732848 | 1073 days ago | IN | 0 ETH | 0.00122487 |
Latest 25 internal transactions (View All)
Advanced mode:
Parent Transaction Hash | Method | Block |
From
|
To
|
|||
---|---|---|---|---|---|---|---|
- | 14722995 | 1074 days ago | 0.1 ETH | ||||
- | 14722789 | 1074 days ago | 504.6596684 ETH | ||||
- | 14722789 | 1074 days ago | 504.6596684 ETH | ||||
- | 14722768 | 1074 days ago | 458.03007424 ETH | ||||
- | 14722768 | 1074 days ago | 458.03007424 ETH | ||||
- | 14722677 | 1074 days ago | 151.40360745 ETH | ||||
- | 14722677 | 1074 days ago | 151.40360745 ETH | ||||
- | 14722055 | 1074 days ago | 15.03951109 ETH | ||||
- | 14722055 | 1074 days ago | 15.03951109 ETH | ||||
- | 14721659 | 1075 days ago | 0.07315315 ETH | ||||
- | 14720041 | 1075 days ago | 0.26648085 ETH | ||||
- | 14720041 | 1075 days ago | 0.26648085 ETH | ||||
- | 14718739 | 1075 days ago | 370 ETH | ||||
- | 14717543 | 1075 days ago | 0.49998617 ETH | ||||
- | 14717543 | 1075 days ago | 0.49998617 ETH | ||||
- | 14716495 | 1075 days ago | 3.02779461 ETH | ||||
- | 14716495 | 1075 days ago | 3.02779461 ETH | ||||
- | 14713770 | 1076 days ago | 5.0800515 ETH | ||||
- | 14713770 | 1076 days ago | 5.0800515 ETH | ||||
- | 14707966 | 1077 days ago | 99.98111387 ETH | ||||
- | 14707966 | 1077 days ago | 99.98111387 ETH | ||||
- | 14707963 | 1077 days ago | 0.49992551 ETH | ||||
- | 14707963 | 1077 days ago | 0.49992551 ETH | ||||
- | 14705256 | 1077 days ago | 2.00780398 ETH | ||||
- | 14705256 | 1077 days ago | 2.00780398 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-05-17 */ // 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/ILiquidityProvisionEventsSubscriber.sol pragma solidity 0.6.12; /** * @dev Liquidity provision events subscriber interface */ interface ILiquidityProvisionEventsSubscriber { 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/liquidity-protection/interfaces/ITransferPositionCallback.sol pragma solidity 0.6.12; /** * @dev Transfer position event callback interface */ interface ITransferPositionCallback { function onTransferPosition( uint256 newId, address provider, bytes calldata data ) 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; function transferPosition(uint256 id, address newProvider) external returns (uint256); function transferPositionAndNotify( uint256 id, address newProvider, ITransferPositionCallback callback, bytes calldata data ) external returns (uint256); } // 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); 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 override 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 callback the callback contract to notify * @param data custom data provided to the callback * * @return new position id */ function transferPositionAndNotify( uint256 id, address newProvider, ITransferPositionCallback callback, bytes calldata data ) external override protected validAddress(newProvider) validAddress(address(callback)) returns (uint256) { uint256 newId = transferPosition(msg.sender, id, newProvider); callback.onTransferPosition(newId, msg.sender, data); 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++) { ILiquidityProvisionEventsSubscriber(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++) { ILiquidityProvisionEventsSubscriber(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
Contract ABI
API[{"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":"contract ITransferPositionCallback","name":"callback","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"transferPositionAndNotify","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
6101c060405260016002553480156200001757600080fd5b50604051620059ae380380620059ae83398181016040526101008110156200003e57600080fd5b508051602082015160408301516060840151608085015160a086015160c087015160e090970151600080546001600160a01b03191633179055959694959394929391929091876200008f816200022e565b876200009b816200022e565b87620000a7816200022e565b87620000b3816200022e565b87620000bf816200022e565b85620000cb816200022e565b6001600160601b031960608f811b82166080528e811b821660a0528d811b821660c0528c811b821660e0528b811b8216610100528a811b82166101405289811b82166101805288901b166101a05260408051637e062a3560e11b815290516001600160a01b038b169163fc0c546a916004808301926020929190829003018186803b1580156200015a57600080fd5b505afa1580156200016f573d6000803e3d6000fd5b505050506040513d60208110156200018657600080fd5b505160601b6001600160601b0319166101205260408051637e062a3560e11b815290516001600160a01b038a169163fc0c546a916004808301926020929190829003018186803b158015620001da57600080fd5b505afa158015620001ef573d6000803e3d6000fd5b505050506040513d60208110156200020657600080fd5b505160601b6001600160601b03191661016052506200028d9c50505050505050505050505050565b6001600160a01b0381166200028a576040805162461bcd60e51b815260206004820152601360248201527f4552525f494e56414c49445f4144445245535300000000000000000000000000604482015290519081900360640190fd5b50565b60805160601c60a05160601c60c05160601c60e05160601c6101005160601c6101205160601c6101405160601c6101605160601c6101805160601c6101a05160601c61556a6200044460003980612f53525080612183528061412052508061215752508061265252806139695280613f08525080610a7e5280611513528061174a5280611de55280612515528061258952806126bf52806127165280613fe152806140ec52806141e45250806105c9528061071e5280610a4f5280610fb352806122aa528061246752806126e152806145f1525080610d0c5280610ef15280611680528061179c528061209d528061231b52806123d452806138da5280613e795280614039528061443e528061462052508061121b5280612eef528061306a52806130ce5250806107af52806109b45280610f1d5280610f8752806110455280611a5c5280612d085280612e3d52806131d65280613a915250806110ea528061123f5280611335528061141b528061155652806115f65280611feb5280612832528061353b52806135de528061379d5280613a025280614223528061431052806143b052806147535280614953525061556a6000f3fe6080604052600436106101395760003560e01c80638da5cb5b116100ab578063caee4c8f1161006f578063caee4c8f146104a9578063d4ee1d90146104e5578063d80528ae146104fa578063e06174e41461050f578063e4a7672614610524578063f2fde38b1461055a57610140565b80638da5cb5b146103be578063975057e7146103d3578063bf3b1101146103e8578063c2250a991461041b578063c83df6631461044e57610140565b8063630d8c63116100fd578063630d8c63146102bf5780636d533e9b146102ef578063782ed90c1461034957806379ba50971461037f578063879015e81461039457806389d94b46146103a957610140565b806324afe2d91461014557806328790b5a1461019157806340083480146101a8578063521eb2731461025557806355bd513f1461028657610140565b3661014057005b600080fd5b34801561015157600080fd5b506101786004803603602081101561016857600080fd5b50356001600160a01b031661058d565b6040805192835260208301919091528051918290030190f35b34801561019d57600080fd5b506101a66105bf565b005b3480156101b457600080fd5b50610243600480360360808110156101cb57600080fd5b8135916001600160a01b03602082013581169260408301359091169190810190608081016060820135600160201b81111561020557600080fd5b82018360208201111561021757600080fd5b803590602001918460018302840111600160201b8311171561023857600080fd5b50909250905061063c565b60408051918252519081900360200190f35b34801561026157600080fd5b5061026a61071c565b604080516001600160a01b039092168252519081900360200190f35b34801561029257600080fd5b50610243600480360360408110156102a957600080fd5b50803590602001356001600160a01b0316610740565b3480156102cb57600080fd5b506101a6600480360360408110156102e257600080fd5b5080359060200135610770565b3480156102fb57600080fd5b5061032b6004803603606081101561031257600080fd5b5080359063ffffffff6020820135169060400135610b1b565b60408051938452602084019290925282820152519081900360600190f35b34801561035557600080fd5b506101a66004803603604081101561036c57600080fd5b508035906020013563ffffffff16610e0d565b34801561038b57600080fd5b506101a6610e38565b3480156103a057600080fd5b5061026a610eef565b3480156103b557600080fd5b506101a6610f13565b3480156103ca57600080fd5b5061026a610f76565b3480156103df57600080fd5b5061026a610f85565b3480156103f457600080fd5b506101a66004803603602081101561040b57600080fd5b50356001600160a01b0316610fa9565b34801561042757600080fd5b506101a66004803603602081101561043e57600080fd5b50356001600160a01b031661103b565b34801561045a57600080fd5b50610243600480360360e081101561047157600080fd5b506001600160a01b03813581169160208101359091169060408101359060608101359060808101359060a08101359060c001356110b2565b610243600480360360808110156104bf57600080fd5b506001600160a01b038135811691602081013582169160408201351690606001356111aa565b3480156104f157600080fd5b5061026a61120a565b34801561050657600080fd5b5061026a611219565b34801561051b57600080fd5b5061026a61123d565b6102436004803603606081101561053a57600080fd5b506001600160a01b03813581169160208101359091169060400135611261565b34801561056657600080fd5b506101a66004803603602081101561057d57600080fd5b50356001600160a01b03166112b5565b6000808261059a81611333565b6105a381611419565b6105ac84611503565b6105b585611745565b9250925050915091565b6105c7611848565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166379ba50976040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561062257600080fd5b505af1158015610636573d6000803e3d6000fd5b50505050565b600061064661189d565b6002805584610654816118e5565b8461065e816118e5565b600061066b338a8a611936565b604051635c2ba84560e01b8152600481018281523360248301819052606060448401908152606484018a90529394506001600160a01b038b1693635c2ba8459386938c928c92608401848480828437600081840152601f19601f82011690508083019250505095505050505050600060405180830381600087803b1580156106f257600080fd5b505af1158015610706573d6000803e3d6000fd5b5050600160025550909998505050505050505050565b7f000000000000000000000000000000000000000000000000000000000000000090565b600061074a61189d565b6002805581610758816118e5565b610763338585611936565b6001600255949350505050565b61077861189d565b6002805560408051637a1036f560e11b81523360048201526024810184905260448101839052905160609182916001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169163f4206dea916064808301926000929190829003018186803b1580156107f557600080fd5b505afa158015610809573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604090815281101561083257600080fd5b8101908080516040519392919084600160201b82111561085157600080fd5b90830190602082018581111561086657600080fd5b82518660208202830111600160201b8211171561088257600080fd5b82525081516020918201928201910280838360005b838110156108af578181015183820152602001610897565b5050505090500160405260200180516040519392919084600160201b8211156108d757600080fd5b9083019060208201858111156108ec57600080fd5b82518660208202830111600160201b8211171561090857600080fd5b82525081516020918201928201910280838360005b8381101561093557818101518382015260200161091d565b5050505090500160405250505091509150600080835190508251811461095757fe5b805b8015610a4657600019810161096c61197d565b85828151811061097857fe5b6020026020010151111561098c5750610a3d565b604080516390e0661b60e01b8152336004820152898301602482015290516001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016916390e0661b91604480830192600092919082900301818387803b1580156109fb57600080fd5b505af1158015610a0f573d6000803e3d6000fd5b50505050610a39868281518110610a2257fe5b60200260200101518561198190919063ffffffff16565b9350505b60001901610959565b508115610b0e577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316635e35359e7f000000000000000000000000000000000000000000000000000000000000000033856040518463ffffffff1660e01b815260040180846001600160a01b03168152602001836001600160a01b031681526020018281526020019350505050600060405180830381600087803b158015610af557600080fd5b505af1158015610b09573d6000803e3d6000fd5b505050505b5050600160025550505050565b600080600084610b2a816119e4565b610b326153de565b610b3b88611a4a565b80519091506001600160a01b0316610b8b576040805162461bcd60e51b815260206004820152600e60248201526d11549497d253959053125117d25160921b604482015290519081900360640190fd5b8060e00151861015610bdc576040805162461bcd60e51b815260206004820152601560248201527404552525f494e56414c49445f54494d455354414d5605c1b604482015290519081900360640190fd5b63ffffffff8716620f424014610c41576060810151620f424090610c099063ffffffff8a811690611b5416565b81610c1057fe5b0460608201526080810151620f424090610c339063ffffffff8a811690611b5416565b81610c3a57fe5b0460808201525b610c4961543e565b610c65826020015183604001518460a001518560c00151611bad565b90506000610c8c8360200151846040015185606001518660800151868860e001518e611ccd565b9050610c9b8360400151611de3565b15610cb157955085945060009350610e03915050565b610cb9615473565b610ccb84602001518560400151611e15565b90506000610cfe6002836000015181610ce057fe5b04610cf8846020015186611b5490919063ffffffff16565b90611f3a565b90506000610dad86606001517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316635121220c89602001516040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015610d7b57600080fd5b505afa158015610d8f573d6000803e3d6000fd5b505050506040513d6020811015610da557600080fd5b505190611981565b9050808211610dbc5781610dbe565b805b91506000610de48460200151610cf86002876000015181610ddb57fe5b87919004611b54565b90506000610df3868389611fa1565b959b509099509397505050505050505b5093509350939050565b610e1561189d565b6002805580610e23816119e4565b610e2e338484612086565b5050600160025550565b6001546001600160a01b03163314610e8b576040805162461bcd60e51b815260206004820152601160248201527011549497d050d0d154d4d7d11153925151607a1b604482015290519081900360640190fd5b600154600080546040516001600160a01b0393841693909116917f343765429aea5a34b3ff6a3785a98a5abb2597aca87bfbb58632c173d585373a91a360018054600080546001600160a01b03199081166001600160a01b03841617909155169055565b7f000000000000000000000000000000000000000000000000000000000000000090565b610f1b611848565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166379ba50976040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561062257600080fd5b6000546001600160a01b031681565b7f000000000000000000000000000000000000000000000000000000000000000090565b610fb1611848565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663f2fde38b826040518263ffffffff1660e01b815260040180826001600160a01b03168152602001915050600060405180830381600087803b15801561102057600080fd5b505af1158015611034573d6000803e3d6000fd5b5050505050565b611043611848565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663f2fde38b826040518263ffffffff1660e01b815260040180826001600160a01b03168152602001915050600060405180830381600087803b15801561102057600080fd5b6000806110c386610cf88988611b54565b90506110cd61543e565b6110d98a8a8787611bad565b905060006111888b8b858c8661117b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663ce3f3adb6040518163ffffffff1660e01b815260040160206040518083038186803b15801561114157600080fd5b505afa158015611155573d6000803e3d6000fd5b505050506040513d602081101561116b57600080fd5b505161117561197d565b906127d3565b61118361197d565b611ccd565b905061119b89610cf883620f4240611b54565b9b9a5050505050505050505050565b60006111b461189d565b60028055846111c2816118e5565b846111cc81611333565b6111d581611419565b85856111e18282612830565b856111eb81612930565b6111f78a8a8a8a612976565b60016002559a9950505050505050505050565b6001546001600160a01b031681565b7f000000000000000000000000000000000000000000000000000000000000000090565b7f000000000000000000000000000000000000000000000000000000000000000090565b600061126b61189d565b600280558361127981611333565b61128281611419565b848461128e8282612830565b8461129881612930565b6112a433898989612976565b600160025598975050505050505050565b6112bd611848565b6000546001600160a01b0382811691161415611311576040805162461bcd60e51b815260206004820152600e60248201526d22a9292fa9a0a6a2afa7aba722a960911b604482015290519081900360640190fd5b600180546001600160a01b0319166001600160a01b0392909216919091179055565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d4f63148826040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156113a057600080fd5b505afa1580156113b4573d6000803e3d6000fd5b505050506040513d60208110156113ca57600080fd5b5051611416576040805162461bcd60e51b815260206004820152601660248201527511549497d413d3d317d393d517d4d5541413d495115160521b604482015290519081900360640190fd5b50565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316632b26a982826040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561148657600080fd5b505afa15801561149a573d6000803e3d6000fd5b505050506040513d60208110156114b057600080fd5b5051611416576040805162461bcd60e51b815260206004820152601860248201527f4552525f504f4f4c5f4e4f545f57484954454c49535445440000000000000000604482015290519081900360640190fd5b60008061150f836129d5565b90507f0000000000000000000000000000000000000000000000000000000000000000600061153e8383612a42565b905060008061154e858486612b45565b9150915060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663943fd08a896040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156115c157600080fd5b505afa1580156115d5573d6000803e3d6000fd5b505050506040513d60208110156115eb57600080fd5b505190508061167c577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663b97b55ce6040518163ffffffff1660e01b815260040160206040518083038186803b15801561164d57600080fd5b505afa158015611661573d6000803e3d6000fd5b505050506040513d602081101561167757600080fd5b505190505b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663350ed8e78a6040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156116eb57600080fd5b505afa1580156116ff573d6000803e3d6000fd5b505050506040513d602081101561171557600080fd5b505190506000816117268482612c45565b03905061173784610cf88388611b54565b9a9950505050505050505050565b6000817f0000000000000000000000000000000000000000000000000000000000000000611771615473565b61177b8383611e15565b905061183f8160200151610cf86001611175856000015161183987600001517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316635121220c8c6040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561180757600080fd5b505afa15801561181b573d6000803e3d6000fd5b505050506040513d602081101561183157600080fd5b505190611b54565b90611981565b95945050505050565b6000546001600160a01b0316331461189b576040805162461bcd60e51b815260206004820152601160248201527011549497d050d0d154d4d7d11153925151607a1b604482015290519081900360640190fd5b565b60016002541461189b576040805162461bcd60e51b815260206004820152600e60248201526d4552525f5245454e5452414e435960901b604482015290519081900360640190fd5b6001600160a01b038116611416576040805162461bcd60e51b81526020600482015260136024820152724552525f494e56414c49445f4144445245535360681b604482015290519081900360640190fd5b60006119406153de565b61194e8585620f4240612c5b565b90506119728382602001518360400151846060015185608001518660e00151612fe1565b9150505b9392505050565b4290565b6000828201838110156119db576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b90505b92915050565b60008163ffffffff16118015611a035750620f424063ffffffff821611155b611416576040805162461bcd60e51b815260206004820152601360248201527222a9292fa4a72b20a624a22fa827a92a24a7a760691b604482015290519081900360640190fd5b611a526153de565b611a5a6153de565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316635290ffbb846040518263ffffffff1660e01b8152600401808281526020019150506101006040518083038186803b158015611abf57600080fd5b505afa158015611ad3573d6000803e3d6000fd5b505050506040513d610100811015611aea57600080fd5b50805160208083015160408085015160608087015160808089015160a0808b015160c0808d015160e09d8e01519d8f019d909d528d019b909b528b01999099528901979097528701959095526001600160a01b039485169086015283169084015216815292915050565b600082611b63575060006119de565b82820282848281611b7057fe5b04146119db5760405162461bcd60e51b81526004018080602001828103825260218152602001806154b46021913960400191505060405180910390fd5b611bb561543e565b611bbd615473565b611bc5615473565b611bcf8787613258565b915091506001600160801b038511158015611bf157506001600160801b038411155b8015611c05575081516001600160801b0310155b8015611c1c57506001600160801b03826020015111155b8015611c30575080516001600160801b0310155b8015611c4757506001600160801b03816020015111155b611c4d57fe5b6040518060c00160405280866001600160801b03168152602001856001600160801b0316815260200183600001516001600160801b0316815260200183602001516001600160801b0316815260200182600001516001600160801b0316815260200182602001516001600160801b0316815250925050505b949350505050565b6000611cd7615473565b611ce18989611e15565b9050611ceb615473565b604051806040016040528087600001516001600160801b0316815260200187602001516001600160801b03168152509050611d24615473565b50604080518082018252908701516001600160801b0390811682526060880151166020820152611d52615473565b604051806040016040528089608001516001600160801b031681526020018960a001516001600160801b031681525090506000611d918b868686613348565b9050611d9b615473565b611da58584613453565b9050611daf615473565b611db98a8a613521565b9050611dd08c611dc98e86612c45565b84846136bd565b9f9e505050505050505050505050505050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0390811691161490565b611e1d615473565b6000836001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b158015611e5857600080fd5b505afa158015611e6c573d6000803e3d6000fd5b505050506040513d6020811015611e8257600080fd5b505190506000611e91856129d5565b90506000816001600160a01b031663d8959512866040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015611ee257600080fd5b505afa158015611ef6573d6000803e3d6000fd5b505050506040513d6020811015611f0c57600080fd5b50516040805180820190915290915080611f27836002611b54565b8152602001939093525090949350505050565b6000808211611f90576040805162461bcd60e51b815260206004820152601a60248201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604482015290519081900360640190fd5b818381611f9957fe5b049392505050565b6000828411611fb257506000611976565b6000611fe78360a001516001600160801b0316610cf885608001516001600160801b0316878903611b5490919063ffffffff16565b90507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663a80c76ff6040518163ffffffff1660e01b815260040160206040518083038186803b15801561204257600080fd5b505afa158015612056573d6000803e3d6000fd5b505050506040513d602081101561206c57600080fd5b5051811061207b579050611976565b506000949350505050565b61208e6153de565b612099848484612c5b565b90507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663332100fa826020015183606001516040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050600060405180830381600087803b15801561211a57600080fd5b505af115801561212e573d6000803e3d6000fd5b5050505061213f8160400151611de3565b15612204576080810151612181906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016908690309061373f565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166342966c6882608001516040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b1580156121eb57600080fd5b505af11580156121ff573d6000803e3d6000fd5b505050505b61220c61543e565b612228826020015183604001518460a001518560c00151611bad565b905061226a81604001516001600160801b031682606001516001600160801b031683608001516001600160801b03168460a001516001600160801b0316613799565b60006122918360200151846040015185606001518660800151868860e0015161118361197d565b90506122a08360400151611de3565b156122e6576122d47f00000000000000000000000000000000000000000000000000000000000000008460200151836138d8565b6122de86826139fb565b5050506127ce565b6122ee615473565b61230084602001518560400151611e15565b905060006123156002836000015181610ce057fe5b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316635121220c87602001516040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561238a57600080fd5b505afa15801561239e573d6000803e3d6000fd5b505050506040513d60208110156123b457600080fd5b505190508082116123c557816123c7565b805b91506000866020015190507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166319c6a5e48860200151856040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050600060405180830381600087803b15801561244d57600080fd5b505af1158015612461573d6000803e3d6000fd5b505050507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316635e35359e8230866040518463ffffffff1660e01b815260040180846001600160a01b03168152602001836001600160a01b031681526020018281526020019350505050600060405180830381600087803b1580156124ed57600080fd5b505af1158015612501573d6000803e3d6000fd5b5050505061253987602001518489604001517f0000000000000000000000000000000000000000000000000000000000000000613b38565b6040870151600090612554906001600160a01b031630613d0e565b6040890151909150612570906001600160a01b03168c83613db7565b600061257d87838a611fa1565b905080156127125760007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156125f457600080fd5b505afa158015612608573d6000803e3d6000fd5b505050506040513d602081101561261e57600080fd5b50519050818110156126b257604080516340c10f1960e01b8152306004820152828403602482015290516001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016916340c10f1991604480830192600092919082900301818387803b15801561269957600080fd5b505af11580156126ad573d6000803e3d6000fd5b505050505b6127066001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000167f000000000000000000000000000000000000000000000000000000000000000084613e25565b6127108d836139fb565b505b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561278157600080fd5b505afa158015612795573d6000803e3d6000fd5b505050506040513d60208110156127ab57600080fd5b5051905080156127c3576127c38a6020015182613e77565b505050505050505050505b505050565b60008282111561282a576040805162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b50900390565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316639e571a6a83836040518363ffffffff1660e01b815260040180836001600160a01b03168152602001826001600160a01b031681526020019250505060206040518083038186803b1580156128ae57600080fd5b505afa1580156128c2573d6000803e3d6000fd5b505050506040513d60208110156128d857600080fd5b50511561292c576040805162461bcd60e51b815260206004820152601a60248201527f4552525f4144445f4c49515549444954595f44495341424c4544000000000000604482015290519081900360640190fd5b5050565b60008111611416576040805162461bcd60e51b815260206004820152600e60248201526d4552525f5a45524f5f56414c554560901b604482015290519081900360640190fd5b600061298183611de3565b156129a2576129906000613f88565b61299b858584613fdc565b9050611cc5565b6129c96129b7846001600160a01b03166141ba565b6129c25760006129c4565b825b613f88565b61183f858585856141df565b6000816001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b158015612a1057600080fd5b505afa158015612a24573d6000803e3d6000fd5b505050506040513d6020811015612a3a57600080fd5b505192915050565b600080836001600160a01b03166319b6401560006040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b158015612a8a57600080fd5b505afa158015612a9e573d6000803e3d6000fd5b505050506040513d6020811015612ab457600080fd5b505190506001600160a01b0380821690841614156119db57836001600160a01b03166319b6401560016040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b158015612b1157600080fd5b505afa158015612b25573d6000803e3d6000fd5b505050506040513d6020811015612b3b57600080fd5b5051949350505050565b600080846001600160a01b031663d8959512856040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015612b9557600080fd5b505afa158015612ba9573d6000803e3d6000fd5b505050506040513d6020811015612bbf57600080fd5b505160408051636c4aca8960e11b81526001600160a01b03868116600483015291519188169163d895951291602480820192602092909190829003018186803b158015612c0b57600080fd5b505afa158015612c1f573d6000803e3d6000fd5b505050506040513d6020811015612c3557600080fd5b505190925090505b935093915050565b6000818311612c5457816119db565b5090919050565b612c636153de565b612c6b6153de565b612c7584866146d6565b9050612c848160200151611419565b612c8c61197d565b8160e0015110612cd3576040805162461bcd60e51b815260206004820152600d60248201526c4552525f544f4f5f4541524c5960981b604482015290519081900360640190fd5b63ffffffff8316620f42401415612d8957612d06848260000151836020015184604001518560600151866080015161474f565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316636f366b71856040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b158015612d6c57600080fd5b505af1158015612d80573d6000803e3d6000fd5b50505050612e9f565b60608101516080820151620f4240612daa8363ffffffff88811690611b5416565b81612db157fe5b0460608401526080830151620f424090612dd49063ffffffff88811690611b5416565b81612ddb57fe5b04608084018190528351602085015160408601516060870151612e03948b949392919061474f565b606083015160808401516040805163161139bd60e31b8152600481018a905292850360248401529083036044830152516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169163b089cde891606480830192600092919082900301818387803b158015612e8457600080fd5b505af1158015612e98573d6000803e3d6000fd5b5050505050505b805160208201516040808401516060850151608086015183516327396b6d60e01b81526001600160a01b0396871660048201529486166024860152918516604485015260648401526084830152517f0000000000000000000000000000000000000000000000000000000000000000909216916327396b6d9160a48082019260009290919082900301818387803b158015612f3957600080fd5b505af1158015612f4d573d6000803e3d6000fd5b505050507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316631d092adf866040518263ffffffff1660e01b815260040180826001600160a01b03168152602001915050600060405180830381600087803b158015612fc057600080fd5b505af1158015612fd4573d6000803e3d6000fd5b5092979650505050505050565b6000612feb615473565b612ff3615473565b612ffd8888613258565b9150915061301d8260000151836020015183600001518460200151613799565b61302a898989898961494f565b60408051630aa558ef60e41b81526001600160a01b038b811660048301528a811660248301528981166044830152606482018990526084820188905291517f00000000000000000000000000000000000000000000000000000000000000009092169163aa558ef09160a48082019260009290919082900301818387803b1580156130b457600080fd5b505af11580156130c8573d6000803e3d6000fd5b505050507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663fd4bc1e68a8a6040518363ffffffff1660e01b815260040180836001600160a01b03168152602001826001600160a01b0316815260200192505050602060405180830381600087803b15801561314c57600080fd5b505af1158015613160573d6000803e3d6000fd5b505050506040513d602081101561317657600080fd5b50508151602080840151604080516361d5f08760e01b81526001600160a01b038e811660048301528d811660248301528c81166044830152606482018c9052608482018b905260a482019590955260c481019290925260e48201889052517f0000000000000000000000000000000000000000000000000000000000000000909316926361d5f08792610104808401939192918290030181600087803b15801561321f57600080fd5b505af1158015613233573d6000803e3d6000fd5b505050506040513d602081101561324957600080fd5b50519998505050505050505050565b613260615473565b613268615473565b6000613273856129d5565b905060006132818286612a42565b9050600080613291848489612b45565b91509150600080856001600160a01b0316631f0181bc8a6040518263ffffffff1660e01b815260040180826001600160a01b03168152602001915050604080518083038186803b1580156132e457600080fd5b505afa1580156132f8573d6000803e3d6000fd5b505050506040513d604081101561330e57600080fd5b5080516020918201516040805180820182529788528784019690965285518087019096529085529084015250919890975095505050505050565b82518151602084015160009283926133739261336d916133689190611b54565b614b3c565b90611b54565b905060006133a2866020015161336d61339d87602001518960000151611b5490919063ffffffff16565b614b5c565b9050818702878382816133b157fe5b0414156133cc578181816133c157fe5b049350505050611cc5565b6000808985116133dd5789856133e0565b848a5b915091506000806133fd848785600019816133f757fe5b04614bb2565b9150915060006134178488878161341057fe5b0490611b54565b905081156134435761343481838686028161342e57fe5b04612c45565b98505050505050505050611cc5565b9c9b505050505050505050505050565b61345b615473565b6020830151825160009161346f9190611b54565b8451602085015191925060009161348591611b54565b905081810260008284838161349657fe5b04146134b4576134a583614b5c565b6134ae85614b5c565b026134bd565b6134bd82614b5c565b905060006134cb8585611981565b9050600281066134fe576002810490506040518060400160405280838303815260200182815250955050505050506119de565b604080518082019091526002909202810382526020820152935050505092915050565b613529615473565b600061353583856127d3565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316632c560f896040518163ffffffff1660e01b815260040160206040518083038186803b15801561359257600080fd5b505afa1580156135a6573d6000803e3d6000fd5b505050506040513d60208110156135bc57600080fd5b50516040805163ce3f3adb60e01b815290519192506000916001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169163ce3f3adb916004808301926020929190829003018186803b15801561362457600080fd5b505afa158015613638573d6000803e3d6000fd5b505050506040513d602081101561364e57600080fd5b505190508183101561367b57604051806040016040528060008152602001600181525093505050506119de565b8083106136a357604051806040016040528060018152602001600181525093505050506119de565b604080518082019091529283526020830152509392505050565b805160009081906136ce9087611b54565b602084015190915060006136eb6136e58484612c45565b88612c45565b90506000806137088860000151896020015185600019816133f757fe5b909250905061173761372761371d8387611b54565b610cf88589611b54565b61183983610cf861373882886127d3565b8e90611b54565b604080516001600160a01b0380861660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b03166323b872dd60e01b179052610636908590614bfa565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166324a088686040518163ffffffff1660e01b815260040160206040518083038186803b1580156137f457600080fd5b505afa158015613808573d6000803e3d6000fd5b505050506040513d602081101561381e57600080fd5b5051620f42400363ffffffff908116915060009061384a90839061336d90829082908b908990611b5416565b90506000613861620f424061336d85818a8a611b54565b90506000613878620f424061336d81818c8a611b54565b905081831115801561388a5750808211155b6138ce576040805162461bcd60e51b815260206004820152601060248201526f4552525f494e56414c49445f5241544560801b604482015290519081900360640190fd5b5050505050505050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663deacd84e83836040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050600060405180830381600087803b15801561394f57600080fd5b505af1158015613963573d6000803e3d6000fd5b505050507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166340c10f1984836040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050600060405180830381600087803b1580156139de57600080fd5b505af11580156139f2573d6000803e3d6000fd5b50505050505050565b6000613a8d7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663045544436040518163ffffffff1660e01b815260040160206040518083038186803b158015613a5957600080fd5b505afa158015613a6d573d6000803e3d6000fd5b505050506040513d6020811015613a8357600080fd5b505161183961197d565b90507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663dbae3a5d8484846040518463ffffffff1660e01b815260040180846001600160a01b031681526020018381526020018281526020019350505050602060405180830381600087803b158015613b0e57600080fd5b505af1158015613b22573d6000803e3d6000fd5b505050506040513d602081101561103457600080fd5b6000613b43856129d5565b604080516002808252606080830184529394509091602083019080368337505060408051600280825260608083018452949550909250906020830190803683370190505090508482600081518110613b9757fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508382600181518110613bc557fe5b60200260200101906001600160a01b031690816001600160a01b031681525050600181600081518110613bf457fe5b602002602001018181525050600181600181518110613c0f57fe5b602002602001018181525050826001600160a01b031663b127c0a58784846040518463ffffffff1660e01b8152600401808481526020018060200180602001838103835285818151815260200191508051906020019060200280838360005b83811015613c86578181015183820152602001613c6e565b50505050905001838103825284818151815260200191508051906020019060200280838360005b83811015613cc5578181015183820152602001613cad565b5050505090500195505050505050600060405180830381600087803b158015613ced57600080fd5b505af1158015613d01573d6000803e3d6000fd5b5050505050505050505050565b6000613d19836141ba565b15613d2f57506001600160a01b038116316119de565b613d3883614cab565b6001600160a01b03166370a08231836040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015613d8457600080fd5b505afa158015613d98573d6000803e3d6000fd5b505050506040513d6020811015613dae57600080fd5b50519392505050565b80613dc1576127ce565b613dca836141ba565b15613e0b576040516001600160a01b0383169082156108fc029083906000818181858888f19350505050158015613e05573d6000803e3d6000fd5b506127ce565b6127ce8282613e1986614cab565b6001600160a01b031691905b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b1790526127ce908490614bfa565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663802fa3ba83836040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050600060405180830381600087803b158015613eee57600080fd5b505af1158015613f02573d6000803e3d6000fd5b505050507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166342966c68826040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b158015613f6c57600080fd5b505af1158015613f80573d6000803e3d6000fd5b505050505050565b803414611416576040805162461bcd60e51b815260206004820152601760248201527f4552525f4554485f414d4f554e545f4d49534d41544348000000000000000000604482015290519081900360640190fd5b6000827f0000000000000000000000000000000000000000000000000000000000000000614008615473565b6140128383611e15565b905060006140358260000151610cf8846020015189611b5490919063ffffffff16565b90507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166319c6a5e485836040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050600060405180830381600087803b1580156140ae57600080fd5b505af11580156140c2573d6000803e3d6000fd5b5050505060006140dd898686858b6140d861197d565b612fe1565b90506141146001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001633308a61373f565b61411e8888613e77565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166340c10f198a896040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050600060405180830381600087803b15801561419557600080fd5b505af11580156141a9573d6000803e3d6000fd5b50929b9a5050505050505050505050565b6001600160a01b03811673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee14919050565b6000837f00000000000000000000000000000000000000000000000000000000000000008261420d836129d5565b905060008061421d838986612b45565b915091507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166312588d0e6040518163ffffffff1660e01b815260040160206040518083038186803b15801561427a57600080fd5b505afa15801561428e573d6000803e3d6000fd5b505050506040513d60208110156142a457600080fd5b50518110156142fa576040805162461bcd60e51b815260206004820152601860248201527f4552525f4e4f545f454e4f5547485f4c49515549444954590000000000000000604482015290519081900360640190fd5b600061430a83610cf88a85611b54565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663943fd08a8c6040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561437b57600080fd5b505afa15801561438f573d6000803e3d6000fd5b505050506040513d60208110156143a557600080fd5b5051905080614436577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663b97b55ce6040518163ffffffff1660e01b815260040160206040518083038186803b15801561440757600080fd5b505afa15801561441b573d6000803e3d6000fd5b505050506040513d602081101561443157600080fd5b505190505b60006144a9837f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663350ed8e78f6040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015610d7b57600080fd5b9050818111156144f9576040805162461bcd60e51b815260206004820152601660248201527511549497d3505617d05353d5539517d4915050d2115160521b604482015290519081900360640190fd5b614504308d856138d8565b6145186001600160a01b0388168785614cae565b61452a8b6001600160a01b03166141ba565b614557576145436001600160a01b038c1633308d614cdf565b6145576001600160a01b038c16878c614cae565b614565868c898d8734614d1a565b6000886001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156145b457600080fd5b505afa1580156145c8573d6000803e3d6000fd5b505050506040513d60208110156145de57600080fd5b505190506146166001600160a01b038a167f000000000000000000000000000000000000000000000000000000000000000083613e25565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001663332100fa8a6002840484036040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050600060405180830381600087803b15801561469257600080fd5b505af11580156146a6573d6000803e3d6000fd5b505050506146c48e8a8e600285816146ba57fe5b048f6140d861197d565b9e9d5050505050505050505050505050565b6146de6153de565b6146e66153de565b6146ef84611a4a565b9050826001600160a01b031681600001516001600160a01b0316146119db576040805162461bcd60e51b815260206004820152601160248201527011549497d050d0d154d4d7d11153925151607a1b604482015290519081900360640190fd5b60607f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316639e2ce9d26040518163ffffffff1660e01b815260040160006040518083038186803b1580156147aa57600080fd5b505afa1580156147be573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405260208110156147e757600080fd5b8101908080516040519392919084600160201b82111561480657600080fd5b90830190602082018581111561481b57600080fd5b82518660208202830111600160201b8211171561483757600080fd5b82525081516020918201928201910280838360005b8381101561486457818101518382015260200161484c565b50505050905001604052505050905060008151905060005b818110156149445782818151811061489057fe5b60200260200101516001600160a01b031663b8128fe68a8a8a8a8a8a6040518763ffffffff1660e01b815260040180878152602001866001600160a01b03168152602001856001600160a01b03168152602001846001600160a01b031681526020018381526020018281526020019650505050505050600060405180830381600087803b15801561492057600080fd5b505af1158015614934573d6000803e3d6000fd5b50506001909201915061487c9050565b505050505050505050565b60607f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316639e2ce9d26040518163ffffffff1660e01b815260040160006040518083038186803b1580156149aa57600080fd5b505afa1580156149be573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405260208110156149e757600080fd5b8101908080516040519392919084600160201b821115614a0657600080fd5b908301906020820185811115614a1b57600080fd5b82518660208202830111600160201b82111715614a3757600080fd5b82525081516020918201928201910280838360005b83811015614a64578181015183820152602001614a4c565b50505050905001604052505050905060008151905060005b818110156138ce57828181518110614a9057fe5b60200260200101516001600160a01b031663139c22ea89898989896040518663ffffffff1660e01b815260040180866001600160a01b03168152602001856001600160a01b03168152602001846001600160a01b0316815260200183815260200182815260200195505050505050600060405180830381600087803b158015614b1857600080fd5b505af1158015614b2c573d6000803e3d6000fd5b505060019092019150614a7c9050565b600080614b4883614b5c565b905082818202146119de5780600101611976565b60008060028304600101905060006002828581614b7557fe5b04830181614b7f57fe5b0490505b80821115614bab578091506002828581614b9957fe5b04830181614ba357fe5b049050614b83565b5092915050565b600080848484821180614bc457508481115b15614bda57614bd4828287614ed3565b90925090505b808214614beb579092509050612c3d565b50600196879650945050505050565b6060614c4f826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316614f0c9092919063ffffffff16565b8051909150156127ce57808060200190516020811015614c6e57600080fd5b50516127ce5760405162461bcd60e51b815260040180806020018281038252602a8152602001806154d5602a913960400191505060405180910390fd5b90565b614cb7836141ba565b15614cc1576127ce565b6127ce8282614ccf86614cab565b6001600160a01b03169190614f1b565b801580614cf05750614cf0846141ba565b15614cfa57610636565b610636838383614d0988614cab565b6001600160a01b031692919061373f565b6040805160028082526060808301845292602083019080368337505060408051600280825260608083018452949550909250906020830190803683370190505090508682600081518110614d6a57fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508582600181518110614d9857fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508481600081518110614dc657fe5b6020026020010181815250508381600181518110614de057fe5b602002602001018181525050876001600160a01b0316637d8916bd84848460016040518563ffffffff1660e01b8152600401808060200180602001848152602001838103835286818151815260200191508051906020019060200280838360005b83811015614e59578181015183820152602001614e41565b50505050905001838103825285818151815260200191508051906020019060200280838360005b83811015614e98578181015183820152602001614e80565b50505050905001955050505050506000604051808303818588803b158015614ebf57600080fd5b505af11580156127c3573d6000803e3d6000fd5b600080838511614ef157614ee8858585614fe0565b91509150612c3d565b600080614eff868887614fe0565b9890975095505050505050565b6060611cc58484600085615099565b80614f25576127ce565b60408051636eb1769f60e11b81523060048201526001600160a01b038481166024830152915160009286169163dd62ed3e916044808301926020929190829003018186803b158015614f7657600080fd5b505afa158015614f8a573d6000803e3d6000fd5b505050506040513d6020811015614fa057600080fd5b50519050818110614fb157506127ce565b8015614fcc57614fcc6001600160a01b0385168460006151f5565b6106366001600160a01b03851684846151f5565b60008060008360001981614ff057fe5b0490508086111561502957600081600101878161500957fe5b04600101905080878161501857fe5b04965080868161502457fe5b049550505b8486146150895785840285870187811061505a5760006150498383615308565b955050508385039250612c3d915050565b60028888030487038210156150785760008694509450505050612c3d565b600180870394509450505050612c3d565b5050600290910493849350915050565b6060824710156150da5760405162461bcd60e51b815260040180806020018281038252602681526020018061548e6026913960400191505060405180910390fd5b6150e385615334565b615134576040805162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015290519081900360640190fd5b60006060866001600160a01b031685876040518082805190602001908083835b602083106151735780518252601f199092019160209182019101615154565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d80600081146151d5576040519150601f19603f3d011682016040523d82523d6000602084013e6151da565b606091505b50915091506151ea82828661533a565b979650505050505050565b80158061527b575060408051636eb1769f60e11b81523060048201526001600160a01b03848116602483015291519185169163dd62ed3e91604480820192602092909190829003018186803b15801561524d57600080fd5b505afa158015615261573d6000803e3d6000fd5b505050506040513d602081101561527757600080fd5b5051155b6152b65760405162461bcd60e51b81526004018080602001828103825260368152602001806154ff6036913960400191505060405180910390fd5b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b1790526127ce908490614bfa565b600060028204820382848161531957fe5b068161532157fe5b0482848161532b57fe5b04019392505050565b3b151590565b60608315615349575081611976565b8251156153595782518084602001fd5b8160405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b838110156153a357818101518382015260200161538b565b50505050905090810190601f1680156153d05780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b60405180610100016040528060006001600160a01b0316815260200160006001600160a01b0316815260200160006001600160a01b0316815260200160008152602001600081526020016000815260200160008152602001600081525090565b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a081019190915290565b60405180604001604052806000815260200160008152509056fe416464726573733a20696e73756666696369656e742062616c616e636520666f722063616c6c536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f775361666545524332303a204552433230206f7065726174696f6e20646964206e6f7420737563636565645361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f20746f206e6f6e2d7a65726f20616c6c6f77616e6365a2646970667358221220cf0c88f5d19edd66e979be80836686045ed01d2ea29d9d3fa80f03a250f3dba464736f6c634300060c0033000000000000000000000000f7d28faa1fe9ea53279fe6e3cde75175859bdf46000000000000000000000000f5fab5dbd2f3bf675de4cb76517d4767013cfb550000000000000000000000009712bb50dc6efb8a3d7d12cea500a50967d2d471000000000000000000000000c4c5634de585d43daec8fa2a6fb6286cd9b87131000000000000000000000000d1d846312b819743974786050848d9b3d06b9b55000000000000000000000000a489c2b5b36835a327851ab917a80562b5afc2440000000000000000000000000887ae1251e180d7d453aedebee26e1639f20113000000000000000000000000f8a2fb650e25a26ce839d64be8a0abbcb0b87b32
Deployed Bytecode
0x6080604052600436106101395760003560e01c80638da5cb5b116100ab578063caee4c8f1161006f578063caee4c8f146104a9578063d4ee1d90146104e5578063d80528ae146104fa578063e06174e41461050f578063e4a7672614610524578063f2fde38b1461055a57610140565b80638da5cb5b146103be578063975057e7146103d3578063bf3b1101146103e8578063c2250a991461041b578063c83df6631461044e57610140565b8063630d8c63116100fd578063630d8c63146102bf5780636d533e9b146102ef578063782ed90c1461034957806379ba50971461037f578063879015e81461039457806389d94b46146103a957610140565b806324afe2d91461014557806328790b5a1461019157806340083480146101a8578063521eb2731461025557806355bd513f1461028657610140565b3661014057005b600080fd5b34801561015157600080fd5b506101786004803603602081101561016857600080fd5b50356001600160a01b031661058d565b6040805192835260208301919091528051918290030190f35b34801561019d57600080fd5b506101a66105bf565b005b3480156101b457600080fd5b50610243600480360360808110156101cb57600080fd5b8135916001600160a01b03602082013581169260408301359091169190810190608081016060820135600160201b81111561020557600080fd5b82018360208201111561021757600080fd5b803590602001918460018302840111600160201b8311171561023857600080fd5b50909250905061063c565b60408051918252519081900360200190f35b34801561026157600080fd5b5061026a61071c565b604080516001600160a01b039092168252519081900360200190f35b34801561029257600080fd5b50610243600480360360408110156102a957600080fd5b50803590602001356001600160a01b0316610740565b3480156102cb57600080fd5b506101a6600480360360408110156102e257600080fd5b5080359060200135610770565b3480156102fb57600080fd5b5061032b6004803603606081101561031257600080fd5b5080359063ffffffff6020820135169060400135610b1b565b60408051938452602084019290925282820152519081900360600190f35b34801561035557600080fd5b506101a66004803603604081101561036c57600080fd5b508035906020013563ffffffff16610e0d565b34801561038b57600080fd5b506101a6610e38565b3480156103a057600080fd5b5061026a610eef565b3480156103b557600080fd5b506101a6610f13565b3480156103ca57600080fd5b5061026a610f76565b3480156103df57600080fd5b5061026a610f85565b3480156103f457600080fd5b506101a66004803603602081101561040b57600080fd5b50356001600160a01b0316610fa9565b34801561042757600080fd5b506101a66004803603602081101561043e57600080fd5b50356001600160a01b031661103b565b34801561045a57600080fd5b50610243600480360360e081101561047157600080fd5b506001600160a01b03813581169160208101359091169060408101359060608101359060808101359060a08101359060c001356110b2565b610243600480360360808110156104bf57600080fd5b506001600160a01b038135811691602081013582169160408201351690606001356111aa565b3480156104f157600080fd5b5061026a61120a565b34801561050657600080fd5b5061026a611219565b34801561051b57600080fd5b5061026a61123d565b6102436004803603606081101561053a57600080fd5b506001600160a01b03813581169160208101359091169060400135611261565b34801561056657600080fd5b506101a66004803603602081101561057d57600080fd5b50356001600160a01b03166112b5565b6000808261059a81611333565b6105a381611419565b6105ac84611503565b6105b585611745565b9250925050915091565b6105c7611848565b7f000000000000000000000000d1d846312b819743974786050848d9b3d06b9b556001600160a01b03166379ba50976040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561062257600080fd5b505af1158015610636573d6000803e3d6000fd5b50505050565b600061064661189d565b6002805584610654816118e5565b8461065e816118e5565b600061066b338a8a611936565b604051635c2ba84560e01b8152600481018281523360248301819052606060448401908152606484018a90529394506001600160a01b038b1693635c2ba8459386938c928c92608401848480828437600081840152601f19601f82011690508083019250505095505050505050600060405180830381600087803b1580156106f257600080fd5b505af1158015610706573d6000803e3d6000fd5b5050600160025550909998505050505050505050565b7f000000000000000000000000d1d846312b819743974786050848d9b3d06b9b5590565b600061074a61189d565b6002805581610758816118e5565b610763338585611936565b6001600255949350505050565b61077861189d565b6002805560408051637a1036f560e11b81523360048201526024810184905260448101839052905160609182916001600160a01b037f000000000000000000000000f5fab5dbd2f3bf675de4cb76517d4767013cfb55169163f4206dea916064808301926000929190829003018186803b1580156107f557600080fd5b505afa158015610809573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604090815281101561083257600080fd5b8101908080516040519392919084600160201b82111561085157600080fd5b90830190602082018581111561086657600080fd5b82518660208202830111600160201b8211171561088257600080fd5b82525081516020918201928201910280838360005b838110156108af578181015183820152602001610897565b5050505090500160405260200180516040519392919084600160201b8211156108d757600080fd5b9083019060208201858111156108ec57600080fd5b82518660208202830111600160201b8211171561090857600080fd5b82525081516020918201928201910280838360005b8381101561093557818101518382015260200161091d565b5050505090500160405250505091509150600080835190508251811461095757fe5b805b8015610a4657600019810161096c61197d565b85828151811061097857fe5b6020026020010151111561098c5750610a3d565b604080516390e0661b60e01b8152336004820152898301602482015290516001600160a01b037f000000000000000000000000f5fab5dbd2f3bf675de4cb76517d4767013cfb5516916390e0661b91604480830192600092919082900301818387803b1580156109fb57600080fd5b505af1158015610a0f573d6000803e3d6000fd5b50505050610a39868281518110610a2257fe5b60200260200101518561198190919063ffffffff16565b9350505b60001901610959565b508115610b0e577f000000000000000000000000d1d846312b819743974786050848d9b3d06b9b556001600160a01b0316635e35359e7f0000000000000000000000001f573d6fb3f13d689ff844b4ce37794d79a7ff1c33856040518463ffffffff1660e01b815260040180846001600160a01b03168152602001836001600160a01b031681526020018281526020019350505050600060405180830381600087803b158015610af557600080fd5b505af1158015610b09573d6000803e3d6000fd5b505050505b5050600160025550505050565b600080600084610b2a816119e4565b610b326153de565b610b3b88611a4a565b80519091506001600160a01b0316610b8b576040805162461bcd60e51b815260206004820152600e60248201526d11549497d253959053125117d25160921b604482015290519081900360640190fd5b8060e00151861015610bdc576040805162461bcd60e51b815260206004820152601560248201527404552525f494e56414c49445f54494d455354414d5605c1b604482015290519081900360640190fd5b63ffffffff8716620f424014610c41576060810151620f424090610c099063ffffffff8a811690611b5416565b81610c1057fe5b0460608201526080810151620f424090610c339063ffffffff8a811690611b5416565b81610c3a57fe5b0460808201525b610c4961543e565b610c65826020015183604001518460a001518560c00151611bad565b90506000610c8c8360200151846040015185606001518660800151868860e001518e611ccd565b9050610c9b8360400151611de3565b15610cb157955085945060009350610e03915050565b610cb9615473565b610ccb84602001518560400151611e15565b90506000610cfe6002836000015181610ce057fe5b04610cf8846020015186611b5490919063ffffffff16565b90611f3a565b90506000610dad86606001517f000000000000000000000000c4c5634de585d43daec8fa2a6fb6286cd9b871316001600160a01b0316635121220c89602001516040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015610d7b57600080fd5b505afa158015610d8f573d6000803e3d6000fd5b505050506040513d6020811015610da557600080fd5b505190611981565b9050808211610dbc5781610dbe565b805b91506000610de48460200151610cf86002876000015181610ddb57fe5b87919004611b54565b90506000610df3868389611fa1565b959b509099509397505050505050505b5093509350939050565b610e1561189d565b6002805580610e23816119e4565b610e2e338484612086565b5050600160025550565b6001546001600160a01b03163314610e8b576040805162461bcd60e51b815260206004820152601160248201527011549497d050d0d154d4d7d11153925151607a1b604482015290519081900360640190fd5b600154600080546040516001600160a01b0393841693909116917f343765429aea5a34b3ff6a3785a98a5abb2597aca87bfbb58632c173d585373a91a360018054600080546001600160a01b03199081166001600160a01b03841617909155169055565b7f000000000000000000000000c4c5634de585d43daec8fa2a6fb6286cd9b8713190565b610f1b611848565b7f000000000000000000000000f5fab5dbd2f3bf675de4cb76517d4767013cfb556001600160a01b03166379ba50976040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561062257600080fd5b6000546001600160a01b031681565b7f000000000000000000000000f5fab5dbd2f3bf675de4cb76517d4767013cfb5590565b610fb1611848565b7f000000000000000000000000d1d846312b819743974786050848d9b3d06b9b556001600160a01b031663f2fde38b826040518263ffffffff1660e01b815260040180826001600160a01b03168152602001915050600060405180830381600087803b15801561102057600080fd5b505af1158015611034573d6000803e3d6000fd5b5050505050565b611043611848565b7f000000000000000000000000f5fab5dbd2f3bf675de4cb76517d4767013cfb556001600160a01b031663f2fde38b826040518263ffffffff1660e01b815260040180826001600160a01b03168152602001915050600060405180830381600087803b15801561102057600080fd5b6000806110c386610cf88988611b54565b90506110cd61543e565b6110d98a8a8787611bad565b905060006111888b8b858c8661117b7f000000000000000000000000f7d28faa1fe9ea53279fe6e3cde75175859bdf466001600160a01b031663ce3f3adb6040518163ffffffff1660e01b815260040160206040518083038186803b15801561114157600080fd5b505afa158015611155573d6000803e3d6000fd5b505050506040513d602081101561116b57600080fd5b505161117561197d565b906127d3565b61118361197d565b611ccd565b905061119b89610cf883620f4240611b54565b9b9a5050505050505050505050565b60006111b461189d565b60028055846111c2816118e5565b846111cc81611333565b6111d581611419565b85856111e18282612830565b856111eb81612930565b6111f78a8a8a8a612976565b60016002559a9950505050505050505050565b6001546001600160a01b031681565b7f0000000000000000000000009712bb50dc6efb8a3d7d12cea500a50967d2d47190565b7f000000000000000000000000f7d28faa1fe9ea53279fe6e3cde75175859bdf4690565b600061126b61189d565b600280558361127981611333565b61128281611419565b848461128e8282612830565b8461129881612930565b6112a433898989612976565b600160025598975050505050505050565b6112bd611848565b6000546001600160a01b0382811691161415611311576040805162461bcd60e51b815260206004820152600e60248201526d22a9292fa9a0a6a2afa7aba722a960911b604482015290519081900360640190fd5b600180546001600160a01b0319166001600160a01b0392909216919091179055565b7f000000000000000000000000f7d28faa1fe9ea53279fe6e3cde75175859bdf466001600160a01b031663d4f63148826040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156113a057600080fd5b505afa1580156113b4573d6000803e3d6000fd5b505050506040513d60208110156113ca57600080fd5b5051611416576040805162461bcd60e51b815260206004820152601660248201527511549497d413d3d317d393d517d4d5541413d495115160521b604482015290519081900360640190fd5b50565b7f000000000000000000000000f7d28faa1fe9ea53279fe6e3cde75175859bdf466001600160a01b0316632b26a982826040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561148657600080fd5b505afa15801561149a573d6000803e3d6000fd5b505050506040513d60208110156114b057600080fd5b5051611416576040805162461bcd60e51b815260206004820152601860248201527f4552525f504f4f4c5f4e4f545f57484954454c49535445440000000000000000604482015290519081900360640190fd5b60008061150f836129d5565b90507f0000000000000000000000001f573d6fb3f13d689ff844b4ce37794d79a7ff1c600061153e8383612a42565b905060008061154e858486612b45565b9150915060007f000000000000000000000000f7d28faa1fe9ea53279fe6e3cde75175859bdf466001600160a01b031663943fd08a896040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156115c157600080fd5b505afa1580156115d5573d6000803e3d6000fd5b505050506040513d60208110156115eb57600080fd5b505190508061167c577f000000000000000000000000f7d28faa1fe9ea53279fe6e3cde75175859bdf466001600160a01b031663b97b55ce6040518163ffffffff1660e01b815260040160206040518083038186803b15801561164d57600080fd5b505afa158015611661573d6000803e3d6000fd5b505050506040513d602081101561167757600080fd5b505190505b60007f000000000000000000000000c4c5634de585d43daec8fa2a6fb6286cd9b871316001600160a01b031663350ed8e78a6040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156116eb57600080fd5b505afa1580156116ff573d6000803e3d6000fd5b505050506040513d602081101561171557600080fd5b505190506000816117268482612c45565b03905061173784610cf88388611b54565b9a9950505050505050505050565b6000817f0000000000000000000000001f573d6fb3f13d689ff844b4ce37794d79a7ff1c611771615473565b61177b8383611e15565b905061183f8160200151610cf86001611175856000015161183987600001517f000000000000000000000000c4c5634de585d43daec8fa2a6fb6286cd9b871316001600160a01b0316635121220c8c6040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561180757600080fd5b505afa15801561181b573d6000803e3d6000fd5b505050506040513d602081101561183157600080fd5b505190611b54565b90611981565b95945050505050565b6000546001600160a01b0316331461189b576040805162461bcd60e51b815260206004820152601160248201527011549497d050d0d154d4d7d11153925151607a1b604482015290519081900360640190fd5b565b60016002541461189b576040805162461bcd60e51b815260206004820152600e60248201526d4552525f5245454e5452414e435960901b604482015290519081900360640190fd5b6001600160a01b038116611416576040805162461bcd60e51b81526020600482015260136024820152724552525f494e56414c49445f4144445245535360681b604482015290519081900360640190fd5b60006119406153de565b61194e8585620f4240612c5b565b90506119728382602001518360400151846060015185608001518660e00151612fe1565b9150505b9392505050565b4290565b6000828201838110156119db576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b90505b92915050565b60008163ffffffff16118015611a035750620f424063ffffffff821611155b611416576040805162461bcd60e51b815260206004820152601360248201527222a9292fa4a72b20a624a22fa827a92a24a7a760691b604482015290519081900360640190fd5b611a526153de565b611a5a6153de565b7f000000000000000000000000f5fab5dbd2f3bf675de4cb76517d4767013cfb556001600160a01b0316635290ffbb846040518263ffffffff1660e01b8152600401808281526020019150506101006040518083038186803b158015611abf57600080fd5b505afa158015611ad3573d6000803e3d6000fd5b505050506040513d610100811015611aea57600080fd5b50805160208083015160408085015160608087015160808089015160a0808b015160c0808d015160e09d8e01519d8f019d909d528d019b909b528b01999099528901979097528701959095526001600160a01b039485169086015283169084015216815292915050565b600082611b63575060006119de565b82820282848281611b7057fe5b04146119db5760405162461bcd60e51b81526004018080602001828103825260218152602001806154b46021913960400191505060405180910390fd5b611bb561543e565b611bbd615473565b611bc5615473565b611bcf8787613258565b915091506001600160801b038511158015611bf157506001600160801b038411155b8015611c05575081516001600160801b0310155b8015611c1c57506001600160801b03826020015111155b8015611c30575080516001600160801b0310155b8015611c4757506001600160801b03816020015111155b611c4d57fe5b6040518060c00160405280866001600160801b03168152602001856001600160801b0316815260200183600001516001600160801b0316815260200183602001516001600160801b0316815260200182600001516001600160801b0316815260200182602001516001600160801b0316815250925050505b949350505050565b6000611cd7615473565b611ce18989611e15565b9050611ceb615473565b604051806040016040528087600001516001600160801b0316815260200187602001516001600160801b03168152509050611d24615473565b50604080518082018252908701516001600160801b0390811682526060880151166020820152611d52615473565b604051806040016040528089608001516001600160801b031681526020018960a001516001600160801b031681525090506000611d918b868686613348565b9050611d9b615473565b611da58584613453565b9050611daf615473565b611db98a8a613521565b9050611dd08c611dc98e86612c45565b84846136bd565b9f9e505050505050505050505050505050565b7f0000000000000000000000001f573d6fb3f13d689ff844b4ce37794d79a7ff1c6001600160a01b0390811691161490565b611e1d615473565b6000836001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b158015611e5857600080fd5b505afa158015611e6c573d6000803e3d6000fd5b505050506040513d6020811015611e8257600080fd5b505190506000611e91856129d5565b90506000816001600160a01b031663d8959512866040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015611ee257600080fd5b505afa158015611ef6573d6000803e3d6000fd5b505050506040513d6020811015611f0c57600080fd5b50516040805180820190915290915080611f27836002611b54565b8152602001939093525090949350505050565b6000808211611f90576040805162461bcd60e51b815260206004820152601a60248201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604482015290519081900360640190fd5b818381611f9957fe5b049392505050565b6000828411611fb257506000611976565b6000611fe78360a001516001600160801b0316610cf885608001516001600160801b0316878903611b5490919063ffffffff16565b90507f000000000000000000000000f7d28faa1fe9ea53279fe6e3cde75175859bdf466001600160a01b031663a80c76ff6040518163ffffffff1660e01b815260040160206040518083038186803b15801561204257600080fd5b505afa158015612056573d6000803e3d6000fd5b505050506040513d602081101561206c57600080fd5b5051811061207b579050611976565b506000949350505050565b61208e6153de565b612099848484612c5b565b90507f000000000000000000000000c4c5634de585d43daec8fa2a6fb6286cd9b871316001600160a01b031663332100fa826020015183606001516040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050600060405180830381600087803b15801561211a57600080fd5b505af115801561212e573d6000803e3d6000fd5b5050505061213f8160400151611de3565b15612204576080810151612181906001600160a01b037f00000000000000000000000048fb253446873234f2febbf9bdeaa72d9d387f9416908690309061373f565b7f0000000000000000000000000887ae1251e180d7d453aedebee26e1639f201136001600160a01b03166342966c6882608001516040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b1580156121eb57600080fd5b505af11580156121ff573d6000803e3d6000fd5b505050505b61220c61543e565b612228826020015183604001518460a001518560c00151611bad565b905061226a81604001516001600160801b031682606001516001600160801b031683608001516001600160801b03168460a001516001600160801b0316613799565b60006122918360200151846040015185606001518660800151868860e0015161118361197d565b90506122a08360400151611de3565b156122e6576122d47f000000000000000000000000d1d846312b819743974786050848d9b3d06b9b558460200151836138d8565b6122de86826139fb565b5050506127ce565b6122ee615473565b61230084602001518560400151611e15565b905060006123156002836000015181610ce057fe5b905060007f000000000000000000000000c4c5634de585d43daec8fa2a6fb6286cd9b871316001600160a01b0316635121220c87602001516040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561238a57600080fd5b505afa15801561239e573d6000803e3d6000fd5b505050506040513d60208110156123b457600080fd5b505190508082116123c557816123c7565b805b91506000866020015190507f000000000000000000000000c4c5634de585d43daec8fa2a6fb6286cd9b871316001600160a01b03166319c6a5e48860200151856040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050600060405180830381600087803b15801561244d57600080fd5b505af1158015612461573d6000803e3d6000fd5b505050507f000000000000000000000000d1d846312b819743974786050848d9b3d06b9b556001600160a01b0316635e35359e8230866040518463ffffffff1660e01b815260040180846001600160a01b03168152602001836001600160a01b031681526020018281526020019350505050600060405180830381600087803b1580156124ed57600080fd5b505af1158015612501573d6000803e3d6000fd5b5050505061253987602001518489604001517f0000000000000000000000001f573d6fb3f13d689ff844b4ce37794d79a7ff1c613b38565b6040870151600090612554906001600160a01b031630613d0e565b6040890151909150612570906001600160a01b03168c83613db7565b600061257d87838a611fa1565b905080156127125760007f0000000000000000000000001f573d6fb3f13d689ff844b4ce37794d79a7ff1c6001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156125f457600080fd5b505afa158015612608573d6000803e3d6000fd5b505050506040513d602081101561261e57600080fd5b50519050818110156126b257604080516340c10f1960e01b8152306004820152828403602482015290516001600160a01b037f000000000000000000000000a489c2b5b36835a327851ab917a80562b5afc24416916340c10f1991604480830192600092919082900301818387803b15801561269957600080fd5b505af11580156126ad573d6000803e3d6000fd5b505050505b6127066001600160a01b037f0000000000000000000000001f573d6fb3f13d689ff844b4ce37794d79a7ff1c167f000000000000000000000000d1d846312b819743974786050848d9b3d06b9b5584613e25565b6127108d836139fb565b505b60007f0000000000000000000000001f573d6fb3f13d689ff844b4ce37794d79a7ff1c6001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561278157600080fd5b505afa158015612795573d6000803e3d6000fd5b505050506040513d60208110156127ab57600080fd5b5051905080156127c3576127c38a6020015182613e77565b505050505050505050505b505050565b60008282111561282a576040805162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b50900390565b7f000000000000000000000000f7d28faa1fe9ea53279fe6e3cde75175859bdf466001600160a01b0316639e571a6a83836040518363ffffffff1660e01b815260040180836001600160a01b03168152602001826001600160a01b031681526020019250505060206040518083038186803b1580156128ae57600080fd5b505afa1580156128c2573d6000803e3d6000fd5b505050506040513d60208110156128d857600080fd5b50511561292c576040805162461bcd60e51b815260206004820152601a60248201527f4552525f4144445f4c49515549444954595f44495341424c4544000000000000604482015290519081900360640190fd5b5050565b60008111611416576040805162461bcd60e51b815260206004820152600e60248201526d4552525f5a45524f5f56414c554560901b604482015290519081900360640190fd5b600061298183611de3565b156129a2576129906000613f88565b61299b858584613fdc565b9050611cc5565b6129c96129b7846001600160a01b03166141ba565b6129c25760006129c4565b825b613f88565b61183f858585856141df565b6000816001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b158015612a1057600080fd5b505afa158015612a24573d6000803e3d6000fd5b505050506040513d6020811015612a3a57600080fd5b505192915050565b600080836001600160a01b03166319b6401560006040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b158015612a8a57600080fd5b505afa158015612a9e573d6000803e3d6000fd5b505050506040513d6020811015612ab457600080fd5b505190506001600160a01b0380821690841614156119db57836001600160a01b03166319b6401560016040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b158015612b1157600080fd5b505afa158015612b25573d6000803e3d6000fd5b505050506040513d6020811015612b3b57600080fd5b5051949350505050565b600080846001600160a01b031663d8959512856040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015612b9557600080fd5b505afa158015612ba9573d6000803e3d6000fd5b505050506040513d6020811015612bbf57600080fd5b505160408051636c4aca8960e11b81526001600160a01b03868116600483015291519188169163d895951291602480820192602092909190829003018186803b158015612c0b57600080fd5b505afa158015612c1f573d6000803e3d6000fd5b505050506040513d6020811015612c3557600080fd5b505190925090505b935093915050565b6000818311612c5457816119db565b5090919050565b612c636153de565b612c6b6153de565b612c7584866146d6565b9050612c848160200151611419565b612c8c61197d565b8160e0015110612cd3576040805162461bcd60e51b815260206004820152600d60248201526c4552525f544f4f5f4541524c5960981b604482015290519081900360640190fd5b63ffffffff8316620f42401415612d8957612d06848260000151836020015184604001518560600151866080015161474f565b7f000000000000000000000000f5fab5dbd2f3bf675de4cb76517d4767013cfb556001600160a01b0316636f366b71856040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b158015612d6c57600080fd5b505af1158015612d80573d6000803e3d6000fd5b50505050612e9f565b60608101516080820151620f4240612daa8363ffffffff88811690611b5416565b81612db157fe5b0460608401526080830151620f424090612dd49063ffffffff88811690611b5416565b81612ddb57fe5b04608084018190528351602085015160408601516060870151612e03948b949392919061474f565b606083015160808401516040805163161139bd60e31b8152600481018a905292850360248401529083036044830152516001600160a01b037f000000000000000000000000f5fab5dbd2f3bf675de4cb76517d4767013cfb55169163b089cde891606480830192600092919082900301818387803b158015612e8457600080fd5b505af1158015612e98573d6000803e3d6000fd5b5050505050505b805160208201516040808401516060850151608086015183516327396b6d60e01b81526001600160a01b0396871660048201529486166024860152918516604485015260648401526084830152517f0000000000000000000000009712bb50dc6efb8a3d7d12cea500a50967d2d471909216916327396b6d9160a48082019260009290919082900301818387803b158015612f3957600080fd5b505af1158015612f4d573d6000803e3d6000fd5b505050507f000000000000000000000000f8a2fb650e25a26ce839d64be8a0abbcb0b87b326001600160a01b0316631d092adf866040518263ffffffff1660e01b815260040180826001600160a01b03168152602001915050600060405180830381600087803b158015612fc057600080fd5b505af1158015612fd4573d6000803e3d6000fd5b5092979650505050505050565b6000612feb615473565b612ff3615473565b612ffd8888613258565b9150915061301d8260000151836020015183600001518460200151613799565b61302a898989898961494f565b60408051630aa558ef60e41b81526001600160a01b038b811660048301528a811660248301528981166044830152606482018990526084820188905291517f0000000000000000000000009712bb50dc6efb8a3d7d12cea500a50967d2d4719092169163aa558ef09160a48082019260009290919082900301818387803b1580156130b457600080fd5b505af11580156130c8573d6000803e3d6000fd5b505050507f0000000000000000000000009712bb50dc6efb8a3d7d12cea500a50967d2d4716001600160a01b031663fd4bc1e68a8a6040518363ffffffff1660e01b815260040180836001600160a01b03168152602001826001600160a01b0316815260200192505050602060405180830381600087803b15801561314c57600080fd5b505af1158015613160573d6000803e3d6000fd5b505050506040513d602081101561317657600080fd5b50508151602080840151604080516361d5f08760e01b81526001600160a01b038e811660048301528d811660248301528c81166044830152606482018c9052608482018b905260a482019590955260c481019290925260e48201889052517f000000000000000000000000f5fab5dbd2f3bf675de4cb76517d4767013cfb55909316926361d5f08792610104808401939192918290030181600087803b15801561321f57600080fd5b505af1158015613233573d6000803e3d6000fd5b505050506040513d602081101561324957600080fd5b50519998505050505050505050565b613260615473565b613268615473565b6000613273856129d5565b905060006132818286612a42565b9050600080613291848489612b45565b91509150600080856001600160a01b0316631f0181bc8a6040518263ffffffff1660e01b815260040180826001600160a01b03168152602001915050604080518083038186803b1580156132e457600080fd5b505afa1580156132f8573d6000803e3d6000fd5b505050506040513d604081101561330e57600080fd5b5080516020918201516040805180820182529788528784019690965285518087019096529085529084015250919890975095505050505050565b82518151602084015160009283926133739261336d916133689190611b54565b614b3c565b90611b54565b905060006133a2866020015161336d61339d87602001518960000151611b5490919063ffffffff16565b614b5c565b9050818702878382816133b157fe5b0414156133cc578181816133c157fe5b049350505050611cc5565b6000808985116133dd5789856133e0565b848a5b915091506000806133fd848785600019816133f757fe5b04614bb2565b9150915060006134178488878161341057fe5b0490611b54565b905081156134435761343481838686028161342e57fe5b04612c45565b98505050505050505050611cc5565b9c9b505050505050505050505050565b61345b615473565b6020830151825160009161346f9190611b54565b8451602085015191925060009161348591611b54565b905081810260008284838161349657fe5b04146134b4576134a583614b5c565b6134ae85614b5c565b026134bd565b6134bd82614b5c565b905060006134cb8585611981565b9050600281066134fe576002810490506040518060400160405280838303815260200182815250955050505050506119de565b604080518082019091526002909202810382526020820152935050505092915050565b613529615473565b600061353583856127d3565b905060007f000000000000000000000000f7d28faa1fe9ea53279fe6e3cde75175859bdf466001600160a01b0316632c560f896040518163ffffffff1660e01b815260040160206040518083038186803b15801561359257600080fd5b505afa1580156135a6573d6000803e3d6000fd5b505050506040513d60208110156135bc57600080fd5b50516040805163ce3f3adb60e01b815290519192506000916001600160a01b037f000000000000000000000000f7d28faa1fe9ea53279fe6e3cde75175859bdf46169163ce3f3adb916004808301926020929190829003018186803b15801561362457600080fd5b505afa158015613638573d6000803e3d6000fd5b505050506040513d602081101561364e57600080fd5b505190508183101561367b57604051806040016040528060008152602001600181525093505050506119de565b8083106136a357604051806040016040528060018152602001600181525093505050506119de565b604080518082019091529283526020830152509392505050565b805160009081906136ce9087611b54565b602084015190915060006136eb6136e58484612c45565b88612c45565b90506000806137088860000151896020015185600019816133f757fe5b909250905061173761372761371d8387611b54565b610cf88589611b54565b61183983610cf861373882886127d3565b8e90611b54565b604080516001600160a01b0380861660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b03166323b872dd60e01b179052610636908590614bfa565b60007f000000000000000000000000f7d28faa1fe9ea53279fe6e3cde75175859bdf466001600160a01b03166324a088686040518163ffffffff1660e01b815260040160206040518083038186803b1580156137f457600080fd5b505afa158015613808573d6000803e3d6000fd5b505050506040513d602081101561381e57600080fd5b5051620f42400363ffffffff908116915060009061384a90839061336d90829082908b908990611b5416565b90506000613861620f424061336d85818a8a611b54565b90506000613878620f424061336d81818c8a611b54565b905081831115801561388a5750808211155b6138ce576040805162461bcd60e51b815260206004820152601060248201526f4552525f494e56414c49445f5241544560801b604482015290519081900360640190fd5b5050505050505050565b7f000000000000000000000000c4c5634de585d43daec8fa2a6fb6286cd9b871316001600160a01b031663deacd84e83836040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050600060405180830381600087803b15801561394f57600080fd5b505af1158015613963573d6000803e3d6000fd5b505050507f000000000000000000000000a489c2b5b36835a327851ab917a80562b5afc2446001600160a01b03166340c10f1984836040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050600060405180830381600087803b1580156139de57600080fd5b505af11580156139f2573d6000803e3d6000fd5b50505050505050565b6000613a8d7f000000000000000000000000f7d28faa1fe9ea53279fe6e3cde75175859bdf466001600160a01b031663045544436040518163ffffffff1660e01b815260040160206040518083038186803b158015613a5957600080fd5b505afa158015613a6d573d6000803e3d6000fd5b505050506040513d6020811015613a8357600080fd5b505161183961197d565b90507f000000000000000000000000f5fab5dbd2f3bf675de4cb76517d4767013cfb556001600160a01b031663dbae3a5d8484846040518463ffffffff1660e01b815260040180846001600160a01b031681526020018381526020018281526020019350505050602060405180830381600087803b158015613b0e57600080fd5b505af1158015613b22573d6000803e3d6000fd5b505050506040513d602081101561103457600080fd5b6000613b43856129d5565b604080516002808252606080830184529394509091602083019080368337505060408051600280825260608083018452949550909250906020830190803683370190505090508482600081518110613b9757fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508382600181518110613bc557fe5b60200260200101906001600160a01b031690816001600160a01b031681525050600181600081518110613bf457fe5b602002602001018181525050600181600181518110613c0f57fe5b602002602001018181525050826001600160a01b031663b127c0a58784846040518463ffffffff1660e01b8152600401808481526020018060200180602001838103835285818151815260200191508051906020019060200280838360005b83811015613c86578181015183820152602001613c6e565b50505050905001838103825284818151815260200191508051906020019060200280838360005b83811015613cc5578181015183820152602001613cad565b5050505090500195505050505050600060405180830381600087803b158015613ced57600080fd5b505af1158015613d01573d6000803e3d6000fd5b5050505050505050505050565b6000613d19836141ba565b15613d2f57506001600160a01b038116316119de565b613d3883614cab565b6001600160a01b03166370a08231836040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015613d8457600080fd5b505afa158015613d98573d6000803e3d6000fd5b505050506040513d6020811015613dae57600080fd5b50519392505050565b80613dc1576127ce565b613dca836141ba565b15613e0b576040516001600160a01b0383169082156108fc029083906000818181858888f19350505050158015613e05573d6000803e3d6000fd5b506127ce565b6127ce8282613e1986614cab565b6001600160a01b031691905b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b1790526127ce908490614bfa565b7f000000000000000000000000c4c5634de585d43daec8fa2a6fb6286cd9b871316001600160a01b031663802fa3ba83836040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050600060405180830381600087803b158015613eee57600080fd5b505af1158015613f02573d6000803e3d6000fd5b505050507f000000000000000000000000a489c2b5b36835a327851ab917a80562b5afc2446001600160a01b03166342966c68826040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b158015613f6c57600080fd5b505af1158015613f80573d6000803e3d6000fd5b505050505050565b803414611416576040805162461bcd60e51b815260206004820152601760248201527f4552525f4554485f414d4f554e545f4d49534d41544348000000000000000000604482015290519081900360640190fd5b6000827f0000000000000000000000001f573d6fb3f13d689ff844b4ce37794d79a7ff1c614008615473565b6140128383611e15565b905060006140358260000151610cf8846020015189611b5490919063ffffffff16565b90507f000000000000000000000000c4c5634de585d43daec8fa2a6fb6286cd9b871316001600160a01b03166319c6a5e485836040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050600060405180830381600087803b1580156140ae57600080fd5b505af11580156140c2573d6000803e3d6000fd5b5050505060006140dd898686858b6140d861197d565b612fe1565b90506141146001600160a01b037f0000000000000000000000001f573d6fb3f13d689ff844b4ce37794d79a7ff1c1633308a61373f565b61411e8888613e77565b7f0000000000000000000000000887ae1251e180d7d453aedebee26e1639f201136001600160a01b03166340c10f198a896040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050600060405180830381600087803b15801561419557600080fd5b505af11580156141a9573d6000803e3d6000fd5b50929b9a5050505050505050505050565b6001600160a01b03811673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee14919050565b6000837f0000000000000000000000001f573d6fb3f13d689ff844b4ce37794d79a7ff1c8261420d836129d5565b905060008061421d838986612b45565b915091507f000000000000000000000000f7d28faa1fe9ea53279fe6e3cde75175859bdf466001600160a01b03166312588d0e6040518163ffffffff1660e01b815260040160206040518083038186803b15801561427a57600080fd5b505afa15801561428e573d6000803e3d6000fd5b505050506040513d60208110156142a457600080fd5b50518110156142fa576040805162461bcd60e51b815260206004820152601860248201527f4552525f4e4f545f454e4f5547485f4c49515549444954590000000000000000604482015290519081900360640190fd5b600061430a83610cf88a85611b54565b905060007f000000000000000000000000f7d28faa1fe9ea53279fe6e3cde75175859bdf466001600160a01b031663943fd08a8c6040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561437b57600080fd5b505afa15801561438f573d6000803e3d6000fd5b505050506040513d60208110156143a557600080fd5b5051905080614436577f000000000000000000000000f7d28faa1fe9ea53279fe6e3cde75175859bdf466001600160a01b031663b97b55ce6040518163ffffffff1660e01b815260040160206040518083038186803b15801561440757600080fd5b505afa15801561441b573d6000803e3d6000fd5b505050506040513d602081101561443157600080fd5b505190505b60006144a9837f000000000000000000000000c4c5634de585d43daec8fa2a6fb6286cd9b871316001600160a01b031663350ed8e78f6040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015610d7b57600080fd5b9050818111156144f9576040805162461bcd60e51b815260206004820152601660248201527511549497d3505617d05353d5539517d4915050d2115160521b604482015290519081900360640190fd5b614504308d856138d8565b6145186001600160a01b0388168785614cae565b61452a8b6001600160a01b03166141ba565b614557576145436001600160a01b038c1633308d614cdf565b6145576001600160a01b038c16878c614cae565b614565868c898d8734614d1a565b6000886001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b1580156145b457600080fd5b505afa1580156145c8573d6000803e3d6000fd5b505050506040513d60208110156145de57600080fd5b505190506146166001600160a01b038a167f000000000000000000000000d1d846312b819743974786050848d9b3d06b9b5583613e25565b6001600160a01b037f000000000000000000000000c4c5634de585d43daec8fa2a6fb6286cd9b871311663332100fa8a6002840484036040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050600060405180830381600087803b15801561469257600080fd5b505af11580156146a6573d6000803e3d6000fd5b505050506146c48e8a8e600285816146ba57fe5b048f6140d861197d565b9e9d5050505050505050505050505050565b6146de6153de565b6146e66153de565b6146ef84611a4a565b9050826001600160a01b031681600001516001600160a01b0316146119db576040805162461bcd60e51b815260206004820152601160248201527011549497d050d0d154d4d7d11153925151607a1b604482015290519081900360640190fd5b60607f000000000000000000000000f7d28faa1fe9ea53279fe6e3cde75175859bdf466001600160a01b0316639e2ce9d26040518163ffffffff1660e01b815260040160006040518083038186803b1580156147aa57600080fd5b505afa1580156147be573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405260208110156147e757600080fd5b8101908080516040519392919084600160201b82111561480657600080fd5b90830190602082018581111561481b57600080fd5b82518660208202830111600160201b8211171561483757600080fd5b82525081516020918201928201910280838360005b8381101561486457818101518382015260200161484c565b50505050905001604052505050905060008151905060005b818110156149445782818151811061489057fe5b60200260200101516001600160a01b031663b8128fe68a8a8a8a8a8a6040518763ffffffff1660e01b815260040180878152602001866001600160a01b03168152602001856001600160a01b03168152602001846001600160a01b031681526020018381526020018281526020019650505050505050600060405180830381600087803b15801561492057600080fd5b505af1158015614934573d6000803e3d6000fd5b50506001909201915061487c9050565b505050505050505050565b60607f000000000000000000000000f7d28faa1fe9ea53279fe6e3cde75175859bdf466001600160a01b0316639e2ce9d26040518163ffffffff1660e01b815260040160006040518083038186803b1580156149aa57600080fd5b505afa1580156149be573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405260208110156149e757600080fd5b8101908080516040519392919084600160201b821115614a0657600080fd5b908301906020820185811115614a1b57600080fd5b82518660208202830111600160201b82111715614a3757600080fd5b82525081516020918201928201910280838360005b83811015614a64578181015183820152602001614a4c565b50505050905001604052505050905060008151905060005b818110156138ce57828181518110614a9057fe5b60200260200101516001600160a01b031663139c22ea89898989896040518663ffffffff1660e01b815260040180866001600160a01b03168152602001856001600160a01b03168152602001846001600160a01b0316815260200183815260200182815260200195505050505050600060405180830381600087803b158015614b1857600080fd5b505af1158015614b2c573d6000803e3d6000fd5b505060019092019150614a7c9050565b600080614b4883614b5c565b905082818202146119de5780600101611976565b60008060028304600101905060006002828581614b7557fe5b04830181614b7f57fe5b0490505b80821115614bab578091506002828581614b9957fe5b04830181614ba357fe5b049050614b83565b5092915050565b600080848484821180614bc457508481115b15614bda57614bd4828287614ed3565b90925090505b808214614beb579092509050612c3d565b50600196879650945050505050565b6060614c4f826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316614f0c9092919063ffffffff16565b8051909150156127ce57808060200190516020811015614c6e57600080fd5b50516127ce5760405162461bcd60e51b815260040180806020018281038252602a8152602001806154d5602a913960400191505060405180910390fd5b90565b614cb7836141ba565b15614cc1576127ce565b6127ce8282614ccf86614cab565b6001600160a01b03169190614f1b565b801580614cf05750614cf0846141ba565b15614cfa57610636565b610636838383614d0988614cab565b6001600160a01b031692919061373f565b6040805160028082526060808301845292602083019080368337505060408051600280825260608083018452949550909250906020830190803683370190505090508682600081518110614d6a57fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508582600181518110614d9857fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508481600081518110614dc657fe5b6020026020010181815250508381600181518110614de057fe5b602002602001018181525050876001600160a01b0316637d8916bd84848460016040518563ffffffff1660e01b8152600401808060200180602001848152602001838103835286818151815260200191508051906020019060200280838360005b83811015614e59578181015183820152602001614e41565b50505050905001838103825285818151815260200191508051906020019060200280838360005b83811015614e98578181015183820152602001614e80565b50505050905001955050505050506000604051808303818588803b158015614ebf57600080fd5b505af11580156127c3573d6000803e3d6000fd5b600080838511614ef157614ee8858585614fe0565b91509150612c3d565b600080614eff868887614fe0565b9890975095505050505050565b6060611cc58484600085615099565b80614f25576127ce565b60408051636eb1769f60e11b81523060048201526001600160a01b038481166024830152915160009286169163dd62ed3e916044808301926020929190829003018186803b158015614f7657600080fd5b505afa158015614f8a573d6000803e3d6000fd5b505050506040513d6020811015614fa057600080fd5b50519050818110614fb157506127ce565b8015614fcc57614fcc6001600160a01b0385168460006151f5565b6106366001600160a01b03851684846151f5565b60008060008360001981614ff057fe5b0490508086111561502957600081600101878161500957fe5b04600101905080878161501857fe5b04965080868161502457fe5b049550505b8486146150895785840285870187811061505a5760006150498383615308565b955050508385039250612c3d915050565b60028888030487038210156150785760008694509450505050612c3d565b600180870394509450505050612c3d565b5050600290910493849350915050565b6060824710156150da5760405162461bcd60e51b815260040180806020018281038252602681526020018061548e6026913960400191505060405180910390fd5b6150e385615334565b615134576040805162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015290519081900360640190fd5b60006060866001600160a01b031685876040518082805190602001908083835b602083106151735780518252601f199092019160209182019101615154565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d80600081146151d5576040519150601f19603f3d011682016040523d82523d6000602084013e6151da565b606091505b50915091506151ea82828661533a565b979650505050505050565b80158061527b575060408051636eb1769f60e11b81523060048201526001600160a01b03848116602483015291519185169163dd62ed3e91604480820192602092909190829003018186803b15801561524d57600080fd5b505afa158015615261573d6000803e3d6000fd5b505050506040513d602081101561527757600080fd5b5051155b6152b65760405162461bcd60e51b81526004018080602001828103825260368152602001806154ff6036913960400191505060405180910390fd5b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b1790526127ce908490614bfa565b600060028204820382848161531957fe5b068161532157fe5b0482848161532b57fe5b04019392505050565b3b151590565b60608315615349575081611976565b8251156153595782518084602001fd5b8160405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b838110156153a357818101518382015260200161538b565b50505050905090810190601f1680156153d05780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b60405180610100016040528060006001600160a01b0316815260200160006001600160a01b0316815260200160006001600160a01b0316815260200160008152602001600081526020016000815260200160008152602001600081525090565b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a081019190915290565b60405180604001604052806000815260200160008152509056fe416464726573733a20696e73756666696369656e742062616c616e636520666f722063616c6c536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f775361666545524332303a204552433230206f7065726174696f6e20646964206e6f7420737563636565645361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f20746f206e6f6e2d7a65726f20616c6c6f77616e6365a2646970667358221220cf0c88f5d19edd66e979be80836686045ed01d2ea29d9d3fa80f03a250f3dba464736f6c634300060c0033
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 | 34 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|---|---|---|---|---|
POL | 100.00% | $0.183756 | 0.0135 | $0.002476 |
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.