Contract Address Details
contract
token

0xA6C090c5572f54d529B0839b8fd2D50a4afB1E6B

Token
xInfra Cluster (DXIN)
Creator
0x5bac8c–d3705d at 0xf6b064–ffa2f7
Balance
0.000000252740820394 xDAI
Tokens
Fetching tokens...
Transactions
1,142 Transactions
Transfers
10,074 Transfers
Gas Used
1,237,125,343
Last Balance Update
22352537
Contract name:
ClusterTokenV2




Optimization enabled
true
Compiler version
v0.8.6+commit.11564f7e




Optimization runs
200
EVM Version
default




Verified at
2021-12-02T08:47:25.848468Z

Constructor Arguments

000000000000000000000000db1255a43e52e05b9d0371db574f23ca1f2082310000000000000000000000005bac8c443ebd0525364dc813005ab72956d3705d00000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003200000000000000000000000000000000000000000000000000000000000000360c062998eef2f766585932e5c291ea2eb10543cc08fbcc27836fde2e86331c0650000000000000000000000000000000000000000000000000000000000000008000000000000000000000000b0c5f3100a4d9d9532a4cfd68c55f1ae8da987eb000000000000000000000000b90d6bec20993be5d72a5ab353343f7a0281f15800000000000000000000000018e9262e68cc6c6004db93105cc7c001bb103e4900000000000000000000000083ff60e2f93f8edd0637ef669c69d5fb4f64ca8e00000000000000000000000097edc0e345fbbbd8460847fcfa3bc2a13bf8641f000000000000000000000000256eb8a51f382650b2a1e946b8811953640ee47d00000000000000000000000021a42669643f45bc0e086b8fc2ed70c23d67509d000000000000000000000000b7d311e2eb55f2f68a9440da38e7989210b9a05e000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000002ae09000000000000000000000000000000000000000000000000000000000000055f000000000000000000000000000000000000000000000000000000000348aed800000000000000000000000000000000000000000000000000000000002f301400000000000000000000000000000000000000000000000000000000000d418e0000000000000000000000000000000000000000000000000000000000d50822000000000000000000000000000000000000000000000000000000000021c294000000000000000000000000000000000000000000000000000000000001a8ce0000000000000000000000000000000000000000000000000000000000000000e78496e66726120436c757374657200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044458494e00000000000000000000000000000000000000000000000000000000

Arg [0] (address) : 0xdb1255a43e52e05b9d0371db574f23ca1f208231
Arg [1] (address) : 0x5bac8c443ebd0525364dc813005ab72956d3705d
Arg [2] (address[]) : [0xb0c5f3100a4d9d9532a4cfd68c55f1ae8da987eb, 0xb90d6bec20993be5d72a5ab353343f7a0281f158, 0x18e9262e68cc6c6004db93105cc7c001bb103e49, 0x83ff60e2f93f8edd0637ef669c69d5fb4f64ca8e, 0x97edc0e345fbbbd8460847fcfa3bc2a13bf8641f, 0x256eb8a51f382650b2a1e946b8811953640ee47d, 0x21a42669643f45bc0e086b8fc2ed70c23d67509d, 0xb7d311e2eb55f2f68a9440da38e7989210b9a05e]
Arg [3] (uint256[]) : [2810000, 22000, 881520000, 49480000, 13900000, 223380000, 35400000, 1740000]
Arg [4] (string) : xInfra Cluster
Arg [5] (string) : DXIN
Arg [6] (uint256) : 87018277761028076566913167557737308773185951363339604627693081277300416299109

              

Contract source code

/**
 *Submitted for verification at BscScan.com on 2021-11-09
*/

// SPDX-License-Identifier: MIT

pragma solidity 0.8.6;



// Part: Address

/**
 * @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;
        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");

        (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");

        (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");

        (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");

        (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

                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

// Part: Context

/*
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}

// Part: IAccessControl

/**
 * @dev External interface of AccessControl declared to support ERC165 detection.
 */
interface IAccessControl {
    function hasRole(bytes32 role, address account) external view returns (bool);

    function getRoleAdmin(bytes32 role) external view returns (bytes32);

    function grantRole(bytes32 role, address account) external;

    function revokeRole(bytes32 role, address account) external;

    function renounceRole(bytes32 role, address account) external;
}

// Part: IClusterToken

interface IClusterToken {
    function assemble(uint256 clusterAmount, bool coverDhvWithEth) external payable returns (uint256);

    function disassemble(uint256 indexAmount, bool coverDhvWithEth) external;

    function withdrawToAccumulation(uint256 _clusterAmount) external;

    function refundFromAccumulation(uint256 _clusterAmount) external;

    function returnDebtFromAccumulation(uint256[] calldata _amounts, uint256 _clusterAmount) external;

    function optimizeProportion(uint256[] memory updatedShares) external returns (uint256[] memory debt, uint256[] memory toWithdraw);

    function getUnderlyingInCluster() external view returns (uint256[] calldata);

    function getUnderlyings() external view returns (address[] calldata);

    function getUnderlyingBalance(address _underlying) external view returns (uint256);

    function getUnderlyingsAmountsFromClusterAmount(uint256 _clusterAmount) external view returns (uint256[] calldata);

    function clusterTokenLock() external view returns (uint256);

    function clusterLock(address _token) external view returns (uint256);

    function controllerChange(address) external;

    function assembleByAdapter(uint256 _clusterAmount) external;

    function disassembleByAdapter(uint256 _clusterAmount) external;
}

// Part: IClusterTokenV2

interface IClusterTokenV2 {
    function addUnderlying(address _underlying, uint256 _share) external;

    function replaceUnderlying(
        address _prevUnderlying,
        address _newUnderlying,
        uint256 _newShare
    ) external;
}

// Part: IClusterTokenEvents

interface IClusterTokenEvents {
    /// @notice Event emitted on each successful deposit for cluster in ETH.
    /// @param clusterAddress Address of cluster deposited upon.
    /// @param buyer Address of user depositing (user who bought token).
    /// @param ethDeposited Amount of ETH deposited in exchange of cluster.
    /// @param clusterAmountBought Amount of cluster bought for given amount of ETH.
    event ClusterAssembledInETH(address indexed clusterAddress, address indexed buyer, uint256 ethDeposited, uint256 clusterAmountBought);
    /// @notice Event emitted on each successful cluster redeeming for ETH.
    /// @param clusterAddress Address of cluster redeemed.
    /// @param redeemer Address of user performing the redeem.
    /// @param clusterAmountRedeemed Amount of cluster redeemed by user.
    event ClusterDisassembledInETH(address indexed clusterAddress, address indexed redeemer, uint256 clusterAmountRedeemed);

    /// @notice Event emitted on each successful deposit for cluster in ETH.
    /// @param clusterAddress Address of cluster deposited upon.
    /// @param buyer Address of user depositing (user who bought token).
    /// @param clusterAmountBought Amount of cluster bought for given amount of ETH.
    event ClusterAssembled(address indexed clusterAddress, address indexed buyer, uint256 clusterAmountBought);
    /// @notice Event emitted on each successful cluster redeeming for ETH.
    /// @param clusterAddress Address of cluster redeemed.
    /// @param redeemer Address of user performing the redeem.
    /// @param clusterAmountRedeemed Amount of cluster redeemed by user.
    event ClusterDisassembled(address indexed clusterAddress, address indexed redeemer, uint256 clusterAmountRedeemed);

    /// @notice Event emitted when tokens are withdrawn to accumulation.
    /// @param clusterAddress Address of cluster, withdrawn from.
    /// @param farmer Address of authorized farmer.
    /// @param clusterAmount Amount of cluster tokens to lock for farming.
    /// @param underlyingsAmounts Amount of underlying tokens, sent to farm.
    /// @param tokens Addresses of underlying tokens, sent to farm.
    event ClusterWithdrawnToAcc(
        address indexed clusterAddress,
        address indexed farmer,
        uint256 clusterAmount,
        uint256[] underlyingsAmounts,
        address[] tokens
    );
    /// @notice Event emitted when tokens are refunded from accumulation.
    /// @param clusterAddress Address of cluster, refunded for.
    /// @param farmer Address of authorized farmer.
    /// @param clusterAmount Amount of cluster tokens to unlock from farming.
    /// @param underlyingsAmounts Amount of underlying tokens, unlocked from farm.
    /// @param tokens Addresses of underlying tokens, unlocked from farm.
    event ClusterRefundedFromAcc(
        address indexed clusterAddress,
        address indexed farmer,
        uint256 clusterAmount,
        uint256[] underlyingsAmounts,
        address[] tokens
    );
    /// @notice Event emitted on optimizing underlyings proportions.
    /// @param clusterAddress Address of cluster, proportions are optimized for.
    /// @param delegate Address of authorized delegate.
    /// @param updatedShares New shares.
    event ProportionsOptimized(address indexed clusterAddress, address indexed delegate, uint256[] updatedShares);
}

// Part: IController

interface IController {
    function getClusterAmountFromEth(uint256 _ethAmount, address _cluster) external view returns (uint256);

    function addClusterToRegister(address indexAddr) external;

    function getDHVPriceInETH(address _cluster) external view returns (uint256);

    function getUnderlyingsInfo(address _cluster, uint256 _ethAmount)
        external
        view
        returns (
            uint256[] memory,
            uint256[] memory,
            uint256,
            uint256
        );

    function getUnderlyingsAmountsFromClusterAmount(uint256 _clusterAmount, address _clusterAddress) external view returns (uint256[] memory);

    function getEthAmountFromUnderlyingsAmounts(uint256[] memory _underlyingsAmounts, address _cluster) external view returns (uint256);

    function adapters(address _cluster) external view returns (address);

    function dhvTokenInstance() external view returns (address);

    function getDepositComission(address _cluster, uint256 _ethValue) external view returns (uint256);

    function getRedeemComission(address _cluster, uint256 _ethValue) external view returns (uint256);

    function getClusterPrice(address _cluster) external view returns (uint256);
}

// Part: IDexAdapter

interface IDexAdapter {
    function router() external view returns (address);

    function swapETHToUnderlying(address underlying, uint256 underlyingAmount) external payable;

    function swapUnderlyingsToETH(uint256[] memory underlyingAmounts, address[] memory underlyings) external;

    function swapTokenToToken(
        uint256 _amountToSwap,
        address _tokenToSwap,
        address _tokenToReceive
    ) external returns (uint256);

    function getUnderlyingAmount(
        uint256 _amount,
        address _tokenToSwap,
        address _tokenToReceive
    ) external view returns (uint256);

    function getPath(address _tokenToSwap, address _tokenToReceive) external view returns (address[] memory);

    function getTokensPrices(address[] memory _tokens) external view returns (uint256[] memory);

    function getEthPrice() external view returns (uint256);

    function getDHVPriceInETH(address _dhvToken) external view returns (uint256);

    function WETH() external view returns (address);

    function getEthAmountWithSlippage(uint256 _amount, address _tokenToSwap) external view returns (uint256);
}

// Part: IERC165

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

// Part: IERC20

/**
 * @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);
}

// Part: Strings

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        // Inspired by OraclizeAPI's implementation - MIT licence
        // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol

        if (value == 0) {
            return "0";
        }
        uint256 temp = value;
        uint256 digits;
        while (temp != 0) {
            digits++;
            temp /= 10;
        }
        bytes memory buffer = new bytes(digits);
        while (value != 0) {
            digits -= 1;
            buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
            value /= 10;
        }
        return string(buffer);
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        if (value == 0) {
            return "0x00";
        }
        uint256 temp = value;
        uint256 length = 0;
        while (temp != 0) {
            length++;
            temp >>= 8;
        }
        return toHexString(value, length);
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = _HEX_SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }
}

// Part: ERC165

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 *
 * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}

// Part: IERC20Metadata

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 *
 * _Available since v4.1._
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}

// Part: Pausable

/**
 * @dev Contract module which allows children to implement an emergency stop
 * mechanism that can be triggered by an authorized account.
 *
 * This module is used through inheritance. It will make available the
 * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
 * the functions of your contract. Note that they will not be pausable by
 * simply including this module, only once the modifiers are put in place.
 */
abstract contract Pausable is Context {
    /**
     * @dev Emitted when the pause is triggered by `account`.
     */
    event Paused(address account);

    /**
     * @dev Emitted when the pause is lifted by `account`.
     */
    event Unpaused(address account);

    bool private _paused;

    /**
     * @dev Initializes the contract in unpaused state.
     */
    constructor() {
        _paused = false;
    }

    /**
     * @dev Returns true if the contract is paused, and false otherwise.
     */
    function paused() public view virtual returns (bool) {
        return _paused;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is not paused.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    modifier whenNotPaused() {
        require(!paused(), "Pausable: paused");
        _;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is paused.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    modifier whenPaused() {
        require(paused(), "Pausable: not paused");
        _;
    }

    /**
     * @dev Triggers stopped state.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    function _pause() internal virtual whenNotPaused {
        _paused = true;
        emit Paused(_msgSender());
    }

    /**
     * @dev Returns to normal state.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    function _unpause() internal virtual whenPaused {
        _paused = false;
        emit Unpaused(_msgSender());
    }
}

// Part: SafeERC20

/**
 * @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 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'
        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) + value;
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    function safeDecreaseAllowance(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            uint256 newAllowance = oldAllowance - value;
            _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
            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
    }
}

// Part: AccessControl

/**
 * @dev Contract module that allows children to implement role-based access
 * control mechanisms. This is a lightweight version that doesn't allow enumerating role
 * members except through off-chain means by accessing the contract event logs. Some
 * applications may benefit from on-chain enumerability, for those cases see
 * {AccessControlEnumerable}.
 *
 * Roles are referred to by their `bytes32` identifier. These should be exposed
 * in the external API and be unique. The best way to achieve this is by
 * using `public constant` hash digests:
 *
 * ```
 * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
 * ```
 *
 * Roles can be used to represent a set of permissions. To restrict access to a
 * function call, use {hasRole}:
 *
 * ```
 * function foo() public {
 *     require(hasRole(MY_ROLE, msg.sender));
 *     ...
 * }
 * ```
 *
 * Roles can be granted and revoked dynamically via the {grantRole} and
 * {revokeRole} functions. Each role has an associated admin role, and only
 * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
 *
 * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
 * that only accounts with this role will be able to grant or revoke other
 * roles. More complex role relationships can be created by using
 * {_setRoleAdmin}.
 *
 * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
 * grant and revoke this role. Extra precautions should be taken to secure
 * accounts that have been granted it.
 */
abstract contract AccessControl is Context, IAccessControl, ERC165 {
    struct RoleData {
        mapping(address => bool) members;
        bytes32 adminRole;
    }

    mapping(bytes32 => RoleData) private _roles;

    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;

    /**
     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
     *
     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
     * {RoleAdminChanged} not being emitted signaling this.
     *
     * _Available since v3.1._
     */
    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);

    /**
     * @dev Emitted when `account` is granted `role`.
     *
     * `sender` is the account that originated the contract call, an admin role
     * bearer except when using {_setupRole}.
     */
    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Emitted when `account` is revoked `role`.
     *
     * `sender` is the account that originated the contract call:
     *   - if using `revokeRole`, it is the admin role bearer
     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
     */
    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Modifier that checks that an account has a specific role. Reverts
     * with a standardized message including the required role.
     *
     * The format of the revert reason is given by the following regular expression:
     *
     *  /^AccessControl: account (0x[0-9a-f]{20}) is missing role (0x[0-9a-f]{32})$/
     *
     * _Available since v4.1._
     */
    modifier onlyRole(bytes32 role) {
        _checkRole(role, _msgSender());
        _;
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) public view override returns (bool) {
        return _roles[role].members[account];
    }

    /**
     * @dev Revert with a standard message if `account` is missing `role`.
     *
     * The format of the revert reason is given by the following regular expression:
     *
     *  /^AccessControl: account (0x[0-9a-f]{20}) is missing role (0x[0-9a-f]{32})$/
     */
    function _checkRole(bytes32 role, address account) internal view {
        if (!hasRole(role, account)) {
            revert(
                string(
                    abi.encodePacked(
                        "AccessControl: account ",
                        Strings.toHexString(uint160(account), 20),
                        " is missing role ",
                        Strings.toHexString(uint256(role), 32)
                    )
                )
            );
        }
    }

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) public view override returns (bytes32) {
        return _roles[role].adminRole;
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
        _grantRole(role, account);
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
        _revokeRole(role, account);
    }

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been granted `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     */
    function renounceRole(bytes32 role, address account) public virtual override {
        require(account == _msgSender(), "AccessControl: can only renounce roles for self");

        _revokeRole(role, account);
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event. Note that unlike {grantRole}, this function doesn't perform any
     * checks on the calling account.
     *
     * [WARNING]
     * ====
     * This function should only be called from the constructor when setting
     * up the initial roles for the system.
     *
     * Using this function in any other way is effectively circumventing the admin
     * system imposed by {AccessControl}.
     * ====
     */
    function _setupRole(bytes32 role, address account) internal virtual {
        _grantRole(role, account);
    }

    /**
     * @dev Sets `adminRole` as ``role``'s admin role.
     *
     * Emits a {RoleAdminChanged} event.
     */
    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
        emit RoleAdminChanged(role, getRoleAdmin(role), adminRole);
        _roles[role].adminRole = adminRole;
    }

    function _grantRole(bytes32 role, address account) private {
        if (!hasRole(role, account)) {
            _roles[role].members[account] = true;
            emit RoleGranted(role, account, _msgSender());
        }
    }

    function _revokeRole(bytes32 role, address account) private {
        if (hasRole(role, account)) {
            _roles[role].members[account] = false;
            emit RoleRevoked(role, account, _msgSender());
        }
    }
}

// Part: ERC20

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 * For a generic mechanism see {ERC20PresetMinterPauser}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * We have followed general OpenZeppelin guidelines: functions revert instead
 * of returning `false` on failure. This behavior is nonetheless conventional
 * and does not conflict with the expectations of ERC20 applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 *
 * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
 * functions have been added to mitigate the well-known issues around setting
 * allowances. See {IERC20-approve}.
 */
contract ERC20 is Context, IERC20, IERC20Metadata {
    mapping(address => uint256) private _balances;

    mapping(address => mapping(address => uint256)) private _allowances;

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * The default value of {decimals} is 18. To select a different value for
     * {decimals} you should overload it.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5,05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei. This is the value {ERC20} uses, unless this function is
     * overridden;
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view virtual override returns (uint8) {
        return 18;
    }

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view virtual override returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view virtual override returns (uint256) {
        return _balances[account];
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `recipient` cannot be the zero address.
     * - the caller must have a balance of at least `amount`.
     */
    function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
        _transfer(_msgSender(), recipient, amount);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender) public view virtual override returns (uint256) {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount) public virtual override returns (bool) {
        _approve(_msgSender(), spender, amount);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20}.
     *
     * Requirements:
     *
     * - `sender` and `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     * - the caller must have allowance for ``sender``'s tokens of at least
     * `amount`.
     */
    function transferFrom(
        address sender,
        address recipient,
        uint256 amount
    ) public virtual override returns (bool) {
        _transfer(sender, recipient, amount);

        uint256 currentAllowance = _allowances[sender][_msgSender()];
        require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance");
        unchecked {
            _approve(sender, _msgSender(), currentAllowance - amount);
        }

        return true;
    }

    /**
     * @dev Atomically increases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
        _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue);
        return true;
    }

    /**
     * @dev Atomically decreases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `spender` must have allowance for the caller of at least
     * `subtractedValue`.
     */
    function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
        uint256 currentAllowance = _allowances[_msgSender()][spender];
        require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
        unchecked {
            _approve(_msgSender(), spender, currentAllowance - subtractedValue);
        }

        return true;
    }

    /**
     * @dev Moves `amount` of tokens from `sender` to `recipient`.
     *
     * This internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * Requirements:
     *
     * - `sender` cannot be the zero address.
     * - `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     */
    function _transfer(
        address sender,
        address recipient,
        uint256 amount
    ) internal virtual {
        require(sender != address(0), "ERC20: transfer from the zero address");
        require(recipient != address(0), "ERC20: transfer to the zero address");

        _beforeTokenTransfer(sender, recipient, amount);

        uint256 senderBalance = _balances[sender];
        require(senderBalance >= amount, "ERC20: transfer amount exceeds balance");
        unchecked {
            _balances[sender] = senderBalance - amount;
        }
        _balances[recipient] += amount;

        emit Transfer(sender, recipient, amount);

        _afterTokenTransfer(sender, recipient, amount);
    }

    /** @dev Creates `amount` tokens and assigns them to `account`, increasing
     * the total supply.
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     */
    function _mint(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: mint to the zero address");

        _beforeTokenTransfer(address(0), account, amount);

        _totalSupply += amount;
        _balances[account] += amount;
        emit Transfer(address(0), account, amount);

        _afterTokenTransfer(address(0), account, amount);
    }

    /**
     * @dev Destroys `amount` tokens from `account`, reducing the
     * total supply.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     * - `account` must have at least `amount` tokens.
     */
    function _burn(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: burn from the zero address");

        _beforeTokenTransfer(account, address(0), amount);

        uint256 accountBalance = _balances[account];
        require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
        unchecked {
            _balances[account] = accountBalance - amount;
        }
        _totalSupply -= amount;

        emit Transfer(account, address(0), amount);

        _afterTokenTransfer(account, address(0), amount);
    }

    /**
     * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
     *
     * This internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     */
    function _approve(
        address owner,
        address spender,
        uint256 amount
    ) internal virtual {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    /**
     * @dev Hook that is called before any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * will be transferred to `to`.
     * - when `from` is zero, `amount` tokens will be minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {}

    /**
     * @dev Hook that is called after any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * has been transferred to `to`.
     * - when `from` is zero, `amount` tokens have been minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _afterTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {}
}

// Part: ClusterToken

/// @title Cluster Token Contract
/// @author Blaize.tech team
/// @notice Cluster Token is a ERC-20 token representing a DeHive cluster of DeFi assets
contract ClusterToken is ERC20, Pausable, AccessControl, IClusterToken, IClusterTokenEvents {
    using SafeERC20 for IERC20;
    using Address for address;

    bytes32 public constant FARMER_ROLE = keccak256("FARMER_ROLE");
    bytes32 public constant DELEGATE_ROLE = keccak256("DELEGATE_ROLE");
    bytes32 public constant ADAPTER_ROLE = keccak256("ADAPTER_ROLE");

    // Version of the cluster contract (according to new functionality and audits)
    // Needed to distinguish different deployments of clusters
    uint256 public constant CLUSTER_VERSION = 2;

    /// @notice Coefficient decimal for underlyings proportion
    uint256 public constant ACCURACY_DECIMALS = 10**6;
    /// @notice cluster Tokens decimals
    uint256 public constant CLUSTER_TOKEN_DECIMALS = 10**18;

    uint256 public constant MAX_COOLDOWN = 2 days;

    /// @notice amount of ETH dust allowed to be sent to the user
    uint256 public constant DUST_AMOUNT = 10**12;

    /// @notice Address of Controller smart contract.
    address public clusterControllerAddress;
    /// @notice Address of treasury with token reserves and comissions storage.
    address internal treasuryAddress;
    /// @notice Instance of the DHV token
    IERC20 public dhvTokenInstance;
    /// @notice Hash id, serves as an additional identifier of the cluster.
    uint256 public clusterHashId;

    /// @notice lock period, during which user can't redeem or transfer cluster after assemble
    uint256 public cooldownPeriod;
    /// @notice List of tokens within the cluster, called underlyings.
    /// @dev Order of addresses is important, as it corresponds with the order in underlyingInCluster.
    address[] public underlyings;
    /// @notice List of shares, multiplied by 10 ** 6
    /// @dev Order of addresses is important, as it corresponds with the order of tokens in underlyings.
    uint256[] public underlyingInCluster;

    /// @notice Amount of cluster token activated (sent to farming)
    uint256 public override clusterTokenLock;
    /// @notice Amounts of underlyings activated (sent to farming)
    mapping(address => uint256) public override clusterLock;

    /// @notice timestamp, before which user can't redeem or transfer cluster.
    /// @dev userAddress => uint.
    mapping(address => uint256) public cooldownTimestamps;

    /// @notice amount of cluster which user can't redeem or transfer due to cooldown.
    /// @dev userAddress => cluster amount.
    mapping(address => uint256) public cooldownAmount;

    /// @notice Checks that lock period on redeeming or transfering cluster is over.
    modifier checkCooldownPeriod(address _from, uint256 _amount) {
        if (cooldownTimestamps[_from] > block.timestamp) {
            uint256 allowedAmount = IERC20(address(this)).balanceOf(_from) - cooldownAmount[_from];
            require(allowedAmount >= _amount, "Cooldown in progress");
        }
        _;
    }

    modifier checkBuyer() {
        require(!_msgSender().isContract(), "Not allowed for contracts");
        _;
    }

    modifier notFarmer() {
        require(!hasRole(FARMER_ROLE, _msgSender()), "Farmer is not allowed");
        _;
    }

    /// @notice Performs initial setup.
    /// @param _clusterControllerAddress Address of the Controller SC.
    /// @param _underlyings List of tokens to be recorded in underlyings.
    /// @param _underlyingInCluster List of shares to be recorded in underlyingInCluster.
    /// @param _name Name of the ERC-20 token.
    /// @param _symbol Symbol of the ERC-20 token.
    /// @param _hashId cluster hash identifier.
    constructor(
        address _clusterControllerAddress,
        address _treasury,
        address[] memory _underlyings,
        uint256[] memory _underlyingInCluster,
        string memory _name,
        string memory _symbol,
        uint256 _hashId
    ) ERC20(_name, _symbol) {
        require(_clusterControllerAddress != address(0), "dev: Controller zero address");
        require(_treasury != address(0), "dev: Treasury zero address");
        require(_underlyings.length == _underlyingInCluster.length, "dev: Arrays' lengths must be equal");
        for (uint256 i = 0; i < _underlyings.length; i++) {
            require(_underlyings[i] != address(0), "dev: Underlying zero address");
            require(_underlyingInCluster[i] > 0, "dev: Share equals zero");
        }
        _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());

        underlyings = _underlyings;
        underlyingInCluster = _underlyingInCluster;
        clusterHashId = _hashId;
        clusterControllerAddress = _clusterControllerAddress;
        treasuryAddress = _treasury;

        dhvTokenInstance = IERC20(IController(_clusterControllerAddress).dhvTokenInstance());
    }

    receive() external payable {}

    /**********
     * ADMIN INTERFACE
     **********/

    /// @notice Function that allows contract owner to update cooldown period on cluster redeeming and transfering.
    /// @param _cooldownPeriod New lock period.
    function setCooldownPeriod(uint256 _cooldownPeriod) external onlyRole(DEFAULT_ADMIN_ROLE) {
        require(_cooldownPeriod <= MAX_COOLDOWN, "Incorrect cooldown");
        cooldownPeriod = _cooldownPeriod;
    }

    /// @notice Function that allows contract owner to update treasury address;
    /// @param _newTreasury Address of new treasury.
    function setTreasuryAddress(address _newTreasury) external onlyRole(DEFAULT_ADMIN_ROLE) {
        require(_newTreasury != address(0), "Incorrect address");
        treasuryAddress = _newTreasury;
    }

    /// @notice withdraws all ETH stucked or kept as dust
    function withdrawDust() external onlyRole(DEFAULT_ADMIN_ROLE) {
        Address.sendValue(payable(_msgSender()), address(this).balance);
    }

    /// @notice Function pauses assemble, dissamble, transfers.
    function pause() external onlyRole(DEFAULT_ADMIN_ROLE) {
        _pause();
    }

    /// @notice Function unpauses assemble, dissamble, transfers.
    function unpause() external onlyRole(DEFAULT_ADMIN_ROLE) {
        _unpause();
    }

    /// @notice Controller ownership transfer.
    function controllerChange(address _controller) external override {
        require(_msgSender() == clusterControllerAddress, "Controller only");
        require(_controller != address(0), "dev: Controller zero address");
        clusterControllerAddress = _controller;
    }

    /**********
     * USER'S INTERFACE
     **********/

    /// @notice Function that allows depositing ETH and purchase respective amount of cluster.
    /// @dev Can only be called when token is not paused.
    /// @param coverDhvWithEth Should DHV to cover commission be purchased during the call,
    /// rather then transferred from the caller's address.
    /// @return Amount of cluster purchased.
    function assemble(uint256 clusterAmount, bool coverDhvWithEth) external payable override whenNotPaused checkBuyer returns (uint256) {
        uint256 balanceBefore = address(this).balance;
        _swapEthToUnderlyings(msg.value, clusterAmount);
        uint256 balanceAfter = address(this).balance;

        uint256 ethSpent = balanceBefore - balanceAfter;

        uint256 _ethCommission = IController(clusterControllerAddress).getDepositComission(address(this), ethSpent);
        uint256 ethAmount = msg.value - ethSpent - _coverCommission(_ethCommission, coverDhvWithEth);

        if (ethAmount > DUST_AMOUNT) {
            Address.sendValue(payable(_msgSender()), ethAmount);
        }

        if (cooldownPeriod > 0) {
            if (cooldownTimestamps[_msgSender()] > block.timestamp) {
                cooldownAmount[_msgSender()] += clusterAmount;
            } else {
                cooldownAmount[_msgSender()] = clusterAmount;
            }
            cooldownTimestamps[_msgSender()] = block.timestamp + cooldownPeriod;
        }

        _mint(_msgSender(), clusterAmount);
        emit ClusterAssembledInETH(address(this), _msgSender(), ethSpent, clusterAmount);

        return clusterAmount;
    }

    /// @notice Function that allows disassembling cluster and receiving respective amount of ETH.
    /// @dev Can only be called when token is not paused.
    /// @param _clusterAmount Amount of cluster to be redeemed, e.g. exchanged to ETH equivalent.
    /// @param coverDhvWithEth Should DHV to cover commission be purchased during the call,
    /// rather then transferred from the caller's address.
    function disassemble(uint256 _clusterAmount, bool coverDhvWithEth) external override whenNotPaused notFarmer checkBuyer {
        require(_clusterAmount > 0 && _clusterAmount <= balanceOf(_msgSender()), "Not enough cluster");
        // Cooldown period is checked within _burn() -> _beforeTokenTransfer()
        _burn(_msgSender(), _clusterAmount);

        uint256[] memory underlyingAmounts = getUnderlyingsAmountsFromClusterAmount(_clusterAmount);

        uint256 balanceBefore = address(this).balance;
        _swapUnderlyingsToEth(underlyingAmounts);
        uint256 balanceAfter = address(this).balance;

        uint256 ethEstimated = balanceAfter - balanceBefore;
        uint256 _ethCommission = IController(clusterControllerAddress).getRedeemComission(address(this), ethEstimated);
        uint256 ethAmount = ethEstimated - _coverCommission(_ethCommission, coverDhvWithEth);

        Address.sendValue(payable(_msgSender()), ethAmount);
        emit ClusterDisassembledInETH(address(this), _msgSender(), _clusterAmount);
    }

    /// @dev Standard ERC20 function override in order to check lock period.
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal override checkCooldownPeriod(from, amount) {}

    /**********
     * FARMER AND DELEGATE INTERFACE
     **********/

    /// @notice Mints tokens for the contract with minter role.
    /// @notice requires approved amounts of underlyings
    /// @param _clusterAmount Cluster token amount to be minted.
    function assembleByAdapter(uint256 _clusterAmount) external override onlyRole(ADAPTER_ROLE) {
        uint256[] memory underlyingAmounts = getUnderlyingsAmountsFromClusterAmount(_clusterAmount);

        for (uint256 i = 0; i < underlyingAmounts.length; i++) {
            IERC20(underlyings[i]).safeTransferFrom(_msgSender(), address(this), underlyingAmounts[i]);
        }

        _mint(_msgSender(), _clusterAmount);

        emit ClusterAssembled(address(this), _msgSender(), _clusterAmount);
    }

    /// @notice Burns cluster tokens after the trade.
    /// @notice requires underlyings to be present
    /// @param _clusterAmount Cluster token amount to be minted.
    function disassembleByAdapter(uint256 _clusterAmount) external override notFarmer onlyRole(ADAPTER_ROLE) {
        uint256[] memory underlyingAmounts = getUnderlyingsAmountsFromClusterAmount(_clusterAmount);

        _burn(_msgSender(), _clusterAmount);

        for (uint256 i = 0; i < underlyingAmounts.length; i++) {
            IERC20(underlyings[i]).safeTransfer(_msgSender(), underlyingAmounts[i]);
        }

        emit ClusterDisassembled(address(this), _msgSender(), _clusterAmount);
    }

    /// @notice Withdraw tokens of cluster for staking.
    /// @param _clusterAmount Amounts of the cluster locked for farming.
    function withdrawToAccumulation(uint256 _clusterAmount) external override onlyRole(FARMER_ROLE) {
        // Check that farmer has enough cluster tokens to withdraw underlyings
        require(IERC20(address(this)).balanceOf(_msgSender()) >= clusterTokenLock + _clusterAmount, "Farmer has not enough cluster tokens");
        uint256[] memory _amounts = getUnderlyingsAmountsFromClusterAmount(_clusterAmount);

        clusterTokenLock += _clusterAmount;
        for (uint256 i = 0; i < underlyings.length; i++) {
            clusterLock[underlyings[i]] += _amounts[i];
            IERC20(underlyings[i]).safeTransfer(_msgSender(), _amounts[i]);
        }

        emit ClusterWithdrawnToAcc(address(this), _msgSender(), _clusterAmount, _amounts, underlyings);
    }

    /// @notice Refund tokens of cluster from staking.
    /// @param _clusterAmount Amount of cluster unlocked from farming.
    function refundFromAccumulation(uint256 _clusterAmount) external override onlyRole(FARMER_ROLE) {
        uint256[] memory _amounts = getUnderlyingsAmountsFromClusterAmount(_clusterAmount);

        clusterTokenLock -= _clusterAmount;
        for (uint256 i = 0; i < underlyings.length; i++) {
            clusterLock[underlyings[i]] -= _amounts[i];
            IERC20(underlyings[i]).safeTransferFrom(_msgSender(), address(this), _amounts[i]);
        }

        emit ClusterRefundedFromAcc(address(this), _msgSender(), _clusterAmount, _amounts, underlyings);
    }

    /// @notice Extra method to return debt.
    /// @param _amounts Amount of underlyings to return.
    /// @param _clusterAmount Amount of cluster unlocked from farming.
    function returnDebtFromAccumulation(uint256[] memory _amounts, uint256 _clusterAmount) external override onlyRole(FARMER_ROLE) {
        clusterTokenLock -= _clusterAmount;

        for (uint256 i = 0; i < underlyings.length; i++) {
            clusterLock[underlyings[i]] -= _amounts[i];
            IERC20(underlyings[i]).safeTransferFrom(_msgSender(), address(this), _amounts[i]);
        }

        emit ClusterRefundedFromAcc(address(this), _msgSender(), _clusterAmount, _amounts, underlyings);
    }

    /// @notice Interface for optimizer for the correction of the underlyings proportion.
    /// @param updatedShares New weights.
    function optimizeProportion(uint256[] memory updatedShares)
        external
        virtual
        override
        onlyRole(DELEGATE_ROLE)
        returns (uint256[] memory debt, uint256[] memory toWithdraw)
    {
        require(updatedShares.length == underlyingInCluster.length, "Wrong array");
        debt = new uint256[](underlyings.length);

        uint256 clusterTokenLockMemo = clusterTokenLock;
        uint256[] memory curSharesAmounts = IController(clusterControllerAddress).getUnderlyingsAmountsFromClusterAmount(
            totalSupply() - clusterTokenLockMemo,
            address(this)
        );
        underlyingInCluster = updatedShares;
        uint256[] memory newSharesAmounts = getUnderlyingsAmountsFromClusterAmount(totalSupply() - clusterTokenLockMemo);
        uint256[] memory newLock = getUnderlyingsAmountsFromClusterAmount(clusterTokenLockMemo);

        for (uint256 i = 0; i < underlyings.length; i++) {
            address tkn = underlyings[i];

            if (newLock[i] > clusterLock[tkn]) {
                debt[i] = newLock[i] - clusterLock[tkn];
            } else {
                debt[i] = 0;
            }
            clusterLock[tkn] = newLock[i];

            if (curSharesAmounts[i] > newSharesAmounts[i]) {
                IERC20(tkn).safeTransfer(_msgSender(), curSharesAmounts[i] - newSharesAmounts[i]);
            } else if (curSharesAmounts[i] < newSharesAmounts[i]) {
                IERC20(tkn).safeTransferFrom(_msgSender(), address(this), newSharesAmounts[i] - curSharesAmounts[i]);
            }
        }
        emit ProportionsOptimized(address(this), _msgSender(), updatedShares);
    }

    /**********
     * VIEW INTERFACE
     **********/

    /// @notice Get list of underlyings addresses for the cluster.
    /// @return underlyings array.
    function getUnderlyings() external view override returns (address[] memory) {
        return underlyings;
    }

    /// @notice Get list of amount of each underlying for 1 cluster.
    /// @return underlyingInCluster array.
    function getUnderlyingInCluster() external view override returns (uint256[] memory) {
        return underlyingInCluster;
    }

    /// @notice Calculates amounts of underlyings over the amount of cluster based on shares amounts.
    /// @param _clusterAmount Amount of cluster to calculate underlyings from.
    /// @return Array, which contains amounts of underlyings.
    function getUnderlyingsAmountsFromClusterAmount(uint256 _clusterAmount) public view override returns (uint256[] memory) {
        uint256[] memory underlyingAmounts = new uint256[](underlyings.length);
        for (uint256 i = 0; i < underlyings.length; i++) {
            underlyingAmounts[i] = (_clusterAmount * underlyingInCluster[i]) / ACCURACY_DECIMALS;
            uint8 decimals = IERC20Metadata(underlyings[i]).decimals();
            if (decimals < 18) {
                underlyingAmounts[i] /= 10**(18 - decimals);
            }
        }
        return underlyingAmounts;
    }

    /// @notice Calculates amount of underlying including locked assets.
    /// @param _underlyingAddress Underlying address.
    /// @return The amount of a certain underlying
    function getUnderlyingBalance(address _underlyingAddress) external view override returns (uint256) {
        return IERC20(_underlyingAddress).balanceOf(address(this)) + clusterLock[_underlyingAddress];
    }

    /**********
     * INTERNAL HELPERS AND PRICE CALCULATIONS
     **********/

    /// @notice Covers commission, necessary for cluster deposit or redeem.
    /// @param _ethCommission Commission in ETH to be covered.
    /// @param coverDhvWithEth Should DHV to cover commission be purchased during the call,
    /// rather then transferred from the caller's address.
    /// @return Amount of Ether, spent on commission.
    function _coverCommission(uint256 _ethCommission, bool coverDhvWithEth) internal returns (uint256) {
        if (_ethCommission != 0) {
            if (coverDhvWithEth) {
                Address.sendValue(payable(treasuryAddress), _ethCommission);
                return _ethCommission;
            } else {
                uint256 _dhvCommission = (_ethCommission * 10**18) / IController(clusterControllerAddress).getDHVPriceInETH(address(this));
                dhvTokenInstance.safeTransferFrom(_msgSender(), treasuryAddress, _dhvCommission);
                return 0;
            }
        }
        return 0;
    }

    /// @notice Allows to swap collected funds into underlyings through the appropriate adapter.
    function _swapEthToUnderlyings(uint256 _ethAmount, uint256 _clusterAmount) internal {
        address adapter = IController(clusterControllerAddress).adapters(address(this));

        (uint256[] memory underlyingAmounts, uint256[] memory ethPortion, , uint256 ethCalculated) = IController(clusterControllerAddress)
            .getUnderlyingsInfo(address(this), _clusterAmount);

        require(_ethAmount >= ethCalculated, "Not enough ether sent");

        for (uint256 i = 0; i < underlyings.length; i++) {
            if (
                IERC20(underlyings[i]).balanceOf(treasuryAddress) >= underlyingAmounts[i] &&
                IERC20(underlyings[i]).allowance(treasuryAddress, address(this)) >= underlyingAmounts[i]
            ) {
                IERC20(underlyings[i]).safeTransferFrom(treasuryAddress, address(this), underlyingAmounts[i]);
                Address.sendValue(payable(treasuryAddress), ethPortion[i]);
            } else {
                IDexAdapter(adapter).swapETHToUnderlying{value: ethPortion[i]}(underlyings[i], underlyingAmounts[i]);
            }
        }
    }

    /// @notice Swaps underlyings tokens into ETH.
    /// @param _underlyingAmounts Array of underlying tokens amounts.
    function _swapUnderlyingsToEth(uint256[] memory _underlyingAmounts) internal {
        address adapter = IController(clusterControllerAddress).adapters(address(this));
        for (uint256 i = 0; i < _underlyingAmounts.length; i++) {
            IERC20(underlyings[i]).safeApprove(adapter, 0);
            IERC20(underlyings[i]).safeApprove(adapter, _underlyingAmounts[i]);
        }

        IDexAdapter(adapter).swapUnderlyingsToETH(_underlyingAmounts, underlyings);
    }
}

// Part: ClusterTokenV2

contract ClusterTokenV2 is ClusterToken, IClusterTokenV2 {
    using SafeERC20 for IERC20;
    using Address for address;

    modifier uniqueUnderlying(address _newUnderlying) {
        for (uint256 i = 0; i < underlyings.length; i++) {
            require(_newUnderlying != underlyings[i], "Underlying is not unique!");
        }
        _;
    }

    constructor(
        address _clusterControllerAddress,
        address _treasury,
        address[] memory _underlyings,
        uint256[] memory _underlyingInCluster,
        string memory _name,
        string memory _symbol,
        uint256 _hashId
    ) ClusterToken(_clusterControllerAddress, _treasury, _underlyings, _underlyingInCluster, _name, _symbol, _hashId) {}

    /**********
     * OPTIMIZER INTERFACE
     **********/

    /// @notice Adds new underlying to cluster.
    /// @param _underlying Address of underlying to be added.
    /// @param _share Proportion of underlying to be added.
    /// @dev This method can only be called by Optimizer contract, since Optimizer performs
    /// all neccesary calculations.
    function addUnderlying(address _underlying, uint256 _share) external override onlyRole(DELEGATE_ROLE) uniqueUnderlying(_underlying) {
        underlyings.push(_underlying);
        underlyingInCluster.push(_share);

        uint256 tokensOnCluster = ((totalSupply() - clusterTokenLock) * _share) / ACCURACY_DECIMALS;
        uint256 tokensOnLock = (clusterTokenLock * _share) / ACCURACY_DECIMALS;

        clusterLock[_underlying] = tokensOnLock;
        IERC20(_underlying).safeTransferFrom(_msgSender(), address(this), tokensOnCluster);
    }

    /// @notice Replaces previous underlying to a new one.
    /// @param _prevUnderlying Previous underlying.
    /// @param _newUnderlying New underlying.
    /// @param _newShare Proportion of new underlying.
    /// @dev This method can only be called by Optimizer contract, since Optimizer performs
    /// all neccesary calculations.
    function replaceUnderlying(
        address _prevUnderlying,
        address _newUnderlying,
        uint256 _newShare
    ) external override onlyRole(DELEGATE_ROLE) uniqueUnderlying(_newUnderlying) {
        for (uint256 i = 0; i < underlyings.length; i++) {
            if (underlyings[i] == _prevUnderlying) {
                underlyings[i] = _newUnderlying;
                underlyingInCluster[i] = _newShare;
            }
        }

        IERC20(_prevUnderlying).safeTransfer(_msgSender(), IERC20(_prevUnderlying).balanceOf(address(this)));

        uint256 tokensOnLock = (clusterTokenLock * _newShare) / ACCURACY_DECIMALS;
        clusterLock[_newUnderlying] = tokensOnLock;

        delete clusterLock[_prevUnderlying];
    }

    /// @notice Updates clusterLock for each underlying, return both debts and amounts to withdrawFromAccumulation.
    /// @param updatedShares Updated proportions of underlyings.
    /// @dev Remove underlying if its share is zero
    function optimizeProportion(uint256[] memory updatedShares)
        external
        override
        onlyRole(DELEGATE_ROLE)
        returns (uint256[] memory debt, uint256[] memory toWithdraw)
    {
        require(updatedShares.length == underlyingInCluster.length, "Wrong array");
        debt = new uint256[](underlyings.length);
        toWithdraw = new uint256[](underlyings.length);

        uint256 clusterTokenLockMemo = clusterTokenLock;
        uint256[] memory curSharesAmounts = getUnderlyingsAmountsFromClusterAmount(totalSupply() - clusterTokenLockMemo);
        underlyingInCluster = updatedShares;
        uint256[] memory newSharesAmounts = getUnderlyingsAmountsFromClusterAmount(totalSupply() - clusterTokenLockMemo);
        uint256[] memory newLock = getUnderlyingsAmountsFromClusterAmount(clusterTokenLockMemo);

        for (uint256 i = 0; i < underlyings.length; i++) {
            address tkn = underlyings[i];

            if (newLock[i] > clusterLock[tkn]) {
                debt[i] = newLock[i] - clusterLock[tkn];
            } else {
                toWithdraw[i] = clusterLock[tkn] - newLock[i];
            }
            clusterLock[tkn] = newLock[i];
            if (curSharesAmounts[i] > newSharesAmounts[i]) {
                IERC20(tkn).safeTransfer(_msgSender(), curSharesAmounts[i] - newSharesAmounts[i]);
            } else if (curSharesAmounts[i] < newSharesAmounts[i]) {
                IERC20(tkn).safeTransferFrom(_msgSender(), address(this), newSharesAmounts[i] - curSharesAmounts[i]);
            }
        }

        for (uint256 i = 0; i < underlyings.length; i++) {
            if (updatedShares[i] == 0) {
                _removeUnderlying(i);
            }
        }
        emit ProportionsOptimized(address(this), _msgSender(), updatedShares);
    }

    /// @notice Removes underlying and rebalances its value through tokens.
    /// @param index Index of underlying to be removed.
    function _removeUnderlying(uint256 index) internal {
        if (index != underlyings.length - 1) {
            underlyings[index] = underlyings[underlyings.length - 1];
            underlyingInCluster[index] = underlyingInCluster[underlyingInCluster.length - 1];
        }

        underlyings.pop();
        underlyingInCluster.pop();
    }
}

        

Contract ABI

[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"address","name":"_clusterControllerAddress","internalType":"address"},{"type":"address","name":"_treasury","internalType":"address"},{"type":"address[]","name":"_underlyings","internalType":"address[]"},{"type":"uint256[]","name":"_underlyingInCluster","internalType":"uint256[]"},{"type":"string","name":"_name","internalType":"string"},{"type":"string","name":"_symbol","internalType":"string"},{"type":"uint256","name":"_hashId","internalType":"uint256"}]},{"type":"event","name":"Approval","inputs":[{"type":"address","name":"owner","internalType":"address","indexed":true},{"type":"address","name":"spender","internalType":"address","indexed":true},{"type":"uint256","name":"value","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"ClusterAssembled","inputs":[{"type":"address","name":"clusterAddress","internalType":"address","indexed":true},{"type":"address","name":"buyer","internalType":"address","indexed":true},{"type":"uint256","name":"clusterAmountBought","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"ClusterAssembledInETH","inputs":[{"type":"address","name":"clusterAddress","internalType":"address","indexed":true},{"type":"address","name":"buyer","internalType":"address","indexed":true},{"type":"uint256","name":"ethDeposited","internalType":"uint256","indexed":false},{"type":"uint256","name":"clusterAmountBought","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"ClusterDisassembled","inputs":[{"type":"address","name":"clusterAddress","internalType":"address","indexed":true},{"type":"address","name":"redeemer","internalType":"address","indexed":true},{"type":"uint256","name":"clusterAmountRedeemed","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"ClusterDisassembledInETH","inputs":[{"type":"address","name":"clusterAddress","internalType":"address","indexed":true},{"type":"address","name":"redeemer","internalType":"address","indexed":true},{"type":"uint256","name":"clusterAmountRedeemed","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"ClusterRefundedFromAcc","inputs":[{"type":"address","name":"clusterAddress","internalType":"address","indexed":true},{"type":"address","name":"farmer","internalType":"address","indexed":true},{"type":"uint256","name":"clusterAmount","internalType":"uint256","indexed":false},{"type":"uint256[]","name":"underlyingsAmounts","internalType":"uint256[]","indexed":false},{"type":"address[]","name":"tokens","internalType":"address[]","indexed":false}],"anonymous":false},{"type":"event","name":"ClusterWithdrawnToAcc","inputs":[{"type":"address","name":"clusterAddress","internalType":"address","indexed":true},{"type":"address","name":"farmer","internalType":"address","indexed":true},{"type":"uint256","name":"clusterAmount","internalType":"uint256","indexed":false},{"type":"uint256[]","name":"underlyingsAmounts","internalType":"uint256[]","indexed":false},{"type":"address[]","name":"tokens","internalType":"address[]","indexed":false}],"anonymous":false},{"type":"event","name":"Paused","inputs":[{"type":"address","name":"account","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"ProportionsOptimized","inputs":[{"type":"address","name":"clusterAddress","internalType":"address","indexed":true},{"type":"address","name":"delegate","internalType":"address","indexed":true},{"type":"uint256[]","name":"updatedShares","internalType":"uint256[]","indexed":false}],"anonymous":false},{"type":"event","name":"RoleAdminChanged","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32","indexed":true},{"type":"bytes32","name":"previousAdminRole","internalType":"bytes32","indexed":true},{"type":"bytes32","name":"newAdminRole","internalType":"bytes32","indexed":true}],"anonymous":false},{"type":"event","name":"RoleGranted","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32","indexed":true},{"type":"address","name":"account","internalType":"address","indexed":true},{"type":"address","name":"sender","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"RoleRevoked","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32","indexed":true},{"type":"address","name":"account","internalType":"address","indexed":true},{"type":"address","name":"sender","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"Transfer","inputs":[{"type":"address","name":"from","internalType":"address","indexed":true},{"type":"address","name":"to","internalType":"address","indexed":true},{"type":"uint256","name":"value","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"Unpaused","inputs":[{"type":"address","name":"account","internalType":"address","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"ACCURACY_DECIMALS","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"ADAPTER_ROLE","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"CLUSTER_TOKEN_DECIMALS","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"CLUSTER_VERSION","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"DEFAULT_ADMIN_ROLE","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"DELEGATE_ROLE","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"DUST_AMOUNT","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"FARMER_ROLE","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"MAX_COOLDOWN","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"addUnderlying","inputs":[{"type":"address","name":"_underlying","internalType":"address"},{"type":"uint256","name":"_share","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"allowance","inputs":[{"type":"address","name":"owner","internalType":"address"},{"type":"address","name":"spender","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"approve","inputs":[{"type":"address","name":"spender","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"payable","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"assemble","inputs":[{"type":"uint256","name":"clusterAmount","internalType":"uint256"},{"type":"bool","name":"coverDhvWithEth","internalType":"bool"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"assembleByAdapter","inputs":[{"type":"uint256","name":"_clusterAmount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"balanceOf","inputs":[{"type":"address","name":"account","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"clusterControllerAddress","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"clusterHashId","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"clusterLock","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"clusterTokenLock","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"controllerChange","inputs":[{"type":"address","name":"_controller","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"cooldownAmount","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"cooldownPeriod","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"cooldownTimestamps","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint8","name":"","internalType":"uint8"}],"name":"decimals","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"decreaseAllowance","inputs":[{"type":"address","name":"spender","internalType":"address"},{"type":"uint256","name":"subtractedValue","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IERC20"}],"name":"dhvTokenInstance","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"disassemble","inputs":[{"type":"uint256","name":"_clusterAmount","internalType":"uint256"},{"type":"bool","name":"coverDhvWithEth","internalType":"bool"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"disassembleByAdapter","inputs":[{"type":"uint256","name":"_clusterAmount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"getRoleAdmin","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getUnderlyingBalance","inputs":[{"type":"address","name":"_underlyingAddress","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256[]","name":"","internalType":"uint256[]"}],"name":"getUnderlyingInCluster","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address[]","name":"","internalType":"address[]"}],"name":"getUnderlyings","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256[]","name":"","internalType":"uint256[]"}],"name":"getUnderlyingsAmountsFromClusterAmount","inputs":[{"type":"uint256","name":"_clusterAmount","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"grantRole","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32"},{"type":"address","name":"account","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"hasRole","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32"},{"type":"address","name":"account","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"increaseAllowance","inputs":[{"type":"address","name":"spender","internalType":"address"},{"type":"uint256","name":"addedValue","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"name","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256[]","name":"debt","internalType":"uint256[]"},{"type":"uint256[]","name":"toWithdraw","internalType":"uint256[]"}],"name":"optimizeProportion","inputs":[{"type":"uint256[]","name":"updatedShares","internalType":"uint256[]"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"pause","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"paused","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"refundFromAccumulation","inputs":[{"type":"uint256","name":"_clusterAmount","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"renounceRole","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32"},{"type":"address","name":"account","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"replaceUnderlying","inputs":[{"type":"address","name":"_prevUnderlying","internalType":"address"},{"type":"address","name":"_newUnderlying","internalType":"address"},{"type":"uint256","name":"_newShare","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"returnDebtFromAccumulation","inputs":[{"type":"uint256[]","name":"_amounts","internalType":"uint256[]"},{"type":"uint256","name":"_clusterAmount","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"revokeRole","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32"},{"type":"address","name":"account","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setCooldownPeriod","inputs":[{"type":"uint256","name":"_cooldownPeriod","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setTreasuryAddress","inputs":[{"type":"address","name":"_newTreasury","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"supportsInterface","inputs":[{"type":"bytes4","name":"interfaceId","internalType":"bytes4"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"symbol","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalSupply","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"transfer","inputs":[{"type":"address","name":"recipient","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"transferFrom","inputs":[{"type":"address","name":"sender","internalType":"address"},{"type":"address","name":"recipient","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"underlyingInCluster","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"underlyings","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"unpause","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"withdrawDust","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"withdrawToAccumulation","inputs":[{"type":"uint256","name":"_clusterAmount","internalType":"uint256"}]},{"type":"receive","stateMutability":"payable"}]
            

Deployed ByteCode

0x6080604052600436106103795760003560e01c806380ea3de1116101d1578063b0741f3a11610102578063d031b0be116100a0578063f0d23e8f1161006f578063f0d23e8f14610a53578063f394397014610a73578063f48f30ac14610a95578063f65baefa14610ac257600080fd5b8063d031b0be146109b6578063d547741f146109cd578063dd62ed3e146109ed578063ebe3391314610a3357600080fd5b8063c0ba241b116100dc578063c0ba241b1461093f578063c8e75c5914610961578063cea0252e14610981578063cfb550f1146109a157600080fd5b8063b0741f3a146108f6578063b63ad23e14610916578063bd5a14ee1461092c57600080fd5b8063953a6b971161016f5780639bb1bebb116101495780639bb1bebb14610881578063a217fddf146108a1578063a457c2d7146108b6578063a9059cbb146108d657600080fd5b8063953a6b971461083357806395d89b411461085357806397f641ab1461086857600080fd5b80638b41d35f116101ab5780638b41d35f146107af5780638e98a220146107c657806390f5fe78146107e657806391d148541461081357600080fd5b806380ea3de11461074d5780638456cb591461076d5780638973fcc81461078257600080fd5b8063313ce567116102ab57806365c6208d1161024957806370a082311161022357806370a082311461069f578063731b383c146106d55780637bba46d0146106f55780637eea34001461071557600080fd5b806365c6208d1461064a5780636605bfda1461065f5780636c91fefa1461067f57600080fd5b8063395093511161028557806339509351146105dd5780633f4ba83a146105fd5780635c975abb1461061257806360b990671461062a57600080fd5b8063313ce5671461058b57806336568abe146105a757806336aaa5fd146105c757600080fd5b806318160ddd11610318578063248a9ca3116102f2578063248a9ca3146104ff5780632d2e86771461052f5780632f2ff15d1461054f5780632f3422ce1461056f57600080fd5b806318160ddd1461049c5780631c29078c146104b157806323b872dd146104df57600080fd5b806304646a491161035457806304646a491461042257806306fdde0314610438578063095ea7b31461045a57806317c424c21461047a57600080fd5b806270fd991461038557806301ffc9a7146103b057806302a882e6146103e057600080fd5b3661038057005b600080fd5b34801561039157600080fd5b5061039a610ae4565b6040516103a791906142d5565b60405180910390f35b3480156103bc57600080fd5b506103d06103cb3660046140f3565b610b3c565b60405190151581526020016103a7565b3480156103ec57600080fd5b506104147fdbeb657137b1822b3d5418bea6fd641226d964b4c3871ef23546db262225887181565b6040519081526020016103a7565b34801561042e57600080fd5b50610414600b5481565b34801561044457600080fd5b5061044d610b73565b6040516103a7919061433b565b34801561046657600080fd5b506103d0610475366004613f7e565b610bfc565b34801561048657600080fd5b5061049a6104953660046140b5565b610c12565b005b3480156104a857600080fd5b50600254610414565b3480156104bd57600080fd5b506104d16104cc366004613faa565b610d00565b6040516103a7929190614316565b3480156104eb57600080fd5b506103d06104fa366004613f3d565b611183565b34801561050b57600080fd5b5061041461051a3660046140b5565b60009081526006602052604090206001015490565b34801561053b57600080fd5b5061041461054a3660046140b5565b61122f565b34801561055b57600080fd5b5061049a61056a3660046140ce565b611250565b34801561057b57600080fd5b50610414670de0b6b3a764000081565b34801561059757600080fd5b50604051601281526020016103a7565b3480156105b357600080fd5b5061049a6105c23660046140ce565b61127b565b3480156105d357600080fd5b50610414600a5481565b3480156105e957600080fd5b506103d06105f8366004613f7e565b6112f9565b34801561060957600080fd5b5061049a611335565b34801561061e57600080fd5b5060055460ff166103d0565b34801561063657600080fd5b5061049a610645366004613f3d565b61134c565b34801561065657600080fd5b50610414600281565b34801561066b57600080fd5b5061049a61067a366004613eca565b611592565b34801561068b57600080fd5b5061049a61069a3660046140b5565b61160b565b3480156106ab57600080fd5b506104146106ba366004613eca565b6001600160a01b031660009081526020819052604090205490565b3480156106e157600080fd5b5061049a6106f03660046140b5565b611716565b34801561070157600080fd5b5061049a6107103660046140b5565b61194c565b34801561072157600080fd5b50600754610735906001600160a01b031681565b6040516001600160a01b0390911681526020016103a7565b34801561075957600080fd5b5061049a6107683660046140b5565b611a47565b34801561077957600080fd5b5061049a611aa1565b34801561078e57600080fd5b5061041461079d366004613eca565b60106020526000908152604090205481565b3480156107bb57600080fd5b506104146202a30081565b3480156107d257600080fd5b5061049a6107e1366004613eca565b611ab5565b3480156107f257600080fd5b50610414610801366004613eca565b60116020526000908152604090205481565b34801561081f57600080fd5b506103d061082e3660046140ce565b611b82565b34801561083f57600080fd5b5061049a61084e366004614053565b611bad565b34801561085f57600080fd5b5061044d611cb9565b34801561087457600080fd5b5061041464e8d4a5100081565b34801561088d57600080fd5b5061039a61089c3660046140b5565b611cc8565b3480156108ad57600080fd5b50610414600081565b3480156108c257600080fd5b506103d06108d1366004613f7e565b611e75565b3480156108e257600080fd5b506103d06108f1366004613f7e565b611f0e565b34801561090257600080fd5b50600954610735906001600160a01b031681565b34801561092257600080fd5b50610414600e5481565b61041461093a366004614136565b611f1b565b34801561094b57600080fd5b506104146000805160206146b883398151915281565b34801561096d57600080fd5b5061049a61097c366004613f7e565b61214b565b34801561098d57600080fd5b5061073561099c3660046140b5565b6122f4565b3480156109ad57600080fd5b5061049a61231e565b3480156109c257600080fd5b50610414620f424081565b3480156109d957600080fd5b5061049a6109e83660046140ce565b612334565b3480156109f957600080fd5b50610414610a08366004613f04565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b348015610a3f57600080fd5b5061049a610a4e366004614136565b61235a565b348015610a5f57600080fd5b50610414610a6e366004613eca565b61259f565b348015610a7f57600080fd5b506104146000805160206146d883398151915281565b348015610aa157600080fd5b50610414610ab0366004613eca565b600f6020526000908152604090205481565b348015610ace57600080fd5b50610ad7612632565b6040516103a79190614288565b6060600d805480602002602001604051908101604052809291908181526020018280548015610b3257602002820191906000526020600020905b815481526020019060010190808311610b1e575b5050505050905090565b60006001600160e01b03198216637965db0b60e01b1480610b6d57506301ffc9a760e01b6001600160e01b03198316145b92915050565b606060038054610b82906145e6565b80601f0160208091040260200160405190810160405280929190818152602001828054610bae906145e6565b8015610b325780601f10610bd057610100808354040283529160200191610b32565b820191906000526020600020905b815481529060010190602001808311610bde57509395945050505050565b6000610c09338484612693565b50600192915050565b7fdbeb657137b1822b3d5418bea6fd641226d964b4c3871ef23546db2622258871610c3d81336127af565b6000610c4883611cc8565b905060005b8151811015610cb857610ca6335b30848481518110610c6e57610c6e614668565b6020026020010151600c8581548110610c8957610c89614668565b6000918252602090912001546001600160a01b0316929190612813565b80610cb081614621565b915050610c4d565b50610cc33384612884565b604051838152339030907fdf882fbcb3ac2f616d4bab2db31dcedd61dd178b733bdaadc75d70525b2a1598906020015b60405180910390a3505050565b6060806000805160206146b8833981519152610d1c81336127af565b600d54845114610d615760405162461bcd60e51b815260206004820152600b60248201526a57726f6e6720617272617960a81b60448201526064015b60405180910390fd5b600c5467ffffffffffffffff811115610d7c57610d7c61467e565b604051908082528060200260200182016040528015610da5578160200160208202803683370190505b50600c5490935067ffffffffffffffff811115610dc457610dc461467e565b604051908082528060200260200182016040528015610ded578160200160208202803683370190505b50600e549092506000610e0d82610e0360025490565b61089c9190614569565b8651909150610e2390600d906020890190613d98565b506000610e3383610e0360025490565b90506000610e4084611cc8565b905060005b600c548110156110f1576000600c8281548110610e6457610e64614668565b60009182526020808320909101546001600160a01b0316808352600f909152604090912054845191925090849084908110610ea157610ea1614668565b60200260200101511115610f11576001600160a01b0381166000908152600f60205260409020548351849084908110610edc57610edc614668565b6020026020010151610eee9190614569565b898381518110610f0057610f00614668565b602002602001018181525050610f7a565b828281518110610f2357610f23614668565b6020026020010151600f6000836001600160a01b03166001600160a01b0316815260200190815260200160002054610f5b9190614569565b888381518110610f6d57610f6d614668565b6020026020010181815250505b828281518110610f8c57610f8c614668565b6020026020010151600f6000836001600160a01b03166001600160a01b0316815260200190815260200160002081905550838281518110610fcf57610fcf614668565b6020026020010151858381518110610fe957610fe9614668565b6020026020010151111561104f5761104a3385848151811061100d5761100d614668565b602002602001015187858151811061102757611027614668565b60200260200101516110399190614569565b6001600160a01b038416919061296f565b6110de565b83828151811061106157611061614668565b602002602001015185838151811061107b5761107b614668565b602002602001015110156110de576110de33308785815181106110a0576110a0614668565b60200260200101518786815181106110ba576110ba614668565b60200260200101516110cc9190614569565b6001600160a01b038516929190612813565b50806110e981614621565b915050610e45565b5060005b600c5481101561113c5788818151811061111157611111614668565b60200260200101516000141561112a5761112a8161299f565b8061113481614621565b9150506110f5565b50604051339030907f23430ac6054603925f82afae7cc6252190a47d0e5fa6b4ac67790c3b3e292eb890611171908c906142d5565b60405180910390a35050505050915091565b6000611190848484612ad1565b6001600160a01b0384166000908152600160209081526040808320338452909152902054828110156112155760405162461bcd60e51b815260206004820152602860248201527f45524332303a207472616e7366657220616d6f756e74206578636565647320616044820152676c6c6f77616e636560c01b6064820152608401610d58565b6112228533858403612693565b60019150505b9392505050565b600d818154811061123f57600080fd5b600091825260209091200154905081565b60008281526006602052604090206001015461126c81336127af565b6112768383612caa565b505050565b6001600160a01b03811633146112eb5760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b6064820152608401610d58565b6112f58282612d30565b5050565b3360008181526001602090815260408083206001600160a01b03871684529091528120549091610c09918590611330908690614422565b612693565b600061134181336127af565b611349612d97565b50565b6000805160206146b883398151915261136581336127af565b8260005b600c548110156113fb57600c818154811061138657611386614668565b6000918252602090912001546001600160a01b03838116911614156113e95760405162461bcd60e51b8152602060048201526019602482015278556e6465726c79696e67206973206e6f7420756e697175652160381b6044820152606401610d58565b806113f381614621565b915050611369565b5060005b600c548110156114b657856001600160a01b0316600c828154811061142657611426614668565b6000918252602090912001546001600160a01b031614156114a45784600c828154811061145557611455614668565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555083600d828154811061149757611497614668565b6000918252602090912001555b806114ae81614621565b9150506113ff565b50611543336040516370a0823160e01b81523060048201526001600160a01b038816906370a082319060240160206040518083038186803b1580156114fa57600080fd5b505afa15801561150e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611532919061411d565b6001600160a01b038816919061296f565b6000620f424084600e54611557919061454a565b611561919061443a565b6001600160a01b039586166000908152600f6020526040808220929092559690951686525050509082209190915550565b600061159e81336127af565b6001600160a01b0382166115e85760405162461bcd60e51b8152602060048201526011602482015270496e636f7272656374206164647265737360781b6044820152606401610d58565b50600880546001600160a01b0319166001600160a01b0392909216919091179055565b6000805160206146d883398151915261162481336127af565b600061162f83611cc8565b905082600e60008282546116439190614569565b90915550600090505b600c548110156116dc5781818151811061166857611668614668565b6020026020010151600f6000600c848154811061168757611687614668565b60009182526020808320909101546001600160a01b03168352820192909252604001812080549091906116bb908490614569565b909155506116ca905033610c5b565b806116d481614621565b91505061164c565b50604051339030907fded8512602a42e2b340fdd5b806b1069a040da129f148c598f54cbe1eb64576190610cf39087908690600c90614398565b6000805160206146d883398151915261172f81336127af565b81600e5461173d9190614422565b306370a08231336040516001600160e01b031960e084901b1681526001600160a01b03909116600482015260240160206040518083038186803b15801561178357600080fd5b505afa158015611797573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117bb919061411d565b10156118155760405162461bcd60e51b8152602060048201526024808201527f4661726d657220686173206e6f7420656e6f75676820636c757374657220746f6044820152636b656e7360e01b6064820152608401610d58565b600061182083611cc8565b905082600e60008282546118349190614422565b90915550600090505b600c548110156119125781818151811061185957611859614668565b6020026020010151600f6000600c848154811061187857611878614668565b60009182526020808320909101546001600160a01b03168352820192909252604001812080549091906118ac908490614422565b909155506119009050335b8383815181106118c9576118c9614668565b6020026020010151600c84815481106118e4576118e4614668565b6000918252602090912001546001600160a01b0316919061296f565b8061190a81614621565b91505061183d565b50604051339030907fc72d630122c8ed4fc97918baa82670432c4d36f10149924b98a8c410ad6b0a2b90610cf39087908690600c90614398565b6119646000805160206146d883398151915233611b82565b156119a95760405162461bcd60e51b815260206004820152601560248201527411985c9b595c881a5cc81b9bdd08185b1b1bddd959605a1b6044820152606401610d58565b7fdbeb657137b1822b3d5418bea6fd641226d964b4c3871ef23546db26222588716119d481336127af565b60006119df83611cc8565b90506119eb3384612e2a565b60005b8151811015611a1257611a00336118b7565b80611a0a81614621565b9150506119ee565b50604051838152339030907f0e519abb97b349157786738cedce344e2eabcd59b8b8c4990b1f4d0c035d9a3090602001610cf3565b6000611a5381336127af565b6202a300821115611a9b5760405162461bcd60e51b815260206004820152601260248201527124b731b7b93932b1ba1031b7b7b63237bbb760711b6044820152606401610d58565b50600b55565b6000611aad81336127af565b611349612f84565b6007546001600160a01b0316336001600160a01b031614611b0a5760405162461bcd60e51b815260206004820152600f60248201526e436f6e74726f6c6c6572206f6e6c7960881b6044820152606401610d58565b6001600160a01b038116611b605760405162461bcd60e51b815260206004820152601c60248201527f6465763a20436f6e74726f6c6c6572207a65726f2061646472657373000000006044820152606401610d58565b600780546001600160a01b0319166001600160a01b0392909216919091179055565b60009182526006602090815260408084206001600160a01b0393909316845291905290205460ff1690565b6000805160206146d8833981519152611bc681336127af565b81600e6000828254611bd89190614569565b90915550600090505b600c54811015611c7f57838181518110611bfd57611bfd614668565b6020026020010151600f6000600c8481548110611c1c57611c1c614668565b60009182526020808320909101546001600160a01b0316835282019290925260400181208054909190611c50908490614569565b90915550611c6d90503330868481518110610c6e57610c6e614668565b80611c7781614621565b915050611be1565b50604051339030907fded8512602a42e2b340fdd5b806b1069a040da129f148c598f54cbe1eb64576190610cf39086908890600c90614398565b606060048054610b82906145e6565b600c5460609060009067ffffffffffffffff811115611ce957611ce961467e565b604051908082528060200260200182016040528015611d12578160200160208202803683370190505b50905060005b600c54811015611e6e57620f4240600d8281548110611d3957611d39614668565b906000526020600020015485611d4f919061454a565b611d59919061443a565b828281518110611d6b57611d6b614668565b6020026020010181815250506000600c8281548110611d8c57611d8c614668565b600091825260209182902001546040805163313ce56760e01b815290516001600160a01b039092169263313ce56792600480840193829003018186803b158015611dd557600080fd5b505afa158015611de9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e0d919061415b565b905060128160ff161015611e5b57611e26816012614580565b611e3190600a61449f565b838381518110611e4357611e43614668565b60200260200101818151611e57919061443a565b9052505b5080611e6681614621565b915050611d18565b5092915050565b3360009081526001602090815260408083206001600160a01b038616845290915281205482811015611ef75760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b6064820152608401610d58565b611f043385858403612693565b5060019392505050565b6000610c09338484612ad1565b6000611f2960055460ff1690565b15611f465760405162461bcd60e51b8152600401610d589061436e565b611f5a335b6001600160a01b03163b151590565b15611fa35760405162461bcd60e51b81526020600482015260196024820152784e6f7420616c6c6f77656420666f7220636f6e74726163747360381b6044820152606401610d58565b47611fae3485612fdc565b476000611fbb8284614569565b600754604051621d5f4960e41b8152306004820152602481018390529192506000916001600160a01b03909116906301d5f4909060440160206040518083038186803b15801561200a57600080fd5b505afa15801561201e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612042919061411d565b905060006120508288613416565b61205a8434614569565b6120649190614569565b905064e8d4a5100081111561207e5761207e335b8261350c565b600b54156120f857336000908152601060205260409020544210156120c75733600090815260116020526040812080548a92906120bc908490614422565b909155506120da9050565b3360009081526011602052604090208890555b600b546120e79042614422565b336000908152601060205260409020555b6121023389612884565b60408051848152602081018a9052339130917f154f5ab3f8529ffdbb246fd1a63a2cce74c42c1b45bd6bbfad82bef2272be9c5910160405180910390a350959695505050505050565b6000805160206146b883398151915261216481336127af565b8260005b600c548110156121fa57600c818154811061218557612185614668565b6000918252602090912001546001600160a01b03838116911614156121e85760405162461bcd60e51b8152602060048201526019602482015278556e6465726c79696e67206973206e6f7420756e697175652160381b6044820152606401610d58565b806121f281614621565b915050612168565b50600c805460018082019092557fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c70180546001600160a01b0319166001600160a01b038716179055600d8054918201815560009081527fd7b6990105719101dabeb77144f2a3385c8033acd3af97e9423a695e81ad1eb5909101849055600e54600254620f424091869161228e9190614569565b612298919061454a565b6122a2919061443a565b90506000620f424085600e546122b8919061454a565b6122c2919061443a565b6001600160a01b0387166000818152600f602052604090208290559091506122ec90333085612813565b505050505050565b600c818154811061230457600080fd5b6000918252602090912001546001600160a01b0316905081565b600061232a81336127af565b611349334761350c565b60008281526006602052604090206001015461235081336127af565b6112768383612d30565b60055460ff161561237d5760405162461bcd60e51b8152600401610d589061436e565b6123956000805160206146d883398151915233611b82565b156123da5760405162461bcd60e51b815260206004820152601560248201527411985c9b595c881a5cc81b9bdd08185b1b1bddd959605a1b6044820152606401610d58565b6123e333611f4b565b1561242c5760405162461bcd60e51b81526020600482015260196024820152784e6f7420616c6c6f77656420666f7220636f6e74726163747360381b6044820152606401610d58565b6000821180156124445750612440336106ba565b8211155b6124855760405162461bcd60e51b81526020600482015260126024820152712737ba1032b737bab3b41031b63ab9ba32b960711b6044820152606401610d58565b61248f3383612e2a565b600061249a83611cc8565b9050476124a682613625565b4760006124b38383614569565b60075460405163263dd1c960e11b8152306004820152602481018390529192506000916001600160a01b0390911690634c7ba3929060440160206040518083038186803b15801561250357600080fd5b505afa158015612517573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061253b919061411d565b905060006125498288613416565b6125539084614569565b905061255e33612078565b604051888152339030907f2ced335a9d5d52c0e402f0cd99d473593830e0b3c1315c50a05c86c4afbd555f9060200160405180910390a35050505050505050565b6001600160a01b0381166000818152600f60205260408082205490516370a0823160e01b8152306004820152919290916370a082319060240160206040518083038186803b1580156125f057600080fd5b505afa158015612604573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612628919061411d565b610b6d9190614422565b6060600c805480602002602001604051908101604052809291908181526020018280548015610b3257602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831161266c575050505050905090565b6001600160a01b0383166126f55760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b6064820152608401610d58565b6001600160a01b0382166127565760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b6064820152608401610d58565b6001600160a01b0383811660008181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259101610cf3565b6127b98282611b82565b6112f5576127d1816001600160a01b03166014613785565b6127dc836020613785565b6040516020016127ed929190614213565b60408051601f198184030181529082905262461bcd60e51b8252610d589160040161433b565b6040516001600160a01b038085166024830152831660448201526064810182905261287e9085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152613921565b50505050565b6001600160a01b0382166128da5760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152606401610d58565b6128e6600083836139f3565b80600260008282546128f89190614422565b90915550506001600160a01b03821660009081526020819052604081208054839290612925908490614422565b90915550506040518181526001600160a01b038316906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a35050565b6040516001600160a01b03831660248201526044810182905261127690849063a9059cbb60e01b90606401612847565b600c546129ae90600190614569565b8114612a7457600c80546129c490600190614569565b815481106129d4576129d4614668565b600091825260209091200154600c80546001600160a01b039092169183908110612a0057612a00614668565b600091825260209091200180546001600160a01b0319166001600160a01b0392909216919091179055600d8054612a3990600190614569565b81548110612a4957612a49614668565b9060005260206000200154600d8281548110612a6757612a67614668565b6000918252602090912001555b600c805480612a8557612a85614652565b600082815260209020810160001990810180546001600160a01b0319169055019055600d805480612ab857612ab8614652565b6001900381819060005260206000200160009055905550565b6001600160a01b038316612b355760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b6064820152608401610d58565b6001600160a01b038216612b975760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b6064820152608401610d58565b612ba28383836139f3565b6001600160a01b03831660009081526020819052604090205481811015612c1a5760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b6064820152608401610d58565b6001600160a01b03808516600090815260208190526040808220858503905591851681529081208054849290612c51908490614422565b92505081905550826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051612c9d91815260200190565b60405180910390a361287e565b612cb48282611b82565b6112f55760008281526006602090815260408083206001600160a01b03851684529091529020805460ff19166001179055612cec3390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b612d3a8282611b82565b156112f55760008281526006602090815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b60055460ff16612de05760405162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b6044820152606401610d58565b6005805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b6001600160a01b038216612e8a5760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b6064820152608401610d58565b612e96826000836139f3565b6001600160a01b03821660009081526020819052604090205481811015612f0a5760405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604482015261636560f01b6064820152608401610d58565b6001600160a01b0383166000908152602081905260408120838303905560028054849290612f39908490614569565b90915550506040518281526000906001600160a01b038516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a3505050565b60055460ff1615612fa75760405162461bcd60e51b8152600401610d589061436e565b6005805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258612e0d3390565b60075460405163395c47ff60e21b81523060048201526000916001600160a01b03169063e5711ffc9060240160206040518083038186803b15801561302057600080fd5b505afa158015613034573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130589190613ee7565b6007546040516360db72ef60e01b815230600482015260248101859052919250600091829182916001600160a01b03909116906360db72ef9060440160006040518083038186803b1580156130ac57600080fd5b505afa1580156130c0573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526130e89190810190613fdf565b93505092509250808610156131375760405162461bcd60e51b8152602060048201526015602482015274139bdd08195b9bdd59da08195d1a195c881cd95b9d605a1b6044820152606401610d58565b60005b600c5481101561340d5783818151811061315657613156614668565b6020026020010151600c828154811061317157613171614668565b6000918252602090912001546008546040516370a0823160e01b81526001600160a01b0391821660048201529116906370a082319060240160206040518083038186803b1580156131c157600080fd5b505afa1580156131d5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131f9919061411d565b101580156132c0575083818151811061321457613214614668565b6020026020010151600c828154811061322f5761322f614668565b600091825260209091200154600854604051636eb1769f60e11b81526001600160a01b03918216600482015230602482015291169063dd62ed3e9060440160206040518083038186803b15801561328557600080fd5b505afa158015613299573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132bd919061411d565b10155b156133235760085484516132ec916001600160a01b0316903090879085908110610c6e57610c6e614668565b600854835161331e916001600160a01b03169085908490811061331157613311614668565b602002602001015161350c565b6133fb565b846001600160a01b03166337f324c984838151811061334457613344614668565b6020026020010151600c848154811061335f5761335f614668565b9060005260206000200160009054906101000a90046001600160a01b031687858151811061338f5761338f614668565b60200260200101516040518463ffffffff1660e01b81526004016133c89291906001600160a01b03929092168252602082015260400190565b6000604051808303818588803b1580156133e157600080fd5b505af11580156133f5573d6000803e3d6000fd5b50505050505b8061340581614621565b91505061313a565b50505050505050565b600082156135035781156134415760085461343a906001600160a01b03168461350c565b5081610b6d565b600754604051632727985160e11b81523060048201526000916001600160a01b031690634e4f30a29060240160206040518083038186803b15801561348557600080fd5b505afa158015613499573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134bd919061411d565b6134cf85670de0b6b3a764000061454a565b6134d9919061443a565b90506134f9336008546009546001600160a01b0390811692911684612813565b6000915050610b6d565b50600092915050565b8047101561355c5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e63650000006044820152606401610d58565b6000826001600160a01b03168260405160006040518083038185875af1925050503d80600081146135a9576040519150601f19603f3d011682016040523d82523d6000602084013e6135ae565b606091505b50509050806112765760405162461bcd60e51b815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d617920686176652072657665727465640000000000006064820152608401610d58565b60075460405163395c47ff60e21b81523060048201526000916001600160a01b03169063e5711ffc9060240160206040518083038186803b15801561366957600080fd5b505afa15801561367d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136a19190613ee7565b905060005b8251811015613727576136e4826000600c84815481106136c8576136c8614668565b6000918252602090912001546001600160a01b03169190613afc565b613715828483815181106136fa576136fa614668565b6020026020010151600c84815481106136c8576136c8614668565b8061371f81614621565b9150506136a6565b50604051632457ff7360e01b81526001600160a01b03821690632457ff7390613757908590600c906004016142e8565b600060405180830381600087803b15801561377157600080fd5b505af11580156122ec573d6000803e3d6000fd5b6060600061379483600261454a565b61379f906002614422565b67ffffffffffffffff8111156137b7576137b761467e565b6040519080825280601f01601f1916602001820160405280156137e1576020820181803683370190505b509050600360fc1b816000815181106137fc576137fc614668565b60200101906001600160f81b031916908160001a905350600f60fb1b8160018151811061382b5761382b614668565b60200101906001600160f81b031916908160001a905350600061384f84600261454a565b61385a906001614422565b90505b60018111156138d2576f181899199a1a9b1b9c1cb0b131b232b360811b85600f166010811061388e5761388e614668565b1a60f81b8282815181106138a4576138a4614668565b60200101906001600160f81b031916908160001a90535060049490941c936138cb816145cf565b905061385d565b5083156112285760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610d58565b6000613976826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316613c209092919063ffffffff16565b80519091501561127657808060200190518101906139949190614098565b6112765760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610d58565b6001600160a01b03831660009081526010602052604090205483908290421015613af5576001600160a01b0382166000818152601160205260408082205490516370a0823160e01b81526004810193909352909130906370a082319060240160206040518083038186803b158015613a6a57600080fd5b505afa158015613a7e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613aa2919061411d565b613aac9190614569565b9050818110156122ec5760405162461bcd60e51b8152602060048201526014602482015273436f6f6c646f776e20696e2070726f677265737360601b6044820152606401610d58565b5050505050565b801580613b855750604051636eb1769f60e11b81523060048201526001600160a01b03838116602483015284169063dd62ed3e9060440160206040518083038186803b158015613b4b57600080fd5b505afa158015613b5f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b83919061411d565b155b613bf05760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b6064820152608401610d58565b6040516001600160a01b03831660248201526044810182905261127690849063095ea7b360e01b90606401612847565b6060613c2f8484600085613c37565b949350505050565b606082471015613c985760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610d58565b843b613ce65760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610d58565b600080866001600160a01b03168587604051613d0291906141f7565b60006040518083038185875af1925050503d8060008114613d3f576040519150601f19603f3d011682016040523d82523d6000602084013e613d44565b606091505b5091509150613d54828286613d5f565b979650505050505050565b60608315613d6e575081611228565b825115613d7e5782518084602001fd5b8160405162461bcd60e51b8152600401610d58919061433b565b828054828255906000526020600020908101928215613dd3579160200282015b82811115613dd3578251825591602001919060010190613db8565b50613ddf929150613de3565b5090565b5b80821115613ddf5760008155600101613de4565b600082601f830112613e0957600080fd5b81356020613e1e613e19836143fe565b6143cd565b80838252828201915082860187848660051b8901011115613e3e57600080fd5b60005b85811015613e5d57813584529284019290840190600101613e41565b5090979650505050505050565b600082601f830112613e7b57600080fd5b81516020613e8b613e19836143fe565b80838252828201915082860187848660051b8901011115613eab57600080fd5b60005b85811015613e5d57815184529284019290840190600101613eae565b600060208284031215613edc57600080fd5b813561122881614694565b600060208284031215613ef957600080fd5b815161122881614694565b60008060408385031215613f1757600080fd5b8235613f2281614694565b91506020830135613f3281614694565b809150509250929050565b600080600060608486031215613f5257600080fd5b8335613f5d81614694565b92506020840135613f6d81614694565b929592945050506040919091013590565b60008060408385031215613f9157600080fd5b8235613f9c81614694565b946020939093013593505050565b600060208284031215613fbc57600080fd5b813567ffffffffffffffff811115613fd357600080fd5b613c2f84828501613df8565b60008060008060808587031215613ff557600080fd5b845167ffffffffffffffff8082111561400d57600080fd5b61401988838901613e6a565b9550602087015191508082111561402f57600080fd5b5061403c87828801613e6a565b604087015160609097015195989097509350505050565b6000806040838503121561406657600080fd5b823567ffffffffffffffff81111561407d57600080fd5b61408985828601613df8565b95602094909401359450505050565b6000602082840312156140aa57600080fd5b8151611228816146a9565b6000602082840312156140c757600080fd5b5035919050565b600080604083850312156140e157600080fd5b823591506020830135613f3281614694565b60006020828403121561410557600080fd5b81356001600160e01b03198116811461122857600080fd5b60006020828403121561412f57600080fd5b5051919050565b6000806040838503121561414957600080fd5b823591506020830135613f32816146a9565b60006020828403121561416d57600080fd5b815160ff8116811461122857600080fd5b6000815480845260208085019450836000528060002060005b838110156141bc5781546001600160a01b031687529582019560019182019101614197565b509495945050505050565b600081518084526020808501945080840160005b838110156141bc578151875295820195908201906001016141db565b600082516142098184602087016145a3565b9190910192915050565b7f416363657373436f6e74726f6c3a206163636f756e742000000000000000000081526000835161424b8160178501602088016145a3565b7001034b99036b4b9b9b4b733903937b6329607d1b601791840191820152835161427c8160288401602088016145a3565b01602801949350505050565b6020808252825182820181905260009190848201906040850190845b818110156142c95783516001600160a01b0316835292840192918401916001016142a4565b50909695505050505050565b60208152600061122860208301846141c7565b6040815260006142fb60408301856141c7565b828103602084015261430d818561417e565b95945050505050565b60408152600061432960408301856141c7565b828103602084015261430d81856141c7565b602081526000825180602084015261435a8160408501602087016145a3565b601f01601f19169190910160400192915050565b60208082526010908201526f14185d5cd8589b194e881c185d5cd95960821b604082015260600190565b8381526060602082015260006143b160608301856141c7565b82810360408401526143c3818561417e565b9695505050505050565b604051601f8201601f1916810167ffffffffffffffff811182821017156143f6576143f661467e565b604052919050565b600067ffffffffffffffff8211156144185761441861467e565b5060051b60200190565b600082198211156144355761443561463c565b500190565b60008261445757634e487b7160e01b600052601260045260246000fd5b500490565b600181815b8085111561449757816000190482111561447d5761447d61463c565b8085161561448a57918102915b93841c9390800290614461565b509250929050565b600061122860ff8416836000826144b857506001610b6d565b816144c557506000610b6d565b81600181146144db57600281146144e557614501565b6001915050610b6d565b60ff8411156144f6576144f661463c565b50506001821b610b6d565b5060208310610133831016604e8410600b8410161715614524575081810a610b6d565b61452e838361445c565b80600019048211156145425761454261463c565b029392505050565b60008160001904831182151516156145645761456461463c565b500290565b60008282101561457b5761457b61463c565b500390565b600060ff821660ff84168082101561459a5761459a61463c565b90039392505050565b60005b838110156145be5781810151838201526020016145a6565b8381111561287e5750506000910152565b6000816145de576145de61463c565b506000190190565b600181811c908216806145fa57607f821691505b6020821081141561461b57634e487b7160e01b600052602260045260246000fd5b50919050565b60006000198214156146355761463561463c565b5060010190565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052603160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b038116811461134957600080fd5b801515811461134957600080fdfe1a82baf2b928242f69f7147fb92490c6288d044f7257b88817e6284f1eec0f157c6181838a71a779e445600d4c6ecbe16bacf2b3c5bda69c29fada66d1b645d1a26469706673582212202d99c73cfee95c8043d1b9e3e9130fc181d7259685afbe7ef19d31f796f168d864736f6c63430008060033