Contract 0xf557b2b73b872e6d2f43826f9d77b7402a363bc0 9

Txn Hash Method
Block
From
To
Value [Txn Fee]
0x27bbbf0bce0d1c41d5c49b0c1a145833c5cbfc4bdce14085b53d641f69ae7907Disassemble327805582024-03-05 17:52:2523 days 11 hrs ago0xa03ea6357d25413421939ac297ad3f6a8601a339 IN  0xf557b2b73b872e6d2f43826f9d77b7402a363bc00 xDAI0.003808089027
0x3828005f413e173d2268b10291b38486915ee49a7d06e138dd15b54bec3e5a4bDisassemble327658402024-03-04 20:45:0524 days 9 hrs ago0x6fde7365bb2c917f8fa5abf9b0bda079b264304d IN  0xf557b2b73b872e6d2f43826f9d77b7402a363bc00 xDAI0.007946374363
0xd564fdd361832dd3bc2a25690c290f03691443a58d31382bcd5e5faf93fc32c5Disassemble325928682024-02-23 10:04:2034 days 19 hrs ago0xfb70a9f9d1e1084d531a8f15d4c92cd855b752f7 IN  0xf557b2b73b872e6d2f43826f9d77b7402a363bc00 xDAI0.00380659503
0xcff22f77b75b1e5373b2044857a24dc244f0d207a95c26f0bf139e24116b7563Disassemble321398832024-01-26 16:28:4562 days 13 hrs ago0x771abb3450944fb5b05857c66a3107578bf0c1e3 IN  0xf557b2b73b872e6d2f43826f9d77b7402a363bc00 xDAI0.018793622512
0xd4d4f6052fed24ee744c8d0164ca3ebabcff5c57d3b93bb5ec641dfadc800b7aApprove321398412024-01-26 16:25:0062 days 13 hrs ago0x771abb3450944fb5b05857c66a3107578bf0c1e3 IN  0xf557b2b73b872e6d2f43826f9d77b7402a363bc00 xDAI0.000445892202
0x19cc7255f9147315844ae44feb6b945f61bfec7414c6225ed8ea36dcc6b36148Assemble321397882024-01-26 16:20:1562 days 13 hrs ago0x771abb3450944fb5b05857c66a3107578bf0c1e3 IN  0xf557b2b73b872e6d2f43826f9d77b7402a363bc01.006852875615532 xDAI0.016702912869
0x2d600b2beb3859498d4fad11c26128269ca8120293b124b15ef14b99731e613dDisassemble306690262023-10-27 22:48:50153 days 7 hrs ago0x78e87757861185ec5e8c0ef6bf0c69fa7832df6c IN  0xf557b2b73b872e6d2f43826f9d77b7402a363bc00 xDAI0.029540717327
0xc9c5bd67527e723af2e03801d0eb55f38070a560952582706caac207c5145f29Disassemble305372782023-10-19 16:17:40161 days 13 hrs ago0x61ae94dd817357b59413202ac60b5997192aae0f IN  0xf557b2b73b872e6d2f43826f9d77b7402a363bc00 xDAI0.004416744199
0xa16021ad2050567391a58e9097c9be63b4d7e9a270ef5d775c5a2bc3d2315343Approve304547882023-10-14 14:06:55166 days 15 hrs ago0x61ae94dd817357b59413202ac60b5997192aae0f IN  0xf557b2b73b872e6d2f43826f9d77b7402a363bc00 xDAI0.000213622271
0x0f9972bae0de56cb14a86419241e85f2a12020e8043b6175552f0f6a9b1f3e51Assemble304547752023-10-14 14:05:45166 days 15 hrs ago0x61ae94dd817357b59413202ac60b5997192aae0f IN  0xf557b2b73b872e6d2f43826f9d77b7402a363bc051.092859141046228 xDAI0.008839185392
0xbdbb2f3cfa3041d774524930709e5d253aae53f70e58ee0d6c1babacac4b7925Disassemble301691692023-09-26 20:30:35184 days 9 hrs ago0xb5d732d7d494d915bc4edcd8b64db756d2c91cfc IN  0xf557b2b73b872e6d2f43826f9d77b7402a363bc00 xDAI0.003545745056
0x8ac0232c489ea800df92d607cadf0160971ab6a15abe9e7704daeee00d2f3f73Disassemble284179932023-06-12 14:02:30290 days 15 hrs ago0xdb03b65bc09c3f0c8b0dcaf12eda57e5946c1838 IN  0xf557b2b73b872e6d2f43826f9d77b7402a363bc00 xDAI0.02360693462
0x5f55f2eaba0a4fc9ca32048cd1498a8a0a818d019b25d30d371bf7fc671102bbApprove282384042023-06-01 16:45:30301 days 13 hrs ago0xdb03b65bc09c3f0c8b0dcaf12eda57e5946c1838 IN  0xf557b2b73b872e6d2f43826f9d77b7402a363bc00 xDAI0.000163845725
0xb91b78b523869a48b0e89675eece84cb027540b896a8e7ed699dd81f846f7624Assemble282383512023-06-01 16:40:45301 days 13 hrs ago0xdb03b65bc09c3f0c8b0dcaf12eda57e5946c1838 IN  0xf557b2b73b872e6d2f43826f9d77b7402a363bc012.128625803600933 xDAI0.00651255381
0x21576f7cd752d6243aac04953eab5ec91b77ee7613528a332748f9c9efa319e3Approve282382682023-06-01 16:33:15301 days 13 hrs ago0xdb03b65bc09c3f0c8b0dcaf12eda57e5946c1838 IN  0xf557b2b73b872e6d2f43826f9d77b7402a363bc00 xDAI0.000110630962
0x60439f85a6c95c91bed40a1d33549af18ab0cecb5996e71f0d7511ff32766337Assemble282382512023-06-01 16:31:50301 days 13 hrs ago0xdb03b65bc09c3f0c8b0dcaf12eda57e5946c1838 IN  0xf557b2b73b872e6d2f43826f9d77b7402a363bc00.10066244594667 xDAI0.004585061047
0xc0c1c60dfcee6d50ae0f938f5d718250c87d9dc67011dbbb122587c3ab729fa6Disassemble269066172023-03-12 13:11:00382 days 16 hrs ago0x9ffaa816a11a5e36ba911f157cc8d0734cc4ca2d IN  0xf557b2b73b872e6d2f43826f9d77b7402a363bc00 xDAI0.009950047531
0xaab846b9af44f1a223c202039f0a1f40977918513b0855a04dea3c561ae774ecDisassemble264242732023-02-11 12:44:35411 days 17 hrs ago0x78b25a013c85b617d4fb38ec2a15f667c03401a5 IN  0xf557b2b73b872e6d2f43826f9d77b7402a363bc00 xDAI0.003546810016
0xd2f181919342ec91a30adf1878b787c2710c62d169ab8bc514cbb0aeb20c24a8Disassemble260456752023-01-19 16:42:45434 days 13 hrs ago0x6d9b0b026d748432923d9962d5a89f5ee6c1631b IN  0xf557b2b73b872e6d2f43826f9d77b7402a363bc00 xDAI0.006870134764
0xff60e6b50b99bd484dadd2be87e1d0113d9239324875452cc0be2c31ec3fa403Approve259873612023-01-16 3:48:10438 days 2 hrs ago0x30194f526c6b08f8e33af1753b04322b0450c6c0 IN  0xf557b2b73b872e6d2f43826f9d77b7402a363bc00 xDAI0.00013486977
0x4f00fd63c9894c789f470421719d56db0ef8530c13eb98800de9d903d47edcf4Assemble259873492023-01-16 3:47:10438 days 2 hrs ago0x30194f526c6b08f8e33af1753b04322b0450c6c0 IN  0xf557b2b73b872e6d2f43826f9d77b7402a363bc00.200301343720607 xDAI0.00559136421
0x7df3e0f7d88dcc2c337f330696ba2ef39616cd93216af0db12e23dec185b3ee5Disassemble252706322022-12-03 10:35:35481 days 19 hrs ago0x15ecd31318fa645b6496f1f21b22ebfe3ad0c865 IN  0xf557b2b73b872e6d2f43826f9d77b7402a363bc00 xDAI0.004012115956
0x682c60de15c5fb8a1c90680f1ac9702c333cdce61cfcd87fd264d2d66460c021Assemble251540462022-11-25 16:31:40489 days 13 hrs ago0xbee2d469aacb46251ae33cca91f482e26c971dff IN  0xf557b2b73b872e6d2f43826f9d77b7402a363bc01.007090033052561 xDAI0.002882146513
0x446d2ce3a6ee4b6b5ac28762f0e05091ca6a2f8566f582fff164a77e45f59bf6Disassemble242934932022-09-22 13:34:55553 days 16 hrs ago0xd15b0342ded129c3bae109f4731ff0ae614592e3 IN  0xf557b2b73b872e6d2f43826f9d77b7402a363bc00 xDAI0.00458516478
0xb01c0c206c8c4bfca8b9bcd9dbbd2881cad12dc32fe2ebd631f344506df8f34dAssemble242796582022-09-21 13:55:50554 days 15 hrs ago0x6d9b0b026d748432923d9962d5a89f5ee6c1631b IN  0xf557b2b73b872e6d2f43826f9d77b7402a363bc01.006864096075776 xDAI0.002856388513
[ Download CSV Export 
Latest 25 internal transaction
Parent Txn Hash Block From To Value
0x27bbbf0bce0d1c41d5c49b0c1a145833c5cbfc4bdce14085b53d641f69ae7907327805582024-03-05 17:52:2523 days 11 hrs ago 0xf557b2b73b872e6d2f43826f9d77b7402a363bc00xa03ea6357d25413421939ac297ad3f6a8601a339625.951353682053363663 xDAI
0x27bbbf0bce0d1c41d5c49b0c1a145833c5cbfc4bdce14085b53d641f69ae7907327805582024-03-05 17:52:2523 days 11 hrs ago 0xf557b2b73b872e6d2f43826f9d77b7402a363bc00x5bac8c443ebd0525364dc813005ab72956d3705d1.883504574770471505 xDAI
0x27bbbf0bce0d1c41d5c49b0c1a145833c5cbfc4bdce14085b53d641f69ae7907327805582024-03-05 17:52:2523 days 11 hrs ago 0xd88be9b86f537796f530d084fdd30c0773d32905 0xf557b2b73b872e6d2f43826f9d77b7402a363bc01.659629439881092609 xDAI
0x27bbbf0bce0d1c41d5c49b0c1a145833c5cbfc4bdce14085b53d641f69ae7907327805582024-03-05 17:52:2523 days 11 hrs ago Honeyswap: Uniswap V2 Router 2 0xf557b2b73b872e6d2f43826f9d77b7402a363bc02.751614814722735142 xDAI
0x27bbbf0bce0d1c41d5c49b0c1a145833c5cbfc4bdce14085b53d641f69ae7907327805582024-03-05 17:52:2523 days 11 hrs ago 0xe5759714998e8b50a33c7333c04c2d02e5dce77f 0xf557b2b73b872e6d2f43826f9d77b7402a363bc02.328279342550093112 xDAI
0x27bbbf0bce0d1c41d5c49b0c1a145833c5cbfc4bdce14085b53d641f69ae7907327805582024-03-05 17:52:2523 days 11 hrs ago Honeyswap: Uniswap V2 Router 2 0xf557b2b73b872e6d2f43826f9d77b7402a363bc029.882843795395363218 xDAI
0x27bbbf0bce0d1c41d5c49b0c1a145833c5cbfc4bdce14085b53d641f69ae7907327805582024-03-05 17:52:2523 days 11 hrs ago SushiSwap: Router 0xf557b2b73b872e6d2f43826f9d77b7402a363bc0128.803307670261810317 xDAI
0x27bbbf0bce0d1c41d5c49b0c1a145833c5cbfc4bdce14085b53d641f69ae7907327805582024-03-05 17:52:2523 days 11 hrs ago 0xd88be9b86f537796f530d084fdd30c0773d32905 0xf557b2b73b872e6d2f43826f9d77b7402a363bc02.532522587461854835 xDAI
0x27bbbf0bce0d1c41d5c49b0c1a145833c5cbfc4bdce14085b53d641f69ae7907327805582024-03-05 17:52:2523 days 11 hrs ago Honeyswap: Uniswap V2 Router 2 0xf557b2b73b872e6d2f43826f9d77b7402a363bc092.751312182648578212 xDAI
0x27bbbf0bce0d1c41d5c49b0c1a145833c5cbfc4bdce14085b53d641f69ae7907327805582024-03-05 17:52:2523 days 11 hrs ago Honeyswap: Uniswap V2 Router 2 0xf557b2b73b872e6d2f43826f9d77b7402a363bc011.907221703907817396 xDAI
0x27bbbf0bce0d1c41d5c49b0c1a145833c5cbfc4bdce14085b53d641f69ae7907327805582024-03-05 17:52:2523 days 11 hrs ago Honeyswap: Uniswap V2 Router 2 0xf557b2b73b872e6d2f43826f9d77b7402a363bc0355.218126719994490327 xDAI
0x3828005f413e173d2268b10291b38486915ee49a7d06e138dd15b54bec3e5a4b327658402024-03-04 20:45:0524 days 9 hrs ago 0xf557b2b73b872e6d2f43826f9d77b7402a363bc00x6fde7365bb2c917f8fa5abf9b0bda079b264304d287.051591798000130922 xDAI
0x3828005f413e173d2268b10291b38486915ee49a7d06e138dd15b54bec3e5a4b327658402024-03-04 20:45:0524 days 9 hrs ago 0xf557b2b73b872e6d2f43826f9d77b7402a363bc00x5bac8c443ebd0525364dc813005ab72956d3705d0.863746013434303302 xDAI
0x3828005f413e173d2268b10291b38486915ee49a7d06e138dd15b54bec3e5a4b327658402024-03-04 20:45:0524 days 9 hrs ago 0xd88be9b86f537796f530d084fdd30c0773d32905 0xf557b2b73b872e6d2f43826f9d77b7402a363bc00.751068959748186129 xDAI
0x3828005f413e173d2268b10291b38486915ee49a7d06e138dd15b54bec3e5a4b327658402024-03-04 20:45:0524 days 9 hrs ago Honeyswap: Uniswap V2 Router 2 0xf557b2b73b872e6d2f43826f9d77b7402a363bc01.226783134586225846 xDAI
0x3828005f413e173d2268b10291b38486915ee49a7d06e138dd15b54bec3e5a4b327658402024-03-04 20:45:0524 days 9 hrs ago 0xe5759714998e8b50a33c7333c04c2d02e5dce77f 0xf557b2b73b872e6d2f43826f9d77b7402a363bc01.062558092315228116 xDAI
0x3828005f413e173d2268b10291b38486915ee49a7d06e138dd15b54bec3e5a4b327658402024-03-04 20:45:0524 days 9 hrs ago Honeyswap: Uniswap V2 Router 2 0xf557b2b73b872e6d2f43826f9d77b7402a363bc013.339630453804578127 xDAI
0x3828005f413e173d2268b10291b38486915ee49a7d06e138dd15b54bec3e5a4b327658402024-03-04 20:45:0524 days 9 hrs ago SushiSwap: Router 0xf557b2b73b872e6d2f43826f9d77b7402a363bc058.638806037290880923 xDAI
0x3828005f413e173d2268b10291b38486915ee49a7d06e138dd15b54bec3e5a4b327658402024-03-04 20:45:0524 days 9 hrs ago 0xd88be9b86f537796f530d084fdd30c0773d32905 0xf557b2b73b872e6d2f43826f9d77b7402a363bc04.101203270255499192 xDAI
0x3828005f413e173d2268b10291b38486915ee49a7d06e138dd15b54bec3e5a4b327658402024-03-04 20:45:0524 days 9 hrs ago Honeyswap: Uniswap V2 Router 2 0xf557b2b73b872e6d2f43826f9d77b7402a363bc042.047211172063900316 xDAI
0x3828005f413e173d2268b10291b38486915ee49a7d06e138dd15b54bec3e5a4b327658402024-03-04 20:45:0524 days 9 hrs ago Honeyswap: Uniswap V2 Router 2 0xf557b2b73b872e6d2f43826f9d77b7402a363bc05.436033829141326956 xDAI
0x3828005f413e173d2268b10291b38486915ee49a7d06e138dd15b54bec3e5a4b327658402024-03-04 20:45:0524 days 9 hrs ago Honeyswap: Uniswap V2 Router 2 0xf557b2b73b872e6d2f43826f9d77b7402a363bc0161.312042862228608619 xDAI
0xd564fdd361832dd3bc2a25690c290f03691443a58d31382bcd5e5faf93fc32c5325928682024-02-23 10:04:2034 days 19 hrs ago 0xf557b2b73b872e6d2f43826f9d77b7402a363bc00xfb70a9f9d1e1084d531a8f15d4c92cd855b752f740.399939270946287897 xDAI
0xd564fdd361832dd3bc2a25690c290f03691443a58d31382bcd5e5faf93fc32c5325928682024-02-23 10:04:2034 days 19 hrs ago 0xf557b2b73b872e6d2f43826f9d77b7402a363bc00x5bac8c443ebd0525364dc813005ab72956d3705d0.121564511346879502 xDAI
0xd564fdd361832dd3bc2a25690c290f03691443a58d31382bcd5e5faf93fc32c5325928682024-02-23 10:04:2034 days 19 hrs ago 0xd88be9b86f537796f530d084fdd30c0773d32905 0xf557b2b73b872e6d2f43826f9d77b7402a363bc00.108958686596651895 xDAI
[ Download CSV Export 
Index Block
Loading

Similar Match Source Code
This contract matches the deployed Bytecode of the Source Code for Contract 0xa6c090c5572f54d529b0839b8fd2d50a4afb1e6b
The constructor portion of the code might be different and could alter the actual behaviour of the contract

Contract Name:
ClusterTokenV2

Compiler Version
v0.8.6+commit.11564f7e

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion

Contract Source Code (Solidity)

/**
 *Submitted for verification at gnosisscan.io on 2022-08-05
*/

/**
 *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

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

Deployed Bytecode

0x6080604052600436106103795760003560e01c806380ea3de1116101d1578063b0741f3a11610102578063d031b0be116100a0578063f0d23e8f1161006f578063f0d23e8f14610a53578063f394397014610a73578063f48f30ac14610a95578063f65baefa14610ac257600080fd5b8063d031b0be146109b6578063d547741f146109cd578063dd62ed3e146109ed578063ebe3391314610a3357600080fd5b8063c0ba241b116100dc578063c0ba241b1461093f578063c8e75c5914610961578063cea0252e14610981578063cfb550f1146109a157600080fd5b8063b0741f3a146108f6578063b63ad23e14610916578063bd5a14ee1461092c57600080fd5b8063953a6b971161016f5780639bb1bebb116101495780639bb1bebb14610881578063a217fddf146108a1578063a457c2d7146108b6578063a9059cbb146108d657600080fd5b8063953a6b971461083357806395d89b411461085357806397f641ab1461086857600080fd5b80638b41d35f116101ab5780638b41d35f146107af5780638e98a220146107c657806390f5fe78146107e657806391d148541461081357600080fd5b806380ea3de11461074d5780638456cb591461076d5780638973fcc81461078257600080fd5b8063313ce567116102ab57806365c6208d1161024957806370a082311161022357806370a082311461069f578063731b383c146106d55780637bba46d0146106f55780637eea34001461071557600080fd5b806365c6208d1461064a5780636605bfda1461065f5780636c91fefa1461067f57600080fd5b8063395093511161028557806339509351146105dd5780633f4ba83a146105fd5780635c975abb1461061257806360b990671461062a57600080fd5b8063313ce5671461058b57806336568abe146105a757806336aaa5fd146105c757600080fd5b806318160ddd11610318578063248a9ca3116102f2578063248a9ca3146104ff5780632d2e86771461052f5780632f2ff15d1461054f5780632f3422ce1461056f57600080fd5b806318160ddd1461049c5780631c29078c146104b157806323b872dd146104df57600080fd5b806304646a491161035457806304646a491461042257806306fdde0314610438578063095ea7b31461045a57806317c424c21461047a57600080fd5b806270fd991461038557806301ffc9a7146103b057806302a882e6146103e057600080fd5b3661038057005b600080fd5b34801561039157600080fd5b5061039a610ae4565b6040516103a791906142d5565b60405180910390f35b3480156103bc57600080fd5b506103d06103cb3660046140f3565b610b3c565b60405190151581526020016103a7565b3480156103ec57600080fd5b506104147fdbeb657137b1822b3d5418bea6fd641226d964b4c3871ef23546db262225887181565b6040519081526020016103a7565b34801561042e57600080fd5b50610414600b5481565b34801561044457600080fd5b5061044d610b73565b6040516103a7919061433b565b34801561046657600080fd5b506103d0610475366004613f7e565b610bfc565b34801561048657600080fd5b5061049a6104953660046140b5565b610c12565b005b3480156104a857600080fd5b50600254610414565b3480156104bd57600080fd5b506104d16104cc366004613faa565b610d00565b6040516103a7929190614316565b3480156104eb57600080fd5b506103d06104fa366004613f3d565b611183565b34801561050b57600080fd5b5061041461051a3660046140b5565b60009081526006602052604090206001015490565b34801561053b57600080fd5b5061041461054a3660046140b5565b61122f565b34801561055b57600080fd5b5061049a61056a3660046140ce565b611250565b34801561057b57600080fd5b50610414670de0b6b3a764000081565b34801561059757600080fd5b50604051601281526020016103a7565b3480156105b357600080fd5b5061049a6105c23660046140ce565b61127b565b3480156105d357600080fd5b50610414600a5481565b3480156105e957600080fd5b506103d06105f8366004613f7e565b6112f9565b34801561060957600080fd5b5061049a611335565b34801561061e57600080fd5b5060055460ff166103d0565b34801561063657600080fd5b5061049a610645366004613f3d565b61134c565b34801561065657600080fd5b50610414600281565b34801561066b57600080fd5b5061049a61067a366004613eca565b611592565b34801561068b57600080fd5b5061049a61069a3660046140b5565b61160b565b3480156106ab57600080fd5b506104146106ba366004613eca565b6001600160a01b031660009081526020819052604090205490565b3480156106e157600080fd5b5061049a6106f03660046140b5565b611716565b34801561070157600080fd5b5061049a6107103660046140b5565b61194c565b34801561072157600080fd5b50600754610735906001600160a01b031681565b6040516001600160a01b0390911681526020016103a7565b34801561075957600080fd5b5061049a6107683660046140b5565b611a47565b34801561077957600080fd5b5061049a611aa1565b34801561078e57600080fd5b5061041461079d366004613eca565b60106020526000908152604090205481565b3480156107bb57600080fd5b506104146202a30081565b3480156107d257600080fd5b5061049a6107e1366004613eca565b611ab5565b3480156107f257600080fd5b50610414610801366004613eca565b60116020526000908152604090205481565b34801561081f57600080fd5b506103d061082e3660046140ce565b611b82565b34801561083f57600080fd5b5061049a61084e366004614053565b611bad565b34801561085f57600080fd5b5061044d611cb9565b34801561087457600080fd5b5061041464e8d4a5100081565b34801561088d57600080fd5b5061039a61089c3660046140b5565b611cc8565b3480156108ad57600080fd5b50610414600081565b3480156108c257600080fd5b506103d06108d1366004613f7e565b611e75565b3480156108e257600080fd5b506103d06108f1366004613f7e565b611f0e565b34801561090257600080fd5b50600954610735906001600160a01b031681565b34801561092257600080fd5b50610414600e5481565b61041461093a366004614136565b611f1b565b34801561094b57600080fd5b506104146000805160206146b883398151915281565b34801561096d57600080fd5b5061049a61097c366004613f7e565b61214b565b34801561098d57600080fd5b5061073561099c3660046140b5565b6122f4565b3480156109ad57600080fd5b5061049a61231e565b3480156109c257600080fd5b50610414620f424081565b3480156109d957600080fd5b5061049a6109e83660046140ce565b612334565b3480156109f957600080fd5b50610414610a08366004613f04565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b348015610a3f57600080fd5b5061049a610a4e366004614136565b61235a565b348015610a5f57600080fd5b50610414610a6e366004613eca565b61259f565b348015610a7f57600080fd5b506104146000805160206146d883398151915281565b348015610aa157600080fd5b50610414610ab0366004613eca565b600f6020526000908152604090205481565b348015610ace57600080fd5b50610ad7612632565b6040516103a79190614288565b6060600d805480602002602001604051908101604052809291908181526020018280548015610b3257602002820191906000526020600020905b815481526020019060010190808311610b1e575b5050505050905090565b60006001600160e01b03198216637965db0b60e01b1480610b6d57506301ffc9a760e01b6001600160e01b03198316145b92915050565b606060038054610b82906145e6565b80601f0160208091040260200160405190810160405280929190818152602001828054610bae906145e6565b8015610b325780601f10610bd057610100808354040283529160200191610b32565b820191906000526020600020905b815481529060010190602001808311610bde57509395945050505050565b6000610c09338484612693565b50600192915050565b7fdbeb657137b1822b3d5418bea6fd641226d964b4c3871ef23546db2622258871610c3d81336127af565b6000610c4883611cc8565b905060005b8151811015610cb857610ca6335b30848481518110610c6e57610c6e614668565b6020026020010151600c8581548110610c8957610c89614668565b6000918252602090912001546001600160a01b0316929190612813565b80610cb081614621565b915050610c4d565b50610cc33384612884565b604051838152339030907fdf882fbcb3ac2f616d4bab2db31dcedd61dd178b733bdaadc75d70525b2a1598906020015b60405180910390a3505050565b6060806000805160206146b8833981519152610d1c81336127af565b600d54845114610d615760405162461bcd60e51b815260206004820152600b60248201526a57726f6e6720617272617960a81b60448201526064015b60405180910390fd5b600c5467ffffffffffffffff811115610d7c57610d7c61467e565b604051908082528060200260200182016040528015610da5578160200160208202803683370190505b50600c5490935067ffffffffffffffff811115610dc457610dc461467e565b604051908082528060200260200182016040528015610ded578160200160208202803683370190505b50600e549092506000610e0d82610e0360025490565b61089c9190614569565b8651909150610e2390600d906020890190613d98565b506000610e3383610e0360025490565b90506000610e4084611cc8565b905060005b600c548110156110f1576000600c8281548110610e6457610e64614668565b60009182526020808320909101546001600160a01b0316808352600f909152604090912054845191925090849084908110610ea157610ea1614668565b60200260200101511115610f11576001600160a01b0381166000908152600f60205260409020548351849084908110610edc57610edc614668565b6020026020010151610eee9190614569565b898381518110610f0057610f00614668565b602002602001018181525050610f7a565b828281518110610f2357610f23614668565b6020026020010151600f6000836001600160a01b03166001600160a01b0316815260200190815260200160002054610f5b9190614569565b888381518110610f6d57610f6d614668565b6020026020010181815250505b828281518110610f8c57610f8c614668565b6020026020010151600f6000836001600160a01b03166001600160a01b0316815260200190815260200160002081905550838281518110610fcf57610fcf614668565b6020026020010151858381518110610fe957610fe9614668565b6020026020010151111561104f5761104a3385848151811061100d5761100d614668565b602002602001015187858151811061102757611027614668565b60200260200101516110399190614569565b6001600160a01b038416919061296f565b6110de565b83828151811061106157611061614668565b602002602001015185838151811061107b5761107b614668565b602002602001015110156110de576110de33308785815181106110a0576110a0614668565b60200260200101518786815181106110ba576110ba614668565b60200260200101516110cc9190614569565b6001600160a01b038516929190612813565b50806110e981614621565b915050610e45565b5060005b600c5481101561113c5788818151811061111157611111614668565b60200260200101516000141561112a5761112a8161299f565b8061113481614621565b9150506110f5565b50604051339030907f23430ac6054603925f82afae7cc6252190a47d0e5fa6b4ac67790c3b3e292eb890611171908c906142d5565b60405180910390a35050505050915091565b6000611190848484612ad1565b6001600160a01b0384166000908152600160209081526040808320338452909152902054828110156112155760405162461bcd60e51b815260206004820152602860248201527f45524332303a207472616e7366657220616d6f756e74206578636565647320616044820152676c6c6f77616e636560c01b6064820152608401610d58565b6112228533858403612693565b60019150505b9392505050565b600d818154811061123f57600080fd5b600091825260209091200154905081565b60008281526006602052604090206001015461126c81336127af565b6112768383612caa565b505050565b6001600160a01b03811633146112eb5760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b6064820152608401610d58565b6112f58282612d30565b5050565b3360008181526001602090815260408083206001600160a01b03871684529091528120549091610c09918590611330908690614422565b612693565b600061134181336127af565b611349612d97565b50565b6000805160206146b883398151915261136581336127af565b8260005b600c548110156113fb57600c818154811061138657611386614668565b6000918252602090912001546001600160a01b03838116911614156113e95760405162461bcd60e51b8152602060048201526019602482015278556e6465726c79696e67206973206e6f7420756e697175652160381b6044820152606401610d58565b806113f381614621565b915050611369565b5060005b600c548110156114b657856001600160a01b0316600c828154811061142657611426614668565b6000918252602090912001546001600160a01b031614156114a45784600c828154811061145557611455614668565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555083600d828154811061149757611497614668565b6000918252602090912001555b806114ae81614621565b9150506113ff565b50611543336040516370a0823160e01b81523060048201526001600160a01b038816906370a082319060240160206040518083038186803b1580156114fa57600080fd5b505afa15801561150e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611532919061411d565b6001600160a01b038816919061296f565b6000620f424084600e54611557919061454a565b611561919061443a565b6001600160a01b039586166000908152600f6020526040808220929092559690951686525050509082209190915550565b600061159e81336127af565b6001600160a01b0382166115e85760405162461bcd60e51b8152602060048201526011602482015270496e636f7272656374206164647265737360781b6044820152606401610d58565b50600880546001600160a01b0319166001600160a01b0392909216919091179055565b6000805160206146d883398151915261162481336127af565b600061162f83611cc8565b905082600e60008282546116439190614569565b90915550600090505b600c548110156116dc5781818151811061166857611668614668565b6020026020010151600f6000600c848154811061168757611687614668565b60009182526020808320909101546001600160a01b03168352820192909252604001812080549091906116bb908490614569565b909155506116ca905033610c5b565b806116d481614621565b91505061164c565b50604051339030907fded8512602a42e2b340fdd5b806b1069a040da129f148c598f54cbe1eb64576190610cf39087908690600c90614398565b6000805160206146d883398151915261172f81336127af565b81600e5461173d9190614422565b306370a08231336040516001600160e01b031960e084901b1681526001600160a01b03909116600482015260240160206040518083038186803b15801561178357600080fd5b505afa158015611797573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117bb919061411d565b10156118155760405162461bcd60e51b8152602060048201526024808201527f4661726d657220686173206e6f7420656e6f75676820636c757374657220746f6044820152636b656e7360e01b6064820152608401610d58565b600061182083611cc8565b905082600e60008282546118349190614422565b90915550600090505b600c548110156119125781818151811061185957611859614668565b6020026020010151600f6000600c848154811061187857611878614668565b60009182526020808320909101546001600160a01b03168352820192909252604001812080549091906118ac908490614422565b909155506119009050335b8383815181106118c9576118c9614668565b6020026020010151600c84815481106118e4576118e4614668565b6000918252602090912001546001600160a01b0316919061296f565b8061190a81614621565b91505061183d565b50604051339030907fc72d630122c8ed4fc97918baa82670432c4d36f10149924b98a8c410ad6b0a2b90610cf39087908690600c90614398565b6119646000805160206146d883398151915233611b82565b156119a95760405162461bcd60e51b815260206004820152601560248201527411985c9b595c881a5cc81b9bdd08185b1b1bddd959605a1b6044820152606401610d58565b7fdbeb657137b1822b3d5418bea6fd641226d964b4c3871ef23546db26222588716119d481336127af565b60006119df83611cc8565b90506119eb3384612e2a565b60005b8151811015611a1257611a00336118b7565b80611a0a81614621565b9150506119ee565b50604051838152339030907f0e519abb97b349157786738cedce344e2eabcd59b8b8c4990b1f4d0c035d9a3090602001610cf3565b6000611a5381336127af565b6202a300821115611a9b5760405162461bcd60e51b815260206004820152601260248201527124b731b7b93932b1ba1031b7b7b63237bbb760711b6044820152606401610d58565b50600b55565b6000611aad81336127af565b611349612f84565b6007546001600160a01b0316336001600160a01b031614611b0a5760405162461bcd60e51b815260206004820152600f60248201526e436f6e74726f6c6c6572206f6e6c7960881b6044820152606401610d58565b6001600160a01b038116611b605760405162461bcd60e51b815260206004820152601c60248201527f6465763a20436f6e74726f6c6c6572207a65726f2061646472657373000000006044820152606401610d58565b600780546001600160a01b0319166001600160a01b0392909216919091179055565b60009182526006602090815260408084206001600160a01b0393909316845291905290205460ff1690565b6000805160206146d8833981519152611bc681336127af565b81600e6000828254611bd89190614569565b90915550600090505b600c54811015611c7f57838181518110611bfd57611bfd614668565b6020026020010151600f6000600c8481548110611c1c57611c1c614668565b60009182526020808320909101546001600160a01b0316835282019290925260400181208054909190611c50908490614569565b90915550611c6d90503330868481518110610c6e57610c6e614668565b80611c7781614621565b915050611be1565b50604051339030907fded8512602a42e2b340fdd5b806b1069a040da129f148c598f54cbe1eb64576190610cf39086908890600c90614398565b606060048054610b82906145e6565b600c5460609060009067ffffffffffffffff811115611ce957611ce961467e565b604051908082528060200260200182016040528015611d12578160200160208202803683370190505b50905060005b600c54811015611e6e57620f4240600d8281548110611d3957611d39614668565b906000526020600020015485611d4f919061454a565b611d59919061443a565b828281518110611d6b57611d6b614668565b6020026020010181815250506000600c8281548110611d8c57611d8c614668565b600091825260209182902001546040805163313ce56760e01b815290516001600160a01b039092169263313ce56792600480840193829003018186803b158015611dd557600080fd5b505afa158015611de9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e0d919061415b565b905060128160ff161015611e5b57611e26816012614580565b611e3190600a61449f565b838381518110611e4357611e43614668565b60200260200101818151611e57919061443a565b9052505b5080611e6681614621565b915050611d18565b5092915050565b3360009081526001602090815260408083206001600160a01b038616845290915281205482811015611ef75760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b6064820152608401610d58565b611f043385858403612693565b5060019392505050565b6000610c09338484612ad1565b6000611f2960055460ff1690565b15611f465760405162461bcd60e51b8152600401610d589061436e565b611f5a335b6001600160a01b03163b151590565b15611fa35760405162461bcd60e51b81526020600482015260196024820152784e6f7420616c6c6f77656420666f7220636f6e74726163747360381b6044820152606401610d58565b47611fae3485612fdc565b476000611fbb8284614569565b600754604051621d5f4960e41b8152306004820152602481018390529192506000916001600160a01b03909116906301d5f4909060440160206040518083038186803b15801561200a57600080fd5b505afa15801561201e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612042919061411d565b905060006120508288613416565b61205a8434614569565b6120649190614569565b905064e8d4a5100081111561207e5761207e335b8261350c565b600b54156120f857336000908152601060205260409020544210156120c75733600090815260116020526040812080548a92906120bc908490614422565b909155506120da9050565b3360009081526011602052604090208890555b600b546120e79042614422565b336000908152601060205260409020555b6121023389612884565b60408051848152602081018a9052339130917f154f5ab3f8529ffdbb246fd1a63a2cce74c42c1b45bd6bbfad82bef2272be9c5910160405180910390a350959695505050505050565b6000805160206146b883398151915261216481336127af565b8260005b600c548110156121fa57600c818154811061218557612185614668565b6000918252602090912001546001600160a01b03838116911614156121e85760405162461bcd60e51b8152602060048201526019602482015278556e6465726c79696e67206973206e6f7420756e697175652160381b6044820152606401610d58565b806121f281614621565b915050612168565b50600c805460018082019092557fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c70180546001600160a01b0319166001600160a01b038716179055600d8054918201815560009081527fd7b6990105719101dabeb77144f2a3385c8033acd3af97e9423a695e81ad1eb5909101849055600e54600254620f424091869161228e9190614569565b612298919061454a565b6122a2919061443a565b90506000620f424085600e546122b8919061454a565b6122c2919061443a565b6001600160a01b0387166000818152600f602052604090208290559091506122ec90333085612813565b505050505050565b600c818154811061230457600080fd5b6000918252602090912001546001600160a01b0316905081565b600061232a81336127af565b611349334761350c565b60008281526006602052604090206001015461235081336127af565b6112768383612d30565b60055460ff161561237d5760405162461bcd60e51b8152600401610d589061436e565b6123956000805160206146d883398151915233611b82565b156123da5760405162461bcd60e51b815260206004820152601560248201527411985c9b595c881a5cc81b9bdd08185b1b1bddd959605a1b6044820152606401610d58565b6123e333611f4b565b1561242c5760405162461bcd60e51b81526020600482015260196024820152784e6f7420616c6c6f77656420666f7220636f6e74726163747360381b6044820152606401610d58565b6000821180156124445750612440336106ba565b8211155b6124855760405162461bcd60e51b81526020600482015260126024820152712737ba1032b737bab3b41031b63ab9ba32b960711b6044820152606401610d58565b61248f3383612e2a565b600061249a83611cc8565b9050476124a682613625565b4760006124b38383614569565b60075460405163263dd1c960e11b8152306004820152602481018390529192506000916001600160a01b0390911690634c7ba3929060440160206040518083038186803b15801561250357600080fd5b505afa158015612517573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061253b919061411d565b905060006125498288613416565b6125539084614569565b905061255e33612078565b604051888152339030907f2ced335a9d5d52c0e402f0cd99d473593830e0b3c1315c50a05c86c4afbd555f9060200160405180910390a35050505050505050565b6001600160a01b0381166000818152600f60205260408082205490516370a0823160e01b8152306004820152919290916370a082319060240160206040518083038186803b1580156125f057600080fd5b505afa158015612604573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612628919061411d565b610b6d9190614422565b6060600c805480602002602001604051908101604052809291908181526020018280548015610b3257602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831161266c575050505050905090565b6001600160a01b0383166126f55760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b6064820152608401610d58565b6001600160a01b0382166127565760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b6064820152608401610d58565b6001600160a01b0383811660008181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259101610cf3565b6127b98282611b82565b6112f5576127d1816001600160a01b03166014613785565b6127dc836020613785565b6040516020016127ed929190614213565b60408051601f198184030181529082905262461bcd60e51b8252610d589160040161433b565b6040516001600160a01b038085166024830152831660448201526064810182905261287e9085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152613921565b50505050565b6001600160a01b0382166128da5760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152606401610d58565b6128e6600083836139f3565b80600260008282546128f89190614422565b90915550506001600160a01b03821660009081526020819052604081208054839290612925908490614422565b90915550506040518181526001600160a01b038316906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a35050565b6040516001600160a01b03831660248201526044810182905261127690849063a9059cbb60e01b90606401612847565b600c546129ae90600190614569565b8114612a7457600c80546129c490600190614569565b815481106129d4576129d4614668565b600091825260209091200154600c80546001600160a01b039092169183908110612a0057612a00614668565b600091825260209091200180546001600160a01b0319166001600160a01b0392909216919091179055600d8054612a3990600190614569565b81548110612a4957612a49614668565b9060005260206000200154600d8281548110612a6757612a67614668565b6000918252602090912001555b600c805480612a8557612a85614652565b600082815260209020810160001990810180546001600160a01b0319169055019055600d805480612ab857612ab8614652565b6001900381819060005260206000200160009055905550565b6001600160a01b038316612b355760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b6064820152608401610d58565b6001600160a01b038216612b975760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b6064820152608401610d58565b612ba28383836139f3565b6001600160a01b03831660009081526020819052604090205481811015612c1a5760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b6064820152608401610d58565b6001600160a01b03808516600090815260208190526040808220858503905591851681529081208054849290612c51908490614422565b92505081905550826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051612c9d91815260200190565b60405180910390a361287e565b612cb48282611b82565b6112f55760008281526006602090815260408083206001600160a01b03851684529091529020805460ff19166001179055612cec3390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b612d3a8282611b82565b156112f55760008281526006602090815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b60055460ff16612de05760405162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b6044820152606401610d58565b6005805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b6001600160a01b038216612e8a5760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b6064820152608401610d58565b612e96826000836139f3565b6001600160a01b03821660009081526020819052604090205481811015612f0a5760405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604482015261636560f01b6064820152608401610d58565b6001600160a01b0383166000908152602081905260408120838303905560028054849290612f39908490614569565b90915550506040518281526000906001600160a01b038516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a3505050565b60055460ff1615612fa75760405162461bcd60e51b8152600401610d589061436e565b6005805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258612e0d3390565b60075460405163395c47ff60e21b81523060048201526000916001600160a01b03169063e5711ffc9060240160206040518083038186803b15801561302057600080fd5b505afa158015613034573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130589190613ee7565b6007546040516360db72ef60e01b815230600482015260248101859052919250600091829182916001600160a01b03909116906360db72ef9060440160006040518083038186803b1580156130ac57600080fd5b505afa1580156130c0573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526130e89190810190613fdf565b93505092509250808610156131375760405162461bcd60e51b8152602060048201526015602482015274139bdd08195b9bdd59da08195d1a195c881cd95b9d605a1b6044820152606401610d58565b60005b600c5481101561340d5783818151811061315657613156614668565b6020026020010151600c828154811061317157613171614668565b6000918252602090912001546008546040516370a0823160e01b81526001600160a01b0391821660048201529116906370a082319060240160206040518083038186803b1580156131c157600080fd5b505afa1580156131d5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131f9919061411d565b101580156132c0575083818151811061321457613214614668565b6020026020010151600c828154811061322f5761322f614668565b600091825260209091200154600854604051636eb1769f60e11b81526001600160a01b03918216600482015230602482015291169063dd62ed3e9060440160206040518083038186803b15801561328557600080fd5b505afa158015613299573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132bd919061411d565b10155b156133235760085484516132ec916001600160a01b0316903090879085908110610c6e57610c6e614668565b600854835161331e916001600160a01b03169085908490811061331157613311614668565b602002602001015161350c565b6133fb565b846001600160a01b03166337f324c984838151811061334457613344614668565b6020026020010151600c848154811061335f5761335f614668565b9060005260206000200160009054906101000a90046001600160a01b031687858151811061338f5761338f614668565b60200260200101516040518463ffffffff1660e01b81526004016133c89291906001600160a01b03929092168252602082015260400190565b6000604051808303818588803b1580156133e157600080fd5b505af11580156133f5573d6000803e3d6000fd5b50505050505b8061340581614621565b91505061313a565b50505050505050565b600082156135035781156134415760085461343a906001600160a01b03168461350c565b5081610b6d565b600754604051632727985160e11b81523060048201526000916001600160a01b031690634e4f30a29060240160206040518083038186803b15801561348557600080fd5b505afa158015613499573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134bd919061411d565b6134cf85670de0b6b3a764000061454a565b6134d9919061443a565b90506134f9336008546009546001600160a01b0390811692911684612813565b6000915050610b6d565b50600092915050565b8047101561355c5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e63650000006044820152606401610d58565b6000826001600160a01b03168260405160006040518083038185875af1925050503d80600081146135a9576040519150601f19603f3d011682016040523d82523d6000602084013e6135ae565b606091505b50509050806112765760405162461bcd60e51b815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d617920686176652072657665727465640000000000006064820152608401610d58565b60075460405163395c47ff60e21b81523060048201526000916001600160a01b03169063e5711ffc9060240160206040518083038186803b15801561366957600080fd5b505afa15801561367d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136a19190613ee7565b905060005b8251811015613727576136e4826000600c84815481106136c8576136c8614668565b6000918252602090912001546001600160a01b03169190613afc565b613715828483815181106136fa576136fa614668565b6020026020010151600c84815481106136c8576136c8614668565b8061371f81614621565b9150506136a6565b50604051632457ff7360e01b81526001600160a01b03821690632457ff7390613757908590600c906004016142e8565b600060405180830381600087803b15801561377157600080fd5b505af11580156122ec573d6000803e3d6000fd5b6060600061379483600261454a565b61379f906002614422565b67ffffffffffffffff8111156137b7576137b761467e565b6040519080825280601f01601f1916602001820160405280156137e1576020820181803683370190505b509050600360fc1b816000815181106137fc576137fc614668565b60200101906001600160f81b031916908160001a905350600f60fb1b8160018151811061382b5761382b614668565b60200101906001600160f81b031916908160001a905350600061384f84600261454a565b61385a906001614422565b90505b60018111156138d2576f181899199a1a9b1b9c1cb0b131b232b360811b85600f166010811061388e5761388e614668565b1a60f81b8282815181106138a4576138a4614668565b60200101906001600160f81b031916908160001a90535060049490941c936138cb816145cf565b905061385d565b5083156112285760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610d58565b6000613976826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316613c209092919063ffffffff16565b80519091501561127657808060200190518101906139949190614098565b6112765760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610d58565b6001600160a01b03831660009081526010602052604090205483908290421015613af5576001600160a01b0382166000818152601160205260408082205490516370a0823160e01b81526004810193909352909130906370a082319060240160206040518083038186803b158015613a6a57600080fd5b505afa158015613a7e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613aa2919061411d565b613aac9190614569565b9050818110156122ec5760405162461bcd60e51b8152602060048201526014602482015273436f6f6c646f776e20696e2070726f677265737360601b6044820152606401610d58565b5050505050565b801580613b855750604051636eb1769f60e11b81523060048201526001600160a01b03838116602483015284169063dd62ed3e9060440160206040518083038186803b158015613b4b57600080fd5b505afa158015613b5f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b83919061411d565b155b613bf05760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b6064820152608401610d58565b6040516001600160a01b03831660248201526044810182905261127690849063095ea7b360e01b90606401612847565b6060613c2f8484600085613c37565b949350505050565b606082471015613c985760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610d58565b843b613ce65760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610d58565b600080866001600160a01b03168587604051613d0291906141f7565b60006040518083038185875af1925050503d8060008114613d3f576040519150601f19603f3d011682016040523d82523d6000602084013e613d44565b606091505b5091509150613d54828286613d5f565b979650505050505050565b60608315613d6e575081611228565b825115613d7e5782518084602001fd5b8160405162461bcd60e51b8152600401610d58919061433b565b828054828255906000526020600020908101928215613dd3579160200282015b82811115613dd3578251825591602001919060010190613db8565b50613ddf929150613de3565b5090565b5b80821115613ddf5760008155600101613de4565b600082601f830112613e0957600080fd5b81356020613e1e613e19836143fe565b6143cd565b80838252828201915082860187848660051b8901011115613e3e57600080fd5b60005b85811015613e5d57813584529284019290840190600101613e41565b5090979650505050505050565b600082601f830112613e7b57600080fd5b81516020613e8b613e19836143fe565b80838252828201915082860187848660051b8901011115613eab57600080fd5b60005b85811015613e5d57815184529284019290840190600101613eae565b600060208284031215613edc57600080fd5b813561122881614694565b600060208284031215613ef957600080fd5b815161122881614694565b60008060408385031215613f1757600080fd5b8235613f2281614694565b91506020830135613f3281614694565b809150509250929050565b600080600060608486031215613f5257600080fd5b8335613f5d81614694565b92506020840135613f6d81614694565b929592945050506040919091013590565b60008060408385031215613f9157600080fd5b8235613f9c81614694565b946020939093013593505050565b600060208284031215613fbc57600080fd5b813567ffffffffffffffff811115613fd357600080fd5b613c2f84828501613df8565b60008060008060808587031215613ff557600080fd5b845167ffffffffffffffff8082111561400d57600080fd5b61401988838901613e6a565b9550602087015191508082111561402f57600080fd5b5061403c87828801613e6a565b604087015160609097015195989097509350505050565b6000806040838503121561406657600080fd5b823567ffffffffffffffff81111561407d57600080fd5b61408985828601613df8565b95602094909401359450505050565b6000602082840312156140aa57600080fd5b8151611228816146a9565b6000602082840312156140c757600080fd5b5035919050565b600080604083850312156140e157600080fd5b823591506020830135613f3281614694565b60006020828403121561410557600080fd5b81356001600160e01b03198116811461122857600080fd5b60006020828403121561412f57600080fd5b5051919050565b6000806040838503121561414957600080fd5b823591506020830135613f32816146a9565b60006020828403121561416d57600080fd5b815160ff8116811461122857600080fd5b6000815480845260208085019450836000528060002060005b838110156141bc5781546001600160a01b031687529582019560019182019101614197565b509495945050505050565b600081518084526020808501945080840160005b838110156141bc578151875295820195908201906001016141db565b600082516142098184602087016145a3565b9190910192915050565b7f416363657373436f6e74726f6c3a206163636f756e742000000000000000000081526000835161424b8160178501602088016145a3565b7001034b99036b4b9b9b4b733903937b6329607d1b601791840191820152835161427c8160288401602088016145a3565b01602801949350505050565b6020808252825182820181905260009190848201906040850190845b818110156142c95783516001600160a01b0316835292840192918401916001016142a4565b50909695505050505050565b60208152600061122860208301846141c7565b6040815260006142fb60408301856141c7565b828103602084015261430d818561417e565b95945050505050565b60408152600061432960408301856141c7565b828103602084015261430d81856141c7565b602081526000825180602084015261435a8160408501602087016145a3565b601f01601f19169190910160400192915050565b60208082526010908201526f14185d5cd8589b194e881c185d5cd95960821b604082015260600190565b8381526060602082015260006143b160608301856141c7565b82810360408401526143c3818561417e565b9695505050505050565b604051601f8201601f1916810167ffffffffffffffff811182821017156143f6576143f661467e565b604052919050565b600067ffffffffffffffff8211156144185761441861467e565b5060051b60200190565b600082198211156144355761443561463c565b500190565b60008261445757634e487b7160e01b600052601260045260246000fd5b500490565b600181815b8085111561449757816000190482111561447d5761447d61463c565b8085161561448a57918102915b93841c9390800290614461565b509250929050565b600061122860ff8416836000826144b857506001610b6d565b816144c557506000610b6d565b81600181146144db57600281146144e557614501565b6001915050610b6d565b60ff8411156144f6576144f661463c565b50506001821b610b6d565b5060208310610133831016604e8410600b8410161715614524575081810a610b6d565b61452e838361445c565b80600019048211156145425761454261463c565b029392505050565b60008160001904831182151516156145645761456461463c565b500290565b60008282101561457b5761457b61463c565b500390565b600060ff821660ff84168082101561459a5761459a61463c565b90039392505050565b60005b838110156145be5781810151838201526020016145a6565b8381111561287e5750506000910152565b6000816145de576145de61463c565b506000190190565b600181811c908216806145fa57607f821691505b6020821081141561461b57634e487b7160e01b600052602260045260246000fd5b50919050565b60006000198214156146355761463561463c565b5060010190565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052603160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b038116811461134957600080fd5b801515811461134957600080fdfe1a82baf2b928242f69f7147fb92490c6288d044f7257b88817e6284f1eec0f157c6181838a71a779e445600d4c6ecbe16bacf2b3c5bda69c29fada66d1b645d1a26469706673582212202d99c73cfee95c8043d1b9e3e9130fc181d7259685afbe7ef19d31f796f168d864736f6c63430008060033

Block Transaction Gas Used Reward
Age Block Fee Address BC Fee Address Voting Power Jailed Incoming
Block Uncle Number Difficulty Gas Used Reward
Loading
Loading
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.