GLMR Price: $0.020553 (+0.26%)

Contract

0xe519E164cd1F8D3474289fF4e52C16699EC9ea17

Overview

GLMR Balance

Moonbeam Chain LogoMoonbeam Chain LogoMoonbeam Chain Logo0 GLMR

GLMR Value

$0.00

Token Holdings

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Block
From
To
Set Favourite NF...41715352023-08-08 19:41:18904 days ago1691523678IN
0xe519E164...99EC9ea17
0 GLMR0.01871598187.67027004
Set Favourite NF...41346042023-08-03 15:29:54909 days ago1691076594IN
0xe519E164...99EC9ea17
0 GLMR0.00716324200.51621865
Set Favourite NF...41267292023-08-02 12:52:18910 days ago1690980738IN
0xe519E164...99EC9ea17
0 GLMR0.0067005187.5
Set Favourite NF...41216612023-08-01 19:51:06911 days ago1690919466IN
0xe519E164...99EC9ea17
0 GLMR0.00487102136.3975
Set Favourite NF...41216352023-08-01 19:45:54911 days ago1690919154IN
0xe519E164...99EC9ea17
0 GLMR0.00669825187.5
Set Favourite NF...41141832023-07-31 18:44:42912 days ago1690829082IN
0xe519E164...99EC9ea17
0 GLMR0.00705813197.57411072
Set Favourite NF...41141732023-07-31 18:42:42912 days ago1690828962IN
0xe519E164...99EC9ea17
0 GLMR0.0070686197.86708621
Set Favourite NF...41141282023-07-31 18:33:42912 days ago1690828422IN
0xe519E164...99EC9ea17
0 GLMR0.00721512201.96862139
Set Favourite NF...41138692023-07-31 17:41:12912 days ago1690825272IN
0xe519E164...99EC9ea17
0 GLMR0.0070069196.14004692
Set Favourite NF...41138532023-07-31 17:38:00912 days ago1690825080IN
0xe519E164...99EC9ea17
0 GLMR0.00699679195.85700914
Set Favourite NF...41138422023-07-31 17:35:48912 days ago1690824948IN
0xe519E164...99EC9ea17
0 GLMR0.00695947194.81227347
Set Favourite NF...41138172023-07-31 17:30:51912 days ago1690824651IN
0xe519E164...99EC9ea17
0 GLMR0.01016263192.38665446
Set Favourite NF...41130282023-07-31 14:50:42912 days ago1690815042IN
0xe519E164...99EC9ea17
0 GLMR0.00644426208.47114703
Set Favourite NF...41130012023-07-31 14:45:12912 days ago1690814712IN
0xe519E164...99EC9ea17
0 GLMR0.00742706207.90130354
Set Favourite NF...41128542023-07-31 14:15:36912 days ago1690812936IN
0xe519E164...99EC9ea17
0 GLMR0.00744664208.44939362
Set Favourite NF...41083132023-07-30 23:00:06913 days ago1690758006IN
0xe519E164...99EC9ea17
0 GLMR0.00672289188.18974681
Set Favourite NF...41081852023-07-30 22:34:30913 days ago1690756470IN
0xe519E164...99EC9ea17
0 GLMR0.00727938203.76729601
Set Favourite NF...41080692023-07-30 22:11:12913 days ago1690755072IN
0xe519E164...99EC9ea17
0 GLMR0.00780767218.55543719
Set Favourite NF...41080262023-07-30 22:02:36913 days ago1690754556IN
0xe519E164...99EC9ea17
0 GLMR0.00784368219.56346988
Set Favourite NF...41079842023-07-30 21:54:06913 days ago1690754046IN
0xe519E164...99EC9ea17
0 GLMR0.00782991219.17786725
Set Favourite NF...41079552023-07-30 21:48:18913 days ago1690753698IN
0xe519E164...99EC9ea17
0 GLMR0.01160636219.71758924
Set Favourite NF...41078152023-07-30 21:20:00913 days ago1690752000IN
0xe519E164...99EC9ea17
0 GLMR0.00678725219.56697734
Set Favourite NF...41073242023-07-30 19:40:48913 days ago1690746048IN
0xe519E164...99EC9ea17
0 GLMR0.0053099148.68685309
Set Favourite NF...41002252023-07-29 19:50:18914 days ago1690660218IN
0xe519E164...99EC9ea17
0 GLMR0.004913137.57305094
Set Favourite NF...40940772023-07-28 23:11:48915 days ago1690585908IN
0xe519E164...99EC9ea17
0 GLMR0.01069301202.42725709
View all transactions

View more zero value Internal Transactions in Advanced View mode

Cross-Chain Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Sinergy

Compiler Version
v0.8.7+commit.e28d00a7

Optimization Enabled:
Yes with 1 runs

Other Settings:
default evmVersion, MIT license

Contract Source Code (Solidity)

/**
 *Submitted for verification at moonbeam.moonscan.io on 2023-07-15
*/

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 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);

        (bool success, ) = recipient.call{value: amount}("");
        require(success);
    }

    /**
     * @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
            functionCallWithValue(
                target,
                data,
                0,
                "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"
        );
        (bool success, bytes memory returndata) = target.call{value: value}(
            data
        );
        return
            verifyCallResultFromTarget(
                target,
                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) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return
            verifyCallResultFromTarget(
                target,
                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) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return
            verifyCallResultFromTarget(
                target,
                success,
                returndata,
                errorMessage
            );
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(
        bytes memory returndata,
        string memory errorMessage
    ) private pure {
        // 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
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert(errorMessage);
        }
    }
}

library Strings {
    bytes16 private constant _SYMBOLS = "0123456789abcdef";
    uint8 private constant _ADDRESS_LENGTH = 20;

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        unchecked {
            uint256 length = Math.log10(value) + 1;
            string memory buffer = new string(length);
            uint256 ptr;
            /// @solidity memory-safe-assembly
            assembly {
                ptr := add(buffer, add(32, length))
            }
            while (true) {
                ptr--;
                /// @solidity memory-safe-assembly
                assembly {
                    mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                }
                value /= 10;
                if (value == 0) break;
            }
            return buffer;
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        unchecked {
            return toHexString(value, Math.log256(value) + 1);
        }
    }

    /**
     * @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] = _SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
     */
    function toHexString(address addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
    }
}

library Math {
    enum Rounding {
        Down, // Toward negative infinity
        Up, // Toward infinity
        Zero // Toward zero
    }

    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
     * with further edits by Uniswap Labs also under MIT license.
     */
    function mulDiv(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod0 := mul(x, y)
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1);

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
            // See https://cs.stackexchange.com/q/138556/92363.

            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 twos = denominator & (~denominator + 1);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)

                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;

            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
            // in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(
        uint256 x,
        uint256 y,
        uint256 denominator,
        Rounding rounding
    ) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(
        uint256 a,
        Rounding rounding
    ) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return
                result +
                (rounding == Rounding.Up && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(
        uint256 value,
        Rounding rounding
    ) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return
                result +
                (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10 ** 64) {
                value /= 10 ** 64;
                result += 64;
            }
            if (value >= 10 ** 32) {
                value /= 10 ** 32;
                result += 32;
            }
            if (value >= 10 ** 16) {
                value /= 10 ** 16;
                result += 16;
            }
            if (value >= 10 ** 8) {
                value /= 10 ** 8;
                result += 8;
            }
            if (value >= 10 ** 4) {
                value /= 10 ** 4;
                result += 4;
            }
            if (value >= 10 ** 2) {
                value /= 10 ** 2;
                result += 2;
            }
            if (value >= 10 ** 1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(
        uint256 value,
        Rounding rounding
    ) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return
                result +
                (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256, rounded down, of a positive value.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(
        uint256 value,
        Rounding rounding
    ) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return
                result +
                (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0);
        }
    }
}

library Counters {
    struct Counter {
        // This variable should never be directly accessed by users of the library: interactions must be restricted to
        // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
        // this feature: see https://github.com/ethereum/solidity/issues/4637
        uint256 _value; // default: 0
    }

    function current(Counter storage counter) internal view returns (uint256) {
        return counter._value;
    }

    function increment(Counter storage counter) internal {
        unchecked {
            counter._value += 1;
        }
    }

    function decrement(Counter storage counter) internal {
        uint256 value = counter._value;
        require(value > 0);
        unchecked {
            counter._value = value - 1;
        }
    }

    function reset(Counter storage counter) internal {
        counter._value = 0;
    }
}

abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

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

interface IERC20 {
    function totalSupply() external view returns (uint256);

    function balanceOf(address account) external view returns (uint256);

    function transfer(address to, uint256 amount) external returns (bool);

    function allowance(
        address owner,
        address spender
    ) external view returns (uint256);

    function approve(address spender, uint256 amount) external returns (bool);

    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) external returns (bool);

    event Transfer(address indexed from, address indexed to, uint256 value);

    event Approval(
        address indexed owner,
        address indexed spender,
        uint256 value
    );
}

interface IERC20Metadata is IERC20 {
    function name() external view returns (string memory);

    function symbol() external view returns (string memory);

    function decimals() external view returns (uint8);
}

interface IERC165 {
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

interface IERC721 is IERC165 {
    event Transfer(
        address indexed from,
        address indexed to,
        uint256 indexed tokenId
    );

    event Approval(
        address indexed owner,
        address indexed approved,
        uint256 indexed tokenId
    );

    event ApprovalForAll(
        address indexed owner,
        address indexed operator,
        bool approved
    );

    function balanceOf(address owner) external view returns (uint256 balance);

    function ownerOf(uint256 tokenId) external view returns (address owner);

    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        bytes calldata data
    ) external;

    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

    function transferFrom(address from, address to, uint256 tokenId) external;

    function approve(address to, uint256 tokenId) external;

    function setApprovalForAll(address operator, bool _approved) external;

    function getApproved(
        uint256 tokenId
    ) external view returns (address operator);

    function isApprovedForAll(
        address owner,
        address operator
    ) external view returns (bool);
}

interface IERC721Metadata is IERC721 {
    function name() external view returns (string memory);

    function symbol() external view returns (string memory);

    function tokenURI(uint256 tokenId) external view returns (string memory);
}

interface IERC721Receiver {
    /**
     * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
     * by `operator` from `from`, this function is called.
     *
     * It must return its Solidity selector to confirm the token transfer.
     * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
     *
     * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
     */
    function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes calldata data
    ) external returns (bytes4);
}

abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(
        bytes4 interfaceId
    ) public view virtual override returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}

contract ERC721 is Context, ERC165, IERC721, IERC721Metadata {
    using Address for address;
    using Strings for uint256;

    // Token name
    string private _name;

    // Token symbol
    string private _symbol;

    // Mapping from token ID to owner address
    mapping(uint256 => address) private _owners;

    // Mapping owner address to token count
    mapping(address => uint256) private _balances;

    // Mapping from token ID to approved address
    mapping(uint256 => address) private _tokenApprovals;

    // Mapping from owner to operator approvals
    mapping(address => mapping(address => bool)) private _operatorApprovals;

    // Sinergy NFT
    mapping(address => uint256[]) public get_my_nfts;
    mapping(address => uint256) public favourite_nft;
    uint256 public total_holders;

    /**
     * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    function updateFromAble() public {}

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

    /**
     * @dev See {IERC721-balanceOf}.
     */
    function balanceOf(
        address owner
    ) public view virtual override returns (uint256) {
        require(owner != address(0));
        return _balances[owner];
    }

    /**
     * @dev See {IERC721-ownerOf}.
     */
    function ownerOf(
        uint256 tokenId
    ) public view virtual override returns (address) {
        address owner = _ownerOf(tokenId);
        require(owner != address(0));
        return owner;
    }

    /**
     * @dev See {IERC721Metadata-name}.
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }

    /**
     * @dev See {IERC721Metadata-symbol}.
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @dev See {IERC721Metadata-tokenURI}.
     */
    function tokenURI(
        uint256 tokenId
    ) public view virtual override returns (string memory) {
        _requireMinted(tokenId);

        string memory baseURI = _baseURI();
        return
            bytes(baseURI).length > 0
                ? string(abi.encodePacked(baseURI, tokenId.toString()))
                : "";
    }

    /**
     * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
     * token will be the concatenation of the `baseURI` and the `tokenId`. Empty
     * by default, can be overridden in child contracts.
     */
    function _baseURI() internal view virtual returns (string memory) {
        return "";
    }

    /**
     * @dev See {IERC721-approve}.
     */
    function approve(address to, uint256 tokenId) public virtual override {
        address owner = ERC721.ownerOf(tokenId);
        require(to != owner);

        require(_msgSender() == owner || isApprovedForAll(owner, _msgSender()));

        _approve(to, tokenId);
    }

    /**
     * @dev See {IERC721-getApproved}.
     */
    function getApproved(
        uint256 tokenId
    ) public view virtual override returns (address) {
        _requireMinted(tokenId);

        return _tokenApprovals[tokenId];
    }

    /**
     * @dev See {IERC721-setApprovalForAll}.
     */
    function setApprovalForAll(
        address operator,
        bool approved
    ) public virtual override {
        _setApprovalForAll(_msgSender(), operator, approved);
    }

    /**
     * @dev See {IERC721-isApprovedForAll}.
     */
    function isApprovedForAll(
        address owner,
        address operator
    ) public view virtual override returns (bool) {
        return _operatorApprovals[owner][operator];
    }

    /**
     * @dev See {IERC721-transferFrom}.
     */
    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) public virtual override {
        //solhint-disable-next-line max-line-length
        require(_isApprovedOrOwner(_msgSender(), tokenId));

        _transfer(from, to, tokenId);
    }

    /**
     * @dev See {IERC721-safeTransferFrom}.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) public virtual override {
        safeTransferFrom(from, to, tokenId, "");
    }

    /**
     * @dev See {IERC721-safeTransferFrom}.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        bytes memory data
    ) public virtual override {
        require(_isApprovedOrOwner(_msgSender(), tokenId));
        _safeTransfer(from, to, tokenId, data);
    }

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
     *
     * `data` is additional data, it has no specified format and it is sent in call to `to`.
     *
     * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g.
     * implement alternative mechanisms to perform token transfer, such as signature-based.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function _safeTransfer(
        address from,
        address to,
        uint256 tokenId,
        bytes memory data
    ) internal virtual {
        _transfer(from, to, tokenId);
        require(_checkOnERC721Received(from, to, tokenId, data));
    }

    /**
     * @dev Returns the owner of the `tokenId`. Does NOT revert if token doesn't exist
     */
    function _ownerOf(uint256 tokenId) internal view virtual returns (address) {
        return _owners[tokenId];
    }

    /**
     * @dev Returns whether `tokenId` exists.
     *
     * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
     *
     * Tokens start existing when they are minted (`_mint`),
     * and stop existing when they are burned (`_burn`).
     */
    function _exists(uint256 tokenId) internal view virtual returns (bool) {
        return _ownerOf(tokenId) != address(0);
    }

    /**
     * @dev Returns whether `spender` is allowed to manage `tokenId`.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function _isApprovedOrOwner(
        address spender,
        uint256 tokenId
    ) internal view virtual returns (bool) {
        address owner = ERC721.ownerOf(tokenId);
        return (spender == owner ||
            isApprovedForAll(owner, spender) ||
            getApproved(tokenId) == spender);
    }

    /**
     * @dev Safely mints `tokenId` and transfers it to `to`.
     *
     * Requirements:
     *
     * - `tokenId` must not exist.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function _safeMint(address to, uint256 tokenId) internal virtual {
        _safeMint(to, tokenId, "");
    }

    /**
     * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
     * forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
     */
    function _safeMint(
        address to,
        uint256 tokenId,
        bytes memory data
    ) internal virtual {
        _mint(to, tokenId);
        require(_checkOnERC721Received(address(0), to, tokenId, data));
    }

    /**
     * @dev Mints `tokenId` and transfers it to `to`.
     *
     * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
     *
     * Requirements:
     *
     * - `tokenId` must not exist.
     * - `to` cannot be the zero address.
     *
     * Emits a {Transfer} event.
     */
    function _mint(address to, uint256 tokenId) internal virtual {
        require(to != address(0));
        require(!_exists(tokenId));

        _beforeTokenTransfer(address(0), to, tokenId);

        // Check that tokenId was not minted by `_beforeTokenTransfer` hook
        require(!_exists(tokenId));

        unchecked {
            // Will not overflow unless all 2**256 token ids are minted to the same
            // Given that tokens are minted one by one, it is impossible in practice that
            // this ever happens. Might change if we allow batch minting.
            // The ERC fails to describe this case.
            _balances[to] += 1;
        }

        _owners[tokenId] = to;

        emit Transfer(address(0), to, tokenId);

        _afterTokenTransfer(address(0), to, tokenId);
    }

    /**
     * @dev Destroys `tokenId`.
     * The approval is cleared when the token is burned.
     * This is an internal function that does not check if the sender is authorized to operate on the token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     *
     * Emits a {Transfer} event.
     */
    function _burn(uint256 tokenId) internal virtual {
        address owner = ERC721.ownerOf(tokenId);

        _beforeTokenTransfer(owner, address(0), tokenId);

        // Update ownership in case tokenId was transferred by `_beforeTokenTransfer` hook
        owner = ERC721.ownerOf(tokenId);

        // Clear approvals
        delete _tokenApprovals[tokenId];

        unchecked {
            // Cannot overflow, as that would require more tokens to be burned/transferred
            // out than the owner initially received through minting and transferring in.
            _balances[owner] -= 1;
        }
        delete _owners[tokenId];

        emit Transfer(owner, address(0), tokenId);

        _afterTokenTransfer(owner, address(0), tokenId);
    }

    function add_nft_before_transfer(uint256 tokenId, address wallet) private {
        get_my_nfts[wallet].push(tokenId);
        if (favourite_nft[wallet] == 0) {
            favourite_nft[wallet] = tokenId;
        }
    }

    function delete_nft_before_transfer(
        uint256 tokenId,
        address wallet
    ) private {
        uint256 i = 0;

        while (get_my_nfts[wallet][i] != tokenId) i++;

        for (i; i < (_balances[wallet] - 1); i++) {
            get_my_nfts[wallet][i] = get_my_nfts[wallet][i + 1];
        }

        get_my_nfts[wallet].pop();

        if (favourite_nft[wallet] == tokenId) {
            favourite_nft[wallet] = get_my_nfts[wallet][0];
        }
    }

    /**
     * @dev Transfers `tokenId` from `from` to `to`.
     *  As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     *
     * Emits a {Transfer} event.
     */
    function _transfer(
        address from,
        address to,
        uint256 tokenId
    ) internal virtual {
        require(ERC721.ownerOf(tokenId) == from);
        require(to != address(0));

        _beforeTokenTransfer(from, to, tokenId);

        // Check that tokenId was not transferred by `_beforeTokenTransfer` hook
        require(ERC721.ownerOf(tokenId) == from);

        // Clear approvals from the previous owner
        delete _tokenApprovals[tokenId];

        unchecked {
            // `_balances[from]` cannot overflow for the same reason as described in `_burn`:
            // `from`'s balance is the number of token held, which is at least one before the current
            // transfer.
            // `_balances[to]` could overflow in the conditions described in `_mint`. That would require
            // all 2**256 token ids to be minted, which in practice is impossible.

            delete_nft_before_transfer(tokenId, from);
            _balances[from] -= 1;

            if (_balances[from] == 0 && total_holders > 0) {
                total_holders--;
            }

            add_nft_before_transfer(tokenId, to);
            if (_balances[to] == 0) {
                total_holders++;
            }
            _balances[to] += 1;
        }
        _owners[tokenId] = to;

        emit Transfer(from, to, tokenId);

        _afterTokenTransfer(from, to, tokenId);
    }

    /**
     * @dev Approve `to` to operate on `tokenId`
     *
     * Emits an {Approval} event.
     */
    function _approve(address to, uint256 tokenId) internal virtual {
        _tokenApprovals[tokenId] = to;
        emit Approval(ERC721.ownerOf(tokenId), to, tokenId);
    }

    /**
     * @dev Approve `operator` to operate on all of `owner` tokens
     *
     * Emits an {ApprovalForAll} event.
     */
    function _setApprovalForAll(
        address owner,
        address operator,
        bool approved
    ) internal virtual {
        require(owner != operator);
        _operatorApprovals[owner][operator] = approved;
        emit ApprovalForAll(owner, operator, approved);
    }

    /**
     * @dev Reverts if the `tokenId` has not been minted yet.
     */
    function _requireMinted(uint256 tokenId) internal view virtual {
        require(_exists(tokenId));
    }

    /**
     * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.
     * The call is not executed if the target address is not a contract.
     *
     * @param from address representing the previous owner of the given token ID
     * @param to target address that will receive the tokens
     * @param tokenId uint256 ID of the token to be transferred
     * @param data bytes optional data to send along with the call
     * @return bool whether the call correctly returned the expected magic value
     */
    function _checkOnERC721Received(
        address from,
        address to,
        uint256 tokenId,
        bytes memory data
    ) private returns (bool) {
        if (to.isContract()) {
            try
                IERC721Receiver(to).onERC721Received(
                    _msgSender(),
                    from,
                    tokenId,
                    data
                )
            returns (bytes4 retval) {
                return retval == IERC721Receiver.onERC721Received.selector;
            } catch (bytes memory reason) {
                if (reason.length == 0) {
                    revert();
                } else {
                    /// @solidity memory-safe-assembly
                    assembly {
                        revert(add(32, reason), mload(reason))
                    }
                }
            }
        } else {
            return true;
        }
    }

    /**
     * @dev Hook that is called before any (single) token transfer. This includes minting and burning.
     * See {_beforeConsecutiveTokenTransfer}.
     *
     * Calling conditions:
     *
     * - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be
     * transferred to `to`.
     * - When `from` is zero, `tokenId` will be minted for `to`.
     * - When `to` is zero, ``from``'s `tokenId` 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 tokenId
    ) internal virtual {}

    /**
     * @dev Hook that is called after any (single) transfer of tokens. This includes minting and burning.
     * See {_afterConsecutiveTokenTransfer}.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero.
     * - `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 tokenId
    ) internal virtual {}

    /**
     * @dev Hook that is called before consecutive token transfers.
     * Calling conditions are similar to {_beforeTokenTransfer}.
     *
     * The default implementation include balances updates that extensions such as {ERC721Consecutive} cannot perform
     * directly.
     */
    function _beforeConsecutiveTokenTransfer(
        address from,
        address to,
        uint256 /*first*/,
        uint96 size
    ) internal virtual {
        if (from != address(0)) {
            _balances[from] -= size;
        }
        if (to != address(0)) {
            _balances[to] += size;
        }
    }

    /**
     * @dev Hook that is called after consecutive token transfers.
     * Calling conditions are similar to {_afterTokenTransfer}.
     */
    function _afterConsecutiveTokenTransfer(
        address /*from*/,
        address /*to*/,
        uint256 /*first*/,
        uint96 /*size*/
    ) internal virtual {}
}

abstract contract ERC721URIStorage is ERC721 {
    using Strings for uint256;

    // Optional mapping for token URIs
    mapping(uint256 => string) private _tokenURIs;

    /**
     * @dev See {IERC721Metadata-tokenURI}.
     */
    function tokenURI(
        uint256 tokenId
    ) public view virtual override returns (string memory) {
        _requireMinted(tokenId);

        string memory _tokenURI = _tokenURIs[tokenId];
        string memory base = _baseURI();

        // If there is no base URI, return the token URI.
        if (bytes(base).length == 0) {
            return _tokenURI;
        }
        // If both are set, concatenate the baseURI and tokenURI (via abi.encodePacked).
        if (bytes(_tokenURI).length > 0) {
            return string(abi.encodePacked(base, _tokenURI));
        }

        return super.tokenURI(tokenId);
    }

    /**
     * @dev Sets `_tokenURI` as the tokenURI of `tokenId`.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function _setTokenURI(
        uint256 tokenId,
        string memory _tokenURI
    ) internal virtual {
        require(_exists(tokenId));
        _tokenURIs[tokenId] = _tokenURI;
    }

    /**
     * @dev See {ERC721-_burn}. This override additionally checks to see if a
     * token-specific URI was set for the token, and if so, it deletes the token URI from
     * the storage mapping.
     */
    function _burn(uint256 tokenId) internal virtual override {
        super._burn(tokenId);

        if (bytes(_tokenURIs[tokenId]).length != 0) {
            delete _tokenURIs[tokenId];
        }
    }
}

abstract contract Ownable is Context {
    address private _owner;

    event OwnershipTransferred(
        address indexed previousOwner,
        address indexed newOwner
    );

    /**
     * @dev Initializes the contract setting the deployer as the initial
     */
    constructor() {
        _transferOwnership(_msgSender());
    }

    /**
     * @dev Throws if called by any account other than the
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the
     */
    function _checkOwner() internal view virtual {
        require(owner() == _msgSender());
    }

    /**
     * @dev Leaves the contract without  It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0));
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

contract Addresses {
    // Wallets
    address public community_wallet =
        0xc8895f6f85D870589C42fd6d531c855bddD27B0f;
    address public development_wallet =
        0x9060723c22dE586c2fA5eFa07A7743F6f4a935f5;
    address public managment_wallet =
        0xc8895f6f85D870589C42fd6d531c855bddD27B0f;

    // Contracts
    uint256 public contracts_id = 1;
    Clock public Cycle;
    User public Qualification;
    ERC20 public Able;
    Sinergy public SinergyBronze;
    Sinergy public SinergySilver;
    Sinergy public SinergyGold;
    Test public VideoTest;
    SinergySale public AbleSale;
    Migration public MigrationSinergy;
    MigrationSale public MigrationSinergySale;
    BaseReward public StablecoinBaseReward;
    ValueReward public StablecoinValueReward;
    ValueReward public AbleValueReward;
    ConstancyReward public StablecoinConstancyReward;
    ConstancyReward public AbleConstancyReward;
    ConfidenceReward public StablecoinConfidenceReward;
    ConfidenceReward public AbleConfidenceReward;

    SinergyNovember public NovemberSinergyBronzeContract; // Noviembre 2022
    SinergyApril public AprilSinergyBronzeContract; // Abril 2023
    SinergySaleMigration public AprilSinergySaleContract; // Abril 2023
    Migration public AprilMigrationSinergy; // Abril 2023
    ERC20Migration public AprilAbleContract; // April 2023
    ERC20Migration public NovemberAbleContract; // November 2022
    ERC20Migration public TripleMigration; // November 2022

    // ERC20s
    ERC20 public Stablecoin;
    ERC20 public USDC;
    ERC20 public USDT;

    // Decimals
    uint256 public USDC_DECIMALS = 6;
    uint256 public USDT_DECIMALS = 6;

    function RefreshAll() public {
        RefreshCycle();
        RefreshQualification();
        RefreshAble();
        RefreshSinergyBronze();
        RefreshSinergySilver();
        RefreshSinergyGold();
        RefreshVideoTest();
        RefreshAbleSale();
        RefreshMigrationSinergy();
        RefreshMigrationSinergySale();
        RefreshStablecoinBaseReward();
        RefreshAbleConstancyReward();
        RefreshStablecoinValueReward();
        RefreshAbleValueReward();
        RefreshStablecoinConstancyReward();
        RefreshStablecoinConfidenceReward();
        RefreshAbleConfidenceReward();
    }

    function RefreshCycle() public {
        if (address(Cycle) == address(0)) return;
        Cycle.RefreshContracts();
    }

    function RefreshQualification() public {
        if (address(Qualification) == address(0)) return;
        Qualification.RefreshContracts();
    }

    function RefreshAble() public {
        if (address(Able) == address(0)) return;
        Able.RefreshContracts();
    }

    function RefreshSinergyBronze() public {
        if (address(SinergyBronze) == address(0)) return;
        SinergyBronze.RefreshContracts();
    }

    function RefreshSinergySilver() public {
        if (address(SinergySilver) == address(0)) return;
        SinergySilver.RefreshContracts();
    }

    function RefreshSinergyGold() public {
        if (address(SinergyGold) == address(0)) return;
        SinergyGold.RefreshContracts();
    }

    function RefreshVideoTest() public {
        if (address(VideoTest) == address(0)) return;
        VideoTest.RefreshContracts();
    }

    function RefreshAbleSale() public {
        if (address(AbleSale) == address(0)) return;
        AbleSale.RefreshContracts();
    }

    function RefreshMigrationSinergy() public {
        if (address(MigrationSinergy) == address(0)) return;
        MigrationSinergy.RefreshContracts();
    }

    function RefreshMigrationSinergySale() public {
        if (address(MigrationSinergySale) == address(0)) return;
        MigrationSinergySale.RefreshContracts();
    }

    function RefreshStablecoinBaseReward() public {
        if (address(StablecoinBaseReward) == address(0)) return;
        StablecoinBaseReward.RefreshContracts();
    }

    function RefreshAbleConstancyReward() public {
        if (address(AbleConstancyReward) == address(0)) return;
        AbleConstancyReward.RefreshContracts();
    }

    function RefreshStablecoinValueReward() public {
        if (address(StablecoinValueReward) == address(0)) return;
        StablecoinValueReward.RefreshContracts();
    }

    function RefreshAbleValueReward() public {
        if (address(AbleValueReward) == address(0)) return;
        AbleValueReward.RefreshContracts();
    }

    function RefreshStablecoinConstancyReward() public {
        if (address(StablecoinConstancyReward) == address(0)) return;
        StablecoinConstancyReward.RefreshContracts();
    }

    function RefreshStablecoinConfidenceReward() public {
        if (address(StablecoinConfidenceReward) == address(0)) return;
        StablecoinConfidenceReward.RefreshContracts();
    }

    function RefreshAbleConfidenceReward() public {
        if (address(AbleConfidenceReward) == address(0)) return;
        AbleConfidenceReward.RefreshContracts();
    }

    function IsOwner(address wallet) public view returns (bool) {
        return (wallet == community_wallet ||
            wallet == development_wallet ||
            wallet == managment_wallet);
    }

    // Set Functions
    function SetCommunityWallet(address wallet) public {
        require(
            msg.sender == community_wallet,
            "Only community wallet can change the community wallet"
        );
        community_wallet = wallet;
    }

    function SetDevelopmentWallet(address wallet) public {
        require(
            msg.sender == development_wallet,
            "Only development wallet can change the community wallet"
        );
        development_wallet = wallet;
    }

    function SetManagmentWallet(address wallet) public {
        require(
            msg.sender == managment_wallet,
            "Only management wallet can change the community wallet"
        );
        managment_wallet = wallet;
    }

    function SetClock(address wallet) public {
        require(IsOwner(msg.sender), "Only Owner can modify Clock Contract.");
        Cycle = Clock(wallet);
        contracts_id++;
    }

    function SetUser(address wallet) public {
        require(IsOwner(msg.sender), "Only Owner can modify User Contract.");
        Qualification = User(wallet);
        contracts_id++;
    }

    function SetAble(address wallet) public {
        require(IsOwner(msg.sender), "Only Owner can modify Able Contract.");
        Able = ERC20(wallet);
        contracts_id++;
    }

    function SetSinergyBronze(address sinergy) public {
        require(
            IsOwner(msg.sender),
            "Only Owner can modify Sinergy Bronze Contract"
        );
        SinergyBronze = Sinergy(sinergy);
        contracts_id++;
    }

    function SetSinergySilver(address sinergy) public {
        require(
            IsOwner(msg.sender),
            "Only Owner can modify Sinergy Silver Contract"
        );
        SinergySilver = Sinergy(sinergy);
        contracts_id++;
    }

    function SetSinergyGold(address sinergy) public {
        require(
            IsOwner(msg.sender),
            "Only Owner can modify Sinergy Gold Contract"
        );
        SinergyGold = Sinergy(sinergy);
        contracts_id++;
    }

    function SetTest(address wallet) public {
        require(IsOwner(msg.sender), "Only Owner can modify Test Contract.");
        VideoTest = Test(wallet);
        contracts_id++;
    }

    function SetSinergySale(address wallet) public {
        require(
            IsOwner(msg.sender),
            "Only Owner can modify SinergySale Contract."
        );
        AbleSale = SinergySale(wallet);
        contracts_id++;
    }

    function SetMigrationSinergy(address wallet) public {
        require(
            IsOwner(msg.sender),
            "Only Owner can modify MigrationSinergy Contract."
        );
        MigrationSinergy = Migration(wallet);
        contracts_id++;
    }

    function SetMigrationSinergySale(address wallet) public {
        require(
            IsOwner(msg.sender),
            "Only Owner can modify MigrationSinergySale Contract."
        );
        MigrationSinergySale = MigrationSale(wallet);
        contracts_id++;
    }

    function SetStablecoinBaseReward(address wallet) public {
        require(
            IsOwner(msg.sender),
            "Only Owner can modify StablecoinBaseReward Contract."
        );
        StablecoinBaseReward = BaseReward(wallet);
        contracts_id++;
    }

    function SetStablecoinValueReward(address wallet) public {
        require(
            IsOwner(msg.sender),
            "Only Owner can modify StablecoinValueReward Contract."
        );
        StablecoinValueReward = ValueReward(wallet);
        contracts_id++;
    }

    function SetAbleValueReward(address wallet) public {
        require(
            IsOwner(msg.sender),
            "Only Owner can modify AbleValueReward Contract."
        );
        AbleValueReward = ValueReward(wallet);
        contracts_id++;
    }

    function SetStablecoinConstancyReward(address wallet) public {
        require(
            IsOwner(msg.sender),
            "Only Owner can modify StablecoinConstancyReward Contract."
        );
        StablecoinConstancyReward = ConstancyReward(wallet);
        contracts_id++;
    }

    function SetAbleConstancyReward(address wallet) public {
        require(
            IsOwner(msg.sender),
            "Only Owner can modify AbleConstancyReward Contract."
        );
        AbleConstancyReward = ConstancyReward(wallet);
        contracts_id++;
    }

    function SetStablecoinConfidenceReward(address wallet) public {
        require(
            IsOwner(msg.sender),
            "Only Owner can modify StablecoinConfidenceReward Contract."
        );
        StablecoinConfidenceReward = ConfidenceReward(wallet);
        contracts_id++;
    }

    function SetAbleConfidenceReward(address wallet) public {
        require(
            IsOwner(msg.sender),
            "Only Owner can modify AbleConfidenceReward Contract."
        );
        AbleConfidenceReward = ConfidenceReward(wallet);
        contracts_id++;
    }

    function SetNovemberSinergyBronzeContract(address wallet) public {
        require(
            IsOwner(msg.sender),
            "Only Owner can modify NovemberSinergyBronzeContract Contract."
        );
        NovemberSinergyBronzeContract = SinergyNovember(wallet);
        contracts_id++;
    }

    function SetAprilSinergyBronzeContract(address wallet) public {
        require(
            IsOwner(msg.sender),
            "Only Owner can modify AprilSinergyBronzeContract Contract."
        );
        AprilSinergyBronzeContract = SinergyApril(wallet);
        contracts_id++;
    }

    function SetAprilSinergySaleContract(address wallet) public {
        require(
            IsOwner(msg.sender),
            "Only Owner can modify AprilSinergySaleContract Contract."
        );
        AprilSinergySaleContract = SinergySaleMigration(wallet);
        contracts_id++;
    }

    function SetAprilAbleContract(address wallet) public {
        require(
            IsOwner(msg.sender),
            "Only Owner can modify AprilAble Contract."
        );
        AprilAbleContract = ERC20Migration(wallet);
        contracts_id++;
    }

    function SetAprilMigrationSinergy(address wallet) public {
        require(
            IsOwner(msg.sender),
            "Only owner can modify AprilMigrationSinergy"
        );
        AprilMigrationSinergy = Migration(wallet);
    }

    function SetNovemberAbleContract(address wallet) public {
        require(
            IsOwner(msg.sender),
            "Only Owner can modify NovemberAble Contract."
        );
        NovemberAbleContract = ERC20Migration(wallet);
        contracts_id++;
    }

    function SetTripleMigrationContract(address wallet) public {
        require(
            IsOwner(msg.sender),
            "Only Owner can modify TripleMigration Contract."
        );
        TripleMigration = ERC20Migration(wallet);
        contracts_id++;
    }

    function SetStablecoin(address wallet) public {
        require(IsOwner(msg.sender), "Only owners can set stablecoin.");
        Stablecoin = ERC20(wallet);
    }

    function SetUSDC(address wallet) public {
        require(IsOwner(msg.sender), "Only owners can set USDC.");
        USDC = ERC20(wallet);
    }

    function SetUSDT(address wallet) public {
        require(IsOwner(msg.sender), "Only owners can set USDT.");
        USDT = ERC20(wallet);
    }

    // Set Decimals
    function SetUSDCDecimals(uint256 decimals) public {
        require(
            IsOwner(msg.sender),
            "You are not qualified to change the USDC Decimals."
        );
        USDC_DECIMALS = decimals;
    }

    function SetUSDTDecimals(uint256 decimals) public {
        require(
            IsOwner(msg.sender),
            "You are not qualified to change the USDC Decimals."
        );
        USDT_DECIMALS = decimals;
    }
}

contract Router {
    // Addresses
    Addresses Wallets = Addresses(0xF7Ca5aeb3d2DBC0012f153224e22a1757c8f83F5);
    // Contracts
    uint256 public contracts_id = 1;
    Clock Cycle;
    User Qualification;
    ERC20 Able;
    Sinergy public SinergyBronze;
    Sinergy SinergySilver;
    Sinergy SinergyGold;
    Test VideoTest;
    SinergySale AbleSale;
    Migration public MigrationSinergy;
    MigrationSale MigrationSinergySale;
    BaseReward StablecoinBaseReward;
    ValueReward StablecoinValueReward;
    ValueReward AbleValueReward;
    ConstancyReward StablecoinConstancyReward;
    ConstancyReward AbleConstancyReward;
    ConfidenceReward StablecoinConfidenceReward;
    ConfidenceReward AbleConfidenceReward;
    SinergyNovember NovemberSinergyBronzeContract; // Noviembre 2022
    SinergyApril AprilSinergyBronzeContract; // Abril 2023
    SinergySaleMigration AprilSinergySaleContract; // Abril 2023
    Migration AprilMigrationSinergy; // Abril 2023
    ERC20Migration AprilAbleContract; // April 2023
    ERC20Migration NovemberAbleContract; // November 2022
    ERC20Migration TripleMigration; // November 2022

    // ERC20s
    ERC20 public Stablecoin;
    ERC20 public USDC;
    ERC20 public USDT;

    function RefreshContracts() public {
        uint256 w_contracts_id = Wallets.contracts_id();
        if (contracts_id == w_contracts_id) return;
        contracts_id = w_contracts_id;
        Cycle = Clock(Wallets.Cycle());
        Qualification = User(Wallets.Qualification());
        Able = ERC20(Wallets.Able());
        SinergyBronze = Sinergy(Wallets.SinergyBronze());
        SinergySilver = Sinergy(Wallets.SinergySilver());
        SinergyGold = Sinergy(Wallets.SinergyGold());
        VideoTest = Test(Wallets.VideoTest());
        AbleSale = SinergySale(Wallets.AbleSale());
        MigrationSinergy = Migration(Wallets.MigrationSinergy());
        MigrationSinergySale = MigrationSale(Wallets.MigrationSinergySale());
        StablecoinBaseReward = BaseReward(Wallets.StablecoinBaseReward());
        StablecoinValueReward = ValueReward(Wallets.StablecoinValueReward());
        AbleValueReward = ValueReward(Wallets.AbleValueReward());
        StablecoinConstancyReward = ConstancyReward(
            Wallets.StablecoinConstancyReward()
        );
        AbleConstancyReward = ConstancyReward(Wallets.AbleConstancyReward());
        StablecoinConfidenceReward = ConfidenceReward(
            Wallets.StablecoinConfidenceReward()
        );
        AbleConfidenceReward = ConfidenceReward(Wallets.AbleConfidenceReward());
        NovemberSinergyBronzeContract = SinergyNovember(
            Wallets.NovemberSinergyBronzeContract()
        );
        AprilSinergyBronzeContract = SinergyApril(
            Wallets.AprilSinergyBronzeContract()
        );
        AprilSinergySaleContract = SinergySaleMigration(
            Wallets.AprilSinergySaleContract()
        );

        AprilMigrationSinergy = Migration(Wallets.AprilMigrationSinergy());
        AprilAbleContract = ERC20Migration(Wallets.AprilAbleContract());
        NovemberAbleContract = ERC20Migration(Wallets.NovemberAbleContract());
        TripleMigration = ERC20Migration(Wallets.TripleMigration());

        Stablecoin = ERC20(Wallets.Stablecoin());
        USDC = ERC20(Wallets.USDC());
        USDT = ERC20(Wallets.USDT());
    }

    function SetAddresses(address wallet) public {
        require(Wallets.IsOwner(msg.sender), "Only owner can set Addresses.");
        Wallets = Addresses(wallet);
    }
}

contract SinergySaleMigration {
    // Constants
    uint256 public constant TIME_OF_CYCLE = 1 days;

    uint256 public MAX_AMOUNT_SELL_TOKEN = 90 ether;
    uint256 public MIN_AMOUNT_SELL_TOKEN = 9 ether;
    uint256 public TOKEN_PRICE = 3;
    uint256 public LIMIT_POST_BY_CYCLE = 1;

    // Global Variables
    uint256 public TOTAL_TOKENS_SOLD;
    mapping(uint256 => uint256) public tokens_sold_by_cycle;

    // ERC20 Contracts
    ERC20Migration public ABLE =
        ERC20Migration(0x0b85cCA1814eE40C6E83E3591F3819eC7e87d0A5);
    ERC20 public TOKEN = ERC20(0x0b85cCA1814eE40C6E83E3591F3819eC7e87d0A5);
    ERC20 public BUSD = ERC20(0x765277EebeCA2e31912C9946eAe1021199B39C61);

    // ERC721 Contract
    SinergyMigration public SinergyContract =
        SinergyMigration(0xaeFDeD1Efb9f370F3663493755a1Da0A4E6F17E6);

    // Wallets
    address payable public communityWallet =
        payable(0xc8895f6f85D870589C42fd6d531c855bddD27B0f);
    address public dev_wallet = 0x9060723c22dE586c2fA5eFa07A7743F6f4a935f5;

    // Sell List
    uint256[] public sell_list;
    mapping(uint256 => bool) public is_in_sell_list;
    mapping(address => uint256) public timestamp_last_post_sell;
    mapping(address => uint256) public amount_of_post_of;

    // Driven List
    uint256[] public driven_list;
    mapping(uint256 => bool) public is_in_driven_list;
    mapping(address => uint256) public turns_in_driven_list_of;

    // Turn of pick
    uint256 public turn; // 0 y 1 => Driven List ||| 2 => Sell List

    // Amount on Sell
    uint256 public total_selling_amount;
    mapping(uint256 => uint256) public selling_amount_of;
    mapping(address => uint256) public post_count_of;

    // User Data
    mapping(address => uint256) public amount_sold_of;

    // Events
    event SellList(uint256 indexed nft_id, bool comeIn, bool driven);

    event SellToken(
        uint256 indexed nft_id,
        address indexed buyer,
        address indexed seller,
        uint256 amount
    );

    event SwapList(uint256 indexed nft_id);

    event BackToQueue(uint256 indexed nft_id, uint256 posAnt, uint256 posAct);

    // Constructor
    constructor() {}

    // Public Methods
    function sell(uint256 _amount, uint256 _tokenID) public {
        require(
            _amount >= MIN_AMOUNT_SELL_TOKEN,
            "The amount to sell have to be more or equal than 9 and less or equal than 90."
        );
        require(
            (selling_amount_of[_tokenID] + _amount) <= MAX_AMOUNT_SELL_TOKEN,
            "The amount to sell have to be more or equal than 9 and less or equal than 90."
        );
        require(
            SinergyContract.ownerOf(_tokenID) == msg.sender,
            "You are not the owner of this NFT."
        );
        require(
            !is_selling(_tokenID),
            "You are already selling tokens on SinergySell"
        );
        require(
            TOKEN.transferFrom(msg.sender, address(this), _amount),
            "Transfer fails."
        );
        require(
            block.timestamp > timestamp_last_post_sell[msg.sender],
            "You have to wait to post another sell."
        );

        selling_amount_of[_tokenID] += _amount;
        total_selling_amount += _amount;
        timestamp_last_post_sell[msg.sender] = block.timestamp + TIME_OF_CYCLE;

        if (can_be_in_driven_list(msg.sender)) {
            set_nft_to_driven_list(_tokenID);
            turns_in_driven_list_of[msg.sender]++;
        } else {
            set_nft_to_sell_list(_tokenID);
        }
    }

    function buy(uint256 _amount, bool incrementBDD) public {
        require(_amount <= total_selling_amount);
        // Transferimos los BUSD a este contrato
        BUSD.transferFrom(msg.sender, address(this), _amount * TOKEN_PRICE);

        // El 33% que va para el vendedor de los tokens, se envian directamente a la billetera del vendedor.
        // Esto se hace en la funcion sell_able_from_list

        // Transferimos 33% para el Admin
        BUSD.transfer(communityWallet, ((_amount * TOKEN_PRICE) / 3));

        // Transferimos 33% para Able, para que lo otorgue en el regalo diario de Able.
        BUSD.transfer(address(ABLE), ((_amount * TOKEN_PRICE) / 3));

        // Transferimos los TOKEN al usuario
        TOKEN.transfer(msg.sender, _amount);

        // Buscamos los vendedores
        uint256 nfts_to_remove_sell;
        uint256 nfts_to_remove_driven;

        (nfts_to_remove_sell, nfts_to_remove_driven) = find_sellers(
            _amount,
            msg.sender
        );

        // Eliminamos de la lista, los vendedores que vendieron el total de su capital.
        for (uint256 i = 0; i < nfts_to_remove_sell; i++) {
            remove_first_nft_from_sell_list();
        }

        for (uint256 i = 0; i < nfts_to_remove_driven; i++) {
            remove_first_nft_from_driven_list();
        }

        // Aumentamos el BDD de esta billetera en ABLE
        if (incrementBDD)
            ABLE.incrementDonationBalance(_amount * TOKEN_PRICE, msg.sender);

        // Aumentamos la cantidad de tokens vendidos.
        TOTAL_TOKENS_SOLD += _amount;
        tokens_sold_by_cycle[ABLE.cycle()] += _amount;
    }

    function quit_sell(uint256 tokenID) public {
        require(is_selling(tokenID));
        require(SinergyContract.ownerOf(tokenID) == msg.sender);
        require(selling_amount_of[tokenID] > 0);
        require(total_selling_amount >= selling_amount_of[tokenID]);
        require(TOKEN.transfer(msg.sender, selling_amount_of[tokenID]));

        // Descontamos el monto que se saca de la venta
        total_selling_amount -= selling_amount_of[tokenID];
        selling_amount_of[tokenID] = 0;

        // Sacamos al NFT de la Lista de Venta TOKEN
        if (is_in_sell_list[tokenID]) {
            remove_nft_from_sell_list(get_index_of_nft_from_sell_list(tokenID));
        } else {
            remove_nft_from_driven_list(
                get_index_of_nft_from_driven_list(tokenID)
            );
        }
    }

    // Check Methods
    function check_sell_list() public {
        // Chequeamos si algun NFT de la Lista de Venta de Able esta descalificado.
        for (uint256 i = 0; i < sell_list.length; i++) {
            uint256 nft_id = sell_list[i];
            if (!SinergyContract.nft_was_qualified(nft_id)) {
                // Traemos todos los NFTs que habia detras una posicion mas adelante
                for (uint256 j = i; j < sell_list.length - 1; j++) {
                    sell_list[j] = sell_list[j + 1];
                }

                // Posicionamos al NFT sancionado en el ultimo lugar de la cola
                sell_list[sell_list.length - 1] = nft_id;

                emit BackToQueue(nft_id, i, sell_list.length - 1);
            }
        }
    }

    function check_driven_list() public {
        // Chequeamos si algun NFT de la Lista de Venta de Able esta descalificado.
        for (uint256 i = 0; i < driven_list.length; i++) {
            uint256 nft_id = driven_list[i];
            if (!SinergyContract.nft_was_qualified(nft_id)) {
                // Traemos todos los NFTs que habia detras una posicion mas adelante
                for (uint256 j = i; j < driven_list.length - 1; j++) {
                    driven_list[j] = sell_list[j + 1];
                }

                // Posicionamos al NFT sancionado en el ultimo lugar de la cola
                driven_list[driven_list.length - 1] = nft_id;

                emit BackToQueue(nft_id, i, driven_list.length - 1);
            }
        }
    }

    // Get Methods
    function get_sell_list_length() public view returns (uint256) {
        return sell_list.length;
    }

    function get_driven_list_length() public view returns (uint256) {
        return driven_list.length;
    }

    // Communication
    function try_to_swap(uint256 _tokenID) public {
        require(
            msg.sender == address(SinergyContract),
            "Only SinergyContract can call to this function."
        );

        if (
            is_selling(_tokenID) &&
            is_in_sell_list[_tokenID] &&
            (get_index_of_nft_from_sell_list(_tokenID) > driven_list.length)
        ) {
            remove_nft_from_sell_list(
                get_index_of_nft_from_sell_list(_tokenID)
            );
            set_nft_to_driven_list(_tokenID);

            emit SwapList(_tokenID);
        }
    }

    function set_sinergy(address _sinergyContract) public {
        require(
            msg.sender == dev_wallet,
            "You are not qualified to call this function."
        );
        SinergyContract = SinergyMigration(_sinergyContract);
    }

    function set_limit_post_by_cycle(uint256 new_limit) public {
        require(
            msg.sender == communityWallet || msg.sender == dev_wallet,
            "You are not qualified to call this function."
        );
        LIMIT_POST_BY_CYCLE = new_limit;
    }

    function set_values_of_sale(
        uint256 max_amount,
        uint256 min_amount,
        uint256 price
    ) public {
        /*
            Ninguno de los parametros tienen que estar en WEI.
            Tienen que estar directamente en Ether.
        */
        require(
            msg.sender == dev_wallet || msg.sender == communityWallet,
            "You are not qualified to call this function"
        );

        MAX_AMOUNT_SELL_TOKEN = max_amount * 1 ether;
        MIN_AMOUNT_SELL_TOKEN = min_amount * 1 ether;
        TOKEN_PRICE = price;
    }

    // Helpers
    function is_selling(uint256 _tokenID) public view returns (bool) {
        return is_in_sell_list[_tokenID] || is_in_driven_list[_tokenID];
    }

    // Sell Helpers
    function is_sell_turn() public view returns (bool) {
        return (turn == 2);
    }

    function sell_list_is_empty() public view returns (bool) {
        return sell_list.length == 0;
    }

    function can_use_sell() public view returns (bool) {
        return (!sell_list_is_empty() &&
            (is_sell_turn() || driven_list_is_empty()));
    }

    // Driven Helpers
    function can_be_in_driven_list(address wallet) public view returns (bool) {
        return
            ABLE.winSaverReward(wallet) &&
            ABLE.winsSaverRewardOf(wallet) > turns_in_driven_list_of[wallet];
    }

    function is_driven_turn() public view returns (bool) {
        return turn == 0 || turn == 1;
    }

    function driven_list_is_empty() public view returns (bool) {
        return driven_list.length == 0;
    }

    function can_use_driven() public view returns (bool) {
        return (!driven_list_is_empty() &&
            (is_driven_turn() || sell_list_is_empty()));
    }

    // Private Methods
    function set_nft_to_sell_list(uint256 _tokenID) private {
        sell_list.push(_tokenID);
        is_in_sell_list[_tokenID] = true;

        emit SellList(_tokenID, true, false);
    }

    function set_nft_to_driven_list(uint256 _tokenID) private {
        driven_list.push(_tokenID);
        is_in_driven_list[_tokenID] = true;

        emit SellList(_tokenID, true, true);
    }

    function remove_first_nft_from_sell_list() private {
        require(sell_list.length > 0);

        uint256 nft_id = sell_list[0];

        for (uint256 i = 0; i < sell_list.length - 1; i++) {
            sell_list[i] = sell_list[i + 1];
        }

        sell_list.pop();

        is_in_sell_list[nft_id] = false;

        emit SellList(nft_id, false, false);
    }

    function remove_first_nft_from_driven_list() private {
        require(driven_list.length > 0);

        uint256 nft_id = driven_list[0];

        for (uint256 i = 0; i < driven_list.length - 1; i++) {
            driven_list[i] = driven_list[i + 1];
        }

        driven_list.pop();

        is_in_driven_list[nft_id] = false;

        emit SellList(nft_id, false, true);
    }

    function remove_nft_from_sell_list(uint256 idx) private {
        require(sell_list.length > idx);

        uint256 nft_id = sell_list[idx];

        for (uint256 i = idx; i < sell_list.length - 1; i++) {
            sell_list[i] = sell_list[i + 1];
        }

        sell_list.pop();

        is_in_sell_list[nft_id] = false;

        emit SellList(nft_id, false, false);
    }

    function remove_nft_from_driven_list(uint256 idx) private {
        require(driven_list.length > idx);

        uint256 nft_id = driven_list[idx];

        for (uint256 i = idx; i < driven_list.length - 1; i++) {
            driven_list[i] = driven_list[i + 1];
        }

        driven_list.pop();

        is_in_driven_list[nft_id] = false;

        emit SellList(nft_id, false, true);
    }

    function can_sell(address wallet) private returns (bool) {
        if (block.timestamp > timestamp_last_post_sell[wallet]) {
            post_count_of[wallet] = 0;
            return true;
        }

        if (post_count_of[wallet] < LIMIT_POST_BY_CYCLE) {
            post_count_of[wallet]++;
            return true;
        }

        return false;
    }

    function get_index_of_nft_from_sell_list(
        uint256 tokenID
    ) private view returns (uint256) {
        for (uint256 i = 0; i < sell_list.length; i++) {
            if (sell_list[i] == tokenID) {
                return i;
            }
        }

        return sell_list.length + 1;
    }

    function get_index_of_nft_from_driven_list(
        uint256 tokenID
    ) private view returns (uint256) {
        for (uint256 i = 0; i < driven_list.length; i++) {
            if (driven_list[i] == tokenID) {
                return i;
            }
        }

        return driven_list.length + 1;
    }

    function sell_able_from_list(
        uint256 _amount,
        uint256 _tokenID,
        address buyer
    ) private {
        address owner = SinergyContract.ownerOf(_tokenID);

        selling_amount_of[_tokenID] -= _amount;
        total_selling_amount -= _amount;

        // 33% van directo al usuario
        BUSD.transfer(owner, ((_amount * TOKEN_PRICE) / 3));
        amount_sold_of[owner] += ((_amount * TOKEN_PRICE) / 3);

        emit SellToken(_tokenID, buyer, owner, _amount);
    }

    // Turn Helper
    function increment_turn() private {
        if (turn == 2) {
            turn = 0;
        } else {
            turn++;
        }
    }

    function find_sellers(
        uint256 _amount,
        address buyer
    ) private returns (uint256, uint256) {
        bool sell_all = false;
        uint256 nft_id;

        uint256 idx_sell = 0;
        uint256 nfts_to_remove_sell;

        uint256 idx_driven = 0;
        uint256 nfts_to_remove_driven;

        while (!sell_all) {
            // Driven List
            while (
                can_use_driven() && idx_driven < driven_list.length && !sell_all
            ) {
                nft_id = driven_list[idx_driven];

                if (_amount <= selling_amount_of[nft_id]) {
                    // Este NFT me da lo que necesito.
                    if (_amount == selling_amount_of[nft_id]) {
                        nfts_to_remove_driven++;
                    }

                    sell_able_from_list(_amount, nft_id, buyer);
                    sell_all = true;
                } else {
                    // Este NFT me ayuda a alcanzar lo que necesito. No me es suficiente lo que me da.
                    _amount -= selling_amount_of[nft_id];
                    sell_able_from_list(
                        selling_amount_of[nft_id],
                        nft_id,
                        buyer
                    );
                    nfts_to_remove_driven++;
                }

                idx_driven++;
                increment_turn();
            }

            // Sell List
            while (can_use_sell() && idx_sell < sell_list.length && !sell_all) {
                nft_id = sell_list[idx_sell];

                if (_amount <= selling_amount_of[nft_id]) {
                    // Este NFT me da lo que necesito.
                    if (_amount == selling_amount_of[nft_id]) {
                        nfts_to_remove_sell++;
                    }

                    sell_able_from_list(_amount, nft_id, buyer);
                    sell_all = true;
                } else {
                    // Este NFT me ayuda a alcanzar lo que necesito. No me es suficiente lo que me da.
                    _amount -= selling_amount_of[nft_id];
                    sell_able_from_list(
                        selling_amount_of[nft_id],
                        nft_id,
                        buyer
                    );
                    nfts_to_remove_sell++;
                }

                idx_sell++;
                increment_turn();
            }
        }

        return (nfts_to_remove_sell, nfts_to_remove_driven);
    }
}

contract SinergySale is Router {
    // Constants
    uint256 public MAX_AMOUNT_SELL_TOKEN = 90 ether;
    uint256 public MIN_AMOUNT_SELL_TOKEN = 9 ether;
    uint256 public MIN_AMOUNT_TOKENS_TO_SELL;
    uint256 public TOKEN_PRICE = 3; // 1 TOKEN = 3 STABLECOIN
    uint256 public LIMIT_POST_BY_CYCLE = 1;
    uint256 public PENALIZATION_PLACES = 3;
    uint256 public LIMIT_POST_BY_WALLET = 9;

    // Global Variables
    bool public need_be_qualified_to_sell;
    uint256 public TOTAL_TOKENS_SOLD;
    mapping(uint256 => uint256) public tokens_sold_by_cycle;

    // ERC20 Contracts
    ERC20 public ABLE;
    ERC20 public TOKEN;
    ERC20 public STABLECOIN;

    // Sell List
    uint256[] public sell_list;
    mapping(uint256 => bool) public is_in_sell_list;
    mapping(address => uint256) public amount_of_post_of;

    // Driven List
    uint256[] public driven_list;
    mapping(uint256 => bool) public is_in_driven_list;
    mapping(address => uint256) public turns_in_driven_list_of;

    // Turn of pick
    uint256 public turn; // 0 y 1 => Driven List ||| 2 => Sell List

    // Amount on Sell
    uint256 public total_selling_amount;
    mapping(uint256 => uint256) public selling_amount_of;

    // User Data
    mapping(address => mapping(uint256 => uint256)) public post_by_cycle;
    mapping(address => uint256) public amount_sold_of;
    mapping(address => uint256) public post_amount_of;
    mapping(address => mapping(uint256 => uint256))
        public amount_bought_by_cycle_of;
    address public last_wallet_qualified_who_bought;

    // Events
    event SellList(uint256 indexed nft_id, bool comeIn, bool driven);

    event SellToken(
        uint256 indexed nft_id,
        address indexed buyer,
        address indexed seller,
        uint256 amount
    );

    event SellTokenPenalization(
        uint256 indexed nft_id,
        address indexed buyer,
        address indexed seller,
        uint256 amount
    );

    event SwapList(uint256 indexed nft_id);

    // Constructor
    constructor(ERC20 _able, ERC20 _busd, ERC20 _token) {
        ABLE = _able;
        TOKEN = _token;
        STABLECOIN = _busd;
    }

    // Migrate Functions
    function Migrate_Send_Sell_List(
        uint256 token_id,
        uint256 selling_amount
    ) public {
        require(
            msg.sender == address(MigrationSinergySale),
            "Only Migration Contract can migrate sell list."
        );
        selling_amount_of[token_id] = selling_amount;
        total_selling_amount += selling_amount;

        _SetNftToSellList(token_id);
    }

    function Migrate_Send_Driven_List(
        uint256 token_id,
        uint256 selling_amount
    ) public {
        require(
            msg.sender == address(MigrationSinergySale),
            "Only Migration Contract can migrate driven list."
        );
        selling_amount_of[token_id] = selling_amount;
        total_selling_amount += selling_amount;

        _SetNftToDrivenList(token_id);
    }

    // Public Methods
    function Sell(uint256 _amount, uint256 _tokenID) public {
        uint256 cycle = Cycle.cycle();
        require(
            Qualification.is_updated(msg.sender, cycle),
            "You have to update first."
        );
        require(
            _amount >= MIN_AMOUNT_SELL_TOKEN,
            "The amount to sell is too low."
        );
        require(
            (selling_amount_of[_tokenID] + _amount) <= MAX_AMOUNT_SELL_TOKEN,
            "The amount to sell is too high."
        );
        require(
            SinergyBronze.ownerOf(_tokenID) == msg.sender,
            "You are not the owner of this NFT."
        );
        require(
            !IsSelling(_tokenID),
            "This NFT is already selling tokens on Sinergy Sale"
        );
        require(
            CanSell(msg.sender),
            "You already get the limit of post by cycle."
        );
        require(
            amount_of_post_of[msg.sender] < LIMIT_POST_BY_WALLET,
            "This wallet cannot post more sells on the list."
        );
        if (need_be_qualified_to_sell) {
            require(
                Qualification.IsQualified(msg.sender),
                "You have to be qualified to sell"
            );
        }

        TOKEN.transferFrom(msg.sender, address(this), _amount);

        selling_amount_of[_tokenID] += _amount;
        total_selling_amount += _amount;
        post_by_cycle[msg.sender][cycle]++;

        if (CanBeInDrivenList(msg.sender)) {
            _SetNftToDrivenList(_tokenID);
            turns_in_driven_list_of[msg.sender]++;
        } else {
            _SetNftToSellList(_tokenID);
        }
    }

    function Buy(uint256 _amount, bool incrementBDD) public {
        require(
            Qualification.is_updated(msg.sender, Cycle.cycle()),
            "You have to update first."
        );
        require(
            _amount <= total_selling_amount,
            "Sinergy Sale doesn't have enought Able to sell that amount."
        );

        // Transferimos los STABLECOIN a este contrato
        STABLECOIN.transferFrom(
            msg.sender,
            address(this),
            _amount * TOKEN_PRICE
        );

        // El 33% que va para el vendedor de los tokens, se envian directamente a la billetera del vendedor.
        // Esto se hace en la funcion _SellAbleFromList

        // Transferimos 33% para el Admin
        STABLECOIN.transfer(
            Wallets.managment_wallet(),
            ((_amount * TOKEN_PRICE) / 3)
        );

        // Transferimos 33% para el Bote del Regalo Diario de Able
        STABLECOIN.transfer(
            address(StablecoinBaseReward),
            ((_amount * TOKEN_PRICE) / 3)
        );

        // Transferimos los TOKEN al usuario
        TOKEN.transfer(msg.sender, _amount);

        // Buscamos los vendedores
        uint256 nfts_to_remove_sell;
        uint256 nfts_to_remove_driven;

        (nfts_to_remove_sell, nfts_to_remove_driven) = _FindSellers(
            _amount,
            msg.sender
        );

        // Eliminamos de la lista, los vendedores que vendieron el total de su capital.
        for (uint256 i = 0; i < nfts_to_remove_sell; i++) {
            _RemoveFirstNftFromSellList();
        }

        for (uint256 i = 0; i < nfts_to_remove_driven; i++) {
            _RemoveFirstNftFromDrivenList();
        }

        // Aumentamos el BDD de esta billetera en ABLE
        if (incrementBDD)
            ABLE.IncreasePoints(_amount * TOKEN_PRICE, msg.sender);

        // Aumentamos la cantidad de tokens vendidos.
        TOTAL_TOKENS_SOLD += _amount;
        tokens_sold_by_cycle[Cycle.cycle()] += _amount;

        // Aumentamos la cantidad de tokens comprados por ciclo.
        amount_bought_by_cycle_of[msg.sender][Cycle.cycle()] +=
            _amount *
            TOKEN_PRICE;

        if (Qualification.IsQualified(msg.sender)) {
            last_wallet_qualified_who_bought = msg.sender;
        }
    }

    function QuitSell(uint256 tokenID) public {
        require(
            Qualification.is_updated(msg.sender, Cycle.cycle()),
            "You have to update first."
        );
        require(IsSelling(tokenID), "This NFT is not selling.");
        require(
            SinergyBronze.ownerOf(tokenID) == msg.sender,
            "You are not the owner of this NFT."
        );
        require(
            selling_amount_of[tokenID] > 0,
            "Your NFT doesnt have anything to sell in the list."
        );
        require(
            total_selling_amount >= selling_amount_of[tokenID],
            "The amount that you are selling is greater than all the other sells. :("
        );

        TOKEN.transfer(msg.sender, selling_amount_of[tokenID]);
        // Descontamos el monto que se saca de la venta
        total_selling_amount -= selling_amount_of[tokenID];
        selling_amount_of[tokenID] = 0;

        // Sacamos al NFT de la Lista de Venta TOKEN
        if (is_in_sell_list[tokenID]) {
            _RemoveNftFromSellList(_GetIndexOfNftFromSellList(tokenID));
        } else {
            _RemoveNftFromDrivenList(_GetIndexOfNftFromDrivenList(tokenID));
        }
    }

    // Helpers
    function IsOwner(address wallet) public view returns (bool) {
        return Wallets.IsOwner(wallet);
    }

    // Get Methods
    function GetSellListLength() public view returns (uint256) {
        return sell_list.length;
    }

    function GetDrivenListLength() public view returns (uint256) {
        return driven_list.length;
    }

    // Set Functions
    function SetMinAmountTokensToSell(uint256 amount) public {
        require(
            IsOwner(msg.sender),
            "Only owner can modify the MIN_AMOUNT_TOKENS_TO_SELL."
        );
        MIN_AMOUNT_TOKENS_TO_SELL = amount;
    }

    function SetLimitPostByCycle(uint256 new_limit) public {
        require(
            IsOwner(msg.sender),
            "You are not qualified to call this function."
        );
        LIMIT_POST_BY_CYCLE = new_limit;
    }

    function SetMaxAmountSellToken(uint256 amount) public {
        require(
            IsOwner(msg.sender),
            "You are not qualified to call this function"
        );

        MAX_AMOUNT_SELL_TOKEN = amount;
    }

    function SetMinAmountSellToken(uint256 amount) public {
        require(
            IsOwner(msg.sender),
            "You are not qualified to call this function"
        );

        MIN_AMOUNT_SELL_TOKEN = amount;
    }

    function SetPriceToken(uint256 amount) public {
        require(
            IsOwner(msg.sender),
            "You are not qualified to call this function"
        );

        TOKEN_PRICE = amount * (1 ether);
    }

    function SetPenalizePlaces(uint256 places) public {
        require(
            IsOwner(msg.sender),
            "You are not qualified to set the penalize places."
        );
        PENALIZATION_PLACES = places;
    }

    function SetNeedBeQualifiedToSell() public {
        require(
            IsOwner(msg.sender),
            "You are not qualified to set the need be qualified to sell."
        );
        need_be_qualified_to_sell = !need_be_qualified_to_sell;
    }

    function SetLimitPostByWallet(uint256 limit) public {
        require(
            IsOwner(msg.sender),
            "You are not qualified to set the need be qualified to sell."
        );
        LIMIT_POST_BY_WALLET = limit;
    }

    // Communication
    function TryToSwap(uint256 _tokenID) public {
        require(
            msg.sender == address(SinergyBronze),
            "Only SinergyContract can call to this function."
        );

        if (
            IsSelling(_tokenID) &&
            is_in_sell_list[_tokenID] &&
            (_GetIndexOfNftFromSellList(_tokenID) > driven_list.length)
        ) {
            _RemoveNftFromSellList(_GetIndexOfNftFromSellList(_tokenID));
            _SetNftToDrivenList(_tokenID);

            emit SwapList(_tokenID);
        }
    }

    // Helpers
    function IsSelling(uint256 _tokenID) public view returns (bool) {
        return is_in_sell_list[_tokenID] || is_in_driven_list[_tokenID];
    }

    // If the owner of the NFT was not qualified in the previous
    // cycle, the NFT will be penalized.
    function IsNftPenalized(uint256 tokenID) public view returns (bool) {
        address wallet = SinergyBronze.ownerOf(tokenID);
        return
            !Qualification.qualified_history(wallet, Cycle.cycle() - 1) ||
            (Able.balanceOf(wallet) < MIN_AMOUNT_TOKENS_TO_SELL);
    }

    // Sell Helpers
    function IsSellTurn() public view returns (bool) {
        return (turn == 2);
    }

    function SellListIsEmpty() public view returns (bool) {
        return sell_list.length == 0;
    }

    function CanUseSell() public view returns (bool) {
        return (!SellListIsEmpty() && (IsSellTurn() || DrivenListIsEmpty()));
    }

    function CanSell(address wallet) public view returns (bool) {
        if (
            post_by_cycle[wallet][Cycle.cycle()] < LIMIT_POST_BY_CYCLE &&
            Able.balanceOf(wallet) >= MIN_AMOUNT_TOKENS_TO_SELL
        ) {
            return true;
        }
        return false;
    }

    // Driven Helpers
    function CanBeInDrivenList(address wallet) public view returns (bool) {
        return
            ABLE.won_able_reward(wallet) &&
            ABLE.amount_of_wins_able_reward_of(wallet) >
            turns_in_driven_list_of[wallet];
    }

    function IsDrivenTurn() public view returns (bool) {
        return turn == 0 || turn == 1;
    }

    function DrivenListIsEmpty() public view returns (bool) {
        return driven_list.length == 0;
    }

    function CanUseDriven() public view returns (bool) {
        return (!DrivenListIsEmpty() && (IsDrivenTurn() || SellListIsEmpty()));
    }

    // Private Methods

    function _SetNftToSellList(uint256 _tokenID) private {
        sell_list.push(_tokenID);
        is_in_sell_list[_tokenID] = true;

        emit SellList(_tokenID, true, false);
    }

    function _SetNftToDrivenList(uint256 _tokenID) private {
        driven_list.push(_tokenID);
        is_in_driven_list[_tokenID] = true;

        emit SellList(_tokenID, true, true);
    }

    function _RemoveFirstNftFromSellList() private {
        require(sell_list.length > 0);

        uint256 nft_id = sell_list[0];

        for (uint256 i = 0; i < sell_list.length - 1; i++) {
            sell_list[i] = sell_list[i + 1];
        }

        sell_list.pop();

        is_in_sell_list[nft_id] = false;

        emit SellList(nft_id, false, false);
    }

    function _RemoveFirstNftFromDrivenList() private {
        require(driven_list.length > 0);

        uint256 nft_id = driven_list[0];

        for (uint256 i = 0; i < driven_list.length - 1; i++) {
            driven_list[i] = driven_list[i + 1];
        }

        driven_list.pop();

        is_in_driven_list[nft_id] = false;

        emit SellList(nft_id, false, true);
    }

    function _RemoveNftFromSellList(uint256 idx) private {
        require(sell_list.length > idx);

        uint256 nft_id = sell_list[idx];

        for (uint256 i = idx; i < sell_list.length - 1; i++) {
            sell_list[i] = sell_list[i + 1];
        }

        sell_list.pop();

        is_in_sell_list[nft_id] = false;

        emit SellList(nft_id, false, false);
    }

    function _RemoveNftFromDrivenList(uint256 idx) private {
        require(driven_list.length > idx);

        uint256 nft_id = driven_list[idx];

        for (uint256 i = idx; i < driven_list.length - 1; i++) {
            driven_list[i] = driven_list[i + 1];
        }

        driven_list.pop();

        is_in_driven_list[nft_id] = false;

        emit SellList(nft_id, false, true);
    }

    function _GetIndexOfNftFromSellList(
        uint256 tokenID
    ) private view returns (uint256) {
        for (uint256 i = 0; i < sell_list.length; i++) {
            if (sell_list[i] == tokenID) {
                return i;
            }
        }

        return sell_list.length + 1;
    }

    function _GetIndexOfNftFromDrivenList(
        uint256 tokenID
    ) private view returns (uint256) {
        for (uint256 i = 0; i < driven_list.length; i++) {
            if (driven_list[i] == tokenID) {
                return i;
            }
        }

        return driven_list.length + 1;
    }

    function _SellAbleFromList(
        uint256 _amount,
        uint256 _tokenID,
        address buyer
    ) private {
        address owner = SinergyBronze.ownerOf(_tokenID);

        selling_amount_of[_tokenID] -= _amount;
        total_selling_amount -= _amount;

        // 33% van directo al usuario
        STABLECOIN.transfer(owner, ((_amount * TOKEN_PRICE) / 3));
        amount_sold_of[owner] += ((_amount * TOKEN_PRICE) / 3);

        emit SellToken(_tokenID, buyer, owner, _amount);
    }

    function _SellAbleFromListPenalize(
        uint256 _amount,
        uint256 _tokenID,
        address buyer
    ) private {
        address owner = SinergyBronze.ownerOf(_tokenID);

        selling_amount_of[_tokenID] -= _amount;
        total_selling_amount -= _amount;

        // 16.5% van directo al usuario
        STABLECOIN.transfer(owner, ((_amount * TOKEN_PRICE) / 6));
        amount_sold_of[owner] += ((_amount * TOKEN_PRICE) / 6);

        // 16.5% van directo al regalo diario de Able
        STABLECOIN.transfer(
            address(StablecoinBaseReward),
            ((_amount * TOKEN_PRICE) / 6)
        );

        emit SellTokenPenalization(_tokenID, buyer, owner, _amount);
    }

    // Turn Helper
    function _IncrementTurn() private {
        if (turn == 2) {
            turn = 0;
        } else {
            turn++;
        }
    }

    function _FindSellers(
        uint256 _amount,
        address buyer
    ) private returns (uint256, uint256) {
        bool sell_all = false;
        uint256 nft_id;

        uint256 idx_sell = 0;
        uint256 nfts_to_remove_sell;

        uint256 idx_driven = 0;
        uint256 nfts_to_remove_driven;

        while (!sell_all) {
            // Driven List
            while (
                CanUseDriven() && idx_driven < driven_list.length && !sell_all
            ) {
                nft_id = driven_list[idx_driven];

                if (_amount <= selling_amount_of[nft_id]) {
                    // Este NFT me da lo que necesito.
                    if (_amount == selling_amount_of[nft_id]) {
                        nfts_to_remove_driven++;
                    }

                    if (IsNftPenalized(nft_id)) {
                        _SellAbleFromListPenalize(_amount, nft_id, buyer);
                    } else {
                        _SellAbleFromList(_amount, nft_id, buyer);
                    }

                    sell_all = true;
                } else {
                    // Este NFT me ayuda a alcanzar lo que necesito. No me es suficiente lo que me da.
                    _amount -= selling_amount_of[nft_id];

                    if (IsNftPenalized(nft_id)) {
                        _SellAbleFromListPenalize(
                            selling_amount_of[nft_id],
                            nft_id,
                            buyer
                        );
                    } else {
                        _SellAbleFromList(
                            selling_amount_of[nft_id],
                            nft_id,
                            buyer
                        );
                    }

                    nfts_to_remove_driven++;
                }

                idx_driven++;

                _IncrementTurn();
            }

            // Sell List
            while (CanUseSell() && idx_sell < sell_list.length && !sell_all) {
                nft_id = sell_list[idx_sell];

                if (_amount <= selling_amount_of[nft_id]) {
                    // Este NFT me da lo que necesito.
                    if (_amount == selling_amount_of[nft_id]) {
                        nfts_to_remove_sell++;
                    }

                    if (IsNftPenalized(nft_id)) {
                        _SellAbleFromListPenalize(_amount, nft_id, buyer);
                    } else {
                        _SellAbleFromList(_amount, nft_id, buyer);
                    }

                    sell_all = true;
                } else {
                    // Este NFT me ayuda a alcanzar lo que necesito. No me es suficiente lo que me da.
                    _amount -= selling_amount_of[nft_id];

                    if (IsNftPenalized(nft_id)) {
                        _SellAbleFromListPenalize(
                            selling_amount_of[nft_id],
                            nft_id,
                            buyer
                        );
                    } else {
                        _SellAbleFromList(
                            selling_amount_of[nft_id],
                            nft_id,
                            buyer
                        );
                    }

                    nfts_to_remove_sell++;
                }

                idx_sell++;

                _IncrementTurn();
            }
        }

        return (nfts_to_remove_sell, nfts_to_remove_driven);
    }
}

contract SinergyMigration is ERC721, ERC721URIStorage, Ownable {
    // Constants
    uint256 public constant PRICE = 36 ether;
    uint256 public constant REWARD = 3 ether;
    uint256 public constant TO_DEVELOPMENT = 3 ether;

    // NFT GENESIS
    string constant NFT_GENESIS_NAME = "GENESIS";
    string constant NFT_GENESIS_INSCRIPTION = "GENESIS INSCRIPTION";
    string constant NFT_GENESIS_VALUE_PROPOSAL = "GENESIS VALUE PROPOSAL";
    string constant NFT_GENESIS_IMAGE_URL =
        "https://res.cloudinary.com/saver-community/image/upload/v1666380501/jvjbls4lg5mtxsxhlhnf.png";

    // Migration
    Sinergy ContractMigration =
        Sinergy(0xEa063b5A744616a161272a013a626A1cBD80Ee1B);

    // Owners
    Addresses public Wallets =
        Addresses(0xbc4E59AE11A28214f84FCc1c9B0535355D408BBf);

    // ERC20's
    ERC20 CDA = ERC20(0x8e3153a724aF487Fd11fB4C4cDA508984dEDf3c4);
    ERC20 ABLE = ERC20(0xd9B9c7A1B42f1ad78D9C3Dd5C7F0381277ddc9Bb);
    ERC20 STABLECOIN = ERC20(0xB856De7DAFf71A0d7eAFD4CC22A7db6F762179de);

    // Able Sale
    SinergySale public ABLE_SALE =
        SinergySale(0xD8c101aA6b225135b437E3B87988457B23Adb2f0);
    SinergySale public TRIPLE_SALE =
        SinergySale(0xD8c101aA6b225135b437E3B87988457B23Adb2f0);

    // Address
    address public communityWallet = 0xc8895f6f85D870589C42fd6d531c855bddD27B0f;
    address public constant developmentWallet =
        0x9060723c22dE586c2fA5eFa07A7743F6f4a935f5;

    uint256 public oneDay = 12 minutes;
    uint256 public timeToNextReward;

    // Counters
    using Counters for Counters.Counter;
    Counters.Counter private _tokenIds;

    // Migration
    mapping(address => bool) public isRecover;
    mapping(uint256 => bool) public nftRecover;
    mapping(uint256 => bool) public first_level_references_recover;
    mapping(uint256 => bool) public second_level_references_recover;
    mapping(uint256 => bool) public third_level_references_recover;
    mapping(uint256 => bool) public four_level_references_recover;
    mapping(uint256 => bool) public five_level_references_recover;
    mapping(uint256 => bool) public six_level_references_recover;
    mapping(uint256 => bool) public seven_level_references_recover;
    mapping(uint256 => bool) public eight_level_references_recover;
    mapping(uint256 => bool) public nine_level_references_recover;
    uint256 public starting_nft_id;
    uint256[] public recovered_nfts;
    uint256 public recovered_nfts_amount;

    // Constructor
    constructor(uint256 initial_tokenID) ERC721("Saver Sinergy", "Sinergy") {
        while (_tokenIds.current() < initial_tokenID) {
            _tokenIds.increment();
        }

        timeToNextReward = block.timestamp + oneDay;
        starting_nft_id = initial_tokenID;
    }

    // NFT's
    uint256 public nfts_qualified;
    mapping(address => bool) public is_qualified;
    // mapping(address => uint256) public favourite_nft;
    // mapping(address => uint256[]) public get_my_nfts;
    mapping(uint256 => string) public get_nft_name;
    mapping(uint256 => string) public get_nft_inscription;
    mapping(uint256 => string) public get_nft_value_proposal;
    mapping(uint256 => uint256) public get_nft_timestamp_created;
    mapping(uint256 => string) public get_nft_image_url;

    // mapping(string => uint256[]) public get_nfts_by_keyword; // me queda implementarlo en el crear nft

    // References
    // Esto sabemos que va (Cantidad de NFTs que estan conectados conmigo en el NIVEL x)
    mapping(uint256 => uint256) public get_first_level_amount_reference;
    mapping(uint256 => uint256) public get_second_level_amount_reference;
    mapping(uint256 => uint256) public get_third_level_amount_reference;
    mapping(uint256 => uint256) public get_four_level_amount_reference;
    mapping(uint256 => uint256) public get_five_level_amount_reference;
    mapping(uint256 => uint256) public get_six_level_amount_reference;
    mapping(uint256 => uint256) public get_seven_level_amount_reference;
    mapping(uint256 => uint256) public get_eight_level_amount_reference;
    mapping(uint256 => uint256) public get_nine_level_amount_reference;

    mapping(uint256 => uint256) public get_total_amount_references; // Cantidad de NFTs que estan conectados conmigo en total.

    // Esto puede fallar (NFT ID de cada uno que esta conectado con nosotros en el NIVEL x)
    mapping(uint256 => uint256[]) public get_first_level_references;
    mapping(uint256 => uint256[]) public get_second_level_references;
    mapping(uint256 => uint256[]) public get_third_level_references;
    mapping(uint256 => uint256[]) public get_four_level_references;
    mapping(uint256 => uint256[]) public get_five_level_references;
    mapping(uint256 => uint256[]) public get_six_level_references;
    mapping(uint256 => uint256[]) public get_seven_level_references;
    mapping(uint256 => uint256[]) public get_eight_level_references;
    mapping(uint256 => uint256[]) public get_nine_level_references;

    // NFT al que me conecte
    mapping(uint256 => uint256) public get_nft_reference;

    // Rewards
    mapping(uint256 => uint256) public get_nft_balance_to_claim;
    mapping(uint256 => uint256) public get_nft_rewards_claimed;
    // Passive Rewards
    uint256 public passiveRewardID;
    mapping(uint256 => uint256) public passiveReward;
    mapping(uint256 => uint256) public passiveRewardClaimed;
    mapping(address => uint256) public timestampToClaimPassiveReward;

    // Constancy Rewards (usa el ID de las pasivas)
    mapping(uint256 => uint256) public constancyReward;
    mapping(uint256 => uint256) public constancyRewardClaimed;
    mapping(address => uint256) public timestampToClaimConstancyReward;

    // Resources
    uint256 public resourcesAmount;

    // Stadistics
    mapping(address => uint256) public total_stablecoin_earned;
    mapping(address => uint256) public total_lost_income;
    mapping(address => uint256) public actual_lost_income;

    // Auxs
    mapping(address => uint256) public amount_nfts_considered;

    // Public Functions
    // Migration
    function migrate() public {
        require(!isRecover[msg.sender]);

        if (msg.sender == communityWallet) {
            create_genesis_nfts();
            isRecover[communityWallet] = true;
            return;
        }

        uint256 amountNFTs = ContractMigration.balanceOf(msg.sender);
        uint256[] storage nfts = get_my_nfts[msg.sender];
        uint256[] storage new_nfts_recovered = recovered_nfts;
        uint256 nftID;

        for (uint256 i = 0; i < amountNFTs; i++) {
            nftID = ContractMigration.get_my_nfts(msg.sender, i);
            nfts.push(nftID);
            new_nfts_recovered.push(nftID);
            recovered_nfts_amount++;
            recoverNFT(nftID);
            _safeMint(msg.sender, nftID);
        }

        favourite_nft[msg.sender] = ContractMigration.favourite_nft(msg.sender);
        get_my_nfts[msg.sender] = nfts;
        recovered_nfts = new_nfts_recovered;
        isRecover[msg.sender] = true;
    }

    function recoverNFT(uint256 nftID) private {
        // NFT Basic Info
        get_nft_name[nftID] = ContractMigration.get_nft_name(nftID);
        get_nft_inscription[nftID] = ContractMigration.get_nft_inscription(
            nftID
        );
        get_nft_value_proposal[nftID] = ContractMigration
            .get_nft_value_proposal(nftID);
        get_nft_timestamp_created[nftID] = ContractMigration
            .get_nft_timestamp_created(nftID);
        get_nft_image_url[nftID] = ContractMigration.get_nft_image_url(nftID);
        get_nft_reference[nftID] = ContractMigration.get_nft_reference(nftID);

        // References
        get_first_level_amount_reference[nftID] = ContractMigration
            .get_first_level_amount_reference(nftID);
        get_second_level_amount_reference[nftID] = ContractMigration
            .get_second_level_amount_reference(nftID);
        get_third_level_amount_reference[nftID] = ContractMigration
            .get_third_level_amount_reference(nftID);
        get_four_level_amount_reference[nftID] = ContractMigration
            .get_four_level_amount_reference(nftID);
        get_five_level_amount_reference[nftID] = ContractMigration
            .get_five_level_amount_reference(nftID);
        get_six_level_amount_reference[nftID] = ContractMigration
            .get_six_level_amount_reference(nftID);
        get_seven_level_amount_reference[nftID] = ContractMigration
            .get_seven_level_amount_reference(nftID);
        get_eight_level_amount_reference[nftID] = ContractMigration
            .get_eight_level_amount_reference(nftID);
        get_nine_level_amount_reference[nftID] = ContractMigration
            .get_nine_level_amount_reference(nftID);

        get_total_amount_references[nftID] = ContractMigration
            .get_total_amount_references(nftID);

        nftRecover[nftID] = true;
    }

    function recoverFirstLevelReferences(uint256 nftID) public {
        require(
            msg.sender == ownerOf(nftID) &&
                !first_level_references_recover[nftID]
        );

        uint256[] storage nfts = get_first_level_references[nftID];
        // uint256 amount = ContractMigration.get_first_level_amount_reference(nftID);

        for (
            uint256 i = 0;
            i < ContractMigration.get_first_level_amount_reference(nftID);
            i++
        ) {
            nfts.push(ContractMigration.get_first_level_references(nftID, i));
        }

        get_first_level_references[nftID] = nfts;
        first_level_references_recover[nftID] = true;
    }

    function recoverSecondLevelReferences(uint256 nftID) public {
        require(
            msg.sender == ownerOf(nftID) &&
                !second_level_references_recover[nftID]
        );

        uint256[] storage nfts = get_second_level_references[nftID];
        // uint256 amount = ContractMigration.get_second_level_amount_reference(nftID);

        for (
            uint256 i = 0;
            i < ContractMigration.get_second_level_amount_reference(nftID);
            i++
        ) {
            nfts.push(ContractMigration.get_second_level_references(nftID, i));
        }

        get_second_level_references[nftID] = nfts;
        second_level_references_recover[nftID] = true;
    }

    function recoverThirdLevelReferences(uint256 nftID) public {
        require(
            msg.sender == ownerOf(nftID) &&
                !third_level_references_recover[nftID]
        );

        uint256[] storage nfts = get_third_level_references[nftID];
        // uint256 amount = ContractMigration.get_third_level_amount_reference(nftID);

        for (
            uint256 i = 0;
            i < ContractMigration.get_third_level_amount_reference(nftID);
            i++
        ) {
            nfts.push(ContractMigration.get_third_level_references(nftID, i));
        }

        get_third_level_references[nftID] = nfts;
        third_level_references_recover[nftID] = true;
    }

    function recoverFourLevelReferences(uint256 nftID) public {
        require(
            msg.sender == ownerOf(nftID) &&
                !four_level_references_recover[nftID]
        );

        uint256[] storage nfts = get_four_level_references[nftID];
        // uint256 amount = ContractMigration.get_four_level_amount_reference(nftID);

        for (
            uint256 i = 0;
            i < ContractMigration.get_four_level_amount_reference(nftID);
            i++
        ) {
            nfts.push(ContractMigration.get_four_level_references(nftID, i));
        }

        get_four_level_references[nftID] = nfts;
        four_level_references_recover[nftID] = true;
    }

    function recoverFiveLevelReferences(uint256 nftID) public {
        require(
            msg.sender == ownerOf(nftID) &&
                !five_level_references_recover[nftID]
        );

        uint256[] storage nfts = get_five_level_references[nftID];
        // uint256 amount = ContractMigration.get_five_level_amount_reference(nftID);

        for (
            uint256 i = 0;
            i < ContractMigration.get_five_level_amount_reference(nftID);
            i++
        ) {
            nfts.push(ContractMigration.get_five_level_references(nftID, i));
        }

        get_five_level_references[nftID] = nfts;
        five_level_references_recover[nftID] = true;
    }

    function recoverSixLevelReferences(uint256 nftID) public {
        require(
            msg.sender == ownerOf(nftID) && !six_level_references_recover[nftID]
        );

        uint256[] storage nfts = get_six_level_references[nftID];
        // uint256 amount = ContractMigration.get_six_level_amount_reference(nftID);

        for (
            uint256 i = 0;
            i < ContractMigration.get_six_level_amount_reference(nftID);
            i++
        ) {
            nfts.push(ContractMigration.get_six_level_references(nftID, i));
        }

        get_six_level_references[nftID] = nfts;
        six_level_references_recover[nftID] = true;
    }

    function recoverSevenLevelReferences(uint256 nftID) public {
        require(
            msg.sender == ownerOf(nftID) &&
                !seven_level_references_recover[nftID]
        );

        uint256[] storage nfts = get_seven_level_references[nftID];
        // uint256 amount = ContractMigration.get_seven_level_amount_reference(nftID);

        for (
            uint256 i = 0;
            i < ContractMigration.get_seven_level_amount_reference(nftID);
            i++
        ) {
            nfts.push(ContractMigration.get_seven_level_references(nftID, i));
        }

        get_seven_level_references[nftID] = nfts;
        seven_level_references_recover[nftID] = true;
    }

    function recoverEightLevelReferences(uint256 nftID) public {
        require(
            msg.sender == ownerOf(nftID) &&
                !eight_level_references_recover[nftID]
        );

        uint256[] storage nfts = get_eight_level_references[nftID];
        // uint256 amount = ContractMigration.get_eight_level_amount_reference(nftID);

        for (
            uint256 i = 0;
            i < ContractMigration.get_eight_level_amount_reference(nftID);
            i++
        ) {
            nfts.push(ContractMigration.get_eight_level_references(nftID, i));
        }

        get_eight_level_references[nftID] = nfts;
        eight_level_references_recover[nftID] = true;
    }

    function recoverNineLevelReferences(uint256 nftID) public {
        require(
            msg.sender == ownerOf(nftID) &&
                !nine_level_references_recover[nftID]
        );

        uint256[] storage nfts = get_nine_level_references[nftID];
        // uint256 amount = ContractMigration.get_nine_level_amount_reference(nftID);

        for (
            uint256 i = 0;
            i < ContractMigration.get_nine_level_amount_reference(nftID);
            i++
        ) {
            nfts.push(ContractMigration.get_nine_level_references(nftID, i));
        }

        get_nine_level_references[nftID] = nfts;
        nine_level_references_recover[nftID] = true;
    }

    // NFT
    function createNFT(
        string memory _name,
        string memory _inscription,
        string memory _valueProposal,
        string memory _uri,
        string memory _imageURL,
        uint256 _ref,
        uint256 _timestamp
    ) public {
        // Get Reference

        uint256 _reference = favourite_nft[
            ABLE_SALE.last_wallet_qualified_who_bought()
        ];

        if (_ref != 0) {
            _reference = _ref;
        }

        if (_reference == 0) {
            _reference = 8;
        }

        require(_reference < _tokenIds.current());

        STABLECOIN.transferFrom(msg.sender, address(this), PRICE);

        update_qualified_nfts(msg.sender);

        ABLE_SALE.TryToSwap(favourite_nft[msg.sender]);

        // Mint NFT
        uint256 tokenID = _tokenIds.current();
        _tokenIds.increment();
        _safeMint(msg.sender, tokenID);

        // Set URI
        _setTokenURI(tokenID, _uri);

        // Add information to the NFT
        get_nft_name[tokenID] = _name;
        get_nft_inscription[tokenID] = _inscription;
        get_nft_value_proposal[tokenID] = _valueProposal;
        get_nft_reference[tokenID] = _reference;

        get_nft_image_url[tokenID] = _imageURL;
        get_nft_timestamp_created[tokenID] = _timestamp;

        if (favourite_nft[msg.sender] == 0) {
            favourite_nft[msg.sender] = tokenID;
        }

        uint256[] storage myNFTS = get_my_nfts[msg.sender];
        myNFTS.push(tokenID);
        get_my_nfts[msg.sender] = myNFTS;

        // Increase the resources amount
        resourcesAmount += TO_DEVELOPMENT;

        // Increase Passive Rewards
        passiveReward[passiveRewardID] += 6 ether;

        // Distribute STABLECOIN's in 9 generations
        distribute(tokenID, _reference, true);

        update(msg.sender);

        // Emit event
        // emit Mint(tokenID, block.timestamp, _name, _valueProposal, msg.sender);
    }

    function modifyNFT(
        string memory _name,
        string memory _inscription,
        string memory _valueProposal,
        uint256 _tokenID
    ) public {
        require(msg.sender == ownerOf(_tokenID));
        STABLECOIN.transferFrom(msg.sender, address(this), PRICE);

        // Modify the NFT
        get_nft_name[_tokenID] = _name;
        get_nft_inscription[_tokenID] = _inscription;
        get_nft_value_proposal[_tokenID] = _valueProposal;

        // Increase the resources amount
        resourcesAmount += TO_DEVELOPMENT;

        // Increase Passive Rewards
        passiveReward[passiveRewardID] += 6 ether;

        // Distribute STABLECOIN in 9 generations
        distribute(_tokenID, get_nft_reference[_tokenID], false);

        update(msg.sender);
    }

    // Rewards
    function claimReward(uint256 _tokenID) public {
        require(
            msg.sender == ownerOf(_tokenID) &&
                get_nft_balance_to_claim[_tokenID] > 0
        );

        STABLECOIN.transfer(msg.sender, get_nft_balance_to_claim[_tokenID]);

        // // Emit events
        // emit Reward(
        //     _tokenID,
        //     get_nft_balance_to_claim[_tokenID],
        //     msg.sender,
        //     block.timestamp
        // );

        get_nft_rewards_claimed[_tokenID] += get_nft_balance_to_claim[_tokenID];
        get_nft_balance_to_claim[_tokenID] = 0;

        update(msg.sender);
    }

    function viewAmountToClaimPassiveReward(
        address wallet
    ) public view returns (uint256) {
        if (passiveRewardID == 0 || nfts_qualified == 0) return 0;
        if (nfts_qualified < balanceOf(wallet)) return 0;

        uint256 amount_raised = passiveReward[passiveRewardID - 1];
        uint256 amount_claimed = passiveRewardClaimed[passiveRewardID - 1];
        uint256 amount_to_claim = ((balanceOf(wallet) * amount_raised) /
            nfts_qualified);

        if (amount_to_claim > (amount_raised - amount_claimed)) {
            return (amount_raised - amount_claimed);
        }

        return amount_to_claim;
    }

    function viewAmountToClaimConstancyReward(
        address wallet
    ) public view returns (uint256) {}

    function claimPassiveReward() public {}

    function claimConstancyReward() public {}

    function claimResources() public {
        require(msg.sender == developmentWallet);

        STABLECOIN.transfer(msg.sender, resourcesAmount);

        // Emit events
        // emit Resources(block.timestamp, resourcesAmount);

        resourcesAmount = 0;

        update(msg.sender);
    }

    // Read functions
    function getAmountOfNftMinted() public view returns (uint256) {
        return _tokenIds.current();
    }

    // Set Functions
    function setAbleAddress(address _ableAddress) public {
        require(msg.sender == developmentWallet);

        ABLE = ERC20(_ableAddress);
    }

    function setFavouriteNFT(uint256 id) public {
        require(_tokenIds.current() > id);
        favourite_nft[msg.sender] = id;
        update(msg.sender);
    }

    function changeCommunityWallet(address newAddress) public {
        require(msg.sender == communityWallet);

        communityWallet = newAddress;
    }

    // Booleans
    function nft_was_qualified(uint256 tokenID) public pure returns (bool) {
        // This is a Random code just to make compile the interface of the Migration Contract without Warnings.
        return (tokenID == 0);
    }

    // Update Functions
    function update_qualified_nfts(address wallet) private {}

    function update_timestamp() private {
        if (block.timestamp > timeToNextReward) {
            if (
                passiveRewardID > 0 &&
                passiveReward[passiveRewardID - 1] >
                passiveRewardClaimed[passiveRewardID - 1]
            ) {
                passiveReward[passiveRewardID] += (passiveReward[
                    passiveRewardID - 1
                ] - passiveRewardClaimed[passiveRewardID - 1]);
            }

            if (
                passiveRewardID > 0 &&
                constancyReward[passiveRewardID - 1] >
                constancyRewardClaimed[passiveRewardID - 1]
            ) {
                constancyReward[passiveRewardID] += (constancyReward[
                    passiveRewardID - 1
                ] - constancyRewardClaimed[passiveRewardID - 1]);
            }

            passiveRewardID++;
            timeToNextReward = block.timestamp + oneDay;
        }
    }

    function update(address wallet) public {}

    function updateFromAble(address wallet) public {
        update_timestamp();
        update_qualified_nfts(wallet);
    }

    function getAbleBalance(address wallet) public view returns (uint256) {
        return ABLE.balanceOf(wallet);
    }

    // SaleToken Public Functions
    function set_able_sale(SinergySale ableSale) public {
        require(msg.sender == developmentWallet);
        ABLE_SALE = ableSale;
    }

    function set_triple_sale(SinergySale tripleSale) public {
        require(msg.sender == developmentWallet);
        TRIPLE_SALE = tripleSale;
    }

    function set_passive_rewards(uint256 amount) public {
        require(
            msg.sender == address(ABLE_SALE) ||
                msg.sender == address(TRIPLE_SALE)
        );
        passiveReward[passiveRewardID] += amount;
    }

    function set_active_rewards(uint256 tokenID, uint256 amount) public {
        require(
            msg.sender == address(ABLE_SALE) ||
                msg.sender == address(TRIPLE_SALE)
        );
        get_nft_balance_to_claim[tokenID] += amount;
    }

    function set_constancy_reward(uint256 amount) public {
        require(
            msg.sender == address(ABLE_SALE) ||
                msg.sender == address(TRIPLE_SALE)
        );
        constancyReward[passiveRewardID] += amount;
    }

    // Private Functions

    // Distribute 9 generations
    function distribute(
        uint256 tokenID,
        uint256 _reference,
        bool created
    ) private {}

    function create_genesis_nfts() private {
        // Crear 8 NFTs para la billetera destinada a Desarrollo y Mantenimiento
        // Estos NFTs deben estar vinculados entre si
        uint256[] storage myNFTS = get_my_nfts[communityWallet];
        uint256[] storage new_nfts_recovered = recovered_nfts;

        for (uint256 i = 0; i < 9; i++) {
            _safeMint(communityWallet, i);

            recoverNFT(i);

            myNFTS.push(i);
            new_nfts_recovered.push(i);
            recovered_nfts_amount++;

            _setTokenURI(
                i,
                "ipfs://QmRi1DvgDu6zAJwpbURGNBBQTM82ZCNZAyTkEArbKZKm1U/0.json"
            );
        }

        get_my_nfts[communityWallet] = myNFTS;
        recovered_nfts = new_nfts_recovered;
    }

    function setReferences(uint256 i, uint256 tokenID, uint256 ref) private {
        if (i == 0) {
            get_first_level_amount_reference[ref]++;
            get_total_amount_references[ref]++;
            uint256[] storage nftIDs = get_first_level_references[ref];
            nftIDs.push(tokenID);
            get_first_level_references[ref] = nftIDs;

            return;
        }

        if (i == 1) {
            get_second_level_amount_reference[ref]++;
            get_total_amount_references[ref]++;
            uint256[] storage nftIDs = get_second_level_references[ref];
            nftIDs.push(tokenID);
            get_second_level_references[ref] = nftIDs;

            return;
        }

        if (i == 2) {
            get_third_level_amount_reference[ref]++;
            get_total_amount_references[ref]++;
            uint256[] storage nftIDs = get_third_level_references[ref];
            nftIDs.push(tokenID);
            get_third_level_references[ref] = nftIDs;

            return;
        }

        if (i == 3) {
            get_four_level_amount_reference[ref]++;
            get_total_amount_references[ref]++;
            uint256[] storage nftIDs = get_four_level_references[ref];
            nftIDs.push(tokenID);
            get_four_level_references[ref] = nftIDs;

            return;
        }

        if (i == 4) {
            get_five_level_amount_reference[ref]++;
            get_total_amount_references[ref]++;
            uint256[] storage nftIDs = get_five_level_references[ref];
            nftIDs.push(tokenID);
            get_five_level_references[ref] = nftIDs;

            return;
        }

        if (i == 5) {
            get_six_level_amount_reference[ref]++;
            get_total_amount_references[ref]++;
            uint256[] storage nftIDs = get_six_level_references[ref];
            nftIDs.push(tokenID);
            get_six_level_references[ref] = nftIDs;

            return;
        }

        if (i == 6) {
            get_seven_level_amount_reference[ref]++;
            get_total_amount_references[ref]++;
            uint256[] storage nftIDs = get_seven_level_references[ref];
            nftIDs.push(tokenID);
            get_seven_level_references[ref] = nftIDs;

            return;
        }

        if (i == 7) {
            get_eight_level_amount_reference[ref]++;
            get_total_amount_references[ref]++;
            uint256[] storage nftIDs = get_eight_level_references[ref];
            nftIDs.push(tokenID);
            get_eight_level_references[ref] = nftIDs;

            return;
        }

        if (i == 8) {
            get_nine_level_amount_reference[ref]++;
            get_total_amount_references[ref]++;
            uint256[] storage nftIDs = get_nine_level_references[ref];
            nftIDs.push(tokenID);
            get_nine_level_references[ref] = nftIDs;

            return;
        }
    }

    // The following functions are overrides required by Solidity.

    function _burn(
        uint256 tokenId
    ) internal override(ERC721, ERC721URIStorage) {
        super._burn(tokenId);
    }

    function tokenURI(
        uint256 tokenId
    ) public view override(ERC721, ERC721URIStorage) returns (string memory) {
        return super.tokenURI(tokenId);
    }
}

contract SinergyNovember is ERC721, ERC721URIStorage, Ownable {
    // Constants
    uint256 public constant PRICE = 36 ether;
    uint256 public constant REWARD = 3 ether;
    uint256 public constant TO_DEVELOPMENT = 3 ether;

    // MSG_FAIL_TRANSFER
    string constant MSG_FAIL_TRANSFER_XBUSD = "BUSD TRANSFER FAILED";
    string constant MSG_FAIL_TRANSFER_BUSD = "BUSD TRANSFER FAILED";
    string constant MSG_FAIL_TRANSFER_CDA = "CDA TRANSFER FAILED";
    string constant MSG_FAIL_TRANSFER_ABLE = "ABLE TRANSFER FAILED";

    string constant MSG_FAIL_OWNER_NFT = "You are not the owner of this NFT";

    // MSG_FAIL_EVENT
    string constant MSG_FAIL_EVENT_ATTENDANCE =
        "You already are in the list of this event";
    string constant MSG_FAIL_EVENT_NOT_EXIST = "This event doesn't exist yet";
    string constant MSG_FAIL_NFT_NOT_EXIST = "This NFT doesn't exist yet";

    // MSG_FAIL_REWARD
    string constant MSG_FAIL_REWARD = "You don't have any reward to claim";
    string constant NFT_GENESIS_NAME = "GENESIS";
    string constant NFT_GENESIS_INSCRIPTION = "GENESIS INSCRIPTION";
    string constant NFT_GENESIS_VALUE_PROPOSAL = "GENESIS VALUE PROPOSAL";
    string constant NFT_GENESIS_IMAGE_URL =
        "https://res.cloudinary.com/saver-community/image/upload/v1666380501/jvjbls4lg5mtxsxhlhnf.png";

    // Migration
    Sinergy ContractMigration =
        Sinergy(0x38e43FCEEE68373e08a30714123010b8d841364d);

    // ERC20's
    ERC20 CDA = ERC20(0x38e43FCEEE68373e08a30714123010b8d841364d);
    ERC20 ABLE = ERC20(0xB13D289830F6512dFf4C6ce97f121F29bD400E39);
    ERC20 BUSD = ERC20(0xd9C7C02dBF4451d89040cd2a576b615327ccF38b);

    // Able Sale
    SinergySale public ABLE_SALE =
        SinergySale(0xd9C7C02dBF4451d89040cd2a576b615327ccF38b);
    SinergySale public TRIPLE_SALE =
        SinergySale(0xd9C7C02dBF4451d89040cd2a576b615327ccF38b);

    // Address
    address public communityWallet = 0xc8895f6f85D870589C42fd6d531c855bddD27B0f;
    address public constant developmentWallet =
        0x9060723c22dE586c2fA5eFa07A7743F6f4a935f5;

    uint256 public oneDay = 1 days;
    uint256 public timeToNextReward;

    // Counters
    using Counters for Counters.Counter;
    Counters.Counter private _tokenIds;

    // Constructor
    constructor(uint256 initial_tokenID) ERC721("Saver Sinergy", "Sinergy") {
        while (_tokenIds.current() < initial_tokenID) {
            _tokenIds.increment();
        }

        timeToNextReward = block.timestamp + oneDay;
    }

    // Migration
    mapping(address => bool) public isRecover;

    // NFT's
    uint256 public nfts_qualified;
    mapping(address => bool) public is_qualified;
    // mapping(address => uint256) public favourite_nft;
    // mapping(address => uint256[]) public get_my_nfts;
    mapping(uint256 => string) public get_nft_name;
    mapping(uint256 => string) public get_nft_inscription;
    mapping(uint256 => string) public get_nft_value_proposal;
    mapping(uint256 => uint256) public get_nft_timestamp_created;
    mapping(uint256 => string) public get_nft_image_url;

    // mapping(string => uint256[]) public get_nfts_by_keyword; // me queda implementarlo en el crear nft

    // References
    // Esto sabemos que va (Cantidad de NFTs que estan conectados conmigo en el NIVEL x)
    mapping(uint256 => uint256) public get_first_level_amount_reference;
    mapping(uint256 => uint256) public get_second_level_amount_reference;
    mapping(uint256 => uint256) public get_third_level_amount_reference;
    mapping(uint256 => uint256) public get_four_level_amount_reference;
    mapping(uint256 => uint256) public get_five_level_amount_reference;
    mapping(uint256 => uint256) public get_six_level_amount_reference;
    mapping(uint256 => uint256) public get_seven_level_amount_reference;
    mapping(uint256 => uint256) public get_eight_level_amount_reference;
    mapping(uint256 => uint256) public get_nine_level_amount_reference;

    mapping(uint256 => uint256) public get_total_amount_references; // Cantidad de NFTs que estan conectados conmigo en total.

    // Esto puede fallar (NFT ID de cada uno que esta conectado con nosotros en el NIVEL x)
    mapping(uint256 => uint256[]) public get_first_level_references;
    mapping(uint256 => uint256[]) public get_second_level_references;
    mapping(uint256 => uint256[]) public get_third_level_references;
    mapping(uint256 => uint256[]) public get_four_level_references;
    mapping(uint256 => uint256[]) public get_five_level_references;
    mapping(uint256 => uint256[]) public get_six_level_references;
    mapping(uint256 => uint256[]) public get_seven_level_references;
    mapping(uint256 => uint256[]) public get_eight_level_references;
    mapping(uint256 => uint256[]) public get_nine_level_references;

    // NFT al que me conecte
    mapping(uint256 => uint256) public get_nft_reference;

    // Rewards
    mapping(uint256 => uint256) public get_nft_balance_to_claim;
    mapping(uint256 => uint256) public get_nft_rewards_claimed;
    // Passive Rewards
    uint256 public passiveRewardID;
    mapping(uint256 => uint256) public passiveReward;
    mapping(uint256 => uint256) public passiveRewardClaimed;
    mapping(address => uint256) public timestampToClaimPassiveReward;

    // Constancy Rewards (usa el ID de las pasivas)
    mapping(uint256 => uint256) public constancyReward;
    mapping(uint256 => uint256) public constancyRewardClaimed;
    mapping(address => uint256) public timestampToClaimConstancyReward;

    // Resources
    uint256 public resourcesAmount;

    // Stadistics
    mapping(address => uint256) public total_stablecoin_earned;
    mapping(address => uint256) public total_lost_income;
    mapping(address => uint256) public actual_lost_income;

    // Auxs
    mapping(address => uint256) public amount_nfts_considered;

    // Events
    event Mint(
        uint256 id,
        uint256 date,
        string indexed name,
        string indexed valueProposal,
        address indexed wallet
    );

    event PassiveReward(
        uint256 amount,
        address indexed wallet,
        uint256 indexed date
    );

    event Resources(uint256 indexed date, uint256 amount);

    // Public Functions
    // Migration
    function migrate() public {
        require(!isRecover[msg.sender], "You already migrated.");
        uint256 amountNFTs = ContractMigration.balanceOf(msg.sender);
        for (uint256 i = 0; i < amountNFTs; i++) {
            get_my_nfts[msg.sender][i] = ContractMigration.get_my_nfts(
                msg.sender,
                i
            );
            recoverNFT(get_my_nfts[msg.sender][i]);
        }

        isRecover[msg.sender] = true;
    }

    function recoverNFT(uint256 nftID) private {
        // NFT Basic Info
        get_nft_name[nftID] = ContractMigration.get_nft_name(nftID);
        get_nft_inscription[nftID] = ContractMigration.get_nft_inscription(
            nftID
        );
        get_nft_value_proposal[nftID] = ContractMigration
            .get_nft_value_proposal(nftID);
        get_nft_timestamp_created[nftID] = ContractMigration
            .get_nft_timestamp_created(nftID);
        get_nft_image_url[nftID] = ContractMigration.get_nft_image_url(nftID);

        // References
        get_first_level_amount_reference[nftID] = ContractMigration
            .get_first_level_amount_reference(nftID);
        get_second_level_amount_reference[nftID] = ContractMigration
            .get_second_level_amount_reference(nftID);
        get_third_level_amount_reference[nftID] = ContractMigration
            .get_third_level_amount_reference(nftID);
        get_four_level_amount_reference[nftID] = ContractMigration
            .get_four_level_amount_reference(nftID);
        get_five_level_amount_reference[nftID] = ContractMigration
            .get_five_level_amount_reference(nftID);
        get_six_level_amount_reference[nftID] = ContractMigration
            .get_six_level_amount_reference(nftID);
        get_seven_level_amount_reference[nftID] = ContractMigration
            .get_seven_level_amount_reference(nftID);
        get_eight_level_amount_reference[nftID] = ContractMigration
            .get_eight_level_amount_reference(nftID);
        get_nine_level_amount_reference[nftID] = ContractMigration
            .get_nine_level_amount_reference(nftID);
        get_total_amount_references[nftID] = ContractMigration
            .get_total_amount_references(nftID);

        recoverFirstLevelReferences(
            get_first_level_amount_reference[nftID],
            nftID
        );
        recoverSecondLevelReferences(
            get_second_level_amount_reference[nftID],
            nftID
        );
        recoverThirdLevelReferences(
            get_third_level_amount_reference[nftID],
            nftID
        );
        recoverFourLevelReferences(
            get_four_level_amount_reference[nftID],
            nftID
        );
        recoverFiveLevelReferences(
            get_five_level_amount_reference[nftID],
            nftID
        );
        recoverSixLevelReferences(get_six_level_amount_reference[nftID], nftID);
        recoverSevenLevelReferences(
            get_seven_level_amount_reference[nftID],
            nftID
        );
        recoverEightLevelReferences(
            get_eight_level_amount_reference[nftID],
            nftID
        );
        recoverNineLevelReferences(
            get_nine_level_amount_reference[nftID],
            nftID
        );
    }

    function recoverFirstLevelReferences(
        uint256 amount,
        uint256 nftID
    ) private {
        for (uint256 i = 0; i < amount; i++) {
            get_first_level_references[nftID][i] = ContractMigration
                .get_first_level_references(nftID, i);
        }
    }

    function recoverSecondLevelReferences(
        uint256 amount,
        uint256 nftID
    ) private {
        for (uint256 i = 0; i < amount; i++) {
            get_second_level_references[nftID][i] = ContractMigration
                .get_second_level_references(nftID, i);
        }
    }

    function recoverThirdLevelReferences(
        uint256 amount,
        uint256 nftID
    ) private {
        for (uint256 i = 0; i < amount; i++) {
            get_third_level_references[nftID][i] = ContractMigration
                .get_third_level_references(nftID, i);
        }
    }

    function recoverFourLevelReferences(uint256 amount, uint256 nftID) private {
        for (uint256 i = 0; i < amount; i++) {
            get_four_level_references[nftID][i] = ContractMigration
                .get_four_level_references(nftID, i);
        }
    }

    function recoverFiveLevelReferences(uint256 amount, uint256 nftID) private {
        for (uint256 i = 0; i < amount; i++) {
            get_five_level_references[nftID][i] = ContractMigration
                .get_five_level_references(nftID, i);
        }
    }

    function recoverSixLevelReferences(uint256 amount, uint256 nftID) private {
        for (uint256 i = 0; i < amount; i++) {
            get_six_level_references[nftID][i] = ContractMigration
                .get_six_level_references(nftID, i);
        }
    }

    function recoverSevenLevelReferences(
        uint256 amount,
        uint256 nftID
    ) private {
        for (uint256 i = 0; i < amount; i++) {
            get_seven_level_references[nftID][i] = ContractMigration
                .get_seven_level_references(nftID, i);
        }
    }

    function recoverEightLevelReferences(
        uint256 amount,
        uint256 nftID
    ) private {
        for (uint256 i = 0; i < amount; i++) {
            get_eight_level_references[nftID][i] = ContractMigration
                .get_eight_level_references(nftID, i);
        }
    }

    function recoverNineLevelReferences(uint256 amount, uint256 nftID) private {
        for (uint256 i = 0; i < amount; i++) {
            get_nine_level_references[nftID][i] = ContractMigration
                .get_nine_level_references(nftID, i);
        }
    }

    // NFT
    function createNFT(
        string memory _name,
        string memory _inscription,
        string memory _valueProposal,
        string memory _uri,
        string memory _imageURL,
        uint256 _timestamp
    ) public {}

    function modifyNFT(
        string memory _name,
        string memory _inscription,
        string memory _valueProposal,
        uint256 _tokenID
    ) public {
        require(msg.sender == ownerOf(_tokenID), MSG_FAIL_OWNER_NFT);
        require(
            BUSD.transferFrom(msg.sender, address(this), PRICE),
            MSG_FAIL_TRANSFER_BUSD
        );

        // Modify the NFT
        get_nft_name[_tokenID] = _name;
        get_nft_inscription[_tokenID] = _inscription;
        get_nft_value_proposal[_tokenID] = _valueProposal;

        // Increase the resources amount
        resourcesAmount += TO_DEVELOPMENT;

        // Increase Passive Rewards
        passiveReward[passiveRewardID] += 6 ether;

        // Distribute BUSD in 9 generations
        distribute(_tokenID, get_nft_reference[_tokenID], false);

        update(msg.sender);
    }

    // Rewards
    function claimReward(uint256 _tokenID) public {}

    function viewAmountToClaimPassiveReward(
        address wallet
    ) public view returns (uint256) {
        if (passiveRewardID == 0 || nfts_qualified == 0) return 0;
        if (nfts_qualified < balanceOf(wallet)) return 0;

        uint256 amount_raised = passiveReward[passiveRewardID - 1];
        uint256 amount_claimed = passiveRewardClaimed[passiveRewardID - 1];
        uint256 amount_to_claim = ((balanceOf(wallet) * amount_raised) /
            nfts_qualified);

        if (amount_to_claim > (amount_raised - amount_claimed)) {
            return (amount_raised - amount_claimed);
        }

        return amount_to_claim;
    }

    function viewAmountToClaimConstancyReward(
        address wallet
    ) public view returns (uint256) {
        if (wallet == address(0)) return balanceOf(msg.sender);
        return 0;
    }

    function claimPassiveReward() public {}

    function claimConstancyReward() public {}

    function claimResources() public {
        require(
            msg.sender == communityWallet,
            "You are not qualified to call this function"
        );

        BUSD.transfer(communityWallet, resourcesAmount);
        CDA.transfer(communityWallet, CDA.balanceOf(address(this)));
        ABLE.transfer(communityWallet, ABLE.balanceOf(address(this)));

        // Emit events
        emit Resources(block.timestamp, resourcesAmount);

        resourcesAmount = 0;

        update(msg.sender);
    }

    // Read functions
    function getAmountOfNftMinted() public view returns (uint256) {
        return _tokenIds.current();
    }

    // Set Functions
    function setAbleAddress(address _ableAddress) public {
        require(
            msg.sender == developmentWallet,
            "You are not qualified to call this function"
        );

        ABLE = ERC20(_ableAddress);
    }

    function setFavouriteNFT(uint256 id) public {
        require(_tokenIds.current() > id, "This NFT doesn't exist yet");
        favourite_nft[msg.sender] = id;
        update(msg.sender);
    }

    function changeCommunityWallet(address newAddress) public {
        require(
            msg.sender == communityWallet,
            "You are not able to call this function"
        );

        communityWallet = newAddress;
    }

    function getDifference(
        uint256 a,
        uint256 b
    ) private pure returns (uint256) {
        if (a > b) {
            return a - b;
        }

        return b - a;
    }

    // Booleans
    function nft_was_qualified(uint256 tokenID) public view returns (bool) {
        if (tokenID == 0) return balanceOf(msg.sender) == 0;
        return true;
    }

    // Update Functions
    function update_qualified_nfts(address wallet) private {}

    function update_timestamp() private {
        if (block.timestamp > timeToNextReward) {
            if (
                passiveRewardID > 0 &&
                passiveReward[passiveRewardID - 1] >
                passiveRewardClaimed[passiveRewardID - 1]
            ) {
                passiveReward[passiveRewardID] += (passiveReward[
                    passiveRewardID - 1
                ] - passiveRewardClaimed[passiveRewardID - 1]);
            }

            if (
                passiveRewardID > 0 &&
                constancyReward[passiveRewardID - 1] >
                constancyRewardClaimed[passiveRewardID - 1]
            ) {
                constancyReward[passiveRewardID] += (constancyReward[
                    passiveRewardID - 1
                ] - constancyRewardClaimed[passiveRewardID - 1]);
            }

            passiveRewardID++;
            timeToNextReward = block.timestamp + oneDay;
        }
    }

    function update(address wallet) public {}

    function updateFromAble(address wallet) public {
        update_timestamp();
        update_qualified_nfts(wallet);
    }

    function getAbleBalance(address wallet) public view returns (uint256) {
        return ABLE.balanceOf(wallet);
    }

    // SaleToken Public Functions
    function set_able_sale(SinergySale ableSale) public {
        require(
            msg.sender == developmentWallet,
            "Only Development Wallet can call to this function."
        );
        ABLE_SALE = ableSale;
    }

    function set_triple_sale(SinergySale tripleSale) public {
        require(
            msg.sender == developmentWallet,
            "Only Development Wallet can call to this function."
        );
        TRIPLE_SALE = tripleSale;
    }

    function set_passive_rewards(uint256 amount) public {
        require(
            msg.sender == address(ABLE_SALE) ||
                msg.sender == address(TRIPLE_SALE),
            "Only ABLE_SALE or TRIPLE_SALE can call to this function"
        );
        passiveReward[passiveRewardID] += amount;
    }

    function set_active_rewards(uint256 tokenID, uint256 amount) public {
        require(
            msg.sender == address(ABLE_SALE) ||
                msg.sender == address(TRIPLE_SALE),
            "Only ABLE_SALE or TRIPLE_SALE can call to this function"
        );
        get_nft_balance_to_claim[tokenID] += amount;
    }

    function set_constancy_reward(uint256 amount) public {
        require(
            msg.sender == address(ABLE_SALE) ||
                msg.sender == address(TRIPLE_SALE),
            "Only ABLE_SALE or TRIPLE_SALE can call to this function"
        );
        constancyReward[passiveRewardID] += amount;
    }

    // Private Functions

    // Distribute 9 generations
    function distribute(
        uint256 tokenID,
        uint256 _reference,
        bool created
    ) private {}

    function setReferences(uint256 i, uint256 tokenID, uint256 ref) private {
        if (i == 0) {
            get_first_level_amount_reference[ref]++;
            get_total_amount_references[ref]++;
            uint256[] storage nftIDs = get_first_level_references[ref];
            nftIDs.push(tokenID);
            get_first_level_references[ref] = nftIDs;

            return;
        }

        if (i == 1) {
            get_second_level_amount_reference[ref]++;
            get_total_amount_references[ref]++;
            uint256[] storage nftIDs = get_second_level_references[ref];
            nftIDs.push(tokenID);
            get_second_level_references[ref] = nftIDs;

            return;
        }

        if (i == 2) {
            get_third_level_amount_reference[ref]++;
            get_total_amount_references[ref]++;
            uint256[] storage nftIDs = get_third_level_references[ref];
            nftIDs.push(tokenID);
            get_third_level_references[ref] = nftIDs;

            return;
        }

        if (i == 3) {
            get_four_level_amount_reference[ref]++;
            get_total_amount_references[ref]++;
            uint256[] storage nftIDs = get_four_level_references[ref];
            nftIDs.push(tokenID);
            get_four_level_references[ref] = nftIDs;

            return;
        }

        if (i == 4) {
            get_five_level_amount_reference[ref]++;
            get_total_amount_references[ref]++;
            uint256[] storage nftIDs = get_five_level_references[ref];
            nftIDs.push(tokenID);
            get_five_level_references[ref] = nftIDs;

            return;
        }

        if (i == 5) {
            get_six_level_amount_reference[ref]++;
            get_total_amount_references[ref]++;
            uint256[] storage nftIDs = get_six_level_references[ref];
            nftIDs.push(tokenID);
            get_six_level_references[ref] = nftIDs;

            return;
        }

        if (i == 6) {
            get_seven_level_amount_reference[ref]++;
            get_total_amount_references[ref]++;
            uint256[] storage nftIDs = get_seven_level_references[ref];
            nftIDs.push(tokenID);
            get_seven_level_references[ref] = nftIDs;

            return;
        }

        if (i == 7) {
            get_eight_level_amount_reference[ref]++;
            get_total_amount_references[ref]++;
            uint256[] storage nftIDs = get_eight_level_references[ref];
            nftIDs.push(tokenID);
            get_eight_level_references[ref] = nftIDs;

            return;
        }

        if (i == 8) {
            get_nine_level_amount_reference[ref]++;
            get_total_amount_references[ref]++;
            uint256[] storage nftIDs = get_nine_level_references[ref];
            nftIDs.push(tokenID);
            get_nine_level_references[ref] = nftIDs;

            return;
        }
    }

    // The following functions are overrides required by Solidity.

    function _burn(
        uint256 tokenId
    ) internal override(ERC721, ERC721URIStorage) {
        super._burn(tokenId);
    }

    function tokenURI(
        uint256 tokenId
    ) public view override(ERC721, ERC721URIStorage) returns (string memory) {
        return super.tokenURI(tokenId);
    }
}

contract SinergyApril is ERC721, ERC721URIStorage, Ownable {
    // Constants
    uint256 public constant PRICE = 36 ether;
    uint256 public constant ABLE_PRICE = 12 ether;
    uint256 public constant REWARD = 3 ether;
    uint256 public constant TO_DEVELOPMENT = 3 ether;

    // NFT GENESIS
    string constant NFT_GENESIS_NAME = "GENESIS";
    string constant NFT_GENESIS_INSCRIPTION = "GENESIS INSCRIPTION";
    string constant NFT_GENESIS_VALUE_PROPOSAL = "GENESIS VALUE PROPOSAL";
    string constant NFT_GENESIS_IMAGE_URL =
        "https://res.cloudinary.com/saver-community/image/upload/v1666380501/jvjbls4lg5mtxsxhlhnf.png";

    // Migration
    SinergyMigration ContractMigration =
        SinergyMigration(0xEa063b5A744616a161272a013a626A1cBD80Ee1B);

    Migration public MigrationContract =
        Migration(0xfd26B8BE868C0E16A5a54E8D586B0C6D7d6892fA);

    // ERC20's
    ERC20 public ABLE = ERC20(0x0b85cCA1814eE40C6E83E3591F3819eC7e87d0A5);
    ERC20 public BUSD = ERC20(0x765277EebeCA2e31912C9946eAe1021199B39C61);

    // Able Sale
    SinergySale public ABLE_SALE =
        SinergySale(0x7fa46675165F0d0Ab1A3bd3FD96AA3eD59167B52);

    // Passive Rewards
    Reward public BUSD_PassiveReward =
        Reward(0x4F19668690b3501fa2404039436d4f1C14079dB8);
    Reward public ABLE_PassiveReward =
        Reward(0x72e29bC0cF7E6f2A3FC99890069E857b736F6dE9);

    // Constancy Rewards (usa el ID de las pasivas)
    Reward public BUSD_ConstancyReward =
        Reward(0x2B06dD06Cf7cdAB0f8cC39a6F79FD88b20cb2C5D);
    Reward public ABLE_ConstancyReward =
        Reward(0xc32AfBC61e4A2Be096cBe27Fa1072EA7f25Aa79d);

    // Address
    address public communityWallet = 0xc8895f6f85D870589C42fd6d531c855bddD27B0f;
    address public constant developmentWallet =
        0x9060723c22dE586c2fA5eFa07A7743F6f4a935f5;

    uint256 public oneDay = 1 days;
    uint256 public timeToNextReward;

    // Counters
    using Counters for Counters.Counter;
    Counters.Counter private _tokenIds;

    uint256 public starting_nft_id;
    uint256 initial_tokenID = 1159;

    // Constructor
    constructor() ERC721("Offer Sinergy", "Sinergy") {
        while (_tokenIds.current() < initial_tokenID) {
            _tokenIds.increment();
        }

        // Mint NFT
        uint256 tokenID = _tokenIds.current();
        _tokenIds.increment();
        _safeMint(developmentWallet, tokenID);
        get_nft_name[tokenID] = NFT_GENESIS_NAME;
        get_nft_inscription[tokenID] = NFT_GENESIS_INSCRIPTION;
        get_nft_value_proposal[tokenID] = NFT_GENESIS_VALUE_PROPOSAL;
        get_nft_image_url[tokenID] = NFT_GENESIS_IMAGE_URL;
        get_nft_timestamp_created[tokenID] = block.timestamp;
        favourite_nft[developmentWallet] = tokenID;

        _setTokenURI(
            tokenID,
            "ipfs://QmRi1DvgDu6zAJwpbURGNBBQTM82ZCNZAyTkEArbKZKm1U/273.json"
        );

        get_my_nfts[developmentWallet].push(tokenID);

        timeToNextReward = block.timestamp + oneDay;
        starting_nft_id = initial_tokenID;

        total_holders++;

        is_holder[developmentWallet] = true;
    }

    // NFT's
    uint256 public nfts_qualified;
    mapping(address => bool) public is_qualified;
    mapping(uint256 => string) public get_nft_name;
    mapping(uint256 => string) public get_nft_inscription;
    mapping(uint256 => string) public get_nft_value_proposal;
    mapping(uint256 => uint256) public get_nft_timestamp_created;
    mapping(uint256 => string) public get_nft_image_url;

    // References
    // Esto sabemos que va (Cantidad de NFTs que estan conectados conmigo en el NIVEL x)
    mapping(uint256 => uint256) public get_first_level_amount_reference;
    mapping(uint256 => uint256) public get_second_level_amount_reference;
    mapping(uint256 => uint256) public get_third_level_amount_reference;
    mapping(uint256 => uint256) public get_four_level_amount_reference;
    mapping(uint256 => uint256) public get_five_level_amount_reference;
    mapping(uint256 => uint256) public get_six_level_amount_reference;
    mapping(uint256 => uint256) public get_seven_level_amount_reference;
    mapping(uint256 => uint256) public get_eight_level_amount_reference;
    mapping(uint256 => uint256) public get_nine_level_amount_reference;

    mapping(uint256 => uint256) public get_total_amount_references; // Cantidad de NFTs que estan conectados conmigo en total.

    // Esto puede fallar (NFT ID de cada uno que esta conectado con nosotros en el NIVEL x)
    mapping(uint256 => uint256[]) public get_first_level_references;
    mapping(uint256 => uint256[]) public get_second_level_references;
    mapping(uint256 => uint256[]) public get_third_level_references;
    mapping(uint256 => uint256[]) public get_four_level_references;
    mapping(uint256 => uint256[]) public get_five_level_references;
    mapping(uint256 => uint256[]) public get_six_level_references;
    mapping(uint256 => uint256[]) public get_seven_level_references;
    mapping(uint256 => uint256[]) public get_eight_level_references;
    mapping(uint256 => uint256[]) public get_nine_level_references;

    // NFT al que me conecte
    mapping(uint256 => uint256) public get_nft_reference;

    // Rewards
    mapping(uint256 => uint256) public nft_affiliate_rewards_earned;

    // Resources
    uint256 public total_raided_for_admin;

    // Stadistics
    uint256 public total_stablecoin_distributed;
    mapping(address => uint256) public total_stablecoin_earned;
    mapping(address => uint256) public total_lost_income;
    mapping(address => uint256) public actual_lost_income;
    mapping(uint256 => uint256) public nfts_created_by_cycle;

    // Holders
    mapping(address => bool) public is_holder;

    // Auxs
    mapping(address => uint256) public amount_nfts_considered;

    // Events
    event Mint(
        uint256 id,
        uint256 date,
        string indexed name,
        string indexed valueProposal,
        address indexed wallet
    );

    event AffiliateRewardEvent(
        uint256 tokenID,
        uint256 amount,
        address indexed wallet,
        uint256 indexed date
    );

    event ChangeFavourite(
        address indexed wallet,
        uint256 previousFavourite,
        uint256 actualFavourite
    );

    // Public Functions
    // Migration

    function set_migration_contract(Migration _migrationContract) public {
        if (msg.sender != developmentWallet) return;
        MigrationContract = _migrationContract;
    }

    function handleRecover(address wallet, uint256 tokenId) public {
        if (msg.sender != address(MigrationContract)) return;

        recoverNFT(tokenId);
        get_my_nfts[wallet].push(tokenId);
        _safeMint(wallet, tokenId);

        if (favourite_nft[wallet] == 0) {
            favourite_nft[wallet] = tokenId;
        }
    }

    function recoverNFT(uint256 nftID) private {
        // NFT Basic Info
        get_nft_name[nftID] = ContractMigration.get_nft_name(nftID);
        get_nft_inscription[nftID] = ContractMigration.get_nft_inscription(
            nftID
        );
        get_nft_value_proposal[nftID] = ContractMigration
            .get_nft_value_proposal(nftID);
        get_nft_timestamp_created[nftID] = ContractMigration
            .get_nft_timestamp_created(nftID);
        get_nft_image_url[nftID] = ContractMigration.get_nft_image_url(nftID);
        get_nft_reference[nftID] = ContractMigration.get_nft_reference(nftID);

        // References
        get_first_level_amount_reference[nftID] = ContractMigration
            .get_first_level_amount_reference(nftID);
        get_second_level_amount_reference[nftID] = ContractMigration
            .get_second_level_amount_reference(nftID);
        get_third_level_amount_reference[nftID] = ContractMigration
            .get_third_level_amount_reference(nftID);
        get_four_level_amount_reference[nftID] = ContractMigration
            .get_four_level_amount_reference(nftID);
        get_five_level_amount_reference[nftID] = ContractMigration
            .get_five_level_amount_reference(nftID);
        get_six_level_amount_reference[nftID] = ContractMigration
            .get_six_level_amount_reference(nftID);
        get_seven_level_amount_reference[nftID] = ContractMigration
            .get_seven_level_amount_reference(nftID);
        get_eight_level_amount_reference[nftID] = ContractMigration
            .get_eight_level_amount_reference(nftID);
        get_nine_level_amount_reference[nftID] = ContractMigration
            .get_nine_level_amount_reference(nftID);

        get_total_amount_references[nftID] = ContractMigration
            .get_total_amount_references(nftID);
    }

    function handlerRecoverFirstLevel(uint256 tokenId, uint256 index) public {
        if (msg.sender != address(MigrationContract)) return;

        get_first_level_references[tokenId].push(
            ContractMigration.get_first_level_references(tokenId, index)
        );
    }

    function handlerRecoverSecondLevel(uint256 tokenId, uint256 index) public {
        if (msg.sender != address(MigrationContract)) return;

        get_second_level_references[tokenId].push(
            ContractMigration.get_second_level_references(tokenId, index)
        );
    }

    function handlerRecoverThirdLevel(uint256 tokenId, uint256 index) public {
        if (msg.sender != address(MigrationContract)) return;

        get_third_level_references[tokenId].push(
            ContractMigration.get_third_level_references(tokenId, index)
        );
    }

    function handlerRecoverFourLevel(uint256 tokenId, uint256 index) public {
        if (msg.sender != address(MigrationContract)) return;

        get_four_level_references[tokenId].push(
            ContractMigration.get_four_level_references(tokenId, index)
        );
    }

    function handlerRecoverFiveLevel(uint256 tokenId, uint256 index) public {
        if (msg.sender != address(MigrationContract)) return;

        get_five_level_references[tokenId].push(
            ContractMigration.get_five_level_references(tokenId, index)
        );
    }

    function handlerRecoverSixLevel(uint256 tokenId, uint256 index) public {
        if (msg.sender != address(MigrationContract)) return;

        get_six_level_references[tokenId].push(
            ContractMigration.get_six_level_references(tokenId, index)
        );
    }

    function handlerRecoverSevenLevel(uint256 tokenId, uint256 index) public {
        if (msg.sender != address(MigrationContract)) return;

        get_seven_level_references[tokenId].push(
            ContractMigration.get_seven_level_references(tokenId, index)
        );
    }

    function handlerRecoverEightLevel(uint256 tokenId, uint256 index) public {
        if (msg.sender != address(MigrationContract)) return;

        get_eight_level_references[tokenId].push(
            ContractMigration.get_eight_level_references(tokenId, index)
        );
    }

    function handlerRecoverNineLevel(uint256 tokenId, uint256 index) public {
        if (msg.sender != address(MigrationContract)) return;

        get_nine_level_references[tokenId].push(
            ContractMigration.get_nine_level_references(tokenId, index)
        );
    }

    // NFT
    function createNFT(
        string memory _name,
        string memory _inscription,
        string memory _valueProposal,
        string memory _uri,
        string memory _imageURL,
        uint256 _ref,
        uint256 _timestamp
    ) public {}

    function modifyNFT(
        string memory _name,
        string memory _inscription,
        string memory _valueProposal,
        uint256 _tokenID
    ) public {
        require(msg.sender == ownerOf(_tokenID));

        BUSD.transferFrom(msg.sender, address(this), PRICE);
        ABLE.transferFrom(msg.sender, address(this), ABLE_PRICE);

        // Transferimos 3 BUSD para el admin
        BUSD.transfer(communityWallet, TO_DEVELOPMENT);

        // Modify the NFT
        get_nft_name[_tokenID] = _name;
        get_nft_inscription[_tokenID] = _inscription;
        get_nft_value_proposal[_tokenID] = _valueProposal;

        // Increase the resources amount
        total_raided_for_admin += TO_DEVELOPMENT;

        // Increase Rewards
        BUSD_PassiveReward.set_reward(3 ether);
        BUSD.transfer(address(BUSD_PassiveReward), 3 ether);

        ABLE_PassiveReward.set_reward(6 ether);
        ABLE.transfer(address(ABLE_PassiveReward), 6 ether);

        ABLE_ConstancyReward.set_reward(6 ether);
        ABLE.transfer(address(ABLE_ConstancyReward), 6 ether);

        BUSD_ConstancyReward.set_reward(3 ether);
        BUSD.transfer(address(ABLE_ConstancyReward), 3 ether);

        // Distribute BUSD in 9 generations
        distribute(_tokenID, get_nft_reference[_tokenID], false);

        update(msg.sender);
    }

    // Read functions
    function getAmountOfNftMinted() public view returns (uint256) {
        return _tokenIds.current();
    }

    // Set Functions
    function setAbleAddress(address _ableAddress) public {
        require(msg.sender == developmentWallet);

        ABLE = ERC20(_ableAddress);
    }

    function setRewards(
        Reward busd_passive,
        Reward busd_constancy,
        Reward able_passive,
        Reward able_constancy
    ) public {
        require(msg.sender == developmentWallet);
        BUSD_PassiveReward = busd_passive;
        BUSD_ConstancyReward = busd_constancy;
        ABLE_PassiveReward = able_passive;
        ABLE_ConstancyReward = able_constancy;
    }

    function setFavouriteNFT(address wallet, uint256 id) public {
        require(id <= _tokenIds.current());
        require(
            msg.sender == ownerOf(id) ||
                msg.sender == address(MigrationContract),
            "Wallet not qualified to setFavouriteNFT"
        );

        uint256 previousFavourite = favourite_nft[wallet];
        favourite_nft[wallet] = id;

        emit ChangeFavourite(msg.sender, previousFavourite, id);
    }

    function changeCommunityWallet(address newAddress) public {
        require(msg.sender == communityWallet);

        communityWallet = newAddress;
    }

    // Booleans
    function nft_was_qualified(uint256 tokenID) public view returns (bool) {}

    // Update Functions
    function update_qualified_nfts(address wallet) private {}

    function update_timestamp() private {
        if (block.timestamp > timeToNextReward) {
            BUSD_PassiveReward.update();
            ABLE_PassiveReward.update();

            BUSD_ConstancyReward.update();
            ABLE_ConstancyReward.update();

            timeToNextReward = block.timestamp + oneDay;
        }
    }

    function update(address wallet) public {}

    function updateFromAble(address wallet) public {
        update_timestamp();
        update_qualified_nfts(wallet);
    }

    function getAbleBalance(address wallet) public view returns (uint256) {
        return ABLE.balanceOf(wallet);
    }

    // SaleToken Public Functions
    function set_able_sale(SinergySale ableSale) public {
        require(msg.sender == developmentWallet);
        ABLE_SALE = ableSale;
    }

    // Private Functions

    // Distribute 9 generations
    function distribute(
        uint256 tokenID,
        uint256 _reference,
        bool created
    ) private {
        address owner;
        uint256 i = 0;
        while (i < 9) {
            if (created) setReferences(i, tokenID, _reference);

            owner = ownerOf(_reference);

            total_stablecoin_earned[owner] += REWARD;
            nft_affiliate_rewards_earned[_reference] += REWARD;
            total_stablecoin_distributed += REWARD;
            BUSD.transfer(owner, REWARD);

            // Emit events
            emit AffiliateRewardEvent(
                _reference,
                REWARD,
                owner,
                block.timestamp
            );

            _reference = get_nft_reference[_reference];

            i++;
        }
    }

    function create_genesis_nfts() public {
        if (msg.sender != address(MigrationContract)) return;

        // Crear 8 NFTs para la billetera destinada a Desarrollo y Mantenimiento
        // Estos NFTs deben estar vinculados entre si
        uint256 affiliate_rewards;

        for (uint256 i = 0; i < 9; i++) {
            _safeMint(communityWallet, i);

            recoverNFT(i);

            affiliate_rewards += ContractMigration.get_nft_balance_to_claim(i);

            get_my_nfts[communityWallet].push(i);

            _setTokenURI(
                i,
                "ipfs://QmRi1DvgDu6zAJwpbURGNBBQTM82ZCNZAyTkEArbKZKm1U/0.json"
            );
        }
    }

    function setReferences(uint256 i, uint256 tokenID, uint256 ref) private {
        if (i == 0) {
            get_first_level_amount_reference[ref]++;
            get_total_amount_references[ref]++;
            // uint256[] storage nftIDs = get_first_level_references[ref];
            get_first_level_references[ref].push(tokenID);
            // get_first_level_references[ref] = nftIDs;

            return;
        }

        if (i == 1) {
            get_second_level_amount_reference[ref]++;
            get_total_amount_references[ref]++;
            // uint256[] storage nftIDs = get_second_level_references[ref];
            get_second_level_references[ref].push(tokenID);
            // get_second_level_references[ref] = nftIDs;

            return;
        }

        if (i == 2) {
            get_third_level_amount_reference[ref]++;
            get_total_amount_references[ref]++;
            // uint256[] storage nftIDs = get_third_level_references[ref];
            get_third_level_references[ref].push(tokenID);
            // get_third_level_references[ref] = nftIDs;

            return;
        }

        if (i == 3) {
            get_four_level_amount_reference[ref]++;
            get_total_amount_references[ref]++;
            // uint256[] storage nftIDs = get_four_level_references[ref];
            get_four_level_references[ref].push(tokenID);
            // get_four_level_references[ref] = nftIDs;

            return;
        }

        if (i == 4) {
            get_five_level_amount_reference[ref]++;
            get_total_amount_references[ref]++;
            // uint256[] storage nftIDs = get_five_level_references[ref];
            get_five_level_references[ref].push(tokenID);
            // get_five_level_references[ref] = nftIDs;

            return;
        }

        if (i == 5) {
            get_six_level_amount_reference[ref]++;
            get_total_amount_references[ref]++;
            // uint256[] storage nftIDs = get_six_level_references[ref];
            get_six_level_references[ref].push(tokenID);
            // get_six_level_references[ref] = nftIDs;

            return;
        }

        if (i == 6) {
            get_seven_level_amount_reference[ref]++;
            get_total_amount_references[ref]++;
            // uint256[] storage nftIDs = get_seven_level_references[ref];
            get_seven_level_references[ref].push(tokenID);
            // get_seven_level_references[ref] = nftIDs;

            return;
        }

        if (i == 7) {
            get_eight_level_amount_reference[ref]++;
            get_total_amount_references[ref]++;
            // uint256[] storage nftIDs = get_eight_level_references[ref];
            get_eight_level_references[ref].push(tokenID);
            // get_eight_level_references[ref] = nftIDs;

            return;
        }

        if (i == 8) {
            get_nine_level_amount_reference[ref]++;
            get_total_amount_references[ref]++;
            // uint256[] storage nftIDs = get_nine_level_references[ref];
            get_nine_level_references[ref].push(tokenID);
            // get_nine_level_references[ref] = nftIDs;

            return;
        }
    }

    // The following functions are overrides required by Solidity.

    function _burn(
        uint256 tokenId
    ) internal override(ERC721, ERC721URIStorage) {
        super._burn(tokenId);
    }

    function tokenURI(
        uint256 tokenId
    ) public view override(ERC721, ERC721URIStorage) returns (string memory) {
        return super.tokenURI(tokenId);
    }
}

contract Sinergy is ERC721, ERC721URIStorage, Ownable, Router {
    // Prices
    uint256 public PRICE = 36 ether;
    uint256 public ABLE_PRICE = 12 ether;

    // Amounts to transfer
    uint256 public PERCENT_REWARD = 1;
    uint256 public PERCENT_DIVIDE_REWARD = 12;

    uint256 public PERCENT_STABLECOIN_VALUE_REWARD = 1;
    uint256 public PERCENT_DIVIDE_STABLECOIN_VALUE_REWARD = 12;

    uint256 public PERCENT_STABLECOIN_CONSTANCY_REWARD = 1;
    uint256 public PERCENT_DIVIDE_STABLECOIN_CONSTANCY_REWARD = 12;

    uint256 public PERCENT_STABLECOIN_CONFIDENCE_REWARD = 1;
    uint256 public PERCENT_DIVIDE_STABLECOIN_CONFIDENCE_REWARD = 12;

    uint256 public PERCENT_ABLE_VALUE_REWARD = 1;
    uint256 public PERCENT_DIVIDE_ABLE_VALUE_REWARD = 3;

    uint256 public PERCENT_ABLE_CONSTANCY_REWARD = 1;
    uint256 public PERCENT_DIVIDE_ABLE_CONSTANCY_REWARD = 3;

    uint256 public PERCENT_ABLE_CONFIDENCE_REWARD = 1;
    uint256 public PERCENT_DIVIDE_ABLE_CONFIDENCE_REWARD = 3;

    // NFT GENESIS
    string constant NFT_GENESIS_NAME = "GENESIS";
    string constant NFT_GENESIS_INSCRIPTION = "GENESIS INSCRIPTION";
    string constant NFT_GENESIS_VALUE_PROPOSAL = "GENESIS VALUE PROPOSAL";
    string constant NFT_GENESIS_IMAGE_URL =
        "https://res.cloudinary.com/saver-community/image/upload/v1666380501/jvjbls4lg5mtxsxhlhnf.png";

    // Requirements to earn affiliate rewards
    uint256 public MIN_AMOUNT_FIRST_LEVEL_CONNECTIONS = 3;

    // Counters
    using Counters for Counters.Counter;
    Counters.Counter private _tokenIds;

    uint256 public starting_nft_id;
    uint256 public initial_tokenID = 1301;

    // Constructor
    constructor() ERC721("Offer Sinergy", "Sinergy") {
        while (_tokenIds.current() < initial_tokenID) {
            _tokenIds.increment();
        }

        address development_wallet = Wallets.development_wallet();

        // Mint NFT
        uint256 tokenID = _tokenIds.current();
        _tokenIds.increment();
        _safeMint(development_wallet, tokenID);
        get_nft_name[tokenID] = NFT_GENESIS_NAME;
        get_nft_inscription[tokenID] = NFT_GENESIS_INSCRIPTION;
        get_nft_value_proposal[tokenID] = NFT_GENESIS_VALUE_PROPOSAL;
        get_nft_image_url[tokenID] = NFT_GENESIS_IMAGE_URL;
        get_nft_timestamp_created[tokenID] = block.timestamp;
        favourite_nft[development_wallet] = tokenID;

        _setTokenURI(
            tokenID,
            "ipfs://QmRi1DvgDu6zAJwpbURGNBBQTM82ZCNZAyTkEArbKZKm1U/273.json"
        );

        get_my_nfts[development_wallet].push(tokenID);

        starting_nft_id = initial_tokenID;

        total_holders++;

        is_holder[development_wallet] = true;
    }

    // NFT's
    mapping(uint256 => uint256) public nfts_qualified_by_cycle;
    uint256 public nfts_qualified;
    mapping(address => bool) public is_qualified;
    mapping(uint256 => string) public get_nft_name;
    mapping(uint256 => string) public get_nft_inscription;
    mapping(uint256 => string) public get_nft_value_proposal;
    mapping(uint256 => uint256) public get_nft_timestamp_created;
    mapping(uint256 => string) public get_nft_image_url;

    // References
    // Esto sabemos que va (Cantidad de NFTs que estan conectados conmigo en el NIVEL x)
    mapping(uint256 => uint256) public get_first_level_amount_reference;
    mapping(uint256 => uint256) public get_second_level_amount_reference;
    mapping(uint256 => uint256) public get_third_level_amount_reference;
    mapping(uint256 => uint256) public get_four_level_amount_reference;
    mapping(uint256 => uint256) public get_five_level_amount_reference;
    mapping(uint256 => uint256) public get_six_level_amount_reference;
    mapping(uint256 => uint256) public get_seven_level_amount_reference;
    mapping(uint256 => uint256) public get_eight_level_amount_reference;
    mapping(uint256 => uint256) public get_nine_level_amount_reference;

    mapping(uint256 => uint256) public get_total_amount_references; // Cantidad de NFTs que estan conectados conmigo en total.

    // Esto puede fallar (NFT ID de cada uno que esta conectado con nosotros en el NIVEL x)
    mapping(uint256 => uint256[]) public get_first_level_references;
    mapping(uint256 => uint256[]) public get_second_level_references;
    mapping(uint256 => uint256[]) public get_third_level_references;
    mapping(uint256 => uint256[]) public get_four_level_references;
    mapping(uint256 => uint256[]) public get_five_level_references;
    mapping(uint256 => uint256[]) public get_six_level_references;
    mapping(uint256 => uint256[]) public get_seven_level_references;
    mapping(uint256 => uint256[]) public get_eight_level_references;
    mapping(uint256 => uint256[]) public get_nine_level_references;

    // NFT al que me conecte
    mapping(uint256 => uint256) public get_nft_reference;

    // Rewards
    mapping(uint256 => uint256) public nft_affiliate_rewards_earned;

    // Stadistics
    uint256 public total_stablecoin_distributed;
    mapping(address => uint256) public total_stablecoin_earned;
    mapping(address => uint256) public total_lost_income;
    mapping(address => uint256) public actual_lost_income;
    mapping(uint256 => uint256) public nfts_created_by_cycle;

    // Holders
    mapping(address => bool) public is_holder;

    // Auxs
    mapping(address => uint256) public amount_nfts_considered;

    // Events
    event Mint(
        uint256 id,
        uint256 date,
        string indexed name,
        string indexed valueProposal,
        address indexed wallet
    );

    event AffiliateRewardEvent(
        uint256 tokenID,
        uint256 amount,
        address indexed wallet,
        uint256 indexed date
    );

    event ChangeFavourite(
        address indexed wallet,
        uint256 previousFavourite,
        uint256 actualFavourite
    );

    // Public Functions
    // Migration

    function HandleRecover(
        address wallet,
        uint256 tokenId,
        address migration_contract,
        uint256 month_contract
    ) public {
        if (msg.sender != address(MigrationSinergy)) return;

        _RecoverNFT(tokenId, migration_contract, month_contract);

        get_my_nfts[wallet].push(tokenId);
        if (balanceOf(wallet) == 0) {
            total_holders++;
        }
        _safeMint(wallet, tokenId);

        if (favourite_nft[wallet] == 0) {
            favourite_nft[wallet] = tokenId;
        }

        if (IsQualified(wallet)) {
            nfts_qualified++;
        }
    }

    function HandlerRecoverFirstLevel(
        uint256 tokenId,
        uint256 index,
        address contract_migration
    ) public {
        if (msg.sender != address(MigrationSinergy)) return;

        SinergyMigration ContractMigration = SinergyMigration(
            contract_migration
        );

        get_first_level_references[tokenId].push(
            ContractMigration.get_first_level_references(tokenId, index)
        );
    }

    function HandlerRecoverSecondLevel(
        uint256 tokenId,
        uint256 index,
        address contract_migration
    ) public {
        if (msg.sender != address(MigrationSinergy)) return;

        SinergyMigration ContractMigration = SinergyMigration(
            contract_migration
        );

        get_second_level_references[tokenId].push(
            ContractMigration.get_second_level_references(tokenId, index)
        );
    }

    function HandlerRecoverThirdLevel(
        uint256 tokenId,
        uint256 index,
        address contract_migration
    ) public {
        if (msg.sender != address(MigrationSinergy)) return;

        SinergyMigration ContractMigration = SinergyMigration(
            contract_migration
        );

        get_third_level_references[tokenId].push(
            ContractMigration.get_third_level_references(tokenId, index)
        );
    }

    function HandlerRecoverFourLevel(
        uint256 tokenId,
        uint256 index,
        address contract_migration
    ) public {
        if (msg.sender != address(MigrationSinergy)) return;

        SinergyMigration ContractMigration = SinergyMigration(
            contract_migration
        );

        get_four_level_references[tokenId].push(
            ContractMigration.get_four_level_references(tokenId, index)
        );
    }

    function HandlerRecoverFiveLevel(
        uint256 tokenId,
        uint256 index,
        address contract_migration
    ) public {
        if (msg.sender != address(MigrationSinergy)) return;

        SinergyMigration ContractMigration = SinergyMigration(
            contract_migration
        );

        get_five_level_references[tokenId].push(
            ContractMigration.get_five_level_references(tokenId, index)
        );
    }

    function HandlerRecoverSixLevel(
        uint256 tokenId,
        uint256 index,
        address contract_migration
    ) public {
        if (msg.sender != address(MigrationSinergy)) return;

        SinergyMigration ContractMigration = SinergyMigration(
            contract_migration
        );

        get_six_level_references[tokenId].push(
            ContractMigration.get_six_level_references(tokenId, index)
        );
    }

    function HandlerRecoverSevenLevel(
        uint256 tokenId,
        uint256 index,
        address contract_migration
    ) public {
        if (msg.sender != address(MigrationSinergy)) return;

        SinergyMigration ContractMigration = SinergyMigration(
            contract_migration
        );

        get_seven_level_references[tokenId].push(
            ContractMigration.get_seven_level_references(tokenId, index)
        );
    }

    function HandlerRecoverEightLevel(
        uint256 tokenId,
        uint256 index,
        address contract_migration
    ) public {
        if (msg.sender != address(MigrationSinergy)) return;

        SinergyMigration ContractMigration = SinergyMigration(
            contract_migration
        );

        get_eight_level_references[tokenId].push(
            ContractMigration.get_eight_level_references(tokenId, index)
        );
    }

    function HandlerRecoverNineLevel(
        uint256 tokenId,
        uint256 index,
        address contract_migration
    ) public {
        if (msg.sender != address(MigrationSinergy)) return;

        SinergyMigration ContractMigration = SinergyMigration(
            contract_migration
        );

        get_nine_level_references[tokenId].push(
            ContractMigration.get_nine_level_references(tokenId, index)
        );
    }

    // NFT
    function CreateNFT(
        string memory _name,
        string memory _inscription,
        string memory _valueProposal,
        string memory _uri,
        string memory _imageURL,
        uint256 _ref,
        uint256 _timestamp
    ) public {
        if (!IsUpdated(msg.sender)) return;

        // Get Reference
        uint256 _reference = favourite_nft[
            AbleSale.last_wallet_qualified_who_bought()
        ];

        if (_ref != 0) {
            _reference = _ref;
        }

        if (_reference == 0) {
            _reference = 8;
        }

        require(_reference < _tokenIds.current());

        Stablecoin.transferFrom(msg.sender, address(this), PRICE);
        Able.transferFrom(msg.sender, address(this), ABLE_PRICE);

        UpdateQualifiedNfts(msg.sender);

        if (balanceOf(msg.sender) > 0) {
            AbleSale.TryToSwap(favourite_nft[msg.sender]);
        }

        // Mint NFT
        uint256 tokenID = _tokenIds.current();
        _tokenIds.increment();
        _safeMint(msg.sender, tokenID);

        // Set URI
        _setTokenURI(tokenID, _uri);

        // Add information to the NFT
        get_nft_name[tokenID] = _name;
        get_nft_inscription[tokenID] = _inscription;
        get_nft_value_proposal[tokenID] = _valueProposal;
        get_nft_reference[tokenID] = _reference;

        get_nft_image_url[tokenID] = _imageURL;
        get_nft_timestamp_created[tokenID] = _timestamp;

        if (favourite_nft[msg.sender] == 0) {
            favourite_nft[msg.sender] = tokenID;
        }

        get_my_nfts[msg.sender].push(tokenID);

        // Transfer to Rewards
        _TransferValueRewards();
        _TransferConstancyRewards();
        _TransferConfidenceRewards();

        // Distribute Stablecoin's in 9 generations
        _Distribute(tokenID, _reference, true);

        // Qualification.Update(msg.sender);

        // Aumentamos la cantidad de NFTs creados en este ciclo
        nfts_created_by_cycle[GetCycle()]++;

        // Holders
        if (!is_holder[msg.sender]) {
            is_holder[msg.sender] = true;
            total_holders++;
        }

        // Emit event
        emit Mint(tokenID, block.timestamp, _name, _valueProposal, msg.sender);
    }

    function ModifyNFT(
        string memory _name,
        string memory _inscription,
        string memory _valueProposal,
        uint256 _tokenID
    ) public {
        if (!IsUpdated(msg.sender) || msg.sender != ownerOf(_tokenID)) return;

        Stablecoin.transferFrom(msg.sender, address(this), PRICE);
        Able.transferFrom(msg.sender, address(this), ABLE_PRICE);

        // Modify the NFT
        get_nft_name[_tokenID] = _name;
        get_nft_inscription[_tokenID] = _inscription;
        get_nft_value_proposal[_tokenID] = _valueProposal;

        // Transfer to Rewards
        _TransferValueRewards();
        _TransferConstancyRewards();
        _TransferConfidenceRewards();

        // Distribute Stablecoin in 9 generations
        _Distribute(_tokenID, get_nft_reference[_tokenID], false);

        // Qualification.Update(msg.sender);
    }

    function NftWasQualified(uint256 tokenID) public view returns (bool) {
        return
            IsQualified(ownerOf(tokenID)) ||
            Qualification.qualified_history(ownerOf(tokenID), GetCycle() - 1);
    }

    function CreateGenesisNfts() public {
        if (msg.sender != address(MigrationSinergy)) return;

        // Crear 8 NFTs para la billetera destinada a Desarrollo y Mantenimiento
        // Estos NFTs deben estar vinculados entre si
        address community_wallet = Wallets.community_wallet();
        for (uint256 i = 0; i < 9; i++) {
            _safeMint(community_wallet, i);
            _setTokenURI(
                i,
                "ipfs://QmRi1DvgDu6zAJwpbURGNBBQTM82ZCNZAyTkEArbKZKm1U/0.json"
            );

            _RecoverNFT(i, address(AprilSinergyBronzeContract), 4);

            get_my_nfts[community_wallet].push(i);
            nft_affiliate_rewards_earned[i] = AprilSinergyBronzeContract
                .nft_affiliate_rewards_earned(i);
        }
    }

    function CloseCycle() public {
        if (msg.sender != address(Cycle)) return;
        nfts_qualified_by_cycle[GetCycle()] = nfts_qualified;
    }

    function UpdateQualifiedNfts(address wallet) public {
        if (is_qualified[wallet]) {
            nfts_qualified -= amount_nfts_considered[wallet];
            amount_nfts_considered[wallet] = 0;
            is_qualified[wallet] = false;
        }

        if (IsQualified(wallet)) {
            nfts_qualified += balanceOf(wallet);
            amount_nfts_considered[wallet] = balanceOf(wallet);
            is_qualified[wallet] = true;
        }
    }

    // Helpers
    function IsOwner(address wallet) public view returns (bool) {
        return Wallets.IsOwner(wallet);
    }

    function IsUpdated(address wallet) public view returns (bool) {
        return Qualification.is_updated(wallet, GetCycle());
    }

    function IsQualified(address wallet) public view returns (bool) {
        return Qualification.IsQualified(wallet);
    }

    // Get Functions
    function GetAbleBalance(address wallet) public view returns (uint256) {
        return Able.balanceOf(wallet);
    }

    function GetCycle() public view returns (uint256) {
        return Cycle.cycle();
    }

    function GetAmountOfNftMinted() public view returns (uint256) {
        return _tokenIds.current();
    }

    function GetAbleToValueReward() public view returns (uint256) {
        return
            (ABLE_PRICE * PERCENT_ABLE_VALUE_REWARD) /
            PERCENT_DIVIDE_ABLE_VALUE_REWARD;
    }

    function GetAbleToConstancyReward() public view returns (uint256) {
        return
            (ABLE_PRICE * PERCENT_ABLE_CONSTANCY_REWARD) /
            PERCENT_DIVIDE_ABLE_CONSTANCY_REWARD;
    }

    function GetAbleToConfidenceReward() public view returns (uint256) {
        return
            (ABLE_PRICE * PERCENT_ABLE_CONFIDENCE_REWARD) /
            PERCENT_DIVIDE_ABLE_CONFIDENCE_REWARD;
    }

    function GetStablecoinToPassiveReward() public view returns (uint256) {
        return
            (PRICE * PERCENT_STABLECOIN_VALUE_REWARD) /
            PERCENT_DIVIDE_STABLECOIN_VALUE_REWARD;
    }

    function GetStablecoinToConstancyReward() public view returns (uint256) {
        return
            (PRICE * PERCENT_STABLECOIN_CONSTANCY_REWARD) /
            PERCENT_DIVIDE_STABLECOIN_CONSTANCY_REWARD;
    }

    function GetStablecoinToConfidenceReward() public view returns (uint256) {
        return
            (PRICE * PERCENT_STABLECOIN_CONFIDENCE_REWARD) /
            PERCENT_DIVIDE_STABLECOIN_CONFIDENCE_REWARD;
    }

    function GetStablecoinToReward() public view returns (uint256) {
        return (PRICE * PERCENT_REWARD) / PERCENT_DIVIDE_REWARD;
    }

    // Set Functions
    function SetFavouriteNFT(address wallet, uint256 id) public {
        if (id > _tokenIds.current()) return;
        if (
            msg.sender != ownerOf(id) && msg.sender != address(MigrationSinergy)
        ) return;

        uint256 previousFavourite = favourite_nft[wallet];
        favourite_nft[wallet] = id;

        emit ChangeFavourite(msg.sender, previousFavourite, id);
    }

    // Percentages
    // Able Value Reward
    function SetAbleValueRewardPercent(uint256 amount) public {
        if (!IsOwner(msg.sender)) return;
        PERCENT_ABLE_VALUE_REWARD = amount;
    }

    function SetAbleValueRewardPercentDivide(uint256 amount) public {
        if (!IsOwner(msg.sender)) return;

        PERCENT_DIVIDE_ABLE_VALUE_REWARD = amount;
    }

    // Able Constancy Reward
    function SetAbleConstancyRewardPercent(uint256 amount) public {
        if (!IsOwner(msg.sender)) return;

        PERCENT_ABLE_CONSTANCY_REWARD = amount;
    }

    function SetAbleConstancyRewardPercentDivide(uint256 amount) public {
        if (!IsOwner(msg.sender)) return;

        PERCENT_DIVIDE_ABLE_CONSTANCY_REWARD = amount;
    }

    // Able Confidence Reward
    function SetAbleConfidenceRewardPercent(uint256 amount) public {
        if (!IsOwner(msg.sender)) return;

        PERCENT_ABLE_CONSTANCY_REWARD = amount;
    }

    function SetAbleConfidenceRewardPercentDivide(uint256 amount) public {
        if (!IsOwner(msg.sender)) return;

        PERCENT_DIVIDE_ABLE_CONFIDENCE_REWARD = amount;
    }

    // Stablecoin Value Reward
    function SetStablecoinValueRewardPercent(uint256 amount) public {
        if (!IsOwner(msg.sender)) return;
        PERCENT_STABLECOIN_VALUE_REWARD = amount;
    }

    function SetStablecoinValueRewardPercentDivide(uint256 amount) public {
        if (!IsOwner(msg.sender)) return;
        PERCENT_DIVIDE_STABLECOIN_VALUE_REWARD = amount;
    }

    // Stablecoin Constancy Reward
    function SetStablecoinConstancyRewardPercent(uint256 amount) public {
        if (!IsOwner(msg.sender)) return;
        PERCENT_STABLECOIN_CONSTANCY_REWARD = amount;
    }

    function SetStablecoinConstancyRewardPercentDivide(uint256 amount) public {
        if (!IsOwner(msg.sender)) return;
        PERCENT_DIVIDE_STABLECOIN_CONSTANCY_REWARD = amount;
    }

    // Stablecoin Confidence Reward
    function SetStablecoinConfidenceRewardPercent(uint256 amount) public {
        if (!IsOwner(msg.sender)) return;
        PERCENT_STABLECOIN_CONFIDENCE_REWARD = amount;
    }

    function SetStablecoinConfidenceRewardPercentDivide(uint256 amount) public {
        if (!IsOwner(msg.sender)) return;
        PERCENT_DIVIDE_STABLECOIN_CONFIDENCE_REWARD = amount;
    }

    // Affiliative Rewards
    function SetStablecoinPercentReward(uint256 amount) public {
        if (!IsOwner(msg.sender)) return;
        PERCENT_REWARD = amount;
    }

    function SetStablecoinPercentRewardDivide(uint256 amount) public {
        if (!IsOwner(msg.sender)) return;
        PERCENT_DIVIDE_REWARD = amount;
    }

    // Prices
    function SetStablecoinPrice(uint256 amount) public {
        if (!IsOwner(msg.sender)) return;
        PRICE = amount * (1 ether);
    }

    function SetAblePrice(uint256 amount) public {
        if (!IsOwner(msg.sender)) return;
        ABLE_PRICE = amount * (1 ether);
    }

    function SetMinAmountFirstLevelConnections(uint256 amount) public {
        if (!IsOwner(msg.sender)) return;
        MIN_AMOUNT_FIRST_LEVEL_CONNECTIONS = amount;
    }

    // Private Functions
    function _TransferValueRewards() private {
        Able.transfer(address(AbleValueReward), GetAbleToValueReward());

        Stablecoin.transfer(
            address(StablecoinValueReward),
            GetStablecoinToPassiveReward()
        );
    }

    function _TransferConstancyRewards() private {
        Able.transfer(address(AbleConstancyReward), GetAbleToConstancyReward());

        Stablecoin.transfer(
            address(StablecoinConstancyReward),
            GetStablecoinToConstancyReward()
        );
    }

    function _TransferConfidenceRewards() private {
        Able.transfer(
            address(AbleConfidenceReward),
            GetAbleToConfidenceReward()
        );

        Stablecoin.transfer(
            address(StablecoinConfidenceReward),
            GetStablecoinToConfidenceReward()
        );
    }

    // Distribute 9 generations
    function _Distribute(
        uint256 tokenID,
        uint256 _reference,
        bool created
    ) private {
        address owner;
        uint256 i = 0;
        uint256 reward_amount = GetStablecoinToReward();
        uint256 commission_reference = _reference;
        while (i < 9) {
            if (created) _SetReferences(i, tokenID, _reference);

            owner = ownerOf(commission_reference);

            commission_reference = _TrnasferStablecoin(
                owner,
                commission_reference,
                reward_amount
            );

            _reference = get_nft_reference[_reference];

            i++;
        }
    }

    /*
        This function transfer the stablecoin to those NFTs that have the rights to receive it.
        And also returns the NFT father of the NFT that receive the transaction.
    */
    function _TrnasferStablecoin(
        address owner,
        uint256 token_id,
        uint256 reward_amount
    ) public returns (uint256) {
        while (
            get_first_level_amount_reference[token_id] <
            MIN_AMOUNT_FIRST_LEVEL_CONNECTIONS &&
            token_id != 0
        ) {
            token_id = get_nft_reference[token_id];
            owner = ownerOf(token_id);
        }

        Stablecoin.transfer(owner, reward_amount);

        total_stablecoin_earned[owner] += reward_amount;
        nft_affiliate_rewards_earned[token_id] += reward_amount;
        total_stablecoin_distributed += reward_amount;

        // Emit events
        emit AffiliateRewardEvent(
            token_id,
            reward_amount,
            owner,
            block.timestamp
        );

        return get_nft_reference[token_id];
    }

    function _SetReferences(uint256 i, uint256 tokenID, uint256 ref) private {
        if (i == 0) {
            get_first_level_amount_reference[ref]++;
            get_total_amount_references[ref]++;
            get_first_level_references[ref].push(tokenID);

            return;
        }

        if (i == 1) {
            get_second_level_amount_reference[ref]++;
            get_total_amount_references[ref]++;
            get_second_level_references[ref].push(tokenID);

            return;
        }

        if (i == 2) {
            get_third_level_amount_reference[ref]++;
            get_total_amount_references[ref]++;
            get_third_level_references[ref].push(tokenID);

            return;
        }

        if (i == 3) {
            get_four_level_amount_reference[ref]++;
            get_total_amount_references[ref]++;
            get_four_level_references[ref].push(tokenID);

            return;
        }

        if (i == 4) {
            get_five_level_amount_reference[ref]++;
            get_total_amount_references[ref]++;
            get_five_level_references[ref].push(tokenID);

            return;
        }

        if (i == 5) {
            get_six_level_amount_reference[ref]++;
            get_total_amount_references[ref]++;
            get_six_level_references[ref].push(tokenID);

            return;
        }

        if (i == 6) {
            get_seven_level_amount_reference[ref]++;
            get_total_amount_references[ref]++;
            get_seven_level_references[ref].push(tokenID);

            return;
        }

        if (i == 7) {
            get_eight_level_amount_reference[ref]++;
            get_total_amount_references[ref]++;
            get_eight_level_references[ref].push(tokenID);

            return;
        }

        if (i == 8) {
            get_nine_level_amount_reference[ref]++;
            get_total_amount_references[ref]++;
            get_nine_level_references[ref].push(tokenID);

            return;
        }
    }

    function _RecoverNFT(
        uint256 nftID,
        address contract_migration,
        uint256 month
    ) private {
        SinergyApril ContractMigration = SinergyApril(contract_migration);
        SinergyNovember ContractMigrationNovember = SinergyNovember(
            contract_migration
        );

        // NFT Basic Info
        get_nft_name[nftID] = ContractMigration.get_nft_name(nftID);
        get_nft_inscription[nftID] = ContractMigration.get_nft_inscription(
            nftID
        );
        get_nft_value_proposal[nftID] = ContractMigration
            .get_nft_value_proposal(nftID);
        get_nft_timestamp_created[nftID] = ContractMigration
            .get_nft_timestamp_created(nftID);
        get_nft_image_url[nftID] = ContractMigration.get_nft_image_url(nftID);
        get_nft_reference[nftID] = ContractMigration.get_nft_reference(nftID);

        // References
        get_first_level_amount_reference[nftID] = ContractMigration
            .get_first_level_amount_reference(nftID);
        get_second_level_amount_reference[nftID] = ContractMigration
            .get_second_level_amount_reference(nftID);
        get_third_level_amount_reference[nftID] = ContractMigration
            .get_third_level_amount_reference(nftID);
        get_four_level_amount_reference[nftID] = ContractMigration
            .get_four_level_amount_reference(nftID);
        get_five_level_amount_reference[nftID] = ContractMigration
            .get_five_level_amount_reference(nftID);
        get_six_level_amount_reference[nftID] = ContractMigration
            .get_six_level_amount_reference(nftID);
        get_seven_level_amount_reference[nftID] = ContractMigration
            .get_seven_level_amount_reference(nftID);
        get_eight_level_amount_reference[nftID] = ContractMigration
            .get_eight_level_amount_reference(nftID);
        get_nine_level_amount_reference[nftID] = ContractMigration
            .get_nine_level_amount_reference(nftID);

        get_total_amount_references[nftID] = ContractMigration
            .get_total_amount_references(nftID);

        if (month == 4) {
            nft_affiliate_rewards_earned[nftID] += ContractMigration
                .nft_affiliate_rewards_earned(nftID);
        } else if (month == 11) {
            nft_affiliate_rewards_earned[nftID] += ContractMigrationNovember
                .get_nft_rewards_claimed(nftID);
        }
    }

    // The following functions are overrides required by Solidity.

    function _burn(
        uint256 tokenId
    ) internal override(ERC721, ERC721URIStorage) {
        super._burn(tokenId);
    }

    function tokenURI(
        uint256 tokenId
    ) public view override(ERC721, ERC721URIStorage) returns (string memory) {
        return super.tokenURI(tokenId);
    }
}

contract MigrationApril {
    // Constants
    uint256 public constant AMOUNT_LIMIT_TO_MIGRATE = 21;

    // Contracts
    SinergyMigration ContractMigration =
        SinergyMigration(0xEa063b5A744616a161272a013a626A1cBD80Ee1B);
    SinergyMigration Contract =
        SinergyMigration(0xaeFDeD1Efb9f370F3663493755a1Da0A4E6F17E6);

    // ERC20
    ERC20 public ABLE = ERC20(0x0b85cCA1814eE40C6E83E3591F3819eC7e87d0A5);

    // Addresses
    address public communityWallet = 0xc8895f6f85D870589C42fd6d531c855bddD27B0f;
    address public constant developmentWallet =
        0x9060723c22dE586c2fA5eFa07A7743F6f4a935f5;

    // Migration
    mapping(address => bool) public isRecover;
    mapping(uint256 => bool) public nftRecover;

    mapping(uint256 => bool) public first_level_references_recover;
    mapping(uint256 => uint256) public first_level_references_recover_amount;

    mapping(uint256 => bool) public second_level_references_recover;
    mapping(uint256 => uint256) public second_level_references_recover_amount;

    mapping(uint256 => bool) public third_level_references_recover;
    mapping(uint256 => uint256) public third_level_references_recover_amount;

    mapping(uint256 => bool) public four_level_references_recover;
    mapping(uint256 => uint256) public four_level_references_recover_amount;

    mapping(uint256 => bool) public five_level_references_recover;
    mapping(uint256 => uint256) public five_level_references_recover_amount;

    mapping(uint256 => bool) public six_level_references_recover;
    mapping(uint256 => uint256) public six_level_references_recover_amount;

    mapping(uint256 => bool) public seven_level_references_recover;
    mapping(uint256 => uint256) public seven_level_references_recover_amount;

    mapping(uint256 => bool) public eight_level_references_recover;
    mapping(uint256 => uint256) public eight_level_references_recover_amount;

    mapping(uint256 => bool) public nine_level_references_recover;
    mapping(uint256 => uint256) public nine_level_references_recover_amount;

    mapping(address => uint256) public nfts_migrated;
    uint256 public starting_nft_id;
    uint256[] public recovered_nfts;
    uint256 public recovered_nfts_amount;

    constructor() {
        isRecover[developmentWallet] = true;
    }

    // Main recover
    function migrate() public {}

    // Recover by level
    function recoverFirstLevelReferences(uint256 nftID) public {}

    function recoverSecondLevelReferences(uint256 nftID) public {}

    function recoverThirdLevelReferences(uint256 nftID) public {}

    function recoverFourLevelReferences(uint256 nftID) public {}

    function recoverFiveLevelReferences(uint256 nftID) public {}

    function recoverSixLevelReferences(uint256 nftID) public {}

    function recoverSevenLevelReferences(uint256 nftID) public {}

    function recoverEightLevelReferences(uint256 nftID) public {}

    function recoverNineLevelReferences(uint256 nftID) public {}

    // Helpers
    function handle_recover_nfts_amount_for_admin() private {
        for (uint8 i = 0; i < 9; i++) {
            recovered_nfts.push(i);
            recovered_nfts_amount++;
        }
    }

    // Set Contracts
    function set_sinergy(Sinergy _sinergyAddress) public {}

    function set_able(ERC20 _able) public {
        if (msg.sender != developmentWallet) return;
        ABLE = ERC20(_able);
    }
}

contract Migration is Router {
    // Constants
    uint256 public constant AMOUNT_LIMIT_TO_MIGRATE = 21;

    // Migration
    mapping(address => bool) public isRecover;
    mapping(uint256 => bool) public nftRecover;

    mapping(uint256 => bool) public first_level_references_recover;
    mapping(uint256 => uint256) public first_level_references_recover_amount;

    mapping(uint256 => bool) public second_level_references_recover;
    mapping(uint256 => uint256) public second_level_references_recover_amount;

    mapping(uint256 => bool) public third_level_references_recover;
    mapping(uint256 => uint256) public third_level_references_recover_amount;

    mapping(uint256 => bool) public four_level_references_recover;
    mapping(uint256 => uint256) public four_level_references_recover_amount;

    mapping(uint256 => bool) public five_level_references_recover;
    mapping(uint256 => uint256) public five_level_references_recover_amount;

    mapping(uint256 => bool) public six_level_references_recover;
    mapping(uint256 => uint256) public six_level_references_recover_amount;

    mapping(uint256 => bool) public seven_level_references_recover;
    mapping(uint256 => uint256) public seven_level_references_recover_amount;

    mapping(uint256 => bool) public eight_level_references_recover;
    mapping(uint256 => uint256) public eight_level_references_recover_amount;

    mapping(uint256 => bool) public nine_level_references_recover;
    mapping(uint256 => uint256) public nine_level_references_recover_amount;

    mapping(address => uint256) public nfts_migrated;

    mapping(address => address) public migration_contract_of;

    uint256 public starting_nft_id;
    uint256[] public recovered_nfts;
    uint256 public recovered_nfts_amount;

    constructor() {
        isRecover[Wallets.development_wallet()] = true;
    }

    // Main recover

    function Migrate() public {
        address community_wallet = Wallets.community_wallet();
        address development_wallet = Wallets.development_wallet();

        // Add msg.sender == development_wallet to migrate the community_wallet faster
        require(
            !isRecover[msg.sender] || msg.sender == development_wallet,
            "Your wallet is recover."
        );

        if (
            (msg.sender == community_wallet ||
                msg.sender == development_wallet) &&
            !isRecover[community_wallet]
        ) {
            SinergyBronze.CreateGenesisNfts();
            isRecover[community_wallet] = true;
            HandleRecoverNftsAmountForAdmin();
            return;
        }

        if (msg.sender == development_wallet) return;

        if (AprilMigrationSinergy.isRecover(msg.sender)) {
            // Migrar del ultimo contrato
            _MigrateApril(AprilSinergyBronzeContract, msg.sender);
            migration_contract_of[msg.sender] = address(
                AprilSinergyBronzeContract
            );
        } else {
            // Migrar del primer contrato
            _MigrateNovember(NovemberSinergyBronzeContract, msg.sender);
            migration_contract_of[msg.sender] = address(
                NovemberSinergyBronzeContract
            );
        }
    }

    // Recover by level
    function RecoverFirstLevelReferences(uint256 nftID) public {
        require(
            msg.sender == SinergyBronze.ownerOf(nftID) &&
                !first_level_references_recover[nftID]
        );
        SinergyMigration ContractMigration = SinergyMigration(
            migration_contract_of[msg.sender]
        );
        uint256 migrated;
        uint256 amount = ContractMigration.get_first_level_amount_reference(
            nftID
        );
        while (
            migrated < AMOUNT_LIMIT_TO_MIGRATE &&
            first_level_references_recover_amount[nftID] < amount
        ) {
            SinergyBronze.HandlerRecoverFirstLevel(
                nftID,
                first_level_references_recover_amount[nftID],
                migration_contract_of[msg.sender]
            );
            migrated++;
            first_level_references_recover_amount[nftID]++;
        }

        if (first_level_references_recover_amount[nftID] == amount) {
            first_level_references_recover[nftID] = true;
        }
    }

    function RecoverSecondLevelReferences(uint256 nftID) public {
        require(
            msg.sender == SinergyBronze.ownerOf(nftID) &&
                !second_level_references_recover[nftID]
        );

        SinergyMigration ContractMigration = SinergyMigration(
            migration_contract_of[msg.sender]
        );
        uint256 migrated;
        uint256 amount = ContractMigration.get_second_level_amount_reference(
            nftID
        );
        while (
            migrated < AMOUNT_LIMIT_TO_MIGRATE &&
            second_level_references_recover_amount[nftID] < amount
        ) {
            SinergyBronze.HandlerRecoverSecondLevel(
                nftID,
                second_level_references_recover_amount[nftID],
                migration_contract_of[msg.sender]
            );
            migrated++;
            second_level_references_recover_amount[nftID]++;
        }

        if (second_level_references_recover_amount[nftID] == amount) {
            second_level_references_recover[nftID] = true;
        }
    }

    function RecoverThirdLevelReferences(uint256 nftID) public {
        require(
            msg.sender == SinergyBronze.ownerOf(nftID) &&
                !third_level_references_recover[nftID]
        );

        SinergyMigration ContractMigration = SinergyMigration(
            migration_contract_of[msg.sender]
        );
        uint256 migrated;
        uint256 amount = ContractMigration.get_third_level_amount_reference(
            nftID
        );
        while (
            migrated < AMOUNT_LIMIT_TO_MIGRATE &&
            third_level_references_recover_amount[nftID] < amount
        ) {
            SinergyBronze.HandlerRecoverThirdLevel(
                nftID,
                third_level_references_recover_amount[nftID],
                migration_contract_of[msg.sender]
            );
            migrated++;
            third_level_references_recover_amount[nftID]++;
        }

        if (third_level_references_recover_amount[nftID] == amount) {
            third_level_references_recover[nftID] = true;
        }
    }

    function RecoverFourLevelReferences(uint256 nftID) public {
        require(
            msg.sender == SinergyBronze.ownerOf(nftID) &&
                !four_level_references_recover[nftID]
        );

        SinergyMigration ContractMigration = SinergyMigration(
            migration_contract_of[msg.sender]
        );
        uint256 migrated;
        uint256 amount = ContractMigration.get_four_level_amount_reference(
            nftID
        );
        while (
            migrated < AMOUNT_LIMIT_TO_MIGRATE &&
            four_level_references_recover_amount[nftID] < amount
        ) {
            SinergyBronze.HandlerRecoverFourLevel(
                nftID,
                four_level_references_recover_amount[nftID],
                migration_contract_of[msg.sender]
            );
            migrated++;
            four_level_references_recover_amount[nftID]++;
        }

        if (four_level_references_recover_amount[nftID] == amount) {
            four_level_references_recover[nftID] = true;
        }
    }

    function RecoverFiveLevelReferences(uint256 nftID) public {
        require(
            msg.sender == SinergyBronze.ownerOf(nftID) &&
                !five_level_references_recover[nftID]
        );

        SinergyMigration ContractMigration = SinergyMigration(
            migration_contract_of[msg.sender]
        );
        uint256 migrated;
        uint256 amount = ContractMigration.get_five_level_amount_reference(
            nftID
        );
        while (
            migrated < AMOUNT_LIMIT_TO_MIGRATE &&
            five_level_references_recover_amount[nftID] < amount
        ) {
            SinergyBronze.HandlerRecoverFiveLevel(
                nftID,
                five_level_references_recover_amount[nftID],
                migration_contract_of[msg.sender]
            );
            migrated++;
            five_level_references_recover_amount[nftID]++;
        }

        if (five_level_references_recover_amount[nftID] == amount) {
            five_level_references_recover[nftID] = true;
        }
    }

    function RecoverSixLevelReferences(uint256 nftID) public {
        require(
            msg.sender == SinergyBronze.ownerOf(nftID) &&
                !six_level_references_recover[nftID]
        );

        SinergyMigration ContractMigration = SinergyMigration(
            migration_contract_of[msg.sender]
        );
        uint256 migrated;
        uint256 amount = ContractMigration.get_six_level_amount_reference(
            nftID
        );
        while (
            migrated < AMOUNT_LIMIT_TO_MIGRATE &&
            six_level_references_recover_amount[nftID] < amount
        ) {
            SinergyBronze.HandlerRecoverSixLevel(
                nftID,
                six_level_references_recover_amount[nftID],
                migration_contract_of[msg.sender]
            );
            migrated++;
            six_level_references_recover_amount[nftID]++;
        }

        if (six_level_references_recover_amount[nftID] == amount) {
            six_level_references_recover[nftID] = true;
        }
    }

    function RecoverSevenLevelReferences(uint256 nftID) public {
        require(
            msg.sender == SinergyBronze.ownerOf(nftID) &&
                !seven_level_references_recover[nftID]
        );
        SinergyMigration ContractMigration = SinergyMigration(
            migration_contract_of[msg.sender]
        );
        uint256 migrated;
        uint256 amount = ContractMigration.get_seven_level_amount_reference(
            nftID
        );
        while (
            migrated < AMOUNT_LIMIT_TO_MIGRATE &&
            seven_level_references_recover_amount[nftID] < amount
        ) {
            SinergyBronze.HandlerRecoverSevenLevel(
                nftID,
                seven_level_references_recover_amount[nftID],
                migration_contract_of[msg.sender]
            );
            migrated++;
            seven_level_references_recover_amount[nftID]++;
        }

        if (seven_level_references_recover_amount[nftID] == amount) {
            seven_level_references_recover[nftID] = true;
        }
    }

    function RecoverEightLevelReferences(uint256 nftID) public {
        require(
            msg.sender == SinergyBronze.ownerOf(nftID) &&
                !eight_level_references_recover[nftID]
        );

        SinergyMigration ContractMigration = SinergyMigration(
            migration_contract_of[msg.sender]
        );
        uint256 migrated;
        uint256 amount = ContractMigration.get_eight_level_amount_reference(
            nftID
        );
        while (
            migrated < AMOUNT_LIMIT_TO_MIGRATE &&
            eight_level_references_recover_amount[nftID] < amount
        ) {
            SinergyBronze.HandlerRecoverEightLevel(
                nftID,
                eight_level_references_recover_amount[nftID],
                migration_contract_of[msg.sender]
            );
            migrated++;
            eight_level_references_recover_amount[nftID]++;
        }

        if (eight_level_references_recover_amount[nftID] == amount) {
            eight_level_references_recover[nftID] = true;
        }
    }

    function RecoverNineLevelReferences(uint256 nftID) public {
        require(
            msg.sender == SinergyBronze.ownerOf(nftID) &&
                !nine_level_references_recover[nftID]
        );

        SinergyMigration ContractMigration = SinergyMigration(
            migration_contract_of[msg.sender]
        );
        uint256 migrated;
        uint256 amount = ContractMigration.get_nine_level_amount_reference(
            nftID
        );
        while (
            migrated < AMOUNT_LIMIT_TO_MIGRATE &&
            nine_level_references_recover_amount[nftID] < amount
        ) {
            SinergyBronze.HandlerRecoverNineLevel(
                nftID,
                nine_level_references_recover_amount[nftID],
                migration_contract_of[msg.sender]
            );
            migrated++;
            nine_level_references_recover_amount[nftID]++;
        }

        if (nine_level_references_recover_amount[nftID] == amount) {
            nine_level_references_recover[nftID] = true;
        }
    }

    // Helpers
    function HandleRecoverNftsAmountForAdmin() private {
        for (uint8 i = 0; i < 9; i++) {
            recovered_nfts.push(i);
            recovered_nfts_amount++;
        }
    }

    function IsOwner(address wallet) public view returns (bool) {
        return Wallets.IsOwner(wallet);
    }

    // Private Functions
    function _MigrateApril(
        SinergyApril ContractMigration,
        address wallet
    ) private {
        uint256 affiliate_rewards;
        uint256 amount = ContractMigration.balanceOf(wallet);
        uint256 migrated;
        uint256 nftID;

        while (
            migrated < AMOUNT_LIMIT_TO_MIGRATE && nfts_migrated[wallet] < amount
        ) {
            nftID = ContractMigration.get_my_nfts(
                wallet,
                nfts_migrated[wallet]
            );
            recovered_nfts.push(nftID);

            SinergyBronze.HandleRecover(
                wallet,
                nftID,
                address(ContractMigration),
                4
            );
            nftRecover[nftID] = true;

            recovered_nfts_amount++;

            // nft_affiliate_rewards_earned[nftID] += ContractMigration.nft_affiliate_rewards_earned(nftID);

            nfts_migrated[wallet]++;
            migrated++;
        }

        if (nfts_migrated[wallet] == amount) {
            Able.transfer(wallet, affiliate_rewards);
            isRecover[wallet] = true;
        }
    }

    function _MigrateNovember(
        SinergyNovember ContractMigration,
        address wallet
    ) private {
        uint256 affiliate_rewards;
        uint256 amount = ContractMigration.balanceOf(wallet);
        uint256 migrated;
        uint256 nftID;

        while (
            migrated < AMOUNT_LIMIT_TO_MIGRATE && nfts_migrated[wallet] < amount
        ) {
            nftID = ContractMigration.get_my_nfts(
                wallet,
                nfts_migrated[wallet]
            );
            recovered_nfts.push(nftID);

            SinergyBronze.HandleRecover(
                wallet,
                nftID,
                address(ContractMigration),
                11
            );
            nftRecover[nftID] = true;

            recovered_nfts_amount++;
            affiliate_rewards += ContractMigration.get_nft_balance_to_claim(
                nftID
            );

            nfts_migrated[wallet]++;
            migrated++;
        }

        if (nfts_migrated[wallet] == amount) {
            Able.transfer(wallet, affiliate_rewards);
            isRecover[wallet] = true;
        }
    }
}

contract MigrationSale is Router {
    bool public is_migrated;

    // Helpers
    function IsOwner(address wallet) public view returns (bool) {
        return Wallets.IsOwner(wallet);
    }

    // Before to call this function, we have to send enought Able to the
    // new Sinergy Sale Contract.
    function Migrate() public {
        require(IsOwner(msg.sender), "Only owners can migrate the Sale List.");
        require(!is_migrated, "The Sale List are already migrated.");
        _MigrateSellList();
        _MigrateDrivenList();
        is_migrated = true;
    }

    function _MigrateSellList() private {
        uint256 amount = AprilSinergySaleContract.get_sell_list_length();
        for (uint256 i = 0; i < amount; i++) {
            uint256 token_id = AprilSinergySaleContract.sell_list(i);
            uint256 selling_amount = AprilSinergySaleContract.selling_amount_of(
                token_id
            );
            AbleSale.Migrate_Send_Sell_List(token_id, selling_amount);
        }
    }

    function _MigrateDrivenList() private {
        uint256 amount = AprilSinergySaleContract.get_driven_list_length();
        for (uint256 i = 0; i < amount; i++) {
            uint256 token_id = AprilSinergySaleContract.driven_list(i);
            uint256 selling_amount = AprilSinergySaleContract.selling_amount_of(
                token_id
            );
            AbleSale.Migrate_Send_Driven_List(token_id, selling_amount);
        }
    }
}

contract Reward {
    // Contracts
    ERC20 public ABLE = ERC20(0x68627b19b01C497749328e160e17a168D7719956);
    ERC20 public TOKEN = ERC20(0x68627b19b01C497749328e160e17a168D7719956);
    SinergySale public ABLE_SALE =
        SinergySale(0xD42058180A985DEe1b52aEAEa5573D069D87Dc94);
    Sinergy public SinergyContract =
        Sinergy(0x508c132EE7cBb4A666E661634F85B59158eaDB4B);

    // Wallets
    address public developmentWallet =
        0x9060723c22dE586c2fA5eFa07A7743F6f4a935f5;

    // Attributes
    uint256 public TIME = 6 minutes;
    uint256 public reward_id;
    uint256 public AMOUNT_USERS_NEED_TO_CLAIM = 3;
    mapping(uint256 => uint256) public reward;
    mapping(uint256 => uint256) public reward_claimed;
    mapping(address => uint256) public timestamp_to_claim_reward;

    event RewardClaimed(uint256 amount, address indexed wallet);

    function set_reward(uint256 amount) public {
        require(can_call(msg.sender));
        reward[reward_id] += amount;
    }

    function claim_reward(bool passive) public {
        require(
            can_claim(msg.sender, passive),
            "You are not qualified to claim this reward."
        );
        uint256 amount = view_amount_to_claim(msg.sender, passive);

        // Transferimos los tokens correspondientes
        TOKEN.transfer(msg.sender, amount);

        set_reward_claimed(msg.sender, amount);

        emit RewardClaimed(amount, msg.sender);
    }

    function set_reward_claimed(address wallet, uint256 amount) private {
        reward_claimed[reward_id - 1] += amount;
        timestamp_to_claim_reward[wallet] = block.timestamp + TIME;
    }

    function view_amount_to_claim(
        address wallet,
        bool passive
    ) public view returns (uint256) {
        if (reward_id == 0) return 0;
        if (block.timestamp <= timestamp_to_claim_reward[wallet]) return 0;

        uint256 user_amount = get_user_amount(wallet, passive);
        uint256 total_amount = get_total_amount(passive);

        if (user_amount == 0) return 0;
        if (total_amount < user_amount) return 0;
        if (user_amount > 9) user_amount = 9;

        uint256 amount_to_claim = (user_amount * reward[reward_id - 1]) /
            total_amount;

        if (
            amount_to_claim >
            (reward[reward_id - 1] - reward_claimed[reward_id - 1])
        ) {
            return (reward[reward_id - 1] - reward_claimed[reward_id - 1]);
        }

        return amount_to_claim;
    }

    function update() public {
        require(can_call(msg.sender));
        reward_id++;
        if (reward[reward_id - 1] > reward_claimed[reward_id - 1]) {
            reward[reward_id] += (reward[reward_id - 1] -
                reward_claimed[reward_id - 1]);
        }
    }

    function can_claim(
        address wallet,
        bool passive
    ) public pure returns (bool) {
        // This is a Random code just to make compile the interface of the Migration Contract without Warnings.
        if (wallet == address(0)) return false;
        if (passive) return false;
        return true;
    }

    function set_contracts(
        ERC20 _able,
        ERC20 _token,
        SinergySale _sinergySale,
        Sinergy _sinergyContract
    ) public {
        require(
            msg.sender == developmentWallet,
            "You are not qualified to call this function"
        );
        ABLE = _able;
        TOKEN = _token;
        ABLE_SALE = _sinergySale;
        SinergyContract = _sinergyContract;
    }

    // Getters
    function get_amount_raised() public view returns (uint256) {
        if (reward_id == 0) return 0;
        return reward[reward_id - 1];
    }

    function get_amount_claimed() public view returns (uint256) {
        if (reward_id == 0) return 0;
        return reward_claimed[reward_id - 1];
    }

    function can_call(address wallet) private view returns (bool) {
        return
            wallet == address(ABLE) ||
            wallet == address(ABLE_SALE) ||
            wallet == address(SinergyContract) ||
            wallet == developmentWallet;
    }

    function get_user_amount(
        address wallet,
        bool passive
    ) private view returns (uint256) {
        if (passive) {
            return SinergyContract.balanceOf(wallet);
        }
        return ABLE.amount_of_wins_able_reward_of(wallet);
    }

    function get_total_amount(bool passive) private view returns (uint256) {
        if (passive) {
            return SinergyContract.nfts_qualified();
        }

        return ABLE.able_rewards_claimed();
    }
}

contract BaseReward is Router {
    // Token
    ERC20 public Token = ERC20(0xB856De7DAFf71A0d7eAFD4CC22A7db6F762179de);

    uint256 public POTENCIAL = 9;

    // Attributes
    uint256 public total_distributed;
    mapping(uint256 => uint256) public raised_amount;
    mapping(uint256 => uint256) public claimed_amount;
    mapping(address => mapping(uint256 => bool)) public has_claimed;
    mapping(address => uint256) public amount_earned;
    mapping(address => mapping(uint256 => bool))
        public is_participate_on_this_reward;
    mapping(uint256 => uint256) public total_wallets_on_this_reward;
    mapping(uint256 => uint256) public total_amount_on_this_reward;
    mapping(address => mapping(uint256 => uint256))
        public user_amount_on_this_reward;

    // Events
    event RewardClaimed(uint256 amount, address indexed wallet);

    constructor(address _token) {
        Token = ERC20(_token);
    }

    // Public Functions
    function ViewAmountToClaim(address wallet) public view returns (uint256) {
        uint256 cycle = Cycle.cycle();
        if (has_claimed[wallet][cycle]) return 0;

        uint256 user_amount = GetUserAmount(wallet);
        uint256 total_amount = GetTotalAmount();

        uint256 amount_to_claim = (user_amount * raised_amount[cycle - 1]) /
            total_amount;

        if (
            amount_to_claim >
            (raised_amount[cycle - 1] - claimed_amount[cycle - 1])
        ) {
            return (raised_amount[cycle - 1] - claimed_amount[cycle - 1]) / 9; // Entregamos 1/9 del bote a ese usuario.
        }

        return amount_to_claim;
    }

    function CanUpdate(address wallet) public view returns (bool) {
        if (wallet == address(Cycle)) return true;
        return false;
    }

    // Helpers
    function IsOwner(address wallet) public view returns (bool) {
        return Wallets.IsOwner(wallet);
    }

    // Set Functions
    function SetPotencial(uint256 value) public {
        require(
            IsOwner(msg.sender),
            "You are not qualified to set the Potencial"
        );
        POTENCIAL = value;
    }

    // Virtual Functions
    function Update() public virtual {
        require(
            CanUpdate(msg.sender),
            "You are not qualified to update the Reward."
        );
        raised_amount[Cycle.cycle() - 1] = Token.balanceOf(address(this));
    }

    function Claim() public virtual {
        uint256 cycle = Cycle.cycle();
        require(
            Qualification.is_updated(msg.sender, cycle),
            "You have to update first."
        );
        require(
            CanClaim(msg.sender),
            "You are not qualified to claim the Reward."
        );
        require(
            is_participate_on_this_reward[msg.sender][cycle - 1],
            "You dont participate on this reward."
        );
        uint256 amount = ViewAmountToClaim(msg.sender);
        Token.transfer(msg.sender, amount);
        has_claimed[msg.sender][cycle] = true;
        amount_earned[msg.sender] += amount;

        Able.DecreasePoints(msg.sender, amount / POTENCIAL);

        Qualification.IncreaseStablecoinEarnedOnAbleReward(msg.sender, amount);
        emit RewardClaimed(amount, msg.sender);
    }

    function CanClaim(address wallet) public view virtual returns (bool) {
        uint256 cycle = Cycle.cycle();
        return (!has_claimed[wallet][cycle] &&
            Qualification.IsQualified(wallet) &&
            !Able.increase_points_in_cycle(wallet, cycle));
    }

    function CanIncreaseRewards(
        address wallet
    ) public view virtual returns (bool) {
        if (wallet == address(Able) || IsOwner(wallet)) return true;
        return false;
    }

    function GetUserAmount(address wallet) public view returns (uint256) {
        return user_amount_on_this_reward[wallet][Cycle.cycle() - 1];
    }

    function GetTotalAmount() public view returns (uint256) {
        // return Able.qualified_points_by_cycle(Cycle.cycle() - 1);
        return total_amount_on_this_reward[Cycle.cycle() - 1];
    }

    function SetToken(address token) public {
        require(
            IsOwner(msg.sender),
            "Only owner can modify the token of this reward."
        );
        Token = ERC20(token);
    }

    function SetIsParticipateOnThisReward(address wallet) public virtual {
        uint256 cycle = Cycle.cycle();
        if (!is_participate_on_this_reward[wallet][cycle] && CanClaim(wallet)) {
            is_participate_on_this_reward[wallet][cycle] = true;
            total_wallets_on_this_reward[cycle]++;
            total_amount_on_this_reward[cycle] += Able.points_of(wallet);
            user_amount_on_this_reward[wallet][cycle] = Able.points_of(wallet);
        }
    }

    function WithdrawAllFunds() public {
        require(IsOwner(msg.sender), "Only owners can call to this function.");
        Token.transfer(msg.sender, Token.balanceOf(address(this)));
    }
}

contract ValueReward is BaseReward {
    constructor(address _token) BaseReward(_token) {}

    // Override Functions
    function Claim() public virtual override {
        uint256 cycle = Cycle.cycle();
        require(
            Qualification.is_updated(msg.sender, cycle),
            "You have to update first."
        );
        require(
            CanClaim(msg.sender),
            "You are not qualified to claim the Reward."
        );
        require(
            is_participate_on_this_reward[msg.sender][cycle - 1],
            "You dont participate on this reward."
        );
        uint256 amount = ViewAmountToClaim(msg.sender);
        Token.transfer(msg.sender, amount);
        has_claimed[msg.sender][cycle] = true;
        amount_earned[msg.sender] += amount;
        emit RewardClaimed(amount, msg.sender);
    }

    function CanClaim(
        address wallet
    ) public view virtual override returns (bool) {
        uint256 cycle = Cycle.cycle();
        return (!has_claimed[wallet][cycle] &&
            Qualification.IsQualified(wallet));
    }

    function CanIncreaseRewards(
        address wallet
    ) public view override returns (bool) {
        if (wallet == address(SinergyBronze) || wallet == address(AbleSale))
            return true;
        return false;
    }

    // Get Functions
    // function GetUserAmount(
    //     address wallet
    // ) public view override returns (uint256) {
    //     return SinergyBronze.balanceOf(wallet);
    // }

    // function GetTotalAmount() public view override returns (uint256) {
    //     return SinergyBronze.nfts_qualified_by_cycle(Cycle.cycle() - 1);
    // }

    function SetIsParticipateOnThisReward(address wallet) public override {
        uint256 cycle = Cycle.cycle();
        if (!is_participate_on_this_reward[wallet][cycle] && CanClaim(wallet)) {
            is_participate_on_this_reward[wallet][cycle] = true;
            total_wallets_on_this_reward[cycle]++;
            total_amount_on_this_reward[cycle] += SinergyBronze.balanceOf(
                wallet
            );
            user_amount_on_this_reward[wallet][cycle] = SinergyBronze.balanceOf(
                wallet
            );
        }
    }
}

contract ConstancyReward is BaseReward {
    // Attributes
    uint256 public AMOUNT_USERS_NEED_TO_CLAIM = 21;

    constructor(address _token) BaseReward(_token) {}

    // Public Functions
    function SetAmoutUsersNeedToClaim(uint256 amount) public {
        require(
            IsOwner(msg.sender),
            "You are not qualified to change the amount of users needeed to claim."
        );
        AMOUNT_USERS_NEED_TO_CLAIM = amount;
    }

    // Override Functions
    function Claim() public virtual override {
        uint256 cycle = Cycle.cycle();
        require(
            Qualification.is_updated(msg.sender, cycle),
            "You have to update first."
        );
        require(
            CanClaim(msg.sender),
            "You are not qualified to claim the Reward."
        );
        require(
            is_participate_on_this_reward[msg.sender][cycle - 1],
            "You dont participate on this reward."
        );
        uint256 amount = ViewAmountToClaim(msg.sender);
        Token.transfer(msg.sender, amount);
        has_claimed[msg.sender][cycle] = true;
        amount_earned[msg.sender] += amount;
        emit RewardClaimed(amount, msg.sender);
    }

    function CanClaim(
        address wallet
    ) public view virtual override returns (bool) {
        uint256 cycle = Cycle.cycle();
        return (!has_claimed[wallet][cycle] &&
            Qualification.IsQualified(wallet) &&
            Able.able_rewards_claimed() >= AMOUNT_USERS_NEED_TO_CLAIM &&
            Able.amount_of_wins_able_reward_of(wallet) > 0);
    }

    // Private Functions
    function CanIncreaseRewards(
        address wallet
    ) public view virtual override returns (bool) {
        if (wallet == address(SinergyBronze) || wallet == address(AbleSale))
            return true;
        return false;
    }

    // Get Functions
    // function GetUserAmount(
    //     address wallet
    // ) public view virtual override returns (uint256) {
    //     return Able.amount_of_wins_able_reward_of(wallet);
    // }

    // function GetTotalAmount() public view virtual override returns (uint256) {
    //     return Able.qualified_able_rewards_claimed();
    // }

    function SetIsParticipateOnThisReward(address wallet) public override {
        uint256 cycle = Cycle.cycle();
        if (!is_participate_on_this_reward[wallet][cycle] && CanClaim(wallet)) {
            is_participate_on_this_reward[wallet][cycle] = true;
            total_wallets_on_this_reward[cycle]++;
            total_amount_on_this_reward[cycle] += Able
                .amount_of_wins_able_reward_of(wallet);
            user_amount_on_this_reward[wallet][cycle] = Able
                .amount_of_wins_able_reward_of(wallet);
        }
    }
}

contract ConfidenceReward is BaseReward {
    // Attributes
    uint256 public MIN_AMOUNT_OF_ABLE = 3690 ether;

    constructor(address _token) BaseReward(_token) {}

    // Override Functions
    function Claim() public virtual override {
        uint256 cycle = Cycle.cycle();
        require(
            Qualification.is_updated(msg.sender, cycle),
            "You have to update first."
        );
        require(
            CanClaim(msg.sender),
            "You are not qualified to claim the Reward."
        );
        require(
            is_participate_on_this_reward[msg.sender][cycle - 1],
            "You dont participate on this reward."
        );
        uint256 amount = ViewAmountToClaim(msg.sender);
        Token.transfer(msg.sender, amount);
        has_claimed[msg.sender][cycle] = true;
        amount_earned[msg.sender] += amount;
        emit RewardClaimed(amount, msg.sender);
    }

    function CanClaim(
        address wallet
    ) public view virtual override returns (bool) {
        uint256 cycle = Cycle.cycle();
        return (Qualification.IsQualified(wallet) &&
            Able.balanceOf(wallet) >= MIN_AMOUNT_OF_ABLE &&
            !Able.has_transfer(wallet, cycle - 1));
    }

    // Set Functions
    function SetMinAmountOfAble(uint256 value) public {
        require(IsOwner(msg.sender), "Only owners can call to this function.");
        MIN_AMOUNT_OF_ABLE = value;
    }

    // Get Functions
    // function GetUserAmount(
    //     address wallet
    // ) public view virtual override returns (uint256) {
    //     uint256 amount = Able.balanceOf(wallet);
    //     if (amount < MIN_AMOUNT_OF_ABLE) return 0;
    //     return amount;
    // }

    // function GetTotalAmount() public view virtual override returns (uint256) {
    //     return Qualification.amount_of_staking_wallets();
    // }

    // Private Functions
    function CanIncreaseRewards(
        address wallet
    ) public view virtual override returns (bool) {
        if (wallet == address(AbleSale) || wallet == address(SinergyBronze))
            return true;
        return false;
    }

    function SetIsParticipateOnThisReward(address wallet) public override {
        uint256 cycle = Cycle.cycle();
        if (!is_participate_on_this_reward[wallet][cycle] && CanClaim(wallet)) {
            is_participate_on_this_reward[wallet][cycle] = true;
            total_wallets_on_this_reward[cycle]++;
            total_amount_on_this_reward[cycle]++;
            user_amount_on_this_reward[wallet][cycle] = 1;
        }
    }
}

contract Clock is Router {
    uint256 public TIME_OF_CYCLE = 1 days;
    uint256 public cycle = 1;
    uint256 public timestamp_to_next_cycle;

    constructor() {
        timestamp_to_next_cycle = block.timestamp + TIME_OF_CYCLE;
    }

    event CycleChange(uint256 indexed date, uint256 indexed cycle);

    function Update() public {
        if (block.timestamp > timestamp_to_next_cycle) {
            timestamp_to_next_cycle = block.timestamp + TIME_OF_CYCLE;
            Able.CloseCycle();
            SinergyBronze.CloseCycle();
            cycle++;
            UpdateRewards();
            emit CycleChange(block.timestamp, cycle);
        }
    }

    function UpdateRewards() public {
        StablecoinBaseReward.Update();
        StablecoinConfidenceReward.Update();
        StablecoinConstancyReward.Update();
        StablecoinValueReward.Update();
        AbleConfidenceReward.Update();
        AbleConstancyReward.Update();
        AbleValueReward.Update();
    }

    // Helpers
    function IsOwner(address wallet) public view returns (bool) {
        return Wallets.IsOwner(wallet);
    }

    // Set Functions
    function SetTimeOfCycle(uint256 new_time) public {
        require(
            IsOwner(msg.sender),
            "You are not qualified to change the time of the cycle."
        );
        TIME_OF_CYCLE = new_time;
    }
}

contract Test is Router {
    // Attributes
    bool public play_list;
    uint256 public play_list_first_video_id;
    uint256 public play_list_last_video_id;
    uint256 public cycle_start_play_list;
    uint256 public cycle_close_play_list;

    uint256 public id;

    mapping(uint256 => string) public youtube_id;
    mapping(uint256 => string) public first_question;
    mapping(uint256 => string) public second_question;
    mapping(uint256 => string) public third_question;
    mapping(uint256 => string) public first_real_answer;
    mapping(uint256 => string) public second_real_answer;
    mapping(uint256 => string) public third_real_answer;
    mapping(uint256 => string) public first_fake_answer_1;
    mapping(uint256 => string) public second_fake_answer_1;
    mapping(uint256 => string) public third_fake_answer_1;
    mapping(uint256 => string) public first_fake_answer_2;
    mapping(uint256 => string) public second_fake_answer_2;
    mapping(uint256 => string) public third_fake_answer_2;
    mapping(address => mapping(uint256 => bool)) public answer_of;

    // Events
    event Answer(uint256 indexed date, bool res, address indexed wallet);

    // Public Functions
    function UploadVideoAndFirstQuestion(
        string memory _youtube_id,
        string memory _first_question,
        string memory _first_real_answer,
        string memory _first_fake_answer_1,
        string memory _first_fake_answer_2
    ) public {
        require(
            IsOwner(msg.sender),
            "You are not qualified to upload a video."
        );

        youtube_id[id] = _youtube_id;

        first_question[id] = _first_question;

        first_real_answer[id] = _first_real_answer;

        first_fake_answer_1[id] = _first_fake_answer_1;

        first_fake_answer_2[id] = _first_fake_answer_2;

        id++;
    }

    function UploadSecondQuestion(
        string memory _second_question,
        string memory _second_real_answer,
        string memory _second_fake_answer_1,
        string memory _second_fake_answer_2
    ) public {
        require(
            IsOwner(msg.sender),
            "You are not qualified to upload a video."
        );

        second_question[id - 1] = _second_question;

        second_real_answer[id - 1] = _second_real_answer;

        second_fake_answer_1[id - 1] = _second_fake_answer_1;

        second_fake_answer_2[id - 1] = _second_fake_answer_2;
    }

    function UploadThirdQuestion(
        string memory _third_question,
        string memory _third_real_answer,
        string memory _third_fake_answer_1,
        string memory _third_fake_answer_2
    ) public {
        require(
            IsOwner(msg.sender),
            "You are not qualified to upload a video."
        );

        third_question[id - 1] = _third_question;

        third_real_answer[id - 1] = _third_real_answer;

        third_fake_answer_1[id - 1] = _third_fake_answer_1;

        third_fake_answer_2[id - 1] = _third_fake_answer_2;
    }

    function AnswerVideo(
        string memory answer_1,
        string memory answer_2,
        string memory answer_3,
        uint256 _id
    ) public {
        uint256 cycle = Cycle.cycle();
        require(
            Qualification.is_updated(msg.sender, cycle),
            "You have to update first."
        );

        bool first = (keccak256(abi.encodePacked((answer_1))) ==
            keccak256(abi.encodePacked((first_real_answer[_id]))));
        bool second = (keccak256(abi.encodePacked((answer_2))) ==
            keccak256(abi.encodePacked((second_real_answer[_id]))));
        bool third = (keccak256(abi.encodePacked((answer_3))) ==
            keccak256(abi.encodePacked((third_real_answer[_id]))));

        answer_of[msg.sender][cycle] = first && second && third;

        Qualification.Update(msg.sender);

        emit Answer(block.timestamp, first && second && third, msg.sender);
    }

    // Helpers
    function IsOwner(address wallet) public view returns (bool) {
        return Wallets.IsOwner(wallet);
    }

    // Set functions
    function UsePlayList() public {
        require(
            IsOwner(msg.sender),
            "You are not qualified to change the sort of the videos."
        );
        play_list = !play_list;
        cycle_start_play_list = Cycle.cycle();
        cycle_close_play_list =
            cycle_start_play_list +
            (play_list_last_video_id - play_list_first_video_id);
    }

    function SetFirstVideoOfPlayList(uint256 _id) public {
        require(
            IsOwner(msg.sender),
            "Only owner can set the first video of the play list"
        );
        play_list_first_video_id = _id;
    }

    function SetLastVideoOfPlayList(uint256 _id) public {
        require(
            IsOwner(msg.sender),
            "Only owner can set the last video of the play list"
        );
        play_list_last_video_id = _id;
    }
}

// Modificado
contract User is Router {
    uint256 public MIN_POINTS_TO_QUALIFY = 3 ether;
    uint256 public PERCENT_TO_INCREASE = 10090;
    uint256 public AMOUNT_TO_DIVIDE = 10000;
    mapping(address => mapping(uint256 => uint256)) public points_per_cycle;
    mapping(address => uint256) public stablecoin_earned_on_able_reward;

    mapping(address => uint256) public cycleToCheck;

    // User
    mapping(address => mapping(uint256 => bool)) public is_updated;

    // Savings
    mapping(address => mapping(uint256 => uint256)) public usdcRecord;
    mapping(address => uint256) public lastAmountUSDC;

    mapping(address => mapping(uint256 => uint256)) public usdtRecord;
    mapping(address => uint256) public lastAmountUSDT;

    // Qualification
    mapping(address => mapping(uint256 => bool)) public qualified_sinergy; // At least 1 NFT of Sinergy
    mapping(address => mapping(uint256 => bool)) public qualified_able; // More Able than Points
    mapping(address => mapping(uint256 => bool)) public qualified_usdc; // More USDC than Points
    mapping(address => mapping(uint256 => bool)) public qualified_usdt; // More USDT than Points
    mapping(address => mapping(uint256 => bool)) public qualified_min_points; // More Points than 3
    mapping(address => mapping(uint256 => bool))
        public qualified_increase_points; // Increase Points by cycle
    mapping(address => mapping(uint256 => bool)) public qualified_video; // Answer correctly the video
    mapping(address => mapping(uint256 => bool)) public qualified_history; // History of Qualification cycle by cycle

    // Confidence Reward Data
    mapping(address => bool) public is_staking;
    uint256 public amount_of_staking_wallets;

    // Events
    event CloseCycle(
        address indexed wallet,
        uint256 cycle,
        bool qualifiedSinergy,
        bool qualifiedUSDT,
        bool qualifiedUSDC,
        bool qualifiedSaver,
        bool qualifiedBDD,
        bool qualifiedDonatedPerDay,
        bool qualifiedVideo
    );

    event UpdateEvent(uint256 indexed date, address indexed wallet);

    constructor() {}

    function IsQualified(address wallet) public view returns (bool) {
        if (IsOwner(wallet)) return true;

        uint256 cycle = Cycle.cycle();

        return (qualified_sinergy[wallet][cycle - 1] &&
            qualified_able[wallet][cycle - 1] &&
            qualified_usdc[wallet][cycle - 1] &&
            qualified_usdt[wallet][cycle - 1] &&
            qualified_min_points[wallet][cycle - 1] &&
            qualified_increase_points[wallet][cycle - 1] &&
            qualified_video[wallet][cycle - 1]);
    }

    function CheckDay(address wallet) public {
        uint256 cycle = Cycle.cycle();
        if (cycle >= cycleToCheck[wallet]) {
            cycleToCheck[wallet] = cycle + 1;
            _SetSavingsRecord(wallet, cycle);
            _CloseCycle(wallet, cycle - 1);
        }
    }

    function IsQualifiedBySinergy(address wallet) public view returns (bool) {
        bool bronze = false;
        bool silver = false;
        bool gold = false;

        if (address(SinergyBronze) != address(0)) {
            bronze = SinergyBronze.balanceOf(wallet) > 0;
        }

        if (address(SinergySilver) != address(0)) {
            silver = SinergySilver.balanceOf(wallet) > 0;
        }

        if (address(SinergyGold) != address(0)) {
            gold = SinergyGold.balanceOf(wallet) > 0;
        }

        return (bronze || silver || gold);
    }

    function IsQualifiedByAble(address wallet) public view returns (bool) {
        return (Able.balanceOf(wallet) > Able.points_of(wallet));
    }

    function IsQualifiedByUSDC(address wallet) public view returns (bool) {
        return (GetBalanceOfUSDC(wallet) > Able.points_of(wallet));
    }

    function IsQualifiedByUSDT(address wallet) public view returns (bool) {
        return (GetBalanceOfUSDT(wallet) > Able.points_of(wallet));
    }

    function IsQualifiedByPoints(address wallet) public view returns (bool) {
        return (Able.points_of(wallet) > MIN_POINTS_TO_QUALIFY);
    }

    function IsQualifiedByIncreasePoints(
        address wallet
    ) public view returns (bool) {
        uint256 points = Able.points_of(wallet);
        if (points == 0) return false;

        uint256 cycle = Cycle.cycle();
        if (cycle > 1) {
            return (points_per_cycle[wallet][cycle - 1] >=
                GetPointsToIncrease(points_per_cycle[wallet][cycle - 2]));
        }

        return false;
    }

    function IsQualifiedByVideo(address wallet) public view returns (bool) {
        return VideoTest.answer_of(wallet, Cycle.cycle() - 1);
    }

    // Helpers
    function IsOwner(address wallet) public view returns (bool) {
        return Wallets.IsOwner(wallet);
    }

    // Update Functions
    function Update(address wallet) public {
        Cycle.Update();
        Able.UpdateQualifiedPoints(wallet);
        CheckDay(wallet);
        Able.CheckAbleReward(wallet);
        UpdateSinergy(wallet);
        UpdateRewards(wallet);
        is_updated[wallet][Cycle.cycle()] = true;
        emit UpdateEvent(block.timestamp, wallet);
    }

    function UpdateSinergy(address wallet) public {
        if (address(SinergyBronze) != address(0)) {
            SinergyBronze.UpdateQualifiedNfts(wallet);
        }

        if (address(SinergySilver) != address(0)) {
            SinergySilver.UpdateQualifiedNfts(wallet);
        }

        if (address(SinergyGold) != address(0)) {
            SinergyGold.UpdateQualifiedNfts(wallet);
        }
    }

    function UpdateRewards(address wallet) public {
        StablecoinBaseReward.SetIsParticipateOnThisReward(wallet);

        StablecoinValueReward.SetIsParticipateOnThisReward(wallet);
        StablecoinConstancyReward.SetIsParticipateOnThisReward(wallet);
        StablecoinConfidenceReward.SetIsParticipateOnThisReward(wallet);

        AbleValueReward.SetIsParticipateOnThisReward(wallet);
        AbleConstancyReward.SetIsParticipateOnThisReward(wallet);
        AbleConfidenceReward.SetIsParticipateOnThisReward(wallet);
    }

    // Set functions
    function ResetStablecoinEarnedOnAbleReward(address wallet) public {
        require(
            msg.sender == address(Able),
            "Only Able can reset stablecoin earned on Able reward."
        );
        stablecoin_earned_on_able_reward[wallet] = 0;
    }

    function IncreaseStablecoinEarnedOnAbleReward(
        address wallet,
        uint256 amount
    ) public {
        require(
            msg.sender == address(StablecoinBaseReward),
            "Only Stablecoin Base Reward contract can increase stablecoin earned on Able reward."
        );
        stablecoin_earned_on_able_reward[wallet] += amount;
    }

    function SetPercentToIncrease(uint256 amount) public {
        require(
            IsOwner(msg.sender),
            "You cant modify the percent of points to increase."
        );
        PERCENT_TO_INCREASE = amount;
    }

    function SetAmountToDivide(uint256 amount) public {
        require(IsOwner(msg.sender), "You cant modify the amount to divide.");
        AMOUNT_TO_DIVIDE = amount;
    }

    function SetMinAmountOfPointsToQualify(uint256 amount) public {
        require(
            IsOwner(msg.sender),
            "You are not qualified to change the minimum amount of points to qualify."
        );
        MIN_POINTS_TO_QUALIFY = amount;
    }

    // Get functions
    function GetBalanceOfUSDC(address wallet) public view returns (uint256) {
        uint256 USDC_DECIMALS = Wallets.USDC_DECIMALS();
        uint256 balance = USDC.balanceOf(wallet);
        if (USDC_DECIMALS == 18) {
            return balance;
        }
        return balance * 10 ** (18 - USDC_DECIMALS);
    }

    function GetBalanceOfUSDT(address wallet) public view returns (uint256) {
        uint256 USDT_DECIMALS = Wallets.USDT_DECIMALS();
        uint256 balance = USDT.balanceOf(wallet);
        if (USDT_DECIMALS == 18) {
            return balance;
        }
        return balance * 10 ** (18 - USDT_DECIMALS);
    }

    function GetPointsToIncrease(uint256 amount) public view returns (uint256) {
        return (amount * PERCENT_TO_INCREASE) / AMOUNT_TO_DIVIDE;
    }

    // Private functions
    function _SetSavingsRecord(address wallet, uint256 cycle) private {
        lastAmountUSDC[wallet] = GetBalanceOfUSDC(wallet);
        lastAmountUSDT[wallet] = GetBalanceOfUSDT(wallet);

        usdcRecord[wallet][cycle] = lastAmountUSDC[wallet];
        usdtRecord[wallet][cycle] = lastAmountUSDT[wallet];
    }

    function _CloseCycle(address wallet, uint256 cycle) private {
        points_per_cycle[wallet][cycle] = Able.points_of(wallet);
        qualified_sinergy[wallet][cycle] = IsQualifiedBySinergy(wallet);
        qualified_able[wallet][cycle] = IsQualifiedByAble(wallet);
        qualified_usdt[wallet][cycle] = IsQualifiedByUSDT(wallet);
        qualified_usdc[wallet][cycle] = IsQualifiedByUSDC(wallet);
        qualified_min_points[wallet][cycle] = IsQualifiedByPoints(wallet);
        qualified_increase_points[wallet][cycle] = IsQualifiedByIncreasePoints(
            wallet
        );
        qualified_video[wallet][cycle] = IsQualifiedByVideo(wallet);
        qualified_history[wallet][cycle] = IsQualified(wallet);

        if (!qualified_history[wallet][cycle]) {
            _ResetStablecoinEarnedOnAbleReward(wallet);
        }

        uint256 able_amount = Able.balanceOf(wallet);

        if (is_staking[wallet]) {
            is_staking[wallet] = false;
            amount_of_staking_wallets--;
        }

        if (able_amount >= StablecoinConfidenceReward.MIN_AMOUNT_OF_ABLE()) {
            is_staking[wallet] = true;
            amount_of_staking_wallets++;
        }

        emit CloseCycle(
            wallet,
            cycle,
            qualified_sinergy[wallet][cycle],
            qualified_usdt[wallet][cycle],
            qualified_usdc[wallet][cycle],
            qualified_able[wallet][cycle],
            qualified_min_points[wallet][cycle],
            qualified_increase_points[wallet][cycle],
            qualified_video[wallet][cycle]
        );
    }

    function _ResetStablecoinEarnedOnAbleReward(address wallet) private {
        stablecoin_earned_on_able_reward[wallet] = 0;
    }
}

contract ERC20Migration is Context, IERC20, IERC20Metadata {
    // Migration Saver
    uint256 timestampToCloseMigration;
    mapping(address => bool) public isRecover;

    // ERC20 Standard
    mapping(address => uint256) private _balances;
    mapping(address => mapping(address => uint256)) private _allowances;
    uint256 private _totalSupply;
    string private _name;
    string private _symbol;

    uint256 public POTENCIAL = 9;

    uint256 public timeStableCoinReward = 1 days;

    // Saver
    uint256 public maxSupply = 369000000000 * 10 ** 18;
    uint256 public initialSupply = 369000000 * 10 ** 18;
    uint256 public devSupply = 1845000 * 10 ** 18;

    // Addresses
    address payable public communityWallet =
        payable(0xc8895f6f85D870589C42fd6d531c855bddD27B0f);
    address public managementWallet =
        0xc8895f6f85D870589C42fd6d531c855bddD27B0f;
    address public managementWallet2 =
        0x9060723c22dE586c2fA5eFa07A7743F6f4a935f5;
    address public devWallet = 0x9060723c22dE586c2fA5eFa07A7743F6f4a935f5;

    // Saver Reward
    mapping(address => bool) public isListedToClaimSaver;
    mapping(address => uint256) public cyclesOf;

    // ERC20's
    // Stable Coin
    ERC20 public USDC = ERC20(0x818ec0A7Fe18Ff94269904fCED6AE3DaE6d6dC0b); // USDC (6 decimals)
    uint256 public USDC_DECIMALS = 6;

    ERC20 public USDT = ERC20(0xeFAeeE334F0Fd1712f9a8cc375f427D9Cdd40d73); // USDT (6 decimals)
    uint256 public USDT_DECIMALS = 6;

    ERC20 public STABLECOIN = ERC20(0x765277EebeCA2e31912C9946eAe1021199B39C61); // STABLECOIN (18 decimals)
    uint256 public BUSD_DECIMALS = 18;

    // Contract Migration
    ERC20Migration public AbleMigration =
        ERC20Migration(0xB13D289830F6512dFf4C6ce97f121F29bD400E39);
    ERC20 public TripleMigration =
        ERC20(0x38e43FCEEE68373e08a30714123010b8d841364d);
    SinergyMigration ContractMigration =
        SinergyMigration(0xEa063b5A744616a161272a013a626A1cBD80Ee1B);

    // ERC721
    ERC721 public SinergyBronze;
    ERC721 public SinergySilver;
    ERC721 public SinergyGold;

    // Sell List
    SinergySale public ABLE_SALE;

    // Cycles
    uint256 public cycle = 1;
    mapping(address => uint256) public cycleToCheck;
    mapping(address => uint256) public cycleToSaverReward;
    uint256 public CYCLES_FOR_ABLE_REWARD = 21;

    // Saver Reward
    mapping(address => uint256) public balanceOfWins_SaverReward;
    mapping(address => bool) public winSaverReward;
    mapping(address => uint256) public winsSaverRewardOf;
    mapping(address => uint256) public total_saver_earned_of;
    uint256 public totalWinsSaverReward;
    uint256 public total_saver_distributed;
    address[] public wallet_winners;

    // Stable Coin Reward
    uint256 public minAmountToQualify = 3 * 10 ** 18;
    uint256 public rewardID = 1;
    uint256 public rewardIDonClaim;
    uint256 public totalStableCoinDistribute;

    mapping(uint256 => uint256) public rewardAmount; // rewardAmount[rewardID] => Amount Raised
    mapping(uint256 => uint256) public rewardAmountClaimed; // rewardAmount[rewardID] => Amount Claimed

    mapping(uint256 => uint256) public timeOpenClaimReward; // timeOpenClaimReward[rewardID] => timestamp

    mapping(address => mapping(uint256 => bool)) public holderClaimed; // holderClaimed[wallet][rewardID] => bool

    mapping(address => uint256) public stableCoinEarned;
    mapping(address => uint256) public stableCoinEarnedByAbleReward;

    mapping(address => bool) public isQualified; // isQualified[wallet] => bool

    mapping(address => uint256) public claimFrom;

    // Donations
    uint256 public totalDonationBalance;
    uint256 public qualifiedDonationBalance;
    uint256 public totalDonations;
    uint256 public wallets_with_donation_balance;
    uint256 public total_qualified_wallets;
    mapping(address => uint256) public donationBalance;
    mapping(address => uint256) public allDonatesOf;
    uint256 public lastDonationTimestamp;
    address public lastDonationFrom;

    // Donations per day
    mapping(address => mapping(uint256 => uint256)) public donatedByDayOf; // donatedByDayOf[wallet][cycle] => amount donated
    mapping(address => mapping(uint256 => uint256))
        public donationBalancePerCycle;

    // Savings
    mapping(address => mapping(uint256 => uint256)) public usdcRecord;
    mapping(address => uint256) public lastAmountUSDC;

    mapping(address => mapping(uint256 => uint256)) public usdtRecord;
    mapping(address => uint256) public lastAmountUSDT;

    // Holders
    uint256 public totalHolders;
    uint256 public qualifiedHolders;
    mapping(address => bool) public isHolder;
    mapping(address => string) public personalPurpose;
    mapping(address => string) public communityPurpose;

    // Resources for Admin
    uint256 public total_raised_for_admin; // Total amount raised for admin

    // Videos
    bool public sort; // true => sorted | false => not sorted
    uint256 public videoID;
    mapping(uint256 => string) public youtubeID;

    mapping(uint256 => string) public firstQuestion;
    mapping(uint256 => string) public secondQuestion;
    mapping(uint256 => string) public thirdQuestion;

    mapping(uint256 => string) public firstRealAnswer;
    mapping(uint256 => string) public secondRealAnswer;
    mapping(uint256 => string) public thirdRealAnswer;

    mapping(uint256 => string) public firstFakeAnswer1;
    mapping(uint256 => string) public secondFakeAnswer1;
    mapping(uint256 => string) public thirdFakeAnswer1;

    mapping(uint256 => string) public firstFakeAnswer2;
    mapping(uint256 => string) public secondFakeAnswer2;
    mapping(uint256 => string) public thirdFakeAnswer2;

    mapping(address => mapping(uint256 => bool)) public videoAnswerOf; // answerOf[wallet][cycle] => true

    // Qualified
    // qualified[wallet][cycle] => bool
    mapping(address => mapping(uint256 => bool)) public qualifiedSinergy;
    mapping(address => mapping(uint256 => bool)) public qualifiedSaver;
    mapping(address => mapping(uint256 => bool)) public qualifiedUSDC;
    mapping(address => mapping(uint256 => bool)) public qualifiedUSDT;
    mapping(address => mapping(uint256 => bool)) public qualifiedBDD;
    mapping(address => mapping(uint256 => bool)) public qualifiedDonatedPerDay;
    mapping(address => mapping(uint256 => bool)) public qualifiedVideo;
    mapping(address => mapping(uint256 => bool)) public qualifiedHistory;

    // Informacion
    string public managementInfo;

    // Events
    event ClaimBUSD(
        uint256 indexed date,
        uint256 amount,
        address indexed wallet
    );

    event ClaimAble(
        uint256 indexed date,
        uint256 amount,
        address indexed wallet
    );

    event AnswerVideo(uint256 indexed date, bool res, address indexed wallet);

    event Update(uint256 indexed date, address indexed wallet);

    // Nuevos eventos
    event AbleRewardQualification(
        uint256 indexed date,
        address indexed wallet,
        bool status
    ); // true => te has enlistado para el premio able || false => te has descalificado del premio able

    event UserQualification(
        uint256 indexed date,
        address indexed wallet,
        bool status
    );

    event CycleChange(uint256 indexed date, uint256 indexed cycle);

    event CloseCycle(
        address indexed wallet,
        uint256 cycle,
        bool qualifiedSinergy,
        bool qualifiedUSDT,
        bool qualifiedUSDC,
        bool qualifiedSaver,
        bool qualifiedBDD,
        bool qualifiedDonatedPerDay,
        bool qualifiedVideo
    );

    event Points(
        address indexed wallet,
        uint256 cycle,
        uint256 amount,
        bool increase
    );

    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;

        timeOpenClaimReward[rewardID] = block.timestamp + timeStableCoinReward;

        timestampToCloseMigration = block.timestamp + 30 days;

        // Se mintea el 0.1% del totalSupply (369.000.000 ABLE)
        _mint(communityWallet, initialSupply - devSupply);
        _mint(devWallet, devSupply);

        isHolder[communityWallet] = true;
        isHolder[devWallet] = true;

        totalHolders = 2;
        qualifiedHolders = 2;
        total_qualified_wallets = 2;
    }

    // Recover Saver
    function get_able_to_recover(address wallet) public view returns (uint256) {
        bool was_active = AbleMigration.canReclaim(wallet);
        uint256 _cycle = AbleMigration.cycle();
        uint256 i;
        uint256 default_amount = AbleMigration.balanceOf(wallet) +
            TripleMigration.balanceOf(wallet);

        while (!was_active && i < 69) {
            was_active = AbleMigration.qualifiedHistory(wallet, _cycle - i);
            i++;
        }
        if (was_active) {
            return (default_amount + AbleMigration.donationBalance(wallet));
        }

        return default_amount;
    }

    function migrate() public {
        require(block.timestamp < timestampToCloseMigration);
        require(!isRecover[msg.sender]);

        // Recover Donation Balance
        donationBalance[msg.sender] += AbleMigration.donationBalance(
            msg.sender
        );
        totalDonationBalance += donationBalance[msg.sender];
        allDonatesOf[msg.sender] += AbleMigration.allDonatesOf(msg.sender);

        // Recover Purposes
        personalPurpose[msg.sender] = AbleMigration.personalPurpose(msg.sender);
        communityPurpose[msg.sender] = AbleMigration.communityPurpose(
            msg.sender
        );

        // Recover SAVF (Last Saver Fast)
        _mint(msg.sender, get_able_to_recover(msg.sender));

        if (AbleMigration.balanceOf(msg.sender) > 0 && !isHolder[msg.sender]) {
            totalHolders++;
            isHolder[msg.sender] = true;
        }

        // Recover Qualified Donation Balance
        if (canReclaim(msg.sender) && !isQualified[msg.sender]) {
            qualifiedDonationBalance += donationBalance[msg.sender];
            isQualified[msg.sender] = true;
            qualifiedHolders++;
            emit UserQualification(block.timestamp, msg.sender, true);
        }

        isRecover[msg.sender] = true;
    }

    function burn_bdd(uint256 amount) public {
        require(donationBalance[msg.sender] >= amount);

        reduceDonationBalance(msg.sender, amount);
    }

    function claim() public {
        uint256 amountRaised = rewardAmount[rewardIDonClaim];
        uint256 amountClaimed = rewardAmountClaimed[rewardIDonClaim];

        require(!holderClaimed[msg.sender][rewardIDonClaim]);

        require(rewardIDonClaim >= claimFrom[msg.sender]);
        require(canReclaim(msg.sender));

        uint256 stableCoinToClaim = viewClaimStableCoin(msg.sender);

        require(stableCoinToClaim > 0);

        require(amountRaised >= (amountClaimed + stableCoinToClaim));

        require(donationBalance[msg.sender] >= (stableCoinToClaim / POTENCIAL));

        require(STABLECOIN.transfer(msg.sender, stableCoinToClaim));

        reduceDonationBalance(msg.sender, stableCoinToClaim / POTENCIAL);

        rewardAmountClaimed[rewardIDonClaim] += stableCoinToClaim;
        holderClaimed[msg.sender][rewardIDonClaim] = true;
        totalStableCoinDistribute += stableCoinToClaim;
        stableCoinEarned[msg.sender] += stableCoinToClaim;
        stableCoinEarnedByAbleReward[msg.sender] += stableCoinToClaim;

        _updateALL(msg.sender);

        // Emit events
        emit ClaimBUSD(block.timestamp, stableCoinToClaim, msg.sender);
    }

    function claimSaver() public {
        _updateALL(msg.sender);
        require((_totalSupply + donationBalance[msg.sender]) < maxSupply);
        require(
            canReclaimSaver(msg.sender),
            "You are not qualified to claim SAVER."
        );

        // Nueva version
        require(
            cycleToSaverReward[msg.sender] < cycle,
            "You have to wait 30 days to claim your SAVER."
        );

        // Emit events
        emit ClaimAble(
            block.timestamp,
            donationBalance[msg.sender],
            msg.sender
        );

        _mint(msg.sender, donationBalance[msg.sender]);

        isListedToClaimSaver[msg.sender] = false;

        if (!winSaverReward[msg.sender]) {
            address[] storage winners = wallet_winners;
            winners.push(msg.sender);
            wallet_winners = winners;
        }

        winSaverReward[msg.sender] = true;

        winsSaverRewardOf[msg.sender]++;
        totalWinsSaverReward++;

        balanceOfWins_SaverReward[msg.sender]++;

        total_saver_distributed += donationBalance[msg.sender];
        total_saver_earned_of[msg.sender] += donationBalance[msg.sender];

        stableCoinEarnedByAbleReward[msg.sender] = 0;

        updateTimestampRewards();
    }

    // Video

    function uploadVideoAndFirstQuestion(
        string memory _youtubeID,
        string memory _firstQuestion,
        string memory _firstRealAnswer,
        string memory _firstFakeAnswer1,
        string memory _firstFakeAnswer2
    ) public {
        require(
            msg.sender == managementWallet ||
                msg.sender == managementWallet2 ||
                msg.sender == communityWallet,
            "You are not qualified to call this function"
        );
        youtubeID[videoID] = _youtubeID;

        firstQuestion[videoID] = _firstQuestion;

        firstRealAnswer[videoID] = _firstRealAnswer;

        firstFakeAnswer1[videoID] = _firstFakeAnswer1;

        firstFakeAnswer2[videoID] = _firstFakeAnswer2;

        videoID++;
    }

    function uploadSecondQuestion(
        string memory _secondQuestion,
        string memory _secondRealAnswer,
        string memory _secondFakeAnswer1,
        string memory _secondFakeAnswer2
    ) public {
        require(
            msg.sender == managementWallet ||
                msg.sender == managementWallet2 ||
                msg.sender == communityWallet,
            "You are not qualified to call this function"
        );

        secondQuestion[videoID - 1] = _secondQuestion;

        secondRealAnswer[videoID - 1] = _secondRealAnswer;

        secondFakeAnswer1[videoID - 1] = _secondFakeAnswer1;

        secondFakeAnswer2[videoID - 1] = _secondFakeAnswer2;
    }

    function uploadThirdQuestion(
        string memory _thirdQuestion,
        string memory _thirdRealAnswer,
        string memory _thirdFakeAnswer1,
        string memory _thirdFakeAnswer2
    ) public {
        require(
            msg.sender == managementWallet ||
                msg.sender == managementWallet2 ||
                msg.sender == communityWallet,
            "You are not qualified to call this function"
        );

        thirdQuestion[videoID - 1] = _thirdQuestion;

        thirdRealAnswer[videoID - 1] = _thirdRealAnswer;

        thirdFakeAnswer1[videoID - 1] = _thirdFakeAnswer1;

        thirdFakeAnswer2[videoID - 1] = _thirdFakeAnswer2;
    }

    function answerVideo(
        string memory answer1,
        string memory answer2,
        string memory answer3,
        uint256 _videoID
    ) public {
        bool first = (keccak256(abi.encodePacked((answer1))) ==
            keccak256(abi.encodePacked((firstRealAnswer[_videoID]))));
        bool second = (keccak256(abi.encodePacked((answer2))) ==
            keccak256(abi.encodePacked((secondRealAnswer[_videoID]))));
        bool third = (keccak256(abi.encodePacked((answer3))) ==
            keccak256(abi.encodePacked((thirdRealAnswer[_videoID]))));

        videoAnswerOf[msg.sender][cycle] = first && second && third;

        emit AnswerVideo(block.timestamp, first && second && third, msg.sender);

        _updateALL(msg.sender);
    }

    function changeSorted() public {
        require(
            msg.sender == communityWallet ||
                msg.sender == managementWallet ||
                msg.sender == managementWallet2,
            "You are not qualified to call this function"
        );
        sort = !sort;
    }

    function changeUSDC(address _newCoin, uint256 _decimals) public {
        require(
            msg.sender == communityWallet || msg.sender == devWallet,
            "You are not able to call this function"
        );
        USDC = ERC20(_newCoin);
        USDC_DECIMALS = _decimals;
    }

    function changeUSDT(address _newCoin, uint256 _decimals) public {
        require(
            msg.sender == communityWallet || msg.sender == devWallet,
            "You are not able to call this function"
        );
        USDT = ERC20(_newCoin);
        USDT_DECIMALS = _decimals;
    }

    function changeBUSD(address _newCoin, uint256 _decimals) public {
        require(
            msg.sender == communityWallet || msg.sender == devWallet,
            "You are not able to call this function"
        );
        STABLECOIN = ERC20(_newCoin);
        BUSD_DECIMALS = _decimals;
    }

    function changeCommunityWallet(address newAddress) public {
        require(
            msg.sender == communityWallet,
            "You are not qualified to call this function."
        );

        communityWallet = payable(newAddress);
    }

    function viewClaimStableCoin(address wallet) public view returns (uint256) {
        if (qualifiedDonationBalance == 0 || !canReclaim(wallet)) return 0;

        uint256 amount = ((rewardAmount[rewardIDonClaim] *
            donationBalance[wallet]) / qualifiedDonationBalance);
        uint256 amountClaimed = rewardAmountClaimed[rewardIDonClaim];
        uint256 amountRaised = rewardAmount[rewardIDonClaim];

        if (amountRaised < (amountClaimed + amount)) {
            amount = (amountRaised - amountClaimed);
        }

        if (amount > (donationBalance[wallet] * POTENCIAL))
            return donationBalance[wallet] * POTENCIAL;

        return amount;
    }

    function qualifiedForBDD(address wallet) public view returns (bool) {
        return (donationBalance[wallet] >= minAmountToQualify);
    }

    function qualifiedForSAVER(address wallet) public view returns (bool) {
        return (_balances[wallet] > donationBalance[wallet]);
    }

    function qualifiedForUSDT(address wallet) public view returns (bool) {
        return (getBalanceOfUSDT(wallet) > donationBalance[wallet]);
    }

    function qualifiedForUSDC(address wallet) public view returns (bool) {
        return (getBalanceOfUSDC(wallet) > donationBalance[wallet]);
    }

    function qualifiedForDonatePerDay(
        address wallet
    ) public view returns (bool) {
        if (donationBalance[wallet] == 0) return false;

        if (cycle > 1) {
            return (donationBalancePerCycle[wallet][cycle - 1] >=
                getZeroPointNinePercent(
                    donationBalancePerCycle[wallet][cycle - 2]
                ));
        }

        return true;
    }

    function getSavingsLimit(address wallet) public view returns (uint256) {
        return ((usdcRecord[wallet][cycle - 1] * 100369) / 100000);
    }

    function getZeroPointNinePercent(
        uint256 amount
    ) public pure returns (uint256) {
        return (amount * 1009) / 1000;
    }

    function qualifiedForVideo(address wallet) public view returns (bool) {
        return videoAnswerOf[wallet][cycle - 1];
    }

    function getMinAmountToDonate(
        address wallet
    ) public view returns (uint256) {
        if (
            getZeroPointNinePercent(
                donationBalancePerCycle[wallet][cycle - 1]
            ) > donationBalance[wallet]
        ) {
            return
                getZeroPointNinePercent(
                    donationBalancePerCycle[wallet][cycle - 1]
                ) - donationBalance[wallet];
        }

        return 0;
    }

    function qualifiedForSinergy(address wallet) public view returns (bool) {
        bool bronze = false;
        bool silver = false;
        bool gold = false;

        if (SinergyBronze != ERC721(address(0))) {
            bronze = SinergyBronze.balanceOf(wallet) > 0;
        }

        if (SinergySilver != ERC721(address(0))) {
            silver = SinergySilver.balanceOf(wallet) > 0;
        }

        if (SinergyGold != ERC721(address(0))) {
            gold = SinergyGold.balanceOf(wallet) > 0;
        }

        return (bronze || silver || gold);
    }

    function canReclaim(address wallet) public view returns (bool) {
        if (wallet == communityWallet || wallet == devWallet) return true;

        return (qualifiedSinergy[wallet][cycle - 1] &&
            qualifiedUSDT[wallet][cycle - 1] &&
            qualifiedUSDC[wallet][cycle - 1] &&
            qualifiedSaver[wallet][cycle - 1] &&
            qualifiedBDD[wallet][cycle - 1] &&
            qualifiedDonatedPerDay[wallet][cycle - 1] &&
            qualifiedVideo[wallet][cycle - 1]);
    }

    function canReclaimSaver(address wallet) public view returns (bool) {
        return (canReclaim(wallet) && isListedToClaimSaver[wallet]);
    }

    // Esta funcion la agregamos porque fallaba el premio able (28-10-2022)
    // Pero luego nos dimos cuenta que estaba bien
    function canReclaimAble(address wallet) public view returns (bool) {
        bool res = true;

        if (cycle < CYCLES_FOR_ABLE_REWARD) return false;

        for (uint256 i = cycle - CYCLES_FOR_ABLE_REWARD; i < cycle; i++) {
            res = res && qualifiedHistory[wallet][i];
        }

        return res;
    }

    /*
        Si los decimales son distintos a 18, entonces para tratar a todos los tokens con 18 decimales retornamos
        el balance que dice el contrato del token multiplicado por 10 elevado a la diferencia de 18 y los decimales
        de ese token.
        Por ejemplo: USDC (6 Decimales)
        Como sus decimales son distintos a 18, hara lo siguiente. El balance que retorne el contrato de USDC en
        6 decimales le agregara 12 decimales mas. (18 - DECIMALS).
        Para asi finalmente tratarlo como un token de 18 decimales.
    */

    function getBalanceOfUSDC(address wallet) public view returns (uint256) {
        if (USDC_DECIMALS == 18) {
            return USDC.balanceOf(wallet);
        }
        return USDC.balanceOf(wallet) * 10 ** (18 - USDC_DECIMALS);
    }

    function getBalanceOfUSDT(address wallet) public view returns (uint256) {
        if (USDT_DECIMALS == 18) {
            return USDT.balanceOf(wallet);
        }
        return USDT.balanceOf(wallet) * 10 ** (18 - USDT_DECIMALS);
    }

    function getBalanceOfBUSD(address wallet) public view returns (uint256) {
        if (BUSD_DECIMALS == 18) {
            return STABLECOIN.balanceOf(wallet);
        }
        return STABLECOIN.balanceOf(wallet) * 10 ** (18 - BUSD_DECIMALS);
    }

    function updateTimestampRewards() public {
        if (block.timestamp > timeOpenClaimReward[rewardID]) {
            // If someone forgot to claim, this reward will appear on the next reward
            rewardAmount[rewardID] += (rewardAmount[rewardIDonClaim] -
                rewardAmountClaimed[rewardIDonClaim]);

            cycle++;
            emit CycleChange(block.timestamp, cycle);

            rewardIDonClaim = rewardID;
            rewardID++;

            // Update times to claim
            timeOpenClaimReward[rewardID] =
                block.timestamp +
                timeStableCoinReward;
        }
    }

    function updateALL(address wallet) public {
        updateTimestampRewards();
        updateSavings(wallet);
        updateQualifiedDonationBalance(wallet);
        checkDay(wallet);
        checkSaverReward(wallet);

        if (SinergyBronze != ERC721(address(0))) {
            SinergyBronze.updateFromAble();
        }

        if (SinergySilver != ERC721(address(0))) {
            SinergySilver.updateFromAble();
        }

        if (SinergyGold != ERC721(address(0))) {
            SinergyGold.updateFromAble();
        }

        emit Update(block.timestamp, wallet);
    }

    function _updateALL(address wallet) private {
        updateTimestampRewards();
        updateSavings(wallet);
        updateQualifiedDonationBalance(wallet);
        checkDay(wallet);
        checkSaverReward(wallet);

        if (SinergyBronze != ERC721(address(0))) {
            SinergyBronze.updateFromAble();
        }

        if (SinergySilver != ERC721(address(0))) {
            SinergySilver.updateFromAble();
        }

        if (SinergyGold != ERC721(address(0))) {
            SinergyGold.updateFromAble();
        }
    }

    function updateFromSinergy(address wallet) public {
        updateTimestampRewards();
        updateSavings(wallet);
        updateQualifiedDonationBalance(wallet);
        checkDay(wallet);
        checkSaverReward(wallet);
    }

    function setPersonalPurpose(string memory _str) public {
        personalPurpose[msg.sender] = _str;
    }

    function setCommunityPurpose(string memory _str) public {
        communityPurpose[msg.sender] = _str;
    }

    function withdrawAllFunds() public {
        require(
            (block.timestamp - lastDonationTimestamp) > 1 days,
            "The contract is still working"
        );
        require(
            msg.sender == communityWallet,
            "You are not qualified to call to this function"
        );

        require(
            STABLECOIN.transfer(msg.sender, address(this).balance),
            "Cannot pay StableCoin"
        );
    }

    function setManagementInfo(string memory info) public {
        require(
            msg.sender == managementWallet || msg.sender == communityWallet,
            "You are not qualified to call this function"
        );

        managementInfo = info;
    }

    function changeManagementWallet(address _managementWallet) public {
        require(
            msg.sender == communityWallet,
            "You are not qualified to call this function"
        );

        managementWallet = _managementWallet;
    }

    function changeManagementWallet2(address _managementWallet) public {
        require(
            msg.sender == communityWallet,
            "You are not qualified to call this function"
        );

        managementWallet2 = _managementWallet;
    }

    function set_potencial(uint256 _potencial) public {
        require(
            msg.sender == communityWallet || msg.sender == devWallet,
            "You are not qualified to call this function."
        );
        POTENCIAL = _potencial;
    }

    function setSinergyBronze(address _sinergyBronze) public {
        require(
            msg.sender == devWallet,
            "You are not qualified to call this function"
        );

        SinergyBronze = ERC721(_sinergyBronze);
    }

    function setSinergySilver(address _sinergySilver) public {
        require(
            msg.sender == devWallet,
            "You are not qualified to call this function"
        );

        SinergySilver = ERC721(_sinergySilver);
    }

    function setSinergyGold(address _sinergyGold) public {
        require(
            msg.sender == devWallet,
            "You are not qualified to call this function"
        );

        SinergyGold = ERC721(_sinergyGold);
    }

    function setSinergySell(SinergySale _ableSale) public {
        require(msg.sender == devWallet);
        ABLE_SALE = _ableSale;
    }

    // Private funcs
    function updateQualifiedDonationBalanceAfterDonate(
        address wallet,
        uint256 amount
    ) private {
        bool previousStatus = isQualified[wallet];

        if (isQualified[wallet]) {
            qualifiedDonationBalance -= donationBalance[wallet];
            isQualified[wallet] = false;
            total_qualified_wallets--;
            qualifiedHolders--;
        }

        if (donationBalance[wallet] == 0) {
            wallets_with_donation_balance++;
        }

        donationBalance[wallet] += amount;
        totalDonationBalance += amount;

        if (canReclaim(wallet)) {
            qualifiedDonationBalance += donationBalance[wallet];
            isQualified[wallet] = true;
            total_qualified_wallets++;
            qualifiedHolders++;
        }

        if (previousStatus != isQualified[wallet]) {
            emit UserQualification(block.timestamp, wallet, !previousStatus);
        }
    }

    function updateQualifiedDonationBalance(address wallet) private {
        bool previousStatus = isQualified[wallet];

        if (isQualified[wallet]) {
            qualifiedDonationBalance -= donationBalance[wallet];
            isQualified[wallet] = false;
            total_qualified_wallets--;
            qualifiedHolders--;
        }

        if (canReclaim(wallet)) {
            qualifiedDonationBalance += donationBalance[wallet];
            isQualified[wallet] = true;
            total_qualified_wallets++;
            qualifiedHolders++;
        }

        if (previousStatus != isQualified[wallet]) {
            emit UserQualification(block.timestamp, wallet, !previousStatus);
        }
    }

    function updateSavings(address wallet) private {
        uint256 last_saving_amount = lastAmountUSDC[wallet] +
            lastAmountUSDT[wallet];
        uint256 actual_saving_amount = getBalanceOfUSDC(wallet) +
            getBalanceOfUSDT(wallet);

        if (actual_saving_amount < last_saving_amount) {
            uint256 dif = last_saving_amount - actual_saving_amount;

            if (dif > donationBalance[wallet]) {
                reduceDonationBalance(wallet, donationBalance[wallet]);
            } else {
                reduceDonationBalance(wallet, dif);
            }
        }
    }

    function checkSaverReward(address wallet) private {
        if (!canReclaim(wallet) && isListedToClaimSaver[wallet]) {
            isListedToClaimSaver[wallet] = false;
            emit AbleRewardQualification(block.timestamp, wallet, false);
            return;
        }

        if (canReclaim(wallet) && !isListedToClaimSaver[wallet]) {
            isListedToClaimSaver[wallet] = true;
            cycleToSaverReward[wallet] = cycle + CYCLES_FOR_ABLE_REWARD;
            emit AbleRewardQualification(block.timestamp, wallet, true);
            return;
        }
    }

    // antes llamada checkDonatedByDay
    function checkDay(address wallet) private {
        if (cycle >= cycleToCheck[wallet]) {
            cycleToCheck[wallet] = cycle + 1;

            lastAmountUSDC[wallet] = getBalanceOfUSDC(wallet);
            lastAmountUSDT[wallet] = getBalanceOfUSDT(wallet);

            usdcRecord[wallet][cycle] = lastAmountUSDC[wallet];
            usdtRecord[wallet][cycle] = lastAmountUSDT[wallet];

            donationBalancePerCycle[wallet][cycle - 1] = donationBalance[
                msg.sender
            ];
            qualifiedSinergy[wallet][cycle - 1] = qualifiedForSinergy(wallet);
            qualifiedSaver[wallet][cycle - 1] = qualifiedForSAVER(wallet);
            qualifiedUSDT[wallet][cycle - 1] = qualifiedForUSDT(wallet);
            qualifiedUSDC[wallet][cycle - 1] = qualifiedForUSDC(wallet);
            qualifiedBDD[wallet][cycle - 1] = qualifiedForBDD(wallet);
            qualifiedDonatedPerDay[wallet][
                cycle - 1
            ] = qualifiedForDonatePerDay(wallet);
            qualifiedVideo[wallet][cycle - 1] = qualifiedForVideo(wallet);

            qualifiedHistory[wallet][cycle - 1] = canReclaim(wallet);

            emit CloseCycle(
                wallet,
                cycle - 1,
                qualifiedSinergy[wallet][cycle - 1],
                qualifiedUSDT[wallet][cycle - 1],
                qualifiedUSDC[wallet][cycle - 1],
                qualifiedSaver[wallet][cycle - 1],
                qualifiedBDD[wallet][cycle - 1],
                qualifiedDonatedPerDay[wallet][cycle - 1],
                qualifiedVideo[wallet][cycle - 1]
            );
        }
    }

    // Esta nueva funcion la tiene que llamar SinergySale cada vez que alguien compra ABLE
    function incrementDonationBalance(
        uint256 amount_spended,
        address wallet
    ) public {
        require(msg.sender == address(ABLE_SALE));
        updateQualifiedDonationBalanceAfterDonate(wallet, amount_spended);
        _updateALL(wallet);
        if (canReclaim(wallet)) {
            lastDonationFrom = msg.sender;
        }
        rewardAmount[rewardID] += (amount_spended / 3);
        allDonatesOf[wallet] += amount_spended;
        donatedByDayOf[wallet][cycle] += amount_spended;
        claimFrom[wallet] = rewardID;
        totalDonations++;

        total_raised_for_admin += (amount_spended / 3);

        lastDonationTimestamp = block.timestamp;

        // Emit Event
        emit Points(wallet, cycle, amount_spended, true);
    }

    function reduceDonationBalance(address wallet, uint256 amount) private {
        bool previousStatus = isQualified[wallet];

        if (isQualified[wallet]) {
            qualifiedDonationBalance -= donationBalance[wallet];
            isQualified[wallet] = false;
            total_qualified_wallets--;
            qualifiedHolders--;
        }

        donationBalance[wallet] -= amount;
        totalDonationBalance -= amount;

        if (donationBalance[wallet] == 0) {
            wallets_with_donation_balance--;
        }

        if (canReclaim(wallet)) {
            qualifiedDonationBalance += donationBalance[wallet];
            isQualified[wallet] = true;
            total_qualified_wallets++;
            qualifiedHolders++;
        }

        if (previousStatus != isQualified[wallet]) {
            emit UserQualification(block.timestamp, wallet, !previousStatus);
        }

        // Emit Event
        emit Points(wallet, cycle, amount, false);
    }

    // Funcs Private view

    // Funcs IERC20

    function name() public view virtual override returns (string memory) {
        return _name;
    }

    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    function decimals() public view virtual override returns (uint8) {
        return 18;
    }

    function totalSupply() public view virtual override returns (uint256) {
        return _totalSupply;
    }

    function balanceOf(
        address account
    ) public view virtual override returns (uint256) {
        return _balances[account];
    }

    function transfer(
        address to,
        uint256 amount
    ) public virtual override returns (bool) {
        address owner = _msgSender();
        _transfer(owner, to, amount);
        return true;
    }

    function allowance(
        address owner,
        address spender
    ) public view virtual override returns (uint256) {
        return _allowances[owner][spender];
    }

    function approve(
        address spender,
        uint256 amount
    ) public virtual override returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, amount);
        return true;
    }

    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) public virtual override returns (bool) {
        address spender = _msgSender();
        _spendAllowance(from, spender, amount);
        _transfer(from, to, amount);
        return true;
    }

    function increaseAllowance(
        address spender,
        uint256 addedValue
    ) public virtual returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, _allowances[owner][spender] + addedValue);
        return true;
    }

    function decreaseAllowance(
        address spender,
        uint256 subtractedValue
    ) public virtual returns (bool) {
        address owner = _msgSender();
        uint256 currentAllowance = _allowances[owner][spender];
        require(
            currentAllowance >= subtractedValue,
            "ERC20: decreased allowance below zero"
        );
        unchecked {
            _approve(owner, spender, currentAllowance - subtractedValue);
        }

        return true;
    }

    function _transfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {
        require(from != address(0), "ERC20: transfer from the zero address");
        require(to != address(0), "ERC20: transfer to the zero address");

        _beforeTokenTransfer(from, to, amount);

        if (!isHolder[to]) {
            totalHolders++;
            isHolder[to] = true;
        }

        uint256 fromBalance = _balances[from];
        require(
            fromBalance >= amount,
            "ERC20: transfer amount exceeds balance"
        );
        unchecked {
            _balances[from] = fromBalance - amount;
        }
        _balances[to] += amount;

        if (_balances[from] == 0) {
            totalHolders--;
            isHolder[from] = false;
        }

        _updateALL(from);
        _updateALL(to);

        updateQualifiedDonationBalance(from);
        updateQualifiedDonationBalance(to);

        emit Transfer(from, to, amount);

        _afterTokenTransfer(from, to, amount);
    }

    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);
    }

    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);
    }

    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);
    }

    function _spendAllowance(
        address owner,
        address spender,
        uint256 amount
    ) internal virtual {
        uint256 currentAllowance = allowance(owner, spender);
        if (currentAllowance != type(uint256).max) {
            require(
                currentAllowance >= amount,
                "ERC20: insufficient allowance"
            );
            unchecked {
                _approve(owner, spender, currentAllowance - amount);
            }
        }
    }

    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {}

    function _afterTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {}
}

// This is the main contract here!
contract ERC20 is Context, IERC20, IERC20Metadata, Router {
    // Migration Saver
    mapping(address => bool) public isRecover;

    // ERC20 Standard
    mapping(address => uint256) private _balances;
    mapping(address => mapping(address => uint256)) private _allowances;
    uint256 private _totalSupply;
    string private _name;
    string private _symbol;

    // Able
    uint256 public initial_supply = 369000000 * 10 ** 18;
    uint256 public development_supply = 1845000 * 10 ** 18;
    mapping(address => mapping(uint256 => bool)) public has_transfer;

    // Holders
    uint256 public total_holders;
    uint256 public qualified_holders;
    mapping(address => bool) public is_holder;
    mapping(address => string) public personal_purpose;
    mapping(address => string) public community_purpose;

    // Saver Reward
    uint256 public POTENCIAL_ABLE = 1;
    uint256 public CYCLES_FOR_ABLE_REWARD = 21;
    mapping(address => bool) public is_listed_to_claim_able;
    mapping(address => uint256) public amount_of_wins_able_reward_of;
    mapping(address => bool) public won_able_reward;
    mapping(address => uint256) public total_able_earned_of;
    mapping(address => uint256) public cycle_to_able_reward;
    uint256 public able_rewards_claimed;
    uint256 public qualified_able_rewards_claimed; // Cantidad de premios able de cuentas calificadas actualmente
    uint256 public total_able_distributed;
    address[] public wallet_winners;

    // Points
    uint256 public total_points;
    uint256 public amount_of_wallets_with_points;
    mapping(address => uint256) public points_of;
    address public last_wallet_who_bought_able;
    mapping(address => mapping(uint256 => bool))
        public increase_points_in_cycle;

    // Qualified Points
    mapping(uint256 => uint256) public qualified_points_by_cycle;
    uint256 public qualified_points;
    uint256 public total_qualified_wallets;
    mapping(address => bool) public is_qualified;

    // Informacion
    string public management_info;

    // Events
    event ClaimAbleEvent(
        uint256 indexed date,
        uint256 amount,
        address indexed wallet
    );

    // Nuevos eventos
    event AbleRewardQualification(
        uint256 indexed date,
        address indexed wallet,
        bool status
    ); // true => te has enlistado para el premio able || false => te has descalificado del premio able

    event Points(
        address indexed wallet,
        uint256 cycle,
        uint256 amount,
        bool increase
    );

    event AbleQualification(
        uint256 indexed date,
        address indexed wallet,
        bool status
    );

    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    // Recover Saver
    function GetAbleToRecover(
        address wallet,
        ERC20Migration _Able,
        bool triple
    ) public view returns (uint256) {
        uint256 default_amount = _Able.balanceOf(wallet);

        if (triple) {
            default_amount += TripleMigration.balanceOf(wallet);
        }

        return default_amount;
    }

    function Migrate() public {
        require(!isRecover[msg.sender]);

        if (AprilAbleContract.isRecover(msg.sender)) {
            _MigrateAble(msg.sender, AprilAbleContract, false);
        } else {
            _MigrateAble(msg.sender, NovemberAbleContract, true);
        }
    }

    // Points
    function BurnPoints(uint256 amount) public {
        require(
            points_of[msg.sender] >= amount,
            "You dont have this amount of points to burn it."
        );
        DecreasePoints(msg.sender, amount);
    }

    // Esta nueva funcion la tiene que llamar SinergySale cada vez que alguien compra ABLE
    function IncreasePoints(uint256 amount_spended, address wallet) public {
        require(
            msg.sender == address(AbleSale),
            "Only Sinergy Sale can increase points."
        );

        uint256 cycle = Cycle.cycle();

        if (amount_spended > 0 && points_of[wallet] == 0) {
            amount_of_wallets_with_points++;
        }

        if (is_qualified[wallet]) {
            qualified_points -= points_of[wallet];
            is_qualified[wallet] = false;
            total_qualified_wallets--;
            qualified_holders--;
            if (amount_of_wins_able_reward_of[wallet] > 0) {
                qualified_able_rewards_claimed -= amount_of_wins_able_reward_of[
                    wallet
                ];
            }
        }

        points_of[wallet] += amount_spended;
        total_points += amount_spended;

        if (Qualification.IsQualified(wallet)) {
            qualified_points += points_of[wallet];
            is_qualified[wallet] = true;
            total_qualified_wallets++;
            qualified_holders++;
            last_wallet_who_bought_able = msg.sender;
            if (amount_of_wins_able_reward_of[wallet] > 0) {
                qualified_able_rewards_claimed += amount_of_wins_able_reward_of[
                    wallet
                ];
            }
        }

        increase_points_in_cycle[wallet][cycle] = true;

        // Emit Event
        emit Points(wallet, cycle, amount_spended, true);
    }

    function DecreasePoints(address wallet, uint256 amount) public {
        require(
            _CanDecreasePoints(msg.sender, wallet),
            "You are not qualified to try to Decrease Points."
        );
        require(
            amount <= points_of[wallet],
            "You dont have that amount of points to decrease it."
        );
        bool previousStatus = is_qualified[wallet];

        if (is_qualified[wallet]) {
            qualified_points -= points_of[wallet];
            is_qualified[wallet] = false;
            total_qualified_wallets--;
            qualified_holders--;
            if (amount_of_wins_able_reward_of[wallet] > 0) {
                qualified_able_rewards_claimed -= amount_of_wins_able_reward_of[
                    wallet
                ];
            }
        }

        points_of[wallet] -= amount;
        total_points -= amount;

        // We asked for amount != 0, because the user can reduce zero points having zero points.
        if (points_of[wallet] == 0 && amount != 0) {
            amount_of_wallets_with_points--;
        }

        if (Qualification.IsQualified(wallet)) {
            qualified_points += points_of[wallet];
            is_qualified[wallet] = true;
            total_qualified_wallets++;
            qualified_holders++;
            if (amount_of_wins_able_reward_of[wallet] > 0) {
                qualified_able_rewards_claimed += amount_of_wins_able_reward_of[
                    wallet
                ];
            }
        }

        if (previousStatus != is_qualified[wallet]) {
            emit AbleQualification(block.timestamp, wallet, !previousStatus);
        }

        // Emit Event
        emit Points(wallet, Cycle.cycle(), amount, false);
    }

    function CloseCycle() public {
        require(
            msg.sender == address(Cycle),
            "Only the Clock can Set Qualified Points"
        );
        qualified_points_by_cycle[Cycle.cycle()] = qualified_points;
    }

    // Able Reward
    function ClaimAble() public {
        require(
            Qualification.is_updated(msg.sender, Cycle.cycle()),
            "You have to update first."
        );
        require(
            CanClaimAble(msg.sender),
            "You are not qualified to claim Able."
        );
        require(
            cycle_to_able_reward[msg.sender] < Cycle.cycle(),
            "You have to wait more cycles to claim your Able."
        );

        // Emit events
        emit ClaimAbleEvent(block.timestamp, points_of[msg.sender], msg.sender);

        _mint(msg.sender, points_of[msg.sender] * POTENCIAL_ABLE);

        is_listed_to_claim_able[msg.sender] = false;

        if (!won_able_reward[msg.sender]) {
            address[] storage winners = wallet_winners;
            winners.push(msg.sender);
            wallet_winners = winners;
        }

        won_able_reward[msg.sender] = true;

        amount_of_wins_able_reward_of[msg.sender]++;
        able_rewards_claimed++;
        qualified_able_rewards_claimed++;

        total_able_distributed += points_of[msg.sender];
        total_able_earned_of[msg.sender] += points_of[msg.sender];

        Qualification.ResetStablecoinEarnedOnAbleReward(msg.sender);
    }

    function CanClaimAble(address wallet) public view returns (bool) {
        return (Qualification.IsQualified(wallet) &&
            is_listed_to_claim_able[wallet]);
    }

    // Update
    function UpdateQualifiedPoints(address wallet) public {
        bool previousStatus = is_qualified[wallet];

        if (is_qualified[wallet]) {
            qualified_points -= points_of[wallet];
            is_qualified[wallet] = false;
            total_qualified_wallets--;
            qualified_holders--;
            if (amount_of_wins_able_reward_of[wallet] > 0) {
                qualified_able_rewards_claimed -= amount_of_wins_able_reward_of[
                    wallet
                ];
            }
        }

        if (Qualification.IsQualified(wallet)) {
            qualified_points += points_of[wallet];
            is_qualified[wallet] = true;
            total_qualified_wallets++;
            qualified_holders++;
            if (amount_of_wins_able_reward_of[wallet] > 0) {
                qualified_able_rewards_claimed += amount_of_wins_able_reward_of[
                    wallet
                ];
            }
        }

        if (previousStatus != is_qualified[wallet]) {
            emit AbleQualification(block.timestamp, wallet, !previousStatus);
        }
    }

    function CheckAbleReward(address wallet) public {
        if (
            !Qualification.IsQualified(wallet) &&
            is_listed_to_claim_able[wallet]
        ) {
            is_listed_to_claim_able[wallet] = false;
            emit AbleRewardQualification(block.timestamp, wallet, false);
            return;
        }

        if (
            Qualification.IsQualified(wallet) &&
            !is_listed_to_claim_able[wallet]
        ) {
            is_listed_to_claim_able[wallet] = true;
            cycle_to_able_reward[wallet] =
                Cycle.cycle() +
                CYCLES_FOR_ABLE_REWARD;
            emit AbleRewardQualification(block.timestamp, wallet, true);
            return;
        }
    }

    // Get Functions
    function GetWalletWinnersLength() public view returns (uint256) {
        return wallet_winners.length;
    }

    // Helpers
    function IsOwner(address wallet) public view returns (bool) {
        return Wallets.IsOwner(wallet);
    }

    // Set Functions
    function SetPersonalPurpose(string memory _str) public {
        personal_purpose[msg.sender] = _str;
    }

    function SetCommunityPurpose(string memory _str) public {
        community_purpose[msg.sender] = _str;
    }

    function SetPotencialAble(uint256 potencial) public {
        require(
            IsOwner(msg.sender),
            "You are not qualified to change the able reward potencial."
        );
        POTENCIAL_ABLE = potencial;
    }

    function SetCyclesForAbleReward(uint256 cycles) public {
        require(
            IsOwner(msg.sender),
            "You are not qualified to change the cycles for able reward."
        );
        CYCLES_FOR_ABLE_REWARD = cycles;
    }

    // Private Functions
    function _MigrateAble(
        address wallet,
        ERC20Migration _Able,
        bool triple
    ) private {
        uint256 last_points_of = points_of[wallet];
        // Recover Donation Balance
        points_of[wallet] += _Able.donationBalance(wallet);
        total_points += points_of[wallet];

        // Recover Purposes
        personal_purpose[wallet] = _Able.personalPurpose(wallet);
        community_purpose[wallet] = _Able.communityPurpose(wallet);

        // Recover SAVF (Last Saver Fast)
        _mint(wallet, GetAbleToRecover(wallet, _Able, triple));

        if (_Able.balanceOf(wallet) > 0 && !is_holder[wallet]) {
            total_holders++;
            is_holder[wallet] = true;
        }

        bool qualification_is_qualified = Qualification.IsQualified(wallet);
        // Recover Qualified Donation Balance
        if (qualification_is_qualified && !is_qualified[wallet]) {
            qualified_points += points_of[wallet];
            is_qualified[wallet] = true;
            total_qualified_wallets++;
            qualified_holders++;
        } else if (qualification_is_qualified && is_qualified[wallet]) {
            qualified_points += points_of[wallet] - last_points_of;
        }

        if (points_of[wallet] > 0 && last_points_of == 0) {
            amount_of_wallets_with_points++;
        }

        isRecover[wallet] = true;
    }

    function _CanDecreasePoints(
        address wallet,
        address owner
    ) private view returns (bool) {
        if (
            wallet == address(this) ||
            wallet == address(Qualification) ||
            wallet == address(StablecoinBaseReward) ||
            wallet == owner
        ) return true;
        return false;
    }

    // Funcs IERC20

    function name() public view virtual override returns (string memory) {
        return _name;
    }

    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    function decimals() public view virtual override returns (uint8) {
        return 18;
    }

    function totalSupply() public view virtual override returns (uint256) {
        return _totalSupply;
    }

    function balanceOf(
        address account
    ) public view virtual override returns (uint256) {
        return _balances[account];
    }

    function transfer(
        address to,
        uint256 amount
    ) public virtual override returns (bool) {
        address owner = _msgSender();
        _transfer(owner, to, amount);
        return true;
    }

    function allowance(
        address owner,
        address spender
    ) public view virtual override returns (uint256) {
        return _allowances[owner][spender];
    }

    function approve(
        address spender,
        uint256 amount
    ) public virtual override returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, amount);
        return true;
    }

    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) public virtual override returns (bool) {
        address spender = _msgSender();
        _spendAllowance(from, spender, amount);
        _transfer(from, to, amount);
        return true;
    }

    function increaseAllowance(
        address spender,
        uint256 addedValue
    ) public virtual returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, _allowances[owner][spender] + addedValue);
        return true;
    }

    function decreaseAllowance(
        address spender,
        uint256 subtractedValue
    ) public virtual returns (bool) {
        address owner = _msgSender();
        uint256 currentAllowance = _allowances[owner][spender];
        require(
            currentAllowance >= subtractedValue,
            "ERC20: decreased allowance below zero"
        );
        unchecked {
            _approve(owner, spender, currentAllowance - subtractedValue);
        }

        return true;
    }

    function _transfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {
        require(from != address(0), "ERC20: transfer from the zero address");
        require(to != address(0), "ERC20: transfer to the zero address");

        _beforeTokenTransfer(from, to, amount);

        if (!is_holder[to] && amount > 0) {
            total_holders++;
            is_holder[to] = true;
        }

        uint256 fromBalance = _balances[from];
        require(
            fromBalance >= amount,
            "ERC20: transfer amount exceeds balance"
        );
        unchecked {
            _balances[from] = fromBalance - amount;
        }
        _balances[to] += amount;

        if (_balances[from] == 0) {
            total_holders--;
            is_holder[from] = false;
        }

        if (to != address(AbleSale)) {
            has_transfer[from][Cycle.cycle()] = true;
        }

        emit Transfer(from, to, amount);

        _afterTokenTransfer(from, to, amount);
    }

    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);
    }

    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);
    }

    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);
    }

    function _spendAllowance(
        address owner,
        address spender,
        uint256 amount
    ) internal virtual {
        uint256 currentAllowance = allowance(owner, spender);
        if (currentAllowance != type(uint256).max) {
            require(
                currentAllowance >= amount,
                "ERC20: insufficient allowance"
            );
            unchecked {
                _approve(owner, spender, currentAllowance - amount);
            }
        }
    }

    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {}

    function _afterTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {}
}

contract AbleSaver is ERC20 {
    constructor() ERC20("ABLE SAVER", "ABLE") {}
}

Contract Security Audit

Contract ABI

API
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"tokenID","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"address","name":"wallet","type":"address"},{"indexed":true,"internalType":"uint256","name":"date","type":"uint256"}],"name":"AffiliateRewardEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"wallet","type":"address"},{"indexed":false,"internalType":"uint256","name":"previousFavourite","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"actualFavourite","type":"uint256"}],"name":"ChangeFavourite","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"date","type":"uint256"},{"indexed":true,"internalType":"string","name":"name","type":"string"},{"indexed":true,"internalType":"string","name":"valueProposal","type":"string"},{"indexed":true,"internalType":"address","name":"wallet","type":"address"}],"name":"Mint","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"ABLE_PRICE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"CloseCycle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"CreateGenesisNfts","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_inscription","type":"string"},{"internalType":"string","name":"_valueProposal","type":"string"},{"internalType":"string","name":"_uri","type":"string"},{"internalType":"string","name":"_imageURL","type":"string"},{"internalType":"uint256","name":"_ref","type":"uint256"},{"internalType":"uint256","name":"_timestamp","type":"uint256"}],"name":"CreateNFT","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"wallet","type":"address"}],"name":"GetAbleBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"GetAbleToConfidenceReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"GetAbleToConstancyReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"GetAbleToValueReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"GetAmountOfNftMinted","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"GetCycle","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"GetStablecoinToConfidenceReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"GetStablecoinToConstancyReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"GetStablecoinToPassiveReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"GetStablecoinToReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"wallet","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"migration_contract","type":"address"},{"internalType":"uint256","name":"month_contract","type":"uint256"}],"name":"HandleRecover","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"address","name":"contract_migration","type":"address"}],"name":"HandlerRecoverEightLevel","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"address","name":"contract_migration","type":"address"}],"name":"HandlerRecoverFirstLevel","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"address","name":"contract_migration","type":"address"}],"name":"HandlerRecoverFiveLevel","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"address","name":"contract_migration","type":"address"}],"name":"HandlerRecoverFourLevel","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"address","name":"contract_migration","type":"address"}],"name":"HandlerRecoverNineLevel","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"address","name":"contract_migration","type":"address"}],"name":"HandlerRecoverSecondLevel","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"address","name":"contract_migration","type":"address"}],"name":"HandlerRecoverSevenLevel","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"address","name":"contract_migration","type":"address"}],"name":"HandlerRecoverSixLevel","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"address","name":"contract_migration","type":"address"}],"name":"HandlerRecoverThirdLevel","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"wallet","type":"address"}],"name":"IsOwner","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"wallet","type":"address"}],"name":"IsQualified","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"wallet","type":"address"}],"name":"IsUpdated","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_AMOUNT_FIRST_LEVEL_CONNECTIONS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MigrationSinergy","outputs":[{"internalType":"contract Migration","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_inscription","type":"string"},{"internalType":"string","name":"_valueProposal","type":"string"},{"internalType":"uint256","name":"_tokenID","type":"uint256"}],"name":"ModifyNFT","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenID","type":"uint256"}],"name":"NftWasQualified","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERCENT_ABLE_CONFIDENCE_REWARD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERCENT_ABLE_CONSTANCY_REWARD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERCENT_ABLE_VALUE_REWARD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERCENT_DIVIDE_ABLE_CONFIDENCE_REWARD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERCENT_DIVIDE_ABLE_CONSTANCY_REWARD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERCENT_DIVIDE_ABLE_VALUE_REWARD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERCENT_DIVIDE_REWARD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERCENT_DIVIDE_STABLECOIN_CONFIDENCE_REWARD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERCENT_DIVIDE_STABLECOIN_CONSTANCY_REWARD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERCENT_DIVIDE_STABLECOIN_VALUE_REWARD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERCENT_REWARD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERCENT_STABLECOIN_CONFIDENCE_REWARD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERCENT_STABLECOIN_CONSTANCY_REWARD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERCENT_STABLECOIN_VALUE_REWARD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PRICE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"RefreshContracts","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"SetAbleConfidenceRewardPercent","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"SetAbleConfidenceRewardPercentDivide","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"SetAbleConstancyRewardPercent","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"SetAbleConstancyRewardPercentDivide","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"SetAblePrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"SetAbleValueRewardPercent","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"SetAbleValueRewardPercentDivide","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"wallet","type":"address"}],"name":"SetAddresses","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"wallet","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"SetFavouriteNFT","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"SetMinAmountFirstLevelConnections","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"SetStablecoinConfidenceRewardPercent","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"SetStablecoinConfidenceRewardPercentDivide","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"SetStablecoinConstancyRewardPercent","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"SetStablecoinConstancyRewardPercentDivide","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"SetStablecoinPercentReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"SetStablecoinPercentRewardDivide","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"SetStablecoinPrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"SetStablecoinValueRewardPercent","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"SetStablecoinValueRewardPercentDivide","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"SinergyBronze","outputs":[{"internalType":"contract Sinergy","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"Stablecoin","outputs":[{"internalType":"contract ERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"USDC","outputs":[{"internalType":"contract ERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"USDT","outputs":[{"internalType":"contract ERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"wallet","type":"address"}],"name":"UpdateQualifiedNfts","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"token_id","type":"uint256"},{"internalType":"uint256","name":"reward_amount","type":"uint256"}],"name":"_TrnasferStablecoin","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"actual_lost_income","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"amount_nfts_considered","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"contracts_id","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"favourite_nft","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"get_eight_level_amount_reference","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"get_eight_level_references","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"get_first_level_amount_reference","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"get_first_level_references","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"get_five_level_amount_reference","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"get_five_level_references","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"get_four_level_amount_reference","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"get_four_level_references","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"get_my_nfts","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"get_nft_image_url","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"get_nft_inscription","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"get_nft_name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"get_nft_reference","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"get_nft_timestamp_created","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"get_nft_value_proposal","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"get_nine_level_amount_reference","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"get_nine_level_references","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"get_second_level_amount_reference","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"get_second_level_references","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"get_seven_level_amount_reference","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"get_seven_level_references","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"get_six_level_amount_reference","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"get_six_level_references","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"get_third_level_amount_reference","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"get_third_level_references","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"get_total_amount_references","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"initial_tokenID","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"is_holder","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"is_qualified","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"nft_affiliate_rewards_earned","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"nfts_created_by_cycle","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nfts_qualified","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"nfts_qualified_by_cycle","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"starting_nft_id","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","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":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"total_holders","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"total_lost_income","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"total_stablecoin_distributed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"total_stablecoin_earned","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"updateFromAble","outputs":[],"stateMutability":"nonpayable","type":"function"}]

608060405273f7ca5aeb3d2dbc0012f153224e22a1757c8f83f5600b60006101000a8154816001600160a01b0302191690836001600160a01b031602179055506001600c556801f399b1438a10000060285567a688906bd8b000006029556001602a55600c602b556001602c55600c602d556001602e55600c602f556001603055600c6031556001603255600360335560016034556003603555600160365560036037556003603855610515603b55348015620000bb57600080fd5b50604080518082018252600d81526c4f666665722053696e6572677960981b60208083019182528351808501909452600784526653696e6572677960c81b908401528151919291620001109160009162000700565b5080516200012690600190602084019062000700565b505050620001436200013d6200042560201b60201c565b62000429565b603b546200015d60396200047b60201b620038dc1760201c565b101562000181576200017b60396200047f60201b620038e01760201c565b62000143565b600b546040805163ddf0604560e01b815290516000926001600160a01b03169163ddf06045916004808301926020929190829003018186803b158015620001c757600080fd5b505afa158015620001dc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620002029190620007a6565b905060006200021d60396200047b60201b620038dc1760201c565b90506200023660396200047f60201b620038e01760201c565b62000242828262000488565b604080518082018252600781526647454e4553495360c81b60208083019182526000858152603f9091529290922090516200027e929062000700565b50604080518082018252601381527f47454e4553495320494e534352495054494f4e000000000000000000000000006020808301918252600085815290849052929092209051620002d0929062000700565b50604080518082018252601681527f47454e455349532056414c55452050524f504f53414c0000000000000000000060208083019182526000858152604190915292909220905162000323929062000700565b506040518060800160405280605c8152602001620066b5605c91396000828152604360209081526040909120825162000363939192919091019062000700565b5060008181526042602090815260408083204290556001600160a01b03851683526007825291829020839055815160608101909252603e808352620003b59284929091906200671190830139620004ae565b6001600160a01b03821660009081526006602090815260408220805460018101825590835290822001829055603b54603a556008805491620003f783620008bc565b9091555050506001600160a01b03166000908152605e60205260409020805460ff19166001179055620008e6565b3390565b600a80546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b5490565b80546001019055565b620004aa828260405180602001604052806000815250620004e960201b60201c565b5050565b620004b9826200050e565b620004c357600080fd5b60008281526009602090815260409091208251620004e49284019062000700565b505050565b620004f583836200052b565b620005046000848484620005d6565b620004e457600080fd5b6000908152600260205260409020546001600160a01b0316151590565b6001600160a01b0382166200053f57600080fd5b6200054a816200050e565b156200055557600080fd5b62000560816200050e565b156200056b57600080fd5b6001600160a01b038216600081815260036020908152604080832080546001019055848352600290915280822080546001600160a01b0319168417905551839291907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050565b6000620005f7846001600160a01b0316620006f160201b620038e91760201c565b15620006e557604051630a85bd0160e11b81526001600160a01b0385169063150b7a02906200063190339089908890889060040162000804565b602060405180830381600087803b1580156200064c57600080fd5b505af19250505080156200067f575060408051601f3d908101601f191682019092526200067c91810190620007d8565b60015b620006ca573d808015620006b0576040519150601f19603f3d011682016040523d82523d6000602084013e620006b5565b606091505b508051620006c257600080fd5b805181602001fd5b6001600160e01b031916630a85bd0160e11b149050620006e9565b5060015b949350505050565b6001600160a01b03163b151590565b8280546200070e906200087f565b90600052602060002090601f0160209004810192826200073257600085556200077d565b82601f106200074d57805160ff19168380011785556200077d565b828001600101855582156200077d579182015b828111156200077d57825182559160200191906001019062000760565b506200078b9291506200078f565b5090565b5b808211156200078b576000815560010162000790565b600060208284031215620007b957600080fd5b81516001600160a01b0381168114620007d157600080fd5b9392505050565b600060208284031215620007eb57600080fd5b81516001600160e01b031981168114620007d157600080fd5b600060018060a01b038087168352602081871681850152856040850152608060608501528451915081608085015260005b82811015620008535785810182015185820160a00152810162000835565b828111156200086657600060a084870101525b5050601f01601f19169190910160a00195945050505050565b600181811c908216806200089457607f821691505b60208210811415620008b657634e487b7160e01b600052602260045260246000fd5b50919050565b6000600019821415620008df57634e487b7160e01b600052601160045260246000fd5b5060010190565b615dbf80620008f66000396000f3fe608060405234801561001057600080fd5b50600436106105c15760003560e01c806301b9f212146105c657806301ffc9a7146105db578063034e63f41461060357806306fdde031461061a578063078f3bd21461062f578063081812fc1461064257806308a1f67f14610662578063095ea7b3146106755780630a5fdca8146106885780630dfc4ef91461069b578063172a327e146106a4578063193767cd146106ad5780631c5c4759146106c05780631dabeeff146106c85780631eb60deb146106d157806323b872dd146106e4578063297e0762146106f75780632e4564661461070a578063328b18a5146107125780633c1bd0831461071b5780633eab2b481461072e57806341a4051b14610741578063425ea88e1461074957806342842e0e146107515780634347ec0514610764578063443c0aa41461076d57806346ada0ec1461078d57806346bce096146107a05780634886d946146107c0578063489693cd146107d35780634a1be8c8146107dc5780634d1ea9c5146107ef57806351efae0d14610802578063529a7e381461081557806353294b70146108285780635616f325146108485780635805de06146108685780635c5699371461087b5780635d215e6f1461088e5780635d27d821146108975780635d3f7f0a146108aa5780636136c156146108bd578063620481a5146108dd578063634ddfd8146108f05780636352211e1461090357806364b43775146109165780636841b0a91461092957806369040b54146106ad578063706e2dfd1461094957806370a082311461096957806370c703831461097c578063715018a61461098f57806372638346146109975780637578a5901461099f578063782300d5146109a85780637ac5e5af146109cb5780637b970727146109de5780637e53bfc0146109f15780637f51e803146109fa578063813e551c14610a0d57806381c9db7714610a2d57806382210e5f14610a4d578063824dcd2514610a6057806382d706ad14610a7357806384370b7a14610a865780638506907814610a8f578063895694bf14610a9757806389a3027114610aaa5780638a9e8b7014610abd5780638acb5bd414610ac65780638b61b56e14610ad95780638d5791dc14610ae15780638d859f3e14610b015780638da5cb5b14610b0a5780639106e0be14610b1257806391dbff7a14610b325780639556fb8914610b4557806395c50b9314610b5857806395d89b4114610b61578063973138fa14610b69578063995672bb14610b7c578063995b145314610b845780639b13263014610b8d578063a104808014610ba0578063a22cb46514610ba9578063a283323814610bbc578063a45179a114610bc5578063a653bfdc14610be5578063a867326c14610c05578063ab3d63a814610c18578063af95166514610c2b578063b0deaa7014610c34578063b3e2067014610c47578063b3ea900114610c4f578063b484ba3614610c57578063b88d4fde14610c6a578063bb68c17014610c7d578063bd8cd14e14610c90578063c27ce47914610cb0578063c2b04ef414610cc3578063c4a1eefc14610cd6578063c54e44eb14610cdf578063c7a0e1dc14610cf2578063c87b56dd14610d15578063c8aaf64b146105d9578063ca14b06d14610d28578063cafb7bec14610d3b578063cb9f22c614610d4e578063cf814b5014610d6e578063d1ce9af314610d81578063d4d792fb14610d8a578063d6003e4e14610d92578063d819abc714610db2578063d8e409d814610dc5578063d94a776214610dd8578063da74d74214610deb578063dad964f414610dfe578063dd0860a814610e1e578063e1aaa2fa14610e31578063e1cdc6f714610e44578063e681204114610e64578063e774620f14610e84578063e985e9c514610e97578063eb20feab14610eaa578063ec78f9cc14610ebd578063ecd8966414610ec6578063f187806b14610ee6578063f2fde38b14610ef9578063f3c304fd14610f0c578063fcd9d7ff14610f15578063feecf85014610f1d575b600080fd5b6105d96105d43660046159a0565b610f26565b005b6105ee6105e936600461577d565b610f3b565b60405190151581526020015b60405180910390f35b61060c60335481565b6040519081526020016105fa565b610622610f8d565b6040516105fa9190615b32565b6105ee61063d3660046159a0565b61101f565b6106556106503660046159a0565b6110ce565b6040516105fa9190615aa4565b61060c6106703660046159d2565b6110f5565b6105d96106833660046156b7565b611126565b6105d96106963660046159a0565b611186565b61060c60085481565b61060c60595481565b6105d96106bb3660046159a0565b61119b565b61060c6111b0565b61060c602e5481565b61060c6106df36600461572b565b6111d4565b6105d96106f23660046155c9565b611365565b61060c6107053660046159d2565b611383565b6105d961139f565b61060c603a5481565b6106226107293660046159a0565b6113d5565b61060c61073c3660046159d2565b61146f565b6105d961148b565b61060c611635565b6105d961075f3660046155c9565b61164a565b61060c60325481565b61060c61077b3660046159a0565b60476020526000908152604090205481565b6105d961079b36600461582d565b611665565b61060c6107ae3660046159a0565b60466020526000908152604090205481565b6105d96107ce3660046159a0565b611af9565b61060c60365481565b6105d96107ea3660046159a0565b611b0e565b6105ee6107fd366004615556565b611b23565b6105d96108103660046159f4565b611b40565b6105d96108233660046159a0565b611c03565b61060c6108363660046159a0565b604c6020526000908152604090205481565b61060c6108563660046159a0565b60486020526000908152604090205481565b6105ee610876366004615556565b611c2b565b6105d96108893660046159f4565b611c5c565b61060c603b5481565b601054610655906001600160a01b031681565b61060c6108b8366004615556565b611cb3565b61060c6108cb366004615556565b605b6020526000908152604090205481565b6105d96108eb3660046159a0565b611d34565b61060c6108fe3660046159d2565b611d49565b6106556109113660046159a0565b611d65565b6105d96109243660046159f4565b611d86565b61060c6109373660046159a0565b605d6020526000908152604090205481565b61060c6109573660046159a0565b60496020526000908152604090205481565b61060c610977366004615556565b611ddd565b61060c61098a3660046159d2565b611e0e565b6105d9611e2a565b61060c611e3e565b61060c600c5481565b6105ee6109b6366004615556565b605e6020526000908152604090205460ff1681565b6105d96109d9366004615556565b611e53565b6105d96109ec366004615556565b611f45565b61060c602d5481565b6105d9610a083660046159a0565b61202f565b61060c610a1b366004615556565b605c6020526000908152604090205481565b61060c610a3b3660046159a0565b604a6020526000908152604090205481565b61060c610a5b3660046156b7565b612057565b6105d9610a6e3660046159a0565b612073565b610622610a813660046159a0565b612088565b61060c603d5481565b61060c6120a1565b61060c610aa53660046159d2565b6120b6565b602654610655906001600160a01b031681565b61060c60385481565b6105d9610ad43660046159f4565b6120d2565b61060c612129565b61060c610aef3660046159a0565b60576020526000908152604090205481565b61060c60285481565b61065561213e565b61060c610b203660046159a0565b60456020526000908152604090205481565b61060c610b403660046159d2565b61214d565b6105d9610b533660046159a0565b612169565b61060c60355481565b61062261217e565b6105d9610b773660046156e3565b61218d565b61060c61226a565b61060c60375481565b6105d9610b9b3660046159f4565b612275565b61060c602b5481565b6105d9610bb7366004615689565b6122cc565b61060c602a5481565b61060c610bd33660046159a0565b603c6020526000908152604090205481565b61060c610bf33660046159a0565b60426020526000908152604090205481565b6105d9610c133660046159a0565b6122d7565b601554610655906001600160a01b031681565b61060c602c5481565b6105d9610c423660046159f4565b6122ec565b61060c612343565b61060c6123c0565b6105d9610c65366004615911565b6123d5565b6105d9610c7836600461560a565b6125ae565b6105d9610c8b3660046159f4565b6125cd565b61060c610c9e366004615556565b60076020526000908152604090205481565b602554610655906001600160a01b031681565b6105d9610cd13660046159a0565b612624565b61060c60295481565b602754610655906001600160a01b031681565b6105ee610d00366004615556565b603e6020526000908152604090205460ff1681565b610622610d233660046159a0565b612639565b6105d9610d363660046159f4565b612644565b61060c610d493660046159d2565b61269b565b61060c610d5c3660046159a0565b604d6020526000908152604090205481565b6105d9610d7c3660046159a0565b6126b7565b61060c60305481565b61060c6126cc565b61060c610da0366004615556565b605f6020526000908152604090205481565b6105d9610dc03660046159a0565b6126e1565b6105d9610dd33660046156b7565b6126f6565b6105d9610de63660046159f4565b6127a2565b610622610df93660046159a0565b6127f9565b61060c610e0c3660046159a0565b60446020526000908152604090205481565b6105ee610e2c366004615556565b612812565b610622610e3f3660046159a0565b612843565b61060c610e523660046159a0565b604b6020526000908152604090205481565b61060c610e72366004615556565b605a6020526000908152604090205481565b6105d9610e923660046159a0565b61285c565b6105ee610ea5366004615590565b612871565b61060c610eb83660046159d2565b61289f565b61060c602f5481565b61060c610ed43660046159a0565b60586020526000908152604090205481565b6105d9610ef43660046159a0565b6128bb565b6105d9610f07366004615556565b6128d0565b61060c60315481565b6105d96128f4565b61060c60345481565b610f2f33612812565b610f365750565b602a55565b60006001600160e01b031982166380ac58cd60e01b1480610f6c57506001600160e01b03198216635b5e139f60e01b145b80610f8757506301ffc9a760e01b6001600160e01b03198316145b92915050565b606060008054610f9c90615c46565b80601f0160208091040260200160405190810160405280929190818152602001828054610fc890615c46565b80156110155780601f10610fea57610100808354040283529160200191611015565b820191906000526020600020905b815481529060010190602001808311610ff857829003601f168201915b5050505050905090565b600061102d61087683611d65565b80610f875750600e546001600160a01b03166346f4d72561104d84611d65565b6001611057612343565b6110619190615c03565b6040518363ffffffff1660e01b815260040161107e929190615b19565b60206040518083038186803b15801561109657600080fd5b505afa1580156110aa573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f879190615760565b60006110d9826138f8565b506000908152600460205260409020546001600160a01b031690565b6053602052816000526040600020818154811061111157600080fd5b90600052602060002001600091509150505481565b600061113182611d65565b9050806001600160a01b0316836001600160a01b0316141561115257600080fd5b336001600160a01b038216148061116e575061116e8133612871565b61117757600080fd5b611181838361390a565b505050565b61118f33612812565b6111965750565b603555565b6111a433612812565b6111ab5750565b603455565b60006031546030546028546111c59190615be4565b6111cf9190615bc2565b905090565b60005b6038546000848152604460205260409020541080156111f557508215155b1561121b57600092835260576020526040909220549161121483611d65565b93506111d7565b60255460405163a9059cbb60e01b81526001600160a01b039091169063a9059cbb9061124d9087908690600401615b19565b602060405180830381600087803b15801561126757600080fd5b505af115801561127b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061129f9190615760565b506001600160a01b0384166000908152605a6020526040812080548492906112c8908490615baa565b9091555050600083815260586020526040812080548492906112eb908490615baa565b9250508190555081605960008282546113049190615baa565b9250508190555042846001600160a01b03167fa421b4aced6f4fcb40d755e461bb29417a90636ae4f2769ff2cb29db7eab79948585604051611347929190615b45565b60405180910390a35050600090815260576020526040902054919050565b61136f3382613978565b61137857600080fd5b6111818383836139d7565b6050602052816000526040600020818154811061111157600080fd5b600d546001600160a01b031633146113b357565b603d54603c60006113c2612343565b8152602081019190915260400160002055565b604360205260009081526040902080546113ee90615c46565b80601f016020809104026020016040519081016040528092919081815260200182805461141a90615c46565b80156114675780601f1061143c57610100808354040283529160200191611467565b820191906000526020600020905b81548152906001019060200180831161144a57829003601f168201915b505050505081565b604e602052816000526040600020818154811061111157600080fd5b6015546001600160a01b0316331461149f57565b600b546040805163adbf1fb960e01b815290516000926001600160a01b03169163adbf1fb9916004808301926020929190829003018186803b1580156114e457600080fd5b505afa1580156114f8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061151c9190615573565b905060005b6009811015611631576115348282613b28565b611556816040518060600160405280603c8152602001615d2e603c9139613b42565b601f5461156f9082906001600160a01b03166004613b73565b6001600160a01b03828116600090815260066020908152604080832080546001810182559084529190922001839055601f549051633b36259960e21b81526004810184905291169063ecd896649060240160206040518083038186803b1580156115d857600080fd5b505afa1580156115ec573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061161091906159b9565b6000828152605860205260409020558061162981615c81565b915050611521565b5050565b6000602b54602a546028546111c59190615be4565b611181838383604051806020016040528060008152506125ae565b61166e33611b23565b61167757611af0565b600060076000601460009054906101000a90046001600160a01b03166001600160a01b03166374d6abcb6040518163ffffffff1660e01b815260040160206040518083038186803b1580156116cb57600080fd5b505afa1580156116df573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117039190615573565b6001600160a01b03168152602081019190915260400160002054905082156117285750815b80611731575060085b603954811061173f57600080fd5b6025546028546040516323b872dd60e01b81526001600160a01b03909216916323b872dd916117749133913091600401615ab8565b602060405180830381600087803b15801561178e57600080fd5b505af11580156117a2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117c69190615760565b50600f546029546040516323b872dd60e01b81526001600160a01b03909216916323b872dd916117fc9133913091600401615ab8565b602060405180830381600087803b15801561181657600080fd5b505af115801561182a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061184e9190615760565b5061185833611f45565b600061186333611ddd565b11156118df576014543360009081526007602052604090819020549051635354b0f160e11b81526001600160a01b039092169163a6a961e2916118ac9160040190815260200190565b600060405180830381600087803b1580156118c657600080fd5b505af11580156118da573d6000803e3d6000fd5b505050505b60006118ea60395490565b90506118fa603980546001019055565b6119043382613b28565b61190e8187613b42565b6000818152603f602090815260409091208a5161192d928c019061545f565b506000818152604060208181529120895161194a928b019061545f565b506000818152604160209081526040909120885161196a928a019061545f565b5060008181526057602090815260408083208590556043825290912086516119949288019061545f565b50600081815260426020908152604080832086905533835260079091529020546119cb573360009081526007602052604090208190555b336000908152600660209081526040822080546001810182559083529120018190556119f56145bf565b6119fd6146e0565b611a05614792565b611a1181836001614844565b605d6000611a1d612343565b81526020019081526020016000206000815480929190611a3c90615c81565b9091555050336000908152605e602052604090205460ff16611a8657336000908152605e60205260408120805460ff191660011790556008805491611a8083615c81565b91905055505b6040513390611a96908990615a59565b60405180910390208a604051611aac9190615a59565b60405180910390207f348a2af61c3e551d263eaa8735e61cbaaa4dbbcde2de30c684dd75431e5088d78442604051611ae5929190615b45565b60405180910390a450505b50505050505050565b611b0233612812565b611b095750565b603855565b611b1733612812565b611b1e5750565b602f55565b600e546000906001600160a01b0316633c992a9183611061612343565b6015546001600160a01b03163314611b5757505050565b6000838152605460205260409081902090516332bedefb60e21b81528291906001600160a01b0383169063cafb7bec90611b979088908890600401615b45565b60206040518083038186803b158015611baf57600080fd5b505afa158015611bc3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611be791906159b9565b8154600181018355600092835260209092209091015550505050565b611c0c33612812565b611c135750565b611c2581670de0b6b3a7640000615be4565b60285550565b600e54604051632c02ef0360e11b81526000916001600160a01b031690635805de069061107e908590600401615aa4565b6015546001600160a01b03163314611c7357505050565b6000838152604e60205260409081902090516307d5656960e31b81528291906001600160a01b03831690633eab2b4890611b979088908890600401615b45565b600f546040516370a0823160e01b81526000916001600160a01b0316906370a0823190611ce4908590600401615aa4565b60206040518083038186803b158015611cfc57600080fd5b505afa158015611d10573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f8791906159b9565b611d3d33612812565b611d445750565b603355565b6055602052816000526040600020818154811061111157600080fd5b600080611d71836148a9565b90506001600160a01b038116610f8757600080fd5b6015546001600160a01b03163314611d9d57505050565b6000838152605360205260409081902090516308a1f67f60e01b81528291906001600160a01b038316906308a1f67f90611b979088908890600401615b45565b60006001600160a01b038216611df257600080fd5b506001600160a01b031660009081526003602052604090205490565b6052602052816000526040600020818154811061111157600080fd5b611e326148c4565b611e3c60006148e0565b565b60006037546036546029546111c59190615be4565b600b54604051631ba10c1560e31b81526001600160a01b039091169063dd0860a890611e83903390600401615aa4565b60206040518083038186803b158015611e9b57600080fd5b505afa158015611eaf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ed39190615760565b611f235760405162461bcd60e51b815260206004820152601d60248201527f4f6e6c79206f776e65722063616e20736574204164647265737365732e000000604482015260640160405180910390fd5b600b80546001600160a01b0319166001600160a01b0392909216919091179055565b6001600160a01b0381166000908152603e602052604090205460ff1615611fc5576001600160a01b0381166000908152605f6020526040812054603d805491929091611f92908490615c03565b90915550506001600160a01b0381166000908152605f60209081526040808320839055603e9091529020805460ff191690555b611fce81611c2b565b1561202c57611fdc81611ddd565b603d6000828254611fed9190615baa565b90915550611ffc905081611ddd565b6001600160a01b0382166000908152605f6020908152604080832093909355603e905220805460ff191660011790555b50565b61203833612812565b61203f5750565b61205181670de0b6b3a7640000615be4565b60295550565b6006602052816000526040600020818154811061111157600080fd5b61207c33612812565b6120835750565b603155565b604160205260009081526040902080546113ee90615c46565b60006035546034546029546111c59190615be4565b6051602052816000526040600020818154811061111157600080fd5b6015546001600160a01b031633146120e957505050565b60008381526051602052604090819020905163895694bf60e01b81528291906001600160a01b0383169063895694bf90611b979088908890600401615b45565b6000602f54602e546028546111c59190615be4565b600a546001600160a01b031690565b604f602052816000526040600020818154811061111157600080fd5b61217233612812565b6121795750565b602c55565b606060018054610f9c90615c46565b6015546001600160a01b031633146121a457612264565b6121af838383613b73565b6001600160a01b0384166000908152600660209081526040822080546001810182559083529120018390556121e384611ddd565b6121fd57600880549060006121f783615c81565b91905055505b6122078484613b28565b6001600160a01b038416600090815260076020526040902054612240576001600160a01b03841660009081526007602052604090208390555b61224984611c2b565b1561226457603d805490600061225e83615c81565b91905055505b50505050565b60006111cf60395490565b6015546001600160a01b0316331461228c57505050565b6000838152604f60205260409081902090516348edffbd60e11b81528291906001600160a01b038316906391dbff7a90611b979088908890600401615b45565b611631338383614932565b6122e033612812565b6122e75750565b603755565b6015546001600160a01b0316331461230357505050565b6000838152605260205260409081902090516370c7038360e01b81528291906001600160a01b038316906370c7038390611b979088908890600401615b45565b600d5460408051636190c9d560e01b815290516000926001600160a01b031691636190c9d5916004808301926020929190829003018186803b15801561238857600080fd5b505afa15801561239c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111cf91906159b9565b60006033546032546029546111c59190615be4565b6123de33611b23565b158061240457506123ee81611d65565b6001600160a01b0316336001600160a01b031614155b1561240e57612264565b6025546028546040516323b872dd60e01b81526001600160a01b03909216916323b872dd916124439133913091600401615ab8565b602060405180830381600087803b15801561245d57600080fd5b505af1158015612471573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124959190615760565b50600f546029546040516323b872dd60e01b81526001600160a01b03909216916323b872dd916124cb9133913091600401615ab8565b602060405180830381600087803b1580156124e557600080fd5b505af11580156124f9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061251d9190615760565b506000818152603f60209081526040909120855161253d9287019061545f565b506000818152604060208181529120845161255a9286019061545f565b506000818152604160209081526040909120835161257a9285019061545f565b506125836145bf565b61258b6146e0565b612593614792565b60008181526057602052604081205461226491839190614844565b6125b83383613978565b6125c157600080fd5b612264848484846149be565b6015546001600160a01b031633146125e457505050565b600083815260556020526040908190209051630c69bbfb60e31b81528291906001600160a01b0383169063634ddfd890611b979088908890600401615b45565b61262d33612812565b6126345750565b602b55565b6060610f87826149de565b6015546001600160a01b0316331461265b57505050565b60008381526056602052604090819020905163eb20feab60e01b81528291906001600160a01b0383169063eb20feab90611b979088908890600401615b45565b6054602052816000526040600020818154811061111157600080fd5b6126c033612812565b6126c75750565b602e55565b6000602d54602c546028546111c59190615be4565b6126ea33612812565b6126f15750565b602d55565b603954811115612704575050565b61270d81611d65565b6001600160a01b0316336001600160a01b03161415801561273957506015546001600160a01b03163314155b15612742575050565b6001600160a01b03821660009081526007602052604090819020805490839055905133907ff1920cbfe18dff4155b1e46e979d6d82313c39f04760c14fbb8f37a21b36e912906127959084908690615b45565b60405180910390a2505050565b6015546001600160a01b031633146127b957505050565b6000838152605060205260409081902090516314bf03b160e11b81528291906001600160a01b0383169063297e076290611b979088908890600401615b45565b603f60205260009081526040902080546113ee90615c46565b600b54604051631ba10c1560e31b81526000916001600160a01b03169063dd0860a89061107e908590600401615aa4565b604060208190526000918252902080546113ee90615c46565b61286533612812565b61286c5750565b603055565b6001600160a01b03918216600090815260056020908152604080832093909416825291909152205460ff1690565b6056602052816000526040600020818154811061111157600080fd5b6128c433612812565b6128cb5750565b603255565b6128d86148c4565b6001600160a01b0381166128eb57600080fd5b61202c816148e0565b600b54604080516307578a5960e41b815290516000926001600160a01b031691637578a590916004808301926020929190829003018186803b15801561293957600080fd5b505afa15801561294d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061297191906159b9565b905080600c5414156129805750565b600c819055600b5460408051635f9ca54b60e11b815290516001600160a01b039092169163bf394a9691600480820192602092909190829003018186803b1580156129ca57600080fd5b505afa1580156129de573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a029190615573565b600d80546001600160a01b0319166001600160a01b03928316179055600b5460408051632d311b9f60e11b815290519190921691635a62373e916004808301926020929190829003018186803b158015612a5b57600080fd5b505afa158015612a6f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a939190615573565b600e80546001600160a01b0319166001600160a01b03928316179055600b5460408051632fc69b7560e21b81529051919092169163bf1a6dd4916004808301926020929190829003018186803b158015612aec57600080fd5b505afa158015612b00573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b249190615573565b600f80546001600160a01b0319166001600160a01b03928316179055600b5460408051635d27d82160e01b815290519190921691635d27d821916004808301926020929190829003018186803b158015612b7d57600080fd5b505afa158015612b91573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612bb59190615573565b601080546001600160a01b0319166001600160a01b03928316179055600b5460408051633739851960e21b81529051919092169163dce61464916004808301926020929190829003018186803b158015612c0e57600080fd5b505afa158015612c22573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c469190615573565b601180546001600160a01b0319166001600160a01b03928316179055600b546040805163fdf6c44360e01b81529051919092169163fdf6c443916004808301926020929190829003018186803b158015612c9f57600080fd5b505afa158015612cb3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612cd79190615573565b601280546001600160a01b0319166001600160a01b03928316179055600b54604080516353962e4560e11b81529051919092169163a72c5c8a916004808301926020929190829003018186803b158015612d3057600080fd5b505afa158015612d44573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d689190615573565b601380546001600160a01b0319166001600160a01b03928316179055600b546040805163e93b941b60e01b81529051919092169163e93b941b916004808301926020929190829003018186803b158015612dc157600080fd5b505afa158015612dd5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612df99190615573565b601480546001600160a01b0319166001600160a01b03928316179055600b5460408051631567ac7560e31b81529051919092169163ab3d63a8916004808301926020929190829003018186803b158015612e5257600080fd5b505afa158015612e66573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e8a9190615573565b601580546001600160a01b0319166001600160a01b03928316179055600b5460408051635ffb42d960e11b81529051919092169163bff685b2916004808301926020929190829003018186803b158015612ee357600080fd5b505afa158015612ef7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f1b9190615573565b601680546001600160a01b0319166001600160a01b03928316179055600b5460408051630bbc5d7d60e31b815290519190921691635de2ebe8916004808301926020929190829003018186803b158015612f7457600080fd5b505afa158015612f88573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612fac9190615573565b601780546001600160a01b0319166001600160a01b03928316179055600b546040805163c586261f60e01b81529051919092169163c586261f916004808301926020929190829003018186803b15801561300557600080fd5b505afa158015613019573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061303d9190615573565b601880546001600160a01b0319166001600160a01b03928316179055600b5460408051637abb1b7360e01b815290519190921691637abb1b73916004808301926020929190829003018186803b15801561309657600080fd5b505afa1580156130aa573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130ce9190615573565b601980546001600160a01b0319166001600160a01b03928316179055600b5460408051633dd5259d60e01b815290519190921691633dd5259d916004808301926020929190829003018186803b15801561312757600080fd5b505afa15801561313b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061315f9190615573565b601a80546001600160a01b0319166001600160a01b03928316179055600b546040805163967db45360e01b81529051919092169163967db453916004808301926020929190829003018186803b1580156131b857600080fd5b505afa1580156131cc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131f09190615573565b601b80546001600160a01b0319166001600160a01b03928316179055600b54604080516310fced2960e01b8152905191909216916310fced29916004808301926020929190829003018186803b15801561324957600080fd5b505afa15801561325d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132819190615573565b601c80546001600160a01b0319166001600160a01b03928316179055600b5460408051634cfd0abf60e11b8152905191909216916399fa157e916004808301926020929190829003018186803b1580156132da57600080fd5b505afa1580156132ee573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133129190615573565b601d80546001600160a01b0319166001600160a01b03928316179055600b5460408051636164467360e01b8152905191909216916361644673916004808301926020929190829003018186803b15801561336b57600080fd5b505afa15801561337f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133a39190615573565b601e80546001600160a01b0319166001600160a01b03928316179055600b5460408051631b7410a160e31b81529051919092169163dba08508916004808301926020929190829003018186803b1580156133fc57600080fd5b505afa158015613410573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134349190615573565b601f80546001600160a01b0319166001600160a01b03928316179055600b546040805163484de36360e11b81529051919092169163909bc6c6916004808301926020929190829003018186803b15801561348d57600080fd5b505afa1580156134a1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134c59190615573565b602080546001600160a01b0319166001600160a01b03928316178155600b5460408051633310a27960e01b815290519190931692633310a279926004808301939192829003018186803b15801561351b57600080fd5b505afa15801561352f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135539190615573565b602180546001600160a01b0319166001600160a01b03928316179055600b5460408051633c9d0ac360e11b81529051919092169163793a1586916004808301926020929190829003018186803b1580156135ac57600080fd5b505afa1580156135c0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135e49190615573565b602280546001600160a01b0319166001600160a01b03928316179055600b546040805163063606b760e51b81529051919092169163c6c0d6e0916004808301926020929190829003018186803b15801561363d57600080fd5b505afa158015613651573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136759190615573565b602380546001600160a01b0319166001600160a01b03928316179055600b5460408051630ffbb3e960e21b815290519190921691633feecfa4916004808301926020929190829003018186803b1580156136ce57600080fd5b505afa1580156136e2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137069190615573565b602480546001600160a01b0319166001600160a01b03928316179055600b546040805163c27ce47960e01b81529051919092169163c27ce479916004808301926020929190829003018186803b15801561375f57600080fd5b505afa158015613773573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137979190615573565b602580546001600160a01b0319166001600160a01b03928316179055600b54604080516389a3027160e01b8152905191909216916389a30271916004808301926020929190829003018186803b1580156137f057600080fd5b505afa158015613804573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138289190615573565b602680546001600160a01b0319166001600160a01b03928316179055600b546040805163c54e44eb60e01b81529051919092169163c54e44eb916004808301926020929190829003018186803b15801561388157600080fd5b505afa158015613895573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138b99190615573565b602780546001600160a01b0319166001600160a01b039290921691909117905550565b5490565b80546001019055565b6001600160a01b03163b151590565b61390181614ae7565b61202c57600080fd5b600081815260046020526040902080546001600160a01b0319166001600160a01b038416908117909155819061393f82611d65565b6001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b60008061398483611d65565b9050806001600160a01b0316846001600160a01b031614806139ab57506139ab8185612871565b806139cf5750836001600160a01b03166139c4846110ce565b6001600160a01b0316145b949350505050565b826001600160a01b03166139ea82611d65565b6001600160a01b0316146139fd57600080fd5b6001600160a01b038216613a1057600080fd5b826001600160a01b0316613a2382611d65565b6001600160a01b031614613a3657600080fd5b600081815260046020526040902080546001600160a01b0319169055613a5c8184614b04565b6001600160a01b038316600090815260036020526040902080546000190190819055158015613a8d57506000600854115b15613a9d57600880546000190190555b613aa78183614cc5565b6001600160a01b038216600090815260036020526040902054613ace576008805460010190555b6001600160a01b03808316600081815260036020908152604080832080546001019055858352600290915280822080546001600160a01b03191684179055518493871691600080516020615d6a83398151915291a4505050565b611631828260405180602001604052806000815250614d19565b613b4b82614ae7565b613b5457600080fd5b600082815260096020908152604090912082516111819284019061545f565b604051636d3a6ba160e11b815260048101849052829081906001600160a01b0382169063da74d7429060240160006040518083038186803b158015613bb757600080fd5b505afa158015613bcb573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052613bf391908101906157b7565b6000868152603f602090815260409091208251613c16939192919091019061545f565b506040516370d5517d60e11b8152600481018690526001600160a01b0383169063e1aaa2fa9060240160006040518083038186803b158015613c5757600080fd5b505afa158015613c6b573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052613c9391908101906157b7565b60008681526040602081815291208251613cb3939192919091019061545f565b506040516382d706ad60e01b8152600481018690526001600160a01b038316906382d706ad9060240160006040518083038186803b158015613cf457600080fd5b505afa158015613d08573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052613d3091908101906157b7565b60008681526041602090815260409091208251613d53939192919091019061545f565b50604051632994eff760e21b8152600481018690526001600160a01b0383169063a653bfdc9060240160206040518083038186803b158015613d9457600080fd5b505afa158015613da8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613dcc91906159b9565b600086815260426020526040908190209190915551633c1bd08360e01b8152600481018690526001600160a01b03831690633c1bd0839060240160006040518083038186803b158015613e1e57600080fd5b505afa158015613e32573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052613e5a91908101906157b7565b60008681526043602090815260409091208251613e7d939192919091019061545f565b50604051632355e47760e21b8152600481018690526001600160a01b03831690638d5791dc9060240160206040518083038186803b158015613ebe57600080fd5b505afa158015613ed2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ef691906159b9565b6000868152605760205260409081902091909155516336b6593d60e21b8152600481018690526001600160a01b0383169063dad964f49060240160206040518083038186803b158015613f4857600080fd5b505afa158015613f5c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613f8091906159b9565b600086815260446020526040908190209190915551634883705f60e11b8152600481018690526001600160a01b03831690639106e0be9060240160206040518083038186803b158015613fd257600080fd5b505afa158015613fe6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061400a91906159b9565b60008681526045602052604090819020919091555163235e704b60e11b8152600481018690526001600160a01b038316906346bce0969060240160206040518083038186803b15801561405c57600080fd5b505afa158015614070573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061409491906159b9565b60008681526046602052604090819020919091555163110f02a960e21b8152600481018690526001600160a01b0383169063443c0aa49060240160206040518083038186803b1580156140e657600080fd5b505afa1580156140fa573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061411e91906159b9565b600086815260476020526040908190209190915551635616f32560e01b8152600481018690526001600160a01b03831690635616f3259060240160206040518083038186803b15801561417057600080fd5b505afa158015614184573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906141a891906159b9565b60008681526048602052604090819020919091555163706e2dfd60e01b8152600481018690526001600160a01b0383169063706e2dfd9060240160206040518083038186803b1580156141fa57600080fd5b505afa15801561420e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061423291906159b9565b6000868152604960205260409081902091909155516381c9db7760e01b8152600481018690526001600160a01b038316906381c9db779060240160206040518083038186803b15801561428457600080fd5b505afa158015614298573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906142bc91906159b9565b6000868152604a602052604090819020919091555163e1cdc6f760e01b8152600481018690526001600160a01b0383169063e1cdc6f79060240160206040518083038186803b15801561430e57600080fd5b505afa158015614322573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061434691906159b9565b6000868152604b602052604090819020919091555163053294b760e41b8152600481018690526001600160a01b038316906353294b709060240160206040518083038186803b15801561439857600080fd5b505afa1580156143ac573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906143d091906159b9565b6000868152604c60205260409081902091909155516365cf916360e11b8152600481018690526001600160a01b0383169063cb9f22c69060240160206040518083038186803b15801561442257600080fd5b505afa158015614436573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061445a91906159b9565b6000868152604d6020526040902055600483141561451357604051633b36259960e21b8152600481018690526001600160a01b0383169063ecd896649060240160206040518083038186803b1580156144b257600080fd5b505afa1580156144c6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906144ea91906159b9565b60008681526058602052604081208054909190614508908490615baa565b909155506145b89050565b82600b14156145b857604051633549d94b60e11b8152600481018690526001600160a01b03821690636a93b2969060240160206040518083038186803b15801561455c57600080fd5b505afa158015614570573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061459491906159b9565b600086815260586020526040812080549091906145b2908490615baa565b90915550505b5050505050565b600f546019546001600160a01b039182169163a9059cbb91166145e06123c0565b6040518363ffffffff1660e01b81526004016145fd929190615b19565b602060405180830381600087803b15801561461757600080fd5b505af115801561462b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061464f9190615760565b506025546018546001600160a01b039182169163a9059cbb91166146716126cc565b6040518363ffffffff1660e01b815260040161468e929190615b19565b602060405180830381600087803b1580156146a857600080fd5b505af11580156146bc573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061202c9190615760565b600f54601b546001600160a01b039182169163a9059cbb91166147016120a1565b6040518363ffffffff1660e01b815260040161471e929190615b19565b602060405180830381600087803b15801561473857600080fd5b505af115801561474c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906147709190615760565b50602554601a546001600160a01b039182169163a9059cbb9116614671612129565b600f54601d546001600160a01b039182169163a9059cbb91166147b3611e3e565b6040518363ffffffff1660e01b81526004016147d0929190615b19565b602060405180830381600087803b1580156147ea57600080fd5b505af11580156147fe573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906148229190615760565b50602554601c546001600160a01b039182169163a9059cbb91166146716111b0565b60008080614850611635565b9050845b6009831015611af057841561486e5761486e838888614d39565b61487781611d65565b93506148848482846111d4565b60009687526057602052604090962054959050826148a181615c81565b935050614854565b6000908152600260205260409020546001600160a01b031690565b336148cd61213e565b6001600160a01b031614611e3c57600080fd5b600a80546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b816001600160a01b0316836001600160a01b0316141561495157600080fd5b6001600160a01b03838116600081815260056020908152604080832094871680845294825291829020805460ff191686151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a3505050565b6149c98484846139d7565b6149d5848484846150e6565b61226457600080fd5b60606149e9826138f8565b60008281526009602052604081208054614a0290615c46565b80601f0160208091040260200160405190810160405280929190818152602001828054614a2e90615c46565b8015614a7b5780601f10614a5057610100808354040283529160200191614a7b565b820191906000526020600020905b815481529060010190602001808311614a5e57829003601f168201915b505050505090506000614a9960408051602081019091526000815290565b9050805160001415614aac575092915050565b815115614ade578082604051602001614ac6929190615a75565b60405160208183030381529060405292505050919050565b6139cf846151e7565b600080614af3836148a9565b6001600160a01b0316141592915050565b60005b6001600160a01b0382166000908152600660205260409020805484919083908110614b3457614b34615cc8565b906000526020600020015414614b565780614b4e81615c81565b915050614b07565b6001600160a01b038216600090815260036020526040902054614b7b90600190615c03565b811015614c14576001600160a01b0382166000908152600660205260409020614ba5826001615baa565b81548110614bb557614bb5615cc8565b906000526020600020015460066000846001600160a01b03166001600160a01b031681526020019081526020016000208281548110614bf657614bf6615cc8565b60009182526020909120015580614c0c81615c81565b915050614b56565b6001600160a01b0382166000908152600660205260409020805480614c3b57614c3b615cb2565b6000828152602080822083016000199081018390559092019092556001600160a01b0384168252600790526040902054831415611181576001600160a01b03821660009081526006602052604081208054909190614c9b57614c9b615cc8565b60009182526020808320909101546001600160a01b03851683526007909152604090912055505050565b6001600160a01b0381166000818152600660209081526040808320805460018101825590845282842001869055928252600790522054611631576001600160a01b0316600090815260076020526040902055565b614d23838361525b565b614d3060008484846150e6565b61118157600080fd5b82614d9e576000818152604460205260408120805491614d5883615c81565b90915550506000818152604d60205260408120805491614d7783615c81565b90915550506000908152604e60209081526040822080546001810182559083529120015550565b8260011415614e07576000818152604560205260408120805491614dc183615c81565b90915550506000818152604d60205260408120805491614de083615c81565b90915550506000908152604f60209081526040822080546001810182559083529120015550565b8260021415614e70576000818152604660205260408120805491614e2a83615c81565b90915550506000818152604d60205260408120805491614e4983615c81565b90915550506000908152605060209081526040822080546001810182559083529120015550565b8260031415614ed9576000818152604760205260408120805491614e9383615c81565b90915550506000818152604d60205260408120805491614eb283615c81565b90915550506000908152605160209081526040822080546001810182559083529120015550565b8260041415614f42576000818152604860205260408120805491614efc83615c81565b90915550506000818152604d60205260408120805491614f1b83615c81565b90915550506000908152605260209081526040822080546001810182559083529120015550565b8260051415614fab576000818152604960205260408120805491614f6583615c81565b90915550506000818152604d60205260408120805491614f8483615c81565b90915550506000908152605360209081526040822080546001810182559083529120015550565b8260061415615014576000818152604a60205260408120805491614fce83615c81565b90915550506000818152604d60205260408120805491614fed83615c81565b90915550506000908152605460209081526040822080546001810182559083529120015550565b826007141561507d576000818152604b6020526040812080549161503783615c81565b90915550506000818152604d6020526040812080549161505683615c81565b90915550506000908152605560209081526040822080546001810182559083529120015550565b8260081415611181576000818152604c602052604081208054916150a083615c81565b90915550506000818152604d602052604081208054916150bf83615c81565b90915550506000908152605660209081526040822080546001810182559083529120015550565b60006150fa846001600160a01b03166138e9565b156151dc57604051630a85bd0160e11b81526001600160a01b0385169063150b7a0290615131903390899088908890600401615adc565b602060405180830381600087803b15801561514b57600080fd5b505af192505050801561517b575060408051601f3d908101601f191682019092526151789181019061579a565b60015b6151c2573d8080156151a9576040519150601f19603f3d011682016040523d82523d6000602084013e6151ae565b606091505b5080516151ba57600080fd5b805181602001fd5b6001600160e01b031916630a85bd0160e11b1490506139cf565b506001949350505050565b60606151f2826138f8565b600061520960408051602081019091526000815290565b905060008151116152295760405180602001604052806000815250615254565b80615233846152ed565b604051602001615244929190615a75565b6040516020818303038152906040525b9392505050565b6001600160a01b03821661526e57600080fd5b61527781614ae7565b1561528157600080fd5b61528a81614ae7565b1561529457600080fd5b6001600160a01b038216600081815260036020908152604080832080546001019055848352600290915280822080546001600160a01b031916841790555183929190600080516020615d6a833981519152908290a45050565b606060006152fa83615389565b60010190506000816001600160401b0381111561531957615319615cde565b6040519080825280601f01601f191660200182016040528015615343576020820181803683370190505b5090508181016020015b600019016f181899199a1a9b1b9c1cb0b131b232b360811b600a86061a8153600a850494508461537c57615381565b61534d565b509392505050565b60008072184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b83106153c85772184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b830492506040015b6904ee2d6d415b85acef8160201b83106153f2576904ee2d6d415b85acef8160201b830492506020015b662386f26fc10000831061541057662386f26fc10000830492506010015b6305f5e1008310615428576305f5e100830492506008015b612710831061543c57612710830492506004015b6064831061544e576064830492506002015b600a8310610f875760010192915050565b82805461546b90615c46565b90600052602060002090601f01602090048101928261548d57600085556154d3565b82601f106154a657805160ff19168380011785556154d3565b828001600101855582156154d3579182015b828111156154d35782518255916020019190600101906154b8565b506154df9291506154e3565b5090565b5b808211156154df57600081556001016154e4565b600061550b61550684615b83565b615b53565b905082815283838301111561551f57600080fd5b828260208301376000602084830101529392505050565b600082601f83011261554757600080fd5b615254838335602085016154f8565b60006020828403121561556857600080fd5b813561525481615cf4565b60006020828403121561558557600080fd5b815161525481615cf4565b600080604083850312156155a357600080fd5b82356155ae81615cf4565b915060208301356155be81615cf4565b809150509250929050565b6000806000606084860312156155de57600080fd5b83356155e981615cf4565b925060208401356155f981615cf4565b929592945050506040919091013590565b6000806000806080858703121561562057600080fd5b843561562b81615cf4565b9350602085013561563b81615cf4565b92506040850135915060608501356001600160401b0381111561565d57600080fd5b8501601f8101871361566e57600080fd5b61567d878235602084016154f8565b91505092959194509250565b6000806040838503121561569c57600080fd5b82356156a781615cf4565b915060208301356155be81615d09565b600080604083850312156156ca57600080fd5b82356156d581615cf4565b946020939093013593505050565b600080600080608085870312156156f957600080fd5b843561570481615cf4565b935060208501359250604085013561571b81615cf4565b9396929550929360600135925050565b60008060006060848603121561574057600080fd5b833561574b81615cf4565b95602085013595506040909401359392505050565b60006020828403121561577257600080fd5b815161525481615d09565b60006020828403121561578f57600080fd5b813561525481615d17565b6000602082840312156157ac57600080fd5b815161525481615d17565b6000602082840312156157c957600080fd5b81516001600160401b038111156157df57600080fd5b8201601f810184136157f057600080fd5b80516157fe61550682615b83565b81815285602083850101111561581357600080fd5b615824826020830160208601615c1a565b95945050505050565b600080600080600080600060e0888a03121561584857600080fd5b87356001600160401b038082111561585f57600080fd5b61586b8b838c01615536565b985060208a013591508082111561588157600080fd5b61588d8b838c01615536565b975060408a01359150808211156158a357600080fd5b6158af8b838c01615536565b965060608a01359150808211156158c557600080fd5b6158d18b838c01615536565b955060808a01359150808211156158e757600080fd5b506158f48a828b01615536565b93505060a0880135915060c0880135905092959891949750929550565b6000806000806080858703121561592757600080fd5b84356001600160401b038082111561593e57600080fd5b61594a88838901615536565b9550602087013591508082111561596057600080fd5b61596c88838901615536565b9450604087013591508082111561598257600080fd5b5061598f87828801615536565b949793965093946060013593505050565b6000602082840312156159b257600080fd5b5035919050565b6000602082840312156159cb57600080fd5b5051919050565b600080604083850312156159e557600080fd5b50508035926020909101359150565b600080600060608486031215615a0957600080fd5b83359250602084013591506040840135615a2281615cf4565b809150509250925092565b60008151808452615a45816020860160208601615c1a565b601f01601f19169290920160200192915050565b60008251615a6b818460208701615c1a565b9190910192915050565b60008351615a87818460208801615c1a565b835190830190615a9b818360208801615c1a565b01949350505050565b6001600160a01b0391909116815260200190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6001600160a01b0385811682528416602082015260408101839052608060608201819052600090615b0f90830184615a2d565b9695505050505050565b6001600160a01b03929092168252602082015260400190565b6020815260006152546020830184615a2d565b918252602082015260400190565b604051601f8201601f191681016001600160401b0381118282101715615b7b57615b7b615cde565b604052919050565b60006001600160401b03821115615b9c57615b9c615cde565b50601f01601f191660200190565b60008219821115615bbd57615bbd615c9c565b500190565b600082615bdf57634e487b7160e01b600052601260045260246000fd5b500490565b6000816000190483118215151615615bfe57615bfe615c9c565b500290565b600082821015615c1557615c15615c9c565b500390565b60005b83811015615c35578181015183820152602001615c1d565b838111156122645750506000910152565b600181811c90821680615c5a57607f821691505b60208210811415615c7b57634e487b7160e01b600052602260045260246000fd5b50919050565b6000600019821415615c9557615c95615c9c565b5060010190565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052603160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b038116811461202c57600080fd5b801515811461202c57600080fd5b6001600160e01b03198116811461202c57600080fdfe697066733a2f2f516d5269314476674475367a414a7770625552474e424251544d38325a434e5a4179546b454172624b5a4b6d31552f302e6a736f6eddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa2646970667358221220868a46f6c1de263806a361876d5eb1974f5af51d22d0d93939ad068c17c9105e64736f6c6343000807003368747470733a2f2f7265732e636c6f7564696e6172792e636f6d2f73617665722d636f6d6d756e6974792f696d6167652f75706c6f61642f76313636363338303530312f6a766a626c73346c67356d74787378686c686e662e706e67697066733a2f2f516d5269314476674475367a414a7770625552474e424251544d38325a434e5a4179546b454172624b5a4b6d31552f3237332e6a736f6e

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106105c15760003560e01c806301b9f212146105c657806301ffc9a7146105db578063034e63f41461060357806306fdde031461061a578063078f3bd21461062f578063081812fc1461064257806308a1f67f14610662578063095ea7b3146106755780630a5fdca8146106885780630dfc4ef91461069b578063172a327e146106a4578063193767cd146106ad5780631c5c4759146106c05780631dabeeff146106c85780631eb60deb146106d157806323b872dd146106e4578063297e0762146106f75780632e4564661461070a578063328b18a5146107125780633c1bd0831461071b5780633eab2b481461072e57806341a4051b14610741578063425ea88e1461074957806342842e0e146107515780634347ec0514610764578063443c0aa41461076d57806346ada0ec1461078d57806346bce096146107a05780634886d946146107c0578063489693cd146107d35780634a1be8c8146107dc5780634d1ea9c5146107ef57806351efae0d14610802578063529a7e381461081557806353294b70146108285780635616f325146108485780635805de06146108685780635c5699371461087b5780635d215e6f1461088e5780635d27d821146108975780635d3f7f0a146108aa5780636136c156146108bd578063620481a5146108dd578063634ddfd8146108f05780636352211e1461090357806364b43775146109165780636841b0a91461092957806369040b54146106ad578063706e2dfd1461094957806370a082311461096957806370c703831461097c578063715018a61461098f57806372638346146109975780637578a5901461099f578063782300d5146109a85780637ac5e5af146109cb5780637b970727146109de5780637e53bfc0146109f15780637f51e803146109fa578063813e551c14610a0d57806381c9db7714610a2d57806382210e5f14610a4d578063824dcd2514610a6057806382d706ad14610a7357806384370b7a14610a865780638506907814610a8f578063895694bf14610a9757806389a3027114610aaa5780638a9e8b7014610abd5780638acb5bd414610ac65780638b61b56e14610ad95780638d5791dc14610ae15780638d859f3e14610b015780638da5cb5b14610b0a5780639106e0be14610b1257806391dbff7a14610b325780639556fb8914610b4557806395c50b9314610b5857806395d89b4114610b61578063973138fa14610b69578063995672bb14610b7c578063995b145314610b845780639b13263014610b8d578063a104808014610ba0578063a22cb46514610ba9578063a283323814610bbc578063a45179a114610bc5578063a653bfdc14610be5578063a867326c14610c05578063ab3d63a814610c18578063af95166514610c2b578063b0deaa7014610c34578063b3e2067014610c47578063b3ea900114610c4f578063b484ba3614610c57578063b88d4fde14610c6a578063bb68c17014610c7d578063bd8cd14e14610c90578063c27ce47914610cb0578063c2b04ef414610cc3578063c4a1eefc14610cd6578063c54e44eb14610cdf578063c7a0e1dc14610cf2578063c87b56dd14610d15578063c8aaf64b146105d9578063ca14b06d14610d28578063cafb7bec14610d3b578063cb9f22c614610d4e578063cf814b5014610d6e578063d1ce9af314610d81578063d4d792fb14610d8a578063d6003e4e14610d92578063d819abc714610db2578063d8e409d814610dc5578063d94a776214610dd8578063da74d74214610deb578063dad964f414610dfe578063dd0860a814610e1e578063e1aaa2fa14610e31578063e1cdc6f714610e44578063e681204114610e64578063e774620f14610e84578063e985e9c514610e97578063eb20feab14610eaa578063ec78f9cc14610ebd578063ecd8966414610ec6578063f187806b14610ee6578063f2fde38b14610ef9578063f3c304fd14610f0c578063fcd9d7ff14610f15578063feecf85014610f1d575b600080fd5b6105d96105d43660046159a0565b610f26565b005b6105ee6105e936600461577d565b610f3b565b60405190151581526020015b60405180910390f35b61060c60335481565b6040519081526020016105fa565b610622610f8d565b6040516105fa9190615b32565b6105ee61063d3660046159a0565b61101f565b6106556106503660046159a0565b6110ce565b6040516105fa9190615aa4565b61060c6106703660046159d2565b6110f5565b6105d96106833660046156b7565b611126565b6105d96106963660046159a0565b611186565b61060c60085481565b61060c60595481565b6105d96106bb3660046159a0565b61119b565b61060c6111b0565b61060c602e5481565b61060c6106df36600461572b565b6111d4565b6105d96106f23660046155c9565b611365565b61060c6107053660046159d2565b611383565b6105d961139f565b61060c603a5481565b6106226107293660046159a0565b6113d5565b61060c61073c3660046159d2565b61146f565b6105d961148b565b61060c611635565b6105d961075f3660046155c9565b61164a565b61060c60325481565b61060c61077b3660046159a0565b60476020526000908152604090205481565b6105d961079b36600461582d565b611665565b61060c6107ae3660046159a0565b60466020526000908152604090205481565b6105d96107ce3660046159a0565b611af9565b61060c60365481565b6105d96107ea3660046159a0565b611b0e565b6105ee6107fd366004615556565b611b23565b6105d96108103660046159f4565b611b40565b6105d96108233660046159a0565b611c03565b61060c6108363660046159a0565b604c6020526000908152604090205481565b61060c6108563660046159a0565b60486020526000908152604090205481565b6105ee610876366004615556565b611c2b565b6105d96108893660046159f4565b611c5c565b61060c603b5481565b601054610655906001600160a01b031681565b61060c6108b8366004615556565b611cb3565b61060c6108cb366004615556565b605b6020526000908152604090205481565b6105d96108eb3660046159a0565b611d34565b61060c6108fe3660046159d2565b611d49565b6106556109113660046159a0565b611d65565b6105d96109243660046159f4565b611d86565b61060c6109373660046159a0565b605d6020526000908152604090205481565b61060c6109573660046159a0565b60496020526000908152604090205481565b61060c610977366004615556565b611ddd565b61060c61098a3660046159d2565b611e0e565b6105d9611e2a565b61060c611e3e565b61060c600c5481565b6105ee6109b6366004615556565b605e6020526000908152604090205460ff1681565b6105d96109d9366004615556565b611e53565b6105d96109ec366004615556565b611f45565b61060c602d5481565b6105d9610a083660046159a0565b61202f565b61060c610a1b366004615556565b605c6020526000908152604090205481565b61060c610a3b3660046159a0565b604a6020526000908152604090205481565b61060c610a5b3660046156b7565b612057565b6105d9610a6e3660046159a0565b612073565b610622610a813660046159a0565b612088565b61060c603d5481565b61060c6120a1565b61060c610aa53660046159d2565b6120b6565b602654610655906001600160a01b031681565b61060c60385481565b6105d9610ad43660046159f4565b6120d2565b61060c612129565b61060c610aef3660046159a0565b60576020526000908152604090205481565b61060c60285481565b61065561213e565b61060c610b203660046159a0565b60456020526000908152604090205481565b61060c610b403660046159d2565b61214d565b6105d9610b533660046159a0565b612169565b61060c60355481565b61062261217e565b6105d9610b773660046156e3565b61218d565b61060c61226a565b61060c60375481565b6105d9610b9b3660046159f4565b612275565b61060c602b5481565b6105d9610bb7366004615689565b6122cc565b61060c602a5481565b61060c610bd33660046159a0565b603c6020526000908152604090205481565b61060c610bf33660046159a0565b60426020526000908152604090205481565b6105d9610c133660046159a0565b6122d7565b601554610655906001600160a01b031681565b61060c602c5481565b6105d9610c423660046159f4565b6122ec565b61060c612343565b61060c6123c0565b6105d9610c65366004615911565b6123d5565b6105d9610c7836600461560a565b6125ae565b6105d9610c8b3660046159f4565b6125cd565b61060c610c9e366004615556565b60076020526000908152604090205481565b602554610655906001600160a01b031681565b6105d9610cd13660046159a0565b612624565b61060c60295481565b602754610655906001600160a01b031681565b6105ee610d00366004615556565b603e6020526000908152604090205460ff1681565b610622610d233660046159a0565b612639565b6105d9610d363660046159f4565b612644565b61060c610d493660046159d2565b61269b565b61060c610d5c3660046159a0565b604d6020526000908152604090205481565b6105d9610d7c3660046159a0565b6126b7565b61060c60305481565b61060c6126cc565b61060c610da0366004615556565b605f6020526000908152604090205481565b6105d9610dc03660046159a0565b6126e1565b6105d9610dd33660046156b7565b6126f6565b6105d9610de63660046159f4565b6127a2565b610622610df93660046159a0565b6127f9565b61060c610e0c3660046159a0565b60446020526000908152604090205481565b6105ee610e2c366004615556565b612812565b610622610e3f3660046159a0565b612843565b61060c610e523660046159a0565b604b6020526000908152604090205481565b61060c610e72366004615556565b605a6020526000908152604090205481565b6105d9610e923660046159a0565b61285c565b6105ee610ea5366004615590565b612871565b61060c610eb83660046159d2565b61289f565b61060c602f5481565b61060c610ed43660046159a0565b60586020526000908152604090205481565b6105d9610ef43660046159a0565b6128bb565b6105d9610f07366004615556565b6128d0565b61060c60315481565b6105d96128f4565b61060c60345481565b610f2f33612812565b610f365750565b602a55565b60006001600160e01b031982166380ac58cd60e01b1480610f6c57506001600160e01b03198216635b5e139f60e01b145b80610f8757506301ffc9a760e01b6001600160e01b03198316145b92915050565b606060008054610f9c90615c46565b80601f0160208091040260200160405190810160405280929190818152602001828054610fc890615c46565b80156110155780601f10610fea57610100808354040283529160200191611015565b820191906000526020600020905b815481529060010190602001808311610ff857829003601f168201915b5050505050905090565b600061102d61087683611d65565b80610f875750600e546001600160a01b03166346f4d72561104d84611d65565b6001611057612343565b6110619190615c03565b6040518363ffffffff1660e01b815260040161107e929190615b19565b60206040518083038186803b15801561109657600080fd5b505afa1580156110aa573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f879190615760565b60006110d9826138f8565b506000908152600460205260409020546001600160a01b031690565b6053602052816000526040600020818154811061111157600080fd5b90600052602060002001600091509150505481565b600061113182611d65565b9050806001600160a01b0316836001600160a01b0316141561115257600080fd5b336001600160a01b038216148061116e575061116e8133612871565b61117757600080fd5b611181838361390a565b505050565b61118f33612812565b6111965750565b603555565b6111a433612812565b6111ab5750565b603455565b60006031546030546028546111c59190615be4565b6111cf9190615bc2565b905090565b60005b6038546000848152604460205260409020541080156111f557508215155b1561121b57600092835260576020526040909220549161121483611d65565b93506111d7565b60255460405163a9059cbb60e01b81526001600160a01b039091169063a9059cbb9061124d9087908690600401615b19565b602060405180830381600087803b15801561126757600080fd5b505af115801561127b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061129f9190615760565b506001600160a01b0384166000908152605a6020526040812080548492906112c8908490615baa565b9091555050600083815260586020526040812080548492906112eb908490615baa565b9250508190555081605960008282546113049190615baa565b9250508190555042846001600160a01b03167fa421b4aced6f4fcb40d755e461bb29417a90636ae4f2769ff2cb29db7eab79948585604051611347929190615b45565b60405180910390a35050600090815260576020526040902054919050565b61136f3382613978565b61137857600080fd5b6111818383836139d7565b6050602052816000526040600020818154811061111157600080fd5b600d546001600160a01b031633146113b357565b603d54603c60006113c2612343565b8152602081019190915260400160002055565b604360205260009081526040902080546113ee90615c46565b80601f016020809104026020016040519081016040528092919081815260200182805461141a90615c46565b80156114675780601f1061143c57610100808354040283529160200191611467565b820191906000526020600020905b81548152906001019060200180831161144a57829003601f168201915b505050505081565b604e602052816000526040600020818154811061111157600080fd5b6015546001600160a01b0316331461149f57565b600b546040805163adbf1fb960e01b815290516000926001600160a01b03169163adbf1fb9916004808301926020929190829003018186803b1580156114e457600080fd5b505afa1580156114f8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061151c9190615573565b905060005b6009811015611631576115348282613b28565b611556816040518060600160405280603c8152602001615d2e603c9139613b42565b601f5461156f9082906001600160a01b03166004613b73565b6001600160a01b03828116600090815260066020908152604080832080546001810182559084529190922001839055601f549051633b36259960e21b81526004810184905291169063ecd896649060240160206040518083038186803b1580156115d857600080fd5b505afa1580156115ec573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061161091906159b9565b6000828152605860205260409020558061162981615c81565b915050611521565b5050565b6000602b54602a546028546111c59190615be4565b611181838383604051806020016040528060008152506125ae565b61166e33611b23565b61167757611af0565b600060076000601460009054906101000a90046001600160a01b03166001600160a01b03166374d6abcb6040518163ffffffff1660e01b815260040160206040518083038186803b1580156116cb57600080fd5b505afa1580156116df573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117039190615573565b6001600160a01b03168152602081019190915260400160002054905082156117285750815b80611731575060085b603954811061173f57600080fd5b6025546028546040516323b872dd60e01b81526001600160a01b03909216916323b872dd916117749133913091600401615ab8565b602060405180830381600087803b15801561178e57600080fd5b505af11580156117a2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117c69190615760565b50600f546029546040516323b872dd60e01b81526001600160a01b03909216916323b872dd916117fc9133913091600401615ab8565b602060405180830381600087803b15801561181657600080fd5b505af115801561182a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061184e9190615760565b5061185833611f45565b600061186333611ddd565b11156118df576014543360009081526007602052604090819020549051635354b0f160e11b81526001600160a01b039092169163a6a961e2916118ac9160040190815260200190565b600060405180830381600087803b1580156118c657600080fd5b505af11580156118da573d6000803e3d6000fd5b505050505b60006118ea60395490565b90506118fa603980546001019055565b6119043382613b28565b61190e8187613b42565b6000818152603f602090815260409091208a5161192d928c019061545f565b506000818152604060208181529120895161194a928b019061545f565b506000818152604160209081526040909120885161196a928a019061545f565b5060008181526057602090815260408083208590556043825290912086516119949288019061545f565b50600081815260426020908152604080832086905533835260079091529020546119cb573360009081526007602052604090208190555b336000908152600660209081526040822080546001810182559083529120018190556119f56145bf565b6119fd6146e0565b611a05614792565b611a1181836001614844565b605d6000611a1d612343565b81526020019081526020016000206000815480929190611a3c90615c81565b9091555050336000908152605e602052604090205460ff16611a8657336000908152605e60205260408120805460ff191660011790556008805491611a8083615c81565b91905055505b6040513390611a96908990615a59565b60405180910390208a604051611aac9190615a59565b60405180910390207f348a2af61c3e551d263eaa8735e61cbaaa4dbbcde2de30c684dd75431e5088d78442604051611ae5929190615b45565b60405180910390a450505b50505050505050565b611b0233612812565b611b095750565b603855565b611b1733612812565b611b1e5750565b602f55565b600e546000906001600160a01b0316633c992a9183611061612343565b6015546001600160a01b03163314611b5757505050565b6000838152605460205260409081902090516332bedefb60e21b81528291906001600160a01b0383169063cafb7bec90611b979088908890600401615b45565b60206040518083038186803b158015611baf57600080fd5b505afa158015611bc3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611be791906159b9565b8154600181018355600092835260209092209091015550505050565b611c0c33612812565b611c135750565b611c2581670de0b6b3a7640000615be4565b60285550565b600e54604051632c02ef0360e11b81526000916001600160a01b031690635805de069061107e908590600401615aa4565b6015546001600160a01b03163314611c7357505050565b6000838152604e60205260409081902090516307d5656960e31b81528291906001600160a01b03831690633eab2b4890611b979088908890600401615b45565b600f546040516370a0823160e01b81526000916001600160a01b0316906370a0823190611ce4908590600401615aa4565b60206040518083038186803b158015611cfc57600080fd5b505afa158015611d10573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f8791906159b9565b611d3d33612812565b611d445750565b603355565b6055602052816000526040600020818154811061111157600080fd5b600080611d71836148a9565b90506001600160a01b038116610f8757600080fd5b6015546001600160a01b03163314611d9d57505050565b6000838152605360205260409081902090516308a1f67f60e01b81528291906001600160a01b038316906308a1f67f90611b979088908890600401615b45565b60006001600160a01b038216611df257600080fd5b506001600160a01b031660009081526003602052604090205490565b6052602052816000526040600020818154811061111157600080fd5b611e326148c4565b611e3c60006148e0565b565b60006037546036546029546111c59190615be4565b600b54604051631ba10c1560e31b81526001600160a01b039091169063dd0860a890611e83903390600401615aa4565b60206040518083038186803b158015611e9b57600080fd5b505afa158015611eaf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ed39190615760565b611f235760405162461bcd60e51b815260206004820152601d60248201527f4f6e6c79206f776e65722063616e20736574204164647265737365732e000000604482015260640160405180910390fd5b600b80546001600160a01b0319166001600160a01b0392909216919091179055565b6001600160a01b0381166000908152603e602052604090205460ff1615611fc5576001600160a01b0381166000908152605f6020526040812054603d805491929091611f92908490615c03565b90915550506001600160a01b0381166000908152605f60209081526040808320839055603e9091529020805460ff191690555b611fce81611c2b565b1561202c57611fdc81611ddd565b603d6000828254611fed9190615baa565b90915550611ffc905081611ddd565b6001600160a01b0382166000908152605f6020908152604080832093909355603e905220805460ff191660011790555b50565b61203833612812565b61203f5750565b61205181670de0b6b3a7640000615be4565b60295550565b6006602052816000526040600020818154811061111157600080fd5b61207c33612812565b6120835750565b603155565b604160205260009081526040902080546113ee90615c46565b60006035546034546029546111c59190615be4565b6051602052816000526040600020818154811061111157600080fd5b6015546001600160a01b031633146120e957505050565b60008381526051602052604090819020905163895694bf60e01b81528291906001600160a01b0383169063895694bf90611b979088908890600401615b45565b6000602f54602e546028546111c59190615be4565b600a546001600160a01b031690565b604f602052816000526040600020818154811061111157600080fd5b61217233612812565b6121795750565b602c55565b606060018054610f9c90615c46565b6015546001600160a01b031633146121a457612264565b6121af838383613b73565b6001600160a01b0384166000908152600660209081526040822080546001810182559083529120018390556121e384611ddd565b6121fd57600880549060006121f783615c81565b91905055505b6122078484613b28565b6001600160a01b038416600090815260076020526040902054612240576001600160a01b03841660009081526007602052604090208390555b61224984611c2b565b1561226457603d805490600061225e83615c81565b91905055505b50505050565b60006111cf60395490565b6015546001600160a01b0316331461228c57505050565b6000838152604f60205260409081902090516348edffbd60e11b81528291906001600160a01b038316906391dbff7a90611b979088908890600401615b45565b611631338383614932565b6122e033612812565b6122e75750565b603755565b6015546001600160a01b0316331461230357505050565b6000838152605260205260409081902090516370c7038360e01b81528291906001600160a01b038316906370c7038390611b979088908890600401615b45565b600d5460408051636190c9d560e01b815290516000926001600160a01b031691636190c9d5916004808301926020929190829003018186803b15801561238857600080fd5b505afa15801561239c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111cf91906159b9565b60006033546032546029546111c59190615be4565b6123de33611b23565b158061240457506123ee81611d65565b6001600160a01b0316336001600160a01b031614155b1561240e57612264565b6025546028546040516323b872dd60e01b81526001600160a01b03909216916323b872dd916124439133913091600401615ab8565b602060405180830381600087803b15801561245d57600080fd5b505af1158015612471573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124959190615760565b50600f546029546040516323b872dd60e01b81526001600160a01b03909216916323b872dd916124cb9133913091600401615ab8565b602060405180830381600087803b1580156124e557600080fd5b505af11580156124f9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061251d9190615760565b506000818152603f60209081526040909120855161253d9287019061545f565b506000818152604060208181529120845161255a9286019061545f565b506000818152604160209081526040909120835161257a9285019061545f565b506125836145bf565b61258b6146e0565b612593614792565b60008181526057602052604081205461226491839190614844565b6125b83383613978565b6125c157600080fd5b612264848484846149be565b6015546001600160a01b031633146125e457505050565b600083815260556020526040908190209051630c69bbfb60e31b81528291906001600160a01b0383169063634ddfd890611b979088908890600401615b45565b61262d33612812565b6126345750565b602b55565b6060610f87826149de565b6015546001600160a01b0316331461265b57505050565b60008381526056602052604090819020905163eb20feab60e01b81528291906001600160a01b0383169063eb20feab90611b979088908890600401615b45565b6054602052816000526040600020818154811061111157600080fd5b6126c033612812565b6126c75750565b602e55565b6000602d54602c546028546111c59190615be4565b6126ea33612812565b6126f15750565b602d55565b603954811115612704575050565b61270d81611d65565b6001600160a01b0316336001600160a01b03161415801561273957506015546001600160a01b03163314155b15612742575050565b6001600160a01b03821660009081526007602052604090819020805490839055905133907ff1920cbfe18dff4155b1e46e979d6d82313c39f04760c14fbb8f37a21b36e912906127959084908690615b45565b60405180910390a2505050565b6015546001600160a01b031633146127b957505050565b6000838152605060205260409081902090516314bf03b160e11b81528291906001600160a01b0383169063297e076290611b979088908890600401615b45565b603f60205260009081526040902080546113ee90615c46565b600b54604051631ba10c1560e31b81526000916001600160a01b03169063dd0860a89061107e908590600401615aa4565b604060208190526000918252902080546113ee90615c46565b61286533612812565b61286c5750565b603055565b6001600160a01b03918216600090815260056020908152604080832093909416825291909152205460ff1690565b6056602052816000526040600020818154811061111157600080fd5b6128c433612812565b6128cb5750565b603255565b6128d86148c4565b6001600160a01b0381166128eb57600080fd5b61202c816148e0565b600b54604080516307578a5960e41b815290516000926001600160a01b031691637578a590916004808301926020929190829003018186803b15801561293957600080fd5b505afa15801561294d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061297191906159b9565b905080600c5414156129805750565b600c819055600b5460408051635f9ca54b60e11b815290516001600160a01b039092169163bf394a9691600480820192602092909190829003018186803b1580156129ca57600080fd5b505afa1580156129de573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a029190615573565b600d80546001600160a01b0319166001600160a01b03928316179055600b5460408051632d311b9f60e11b815290519190921691635a62373e916004808301926020929190829003018186803b158015612a5b57600080fd5b505afa158015612a6f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a939190615573565b600e80546001600160a01b0319166001600160a01b03928316179055600b5460408051632fc69b7560e21b81529051919092169163bf1a6dd4916004808301926020929190829003018186803b158015612aec57600080fd5b505afa158015612b00573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b249190615573565b600f80546001600160a01b0319166001600160a01b03928316179055600b5460408051635d27d82160e01b815290519190921691635d27d821916004808301926020929190829003018186803b158015612b7d57600080fd5b505afa158015612b91573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612bb59190615573565b601080546001600160a01b0319166001600160a01b03928316179055600b5460408051633739851960e21b81529051919092169163dce61464916004808301926020929190829003018186803b158015612c0e57600080fd5b505afa158015612c22573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c469190615573565b601180546001600160a01b0319166001600160a01b03928316179055600b546040805163fdf6c44360e01b81529051919092169163fdf6c443916004808301926020929190829003018186803b158015612c9f57600080fd5b505afa158015612cb3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612cd79190615573565b601280546001600160a01b0319166001600160a01b03928316179055600b54604080516353962e4560e11b81529051919092169163a72c5c8a916004808301926020929190829003018186803b158015612d3057600080fd5b505afa158015612d44573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d689190615573565b601380546001600160a01b0319166001600160a01b03928316179055600b546040805163e93b941b60e01b81529051919092169163e93b941b916004808301926020929190829003018186803b158015612dc157600080fd5b505afa158015612dd5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612df99190615573565b601480546001600160a01b0319166001600160a01b03928316179055600b5460408051631567ac7560e31b81529051919092169163ab3d63a8916004808301926020929190829003018186803b158015612e5257600080fd5b505afa158015612e66573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e8a9190615573565b601580546001600160a01b0319166001600160a01b03928316179055600b5460408051635ffb42d960e11b81529051919092169163bff685b2916004808301926020929190829003018186803b158015612ee357600080fd5b505afa158015612ef7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f1b9190615573565b601680546001600160a01b0319166001600160a01b03928316179055600b5460408051630bbc5d7d60e31b815290519190921691635de2ebe8916004808301926020929190829003018186803b158015612f7457600080fd5b505afa158015612f88573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612fac9190615573565b601780546001600160a01b0319166001600160a01b03928316179055600b546040805163c586261f60e01b81529051919092169163c586261f916004808301926020929190829003018186803b15801561300557600080fd5b505afa158015613019573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061303d9190615573565b601880546001600160a01b0319166001600160a01b03928316179055600b5460408051637abb1b7360e01b815290519190921691637abb1b73916004808301926020929190829003018186803b15801561309657600080fd5b505afa1580156130aa573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130ce9190615573565b601980546001600160a01b0319166001600160a01b03928316179055600b5460408051633dd5259d60e01b815290519190921691633dd5259d916004808301926020929190829003018186803b15801561312757600080fd5b505afa15801561313b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061315f9190615573565b601a80546001600160a01b0319166001600160a01b03928316179055600b546040805163967db45360e01b81529051919092169163967db453916004808301926020929190829003018186803b1580156131b857600080fd5b505afa1580156131cc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131f09190615573565b601b80546001600160a01b0319166001600160a01b03928316179055600b54604080516310fced2960e01b8152905191909216916310fced29916004808301926020929190829003018186803b15801561324957600080fd5b505afa15801561325d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132819190615573565b601c80546001600160a01b0319166001600160a01b03928316179055600b5460408051634cfd0abf60e11b8152905191909216916399fa157e916004808301926020929190829003018186803b1580156132da57600080fd5b505afa1580156132ee573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133129190615573565b601d80546001600160a01b0319166001600160a01b03928316179055600b5460408051636164467360e01b8152905191909216916361644673916004808301926020929190829003018186803b15801561336b57600080fd5b505afa15801561337f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133a39190615573565b601e80546001600160a01b0319166001600160a01b03928316179055600b5460408051631b7410a160e31b81529051919092169163dba08508916004808301926020929190829003018186803b1580156133fc57600080fd5b505afa158015613410573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134349190615573565b601f80546001600160a01b0319166001600160a01b03928316179055600b546040805163484de36360e11b81529051919092169163909bc6c6916004808301926020929190829003018186803b15801561348d57600080fd5b505afa1580156134a1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134c59190615573565b602080546001600160a01b0319166001600160a01b03928316178155600b5460408051633310a27960e01b815290519190931692633310a279926004808301939192829003018186803b15801561351b57600080fd5b505afa15801561352f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135539190615573565b602180546001600160a01b0319166001600160a01b03928316179055600b5460408051633c9d0ac360e11b81529051919092169163793a1586916004808301926020929190829003018186803b1580156135ac57600080fd5b505afa1580156135c0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135e49190615573565b602280546001600160a01b0319166001600160a01b03928316179055600b546040805163063606b760e51b81529051919092169163c6c0d6e0916004808301926020929190829003018186803b15801561363d57600080fd5b505afa158015613651573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136759190615573565b602380546001600160a01b0319166001600160a01b03928316179055600b5460408051630ffbb3e960e21b815290519190921691633feecfa4916004808301926020929190829003018186803b1580156136ce57600080fd5b505afa1580156136e2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137069190615573565b602480546001600160a01b0319166001600160a01b03928316179055600b546040805163c27ce47960e01b81529051919092169163c27ce479916004808301926020929190829003018186803b15801561375f57600080fd5b505afa158015613773573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137979190615573565b602580546001600160a01b0319166001600160a01b03928316179055600b54604080516389a3027160e01b8152905191909216916389a30271916004808301926020929190829003018186803b1580156137f057600080fd5b505afa158015613804573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138289190615573565b602680546001600160a01b0319166001600160a01b03928316179055600b546040805163c54e44eb60e01b81529051919092169163c54e44eb916004808301926020929190829003018186803b15801561388157600080fd5b505afa158015613895573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138b99190615573565b602780546001600160a01b0319166001600160a01b039290921691909117905550565b5490565b80546001019055565b6001600160a01b03163b151590565b61390181614ae7565b61202c57600080fd5b600081815260046020526040902080546001600160a01b0319166001600160a01b038416908117909155819061393f82611d65565b6001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b60008061398483611d65565b9050806001600160a01b0316846001600160a01b031614806139ab57506139ab8185612871565b806139cf5750836001600160a01b03166139c4846110ce565b6001600160a01b0316145b949350505050565b826001600160a01b03166139ea82611d65565b6001600160a01b0316146139fd57600080fd5b6001600160a01b038216613a1057600080fd5b826001600160a01b0316613a2382611d65565b6001600160a01b031614613a3657600080fd5b600081815260046020526040902080546001600160a01b0319169055613a5c8184614b04565b6001600160a01b038316600090815260036020526040902080546000190190819055158015613a8d57506000600854115b15613a9d57600880546000190190555b613aa78183614cc5565b6001600160a01b038216600090815260036020526040902054613ace576008805460010190555b6001600160a01b03808316600081815260036020908152604080832080546001019055858352600290915280822080546001600160a01b03191684179055518493871691600080516020615d6a83398151915291a4505050565b611631828260405180602001604052806000815250614d19565b613b4b82614ae7565b613b5457600080fd5b600082815260096020908152604090912082516111819284019061545f565b604051636d3a6ba160e11b815260048101849052829081906001600160a01b0382169063da74d7429060240160006040518083038186803b158015613bb757600080fd5b505afa158015613bcb573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052613bf391908101906157b7565b6000868152603f602090815260409091208251613c16939192919091019061545f565b506040516370d5517d60e11b8152600481018690526001600160a01b0383169063e1aaa2fa9060240160006040518083038186803b158015613c5757600080fd5b505afa158015613c6b573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052613c9391908101906157b7565b60008681526040602081815291208251613cb3939192919091019061545f565b506040516382d706ad60e01b8152600481018690526001600160a01b038316906382d706ad9060240160006040518083038186803b158015613cf457600080fd5b505afa158015613d08573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052613d3091908101906157b7565b60008681526041602090815260409091208251613d53939192919091019061545f565b50604051632994eff760e21b8152600481018690526001600160a01b0383169063a653bfdc9060240160206040518083038186803b158015613d9457600080fd5b505afa158015613da8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613dcc91906159b9565b600086815260426020526040908190209190915551633c1bd08360e01b8152600481018690526001600160a01b03831690633c1bd0839060240160006040518083038186803b158015613e1e57600080fd5b505afa158015613e32573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052613e5a91908101906157b7565b60008681526043602090815260409091208251613e7d939192919091019061545f565b50604051632355e47760e21b8152600481018690526001600160a01b03831690638d5791dc9060240160206040518083038186803b158015613ebe57600080fd5b505afa158015613ed2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ef691906159b9565b6000868152605760205260409081902091909155516336b6593d60e21b8152600481018690526001600160a01b0383169063dad964f49060240160206040518083038186803b158015613f4857600080fd5b505afa158015613f5c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613f8091906159b9565b600086815260446020526040908190209190915551634883705f60e11b8152600481018690526001600160a01b03831690639106e0be9060240160206040518083038186803b158015613fd257600080fd5b505afa158015613fe6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061400a91906159b9565b60008681526045602052604090819020919091555163235e704b60e11b8152600481018690526001600160a01b038316906346bce0969060240160206040518083038186803b15801561405c57600080fd5b505afa158015614070573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061409491906159b9565b60008681526046602052604090819020919091555163110f02a960e21b8152600481018690526001600160a01b0383169063443c0aa49060240160206040518083038186803b1580156140e657600080fd5b505afa1580156140fa573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061411e91906159b9565b600086815260476020526040908190209190915551635616f32560e01b8152600481018690526001600160a01b03831690635616f3259060240160206040518083038186803b15801561417057600080fd5b505afa158015614184573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906141a891906159b9565b60008681526048602052604090819020919091555163706e2dfd60e01b8152600481018690526001600160a01b0383169063706e2dfd9060240160206040518083038186803b1580156141fa57600080fd5b505afa15801561420e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061423291906159b9565b6000868152604960205260409081902091909155516381c9db7760e01b8152600481018690526001600160a01b038316906381c9db779060240160206040518083038186803b15801561428457600080fd5b505afa158015614298573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906142bc91906159b9565b6000868152604a602052604090819020919091555163e1cdc6f760e01b8152600481018690526001600160a01b0383169063e1cdc6f79060240160206040518083038186803b15801561430e57600080fd5b505afa158015614322573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061434691906159b9565b6000868152604b602052604090819020919091555163053294b760e41b8152600481018690526001600160a01b038316906353294b709060240160206040518083038186803b15801561439857600080fd5b505afa1580156143ac573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906143d091906159b9565b6000868152604c60205260409081902091909155516365cf916360e11b8152600481018690526001600160a01b0383169063cb9f22c69060240160206040518083038186803b15801561442257600080fd5b505afa158015614436573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061445a91906159b9565b6000868152604d6020526040902055600483141561451357604051633b36259960e21b8152600481018690526001600160a01b0383169063ecd896649060240160206040518083038186803b1580156144b257600080fd5b505afa1580156144c6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906144ea91906159b9565b60008681526058602052604081208054909190614508908490615baa565b909155506145b89050565b82600b14156145b857604051633549d94b60e11b8152600481018690526001600160a01b03821690636a93b2969060240160206040518083038186803b15801561455c57600080fd5b505afa158015614570573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061459491906159b9565b600086815260586020526040812080549091906145b2908490615baa565b90915550505b5050505050565b600f546019546001600160a01b039182169163a9059cbb91166145e06123c0565b6040518363ffffffff1660e01b81526004016145fd929190615b19565b602060405180830381600087803b15801561461757600080fd5b505af115801561462b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061464f9190615760565b506025546018546001600160a01b039182169163a9059cbb91166146716126cc565b6040518363ffffffff1660e01b815260040161468e929190615b19565b602060405180830381600087803b1580156146a857600080fd5b505af11580156146bc573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061202c9190615760565b600f54601b546001600160a01b039182169163a9059cbb91166147016120a1565b6040518363ffffffff1660e01b815260040161471e929190615b19565b602060405180830381600087803b15801561473857600080fd5b505af115801561474c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906147709190615760565b50602554601a546001600160a01b039182169163a9059cbb9116614671612129565b600f54601d546001600160a01b039182169163a9059cbb91166147b3611e3e565b6040518363ffffffff1660e01b81526004016147d0929190615b19565b602060405180830381600087803b1580156147ea57600080fd5b505af11580156147fe573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906148229190615760565b50602554601c546001600160a01b039182169163a9059cbb91166146716111b0565b60008080614850611635565b9050845b6009831015611af057841561486e5761486e838888614d39565b61487781611d65565b93506148848482846111d4565b60009687526057602052604090962054959050826148a181615c81565b935050614854565b6000908152600260205260409020546001600160a01b031690565b336148cd61213e565b6001600160a01b031614611e3c57600080fd5b600a80546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b816001600160a01b0316836001600160a01b0316141561495157600080fd5b6001600160a01b03838116600081815260056020908152604080832094871680845294825291829020805460ff191686151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a3505050565b6149c98484846139d7565b6149d5848484846150e6565b61226457600080fd5b60606149e9826138f8565b60008281526009602052604081208054614a0290615c46565b80601f0160208091040260200160405190810160405280929190818152602001828054614a2e90615c46565b8015614a7b5780601f10614a5057610100808354040283529160200191614a7b565b820191906000526020600020905b815481529060010190602001808311614a5e57829003601f168201915b505050505090506000614a9960408051602081019091526000815290565b9050805160001415614aac575092915050565b815115614ade578082604051602001614ac6929190615a75565b60405160208183030381529060405292505050919050565b6139cf846151e7565b600080614af3836148a9565b6001600160a01b0316141592915050565b60005b6001600160a01b0382166000908152600660205260409020805484919083908110614b3457614b34615cc8565b906000526020600020015414614b565780614b4e81615c81565b915050614b07565b6001600160a01b038216600090815260036020526040902054614b7b90600190615c03565b811015614c14576001600160a01b0382166000908152600660205260409020614ba5826001615baa565b81548110614bb557614bb5615cc8565b906000526020600020015460066000846001600160a01b03166001600160a01b031681526020019081526020016000208281548110614bf657614bf6615cc8565b60009182526020909120015580614c0c81615c81565b915050614b56565b6001600160a01b0382166000908152600660205260409020805480614c3b57614c3b615cb2565b6000828152602080822083016000199081018390559092019092556001600160a01b0384168252600790526040902054831415611181576001600160a01b03821660009081526006602052604081208054909190614c9b57614c9b615cc8565b60009182526020808320909101546001600160a01b03851683526007909152604090912055505050565b6001600160a01b0381166000818152600660209081526040808320805460018101825590845282842001869055928252600790522054611631576001600160a01b0316600090815260076020526040902055565b614d23838361525b565b614d3060008484846150e6565b61118157600080fd5b82614d9e576000818152604460205260408120805491614d5883615c81565b90915550506000818152604d60205260408120805491614d7783615c81565b90915550506000908152604e60209081526040822080546001810182559083529120015550565b8260011415614e07576000818152604560205260408120805491614dc183615c81565b90915550506000818152604d60205260408120805491614de083615c81565b90915550506000908152604f60209081526040822080546001810182559083529120015550565b8260021415614e70576000818152604660205260408120805491614e2a83615c81565b90915550506000818152604d60205260408120805491614e4983615c81565b90915550506000908152605060209081526040822080546001810182559083529120015550565b8260031415614ed9576000818152604760205260408120805491614e9383615c81565b90915550506000818152604d60205260408120805491614eb283615c81565b90915550506000908152605160209081526040822080546001810182559083529120015550565b8260041415614f42576000818152604860205260408120805491614efc83615c81565b90915550506000818152604d60205260408120805491614f1b83615c81565b90915550506000908152605260209081526040822080546001810182559083529120015550565b8260051415614fab576000818152604960205260408120805491614f6583615c81565b90915550506000818152604d60205260408120805491614f8483615c81565b90915550506000908152605360209081526040822080546001810182559083529120015550565b8260061415615014576000818152604a60205260408120805491614fce83615c81565b90915550506000818152604d60205260408120805491614fed83615c81565b90915550506000908152605460209081526040822080546001810182559083529120015550565b826007141561507d576000818152604b6020526040812080549161503783615c81565b90915550506000818152604d6020526040812080549161505683615c81565b90915550506000908152605560209081526040822080546001810182559083529120015550565b8260081415611181576000818152604c602052604081208054916150a083615c81565b90915550506000818152604d602052604081208054916150bf83615c81565b90915550506000908152605660209081526040822080546001810182559083529120015550565b60006150fa846001600160a01b03166138e9565b156151dc57604051630a85bd0160e11b81526001600160a01b0385169063150b7a0290615131903390899088908890600401615adc565b602060405180830381600087803b15801561514b57600080fd5b505af192505050801561517b575060408051601f3d908101601f191682019092526151789181019061579a565b60015b6151c2573d8080156151a9576040519150601f19603f3d011682016040523d82523d6000602084013e6151ae565b606091505b5080516151ba57600080fd5b805181602001fd5b6001600160e01b031916630a85bd0160e11b1490506139cf565b506001949350505050565b60606151f2826138f8565b600061520960408051602081019091526000815290565b905060008151116152295760405180602001604052806000815250615254565b80615233846152ed565b604051602001615244929190615a75565b6040516020818303038152906040525b9392505050565b6001600160a01b03821661526e57600080fd5b61527781614ae7565b1561528157600080fd5b61528a81614ae7565b1561529457600080fd5b6001600160a01b038216600081815260036020908152604080832080546001019055848352600290915280822080546001600160a01b031916841790555183929190600080516020615d6a833981519152908290a45050565b606060006152fa83615389565b60010190506000816001600160401b0381111561531957615319615cde565b6040519080825280601f01601f191660200182016040528015615343576020820181803683370190505b5090508181016020015b600019016f181899199a1a9b1b9c1cb0b131b232b360811b600a86061a8153600a850494508461537c57615381565b61534d565b509392505050565b60008072184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b83106153c85772184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b830492506040015b6904ee2d6d415b85acef8160201b83106153f2576904ee2d6d415b85acef8160201b830492506020015b662386f26fc10000831061541057662386f26fc10000830492506010015b6305f5e1008310615428576305f5e100830492506008015b612710831061543c57612710830492506004015b6064831061544e576064830492506002015b600a8310610f875760010192915050565b82805461546b90615c46565b90600052602060002090601f01602090048101928261548d57600085556154d3565b82601f106154a657805160ff19168380011785556154d3565b828001600101855582156154d3579182015b828111156154d35782518255916020019190600101906154b8565b506154df9291506154e3565b5090565b5b808211156154df57600081556001016154e4565b600061550b61550684615b83565b615b53565b905082815283838301111561551f57600080fd5b828260208301376000602084830101529392505050565b600082601f83011261554757600080fd5b615254838335602085016154f8565b60006020828403121561556857600080fd5b813561525481615cf4565b60006020828403121561558557600080fd5b815161525481615cf4565b600080604083850312156155a357600080fd5b82356155ae81615cf4565b915060208301356155be81615cf4565b809150509250929050565b6000806000606084860312156155de57600080fd5b83356155e981615cf4565b925060208401356155f981615cf4565b929592945050506040919091013590565b6000806000806080858703121561562057600080fd5b843561562b81615cf4565b9350602085013561563b81615cf4565b92506040850135915060608501356001600160401b0381111561565d57600080fd5b8501601f8101871361566e57600080fd5b61567d878235602084016154f8565b91505092959194509250565b6000806040838503121561569c57600080fd5b82356156a781615cf4565b915060208301356155be81615d09565b600080604083850312156156ca57600080fd5b82356156d581615cf4565b946020939093013593505050565b600080600080608085870312156156f957600080fd5b843561570481615cf4565b935060208501359250604085013561571b81615cf4565b9396929550929360600135925050565b60008060006060848603121561574057600080fd5b833561574b81615cf4565b95602085013595506040909401359392505050565b60006020828403121561577257600080fd5b815161525481615d09565b60006020828403121561578f57600080fd5b813561525481615d17565b6000602082840312156157ac57600080fd5b815161525481615d17565b6000602082840312156157c957600080fd5b81516001600160401b038111156157df57600080fd5b8201601f810184136157f057600080fd5b80516157fe61550682615b83565b81815285602083850101111561581357600080fd5b615824826020830160208601615c1a565b95945050505050565b600080600080600080600060e0888a03121561584857600080fd5b87356001600160401b038082111561585f57600080fd5b61586b8b838c01615536565b985060208a013591508082111561588157600080fd5b61588d8b838c01615536565b975060408a01359150808211156158a357600080fd5b6158af8b838c01615536565b965060608a01359150808211156158c557600080fd5b6158d18b838c01615536565b955060808a01359150808211156158e757600080fd5b506158f48a828b01615536565b93505060a0880135915060c0880135905092959891949750929550565b6000806000806080858703121561592757600080fd5b84356001600160401b038082111561593e57600080fd5b61594a88838901615536565b9550602087013591508082111561596057600080fd5b61596c88838901615536565b9450604087013591508082111561598257600080fd5b5061598f87828801615536565b949793965093946060013593505050565b6000602082840312156159b257600080fd5b5035919050565b6000602082840312156159cb57600080fd5b5051919050565b600080604083850312156159e557600080fd5b50508035926020909101359150565b600080600060608486031215615a0957600080fd5b83359250602084013591506040840135615a2281615cf4565b809150509250925092565b60008151808452615a45816020860160208601615c1a565b601f01601f19169290920160200192915050565b60008251615a6b818460208701615c1a565b9190910192915050565b60008351615a87818460208801615c1a565b835190830190615a9b818360208801615c1a565b01949350505050565b6001600160a01b0391909116815260200190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6001600160a01b0385811682528416602082015260408101839052608060608201819052600090615b0f90830184615a2d565b9695505050505050565b6001600160a01b03929092168252602082015260400190565b6020815260006152546020830184615a2d565b918252602082015260400190565b604051601f8201601f191681016001600160401b0381118282101715615b7b57615b7b615cde565b604052919050565b60006001600160401b03821115615b9c57615b9c615cde565b50601f01601f191660200190565b60008219821115615bbd57615bbd615c9c565b500190565b600082615bdf57634e487b7160e01b600052601260045260246000fd5b500490565b6000816000190483118215151615615bfe57615bfe615c9c565b500290565b600082821015615c1557615c15615c9c565b500390565b60005b83811015615c35578181015183820152602001615c1d565b838111156122645750506000910152565b600181811c90821680615c5a57607f821691505b60208210811415615c7b57634e487b7160e01b600052602260045260246000fd5b50919050565b6000600019821415615c9557615c95615c9c565b5060010190565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052603160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b038116811461202c57600080fd5b801515811461202c57600080fd5b6001600160e01b03198116811461202c57600080fdfe697066733a2f2f516d5269314476674475367a414a7770625552474e424251544d38325a434e5a4179546b454172624b5a4b6d31552f302e6a736f6eddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa2646970667358221220868a46f6c1de263806a361876d5eb1974f5af51d22d0d93939ad068c17c9105e64736f6c63430008070033

Deployed Bytecode Sourcemap

178059:29235:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;198842:144;;;;;;:::i;:::-;;:::i;:::-;;31079:321;;;;;;:::i;:::-;;:::i;:::-;;;15849:14:1;;15842:22;15824:41;;15812:2;15797:18;31079:321:0;;;;;;;;178785:51;;;;;;;;;17279:25:1;;;17267:2;17252:18;178785:51:0;17133:177:1;31982:100:0;;;:::i;:::-;;;;;;;:::i;192142:219::-;;;;;;:::i;:::-;;:::i;33418:187::-;;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;182656:61::-;;;;;;:::i;:::-;;:::i;33075:277::-;;;;;;:::i;:::-;;:::i;197014:177::-;;;;;;:::i;:::-;;:::i;30695:28::-;;;;;;183133:43;;;;;;196842:164;;;;;;:::i;:::-;;:::i;195630:217::-;;;:::i;178468:54::-;;;;;;201462:879;;;;;;:::i;:::-;;:::i;34184:286::-;;;;;;:::i;:::-;;:::i;182448:63::-;;;;;;:::i;:::-;;:::i;193178:151::-;;;:::i;179665:30::-;;;;;;181251:51;;;;;;:::i;:::-;;:::i;182307:63::-;;;;;;:::i;:::-;;:::i;192369:801::-;;;:::i;195855:137::-;;;:::i;34541:185::-;;;;;;:::i;:::-;;:::i;178734:44::-;;;;;;181643:66;;;;;;:::i;:::-;;;;;;;;;;;;;;188914:2322;;;;;;:::i;:::-;;:::i;181569:67::-;;;;;;:::i;:::-;;;;;;;;;;;;;;199467:171;;;;;;:::i;:::-;;:::i;178964:49::-;;;;;;198201:187;;;;;;:::i;:::-;;:::i;193947:132::-;;;;;;:::i;:::-;;:::i;187519:454::-;;;;;;:::i;:::-;;:::i;199174:139::-;;;;;;:::i;:::-;;:::i;182009:66::-;;;;;;:::i;:::-;;;;;;;;;;;;;;181716;;;;;;:::i;:::-;;;;;;;;;;;;;;194087:123;;;;;;:::i;:::-;;:::i;184756:454::-;;;;;;:::i;:::-;;:::i;179702:37::-;;;;;;65027:28;;;;;-1:-1:-1;;;;;65027:28:0;;;194240:118;;;;;;:::i;:::-;;:::i;183248:52::-;;;;;;:::i;:::-;;;;;;;;;;;;;;196635:169;;;;;;:::i;:::-;;:::i;182794:63::-;;;;;;:::i;:::-;;:::i;31704:211::-;;;;;;:::i;:::-;;:::i;187063:448::-;;;;;;:::i;:::-;;:::i;183367:56::-;;;;;;:::i;:::-;;;;;;;;;;;;;;181789:65;;;;;;:::i;:::-;;;;;;;;;;;;;;31464:178;;;;;;:::i;:::-;;:::i;182587:62::-;;;;;;:::i;:::-;;:::i;50585:103::-;;;:::i;194984:204::-;;;:::i;64929:31::-;;;;;;183448:41;;;;;;:::i;:::-;;;;;;;;;;;;;;;;68215:171;;;;;;:::i;:::-;;:::i;193337:469::-;;;;;;:::i;:::-;;:::i;178401:58::-;;;;;;199321:138;;;;;;:::i;:::-;;:::i;183307:53::-;;;;;;:::i;:::-;;;;;;;;;;;;;;181861:67;;;;;;:::i;:::-;;;;;;;;;;;;;;30585:48;;;;;;:::i;:::-;;:::i;198617:189::-;;;;;;:::i;:::-;;:::i;181121:56::-;;;;;;:::i;:::-;;:::i;180921:29::-;;;;;;194775:201;;;:::i;182518:62::-;;;;;;:::i;:::-;;:::i;66002:17::-;;;;;-1:-1:-1;;;;;66002:17:0;;;179501:53;;;;;;186145:451;;;;;;:::i;:::-;;:::i;195408:214::-;;;:::i;182965:52::-;;;;;;:::i;:::-;;;;;;;;;;;;;;178143:31;;;;;;50000:87;;;:::i;181494:68::-;;;;;;:::i;:::-;;;;;;;;;;;;;;182377:64;;;;;;:::i;:::-;;:::i;197622:166::-;;;;;;:::i;:::-;;:::i;178900:55::-;;;;;;32151:104;;;:::i;184106:642::-;;;;;;:::i;:::-;;:::i;194463:107::-;;;:::i;179020:56::-;;;;;;185218:457;;;;;;:::i;:::-;;:::i;178294:41::-;;;;;;33677:180;;;;;;:::i;:::-;;:::i;178254:33::-;;;;;;180856:58;;;;;;:::i;:::-;;;;;;;;;;;;;;181184:60;;;;;;:::i;:::-;;;;;;;;;;;;;;197403:179;;;;;;:::i;:::-;;:::i;65164:33::-;;;;;-1:-1:-1;;;;;65164:33:0;;;178344:50;;;;;;186604:451;;;;;;:::i;:::-;;:::i;194366:89::-;;;:::i;194578:189::-;;;:::i;191244:890::-;;;;;;:::i;:::-;;:::i;34797:273::-;;;;;;:::i;:::-;;:::i;187981:454::-;;;;;;:::i;:::-;;:::i;30640:48::-;;;;;;:::i;:::-;;;;;;;;;;;;;;65972:23;;;;;-1:-1:-1;;;;;65972:23:0;;;198994:157;;;;;;:::i;:::-;;:::i;178181:36::-;;;;;;66026:17;;;;;-1:-1:-1;;;;;66026:17:0;;;180957:44;;;;;;:::i;:::-;;;;;;;;;;;;;;;;207120:171;;;;;;:::i;:::-;;:::i;188443:451::-;;;;;;:::i;:::-;;:::i;182724:63::-;;;;;;:::i;:::-;;:::i;182084:62::-;;;;;;:::i;:::-;;;;;;;;;;;;;;198019:174;;;;;;:::i;:::-;;:::i;178600:55::-;;;;;;195196:204;;;:::i;183511:57::-;;;;;;:::i;:::-;;;;;;;;;;;;;;197796:179;;;;;;:::i;:::-;;:::i;196022:397::-;;;;;;:::i;:::-;;:::i;185683:454::-;;;;;;:::i;:::-;;:::i;181008:46::-;;;;;;:::i;:::-;;:::i;181420:67::-;;;;;;:::i;:::-;;;;;;;;;;;;;;193830:109;;;;;;:::i;:::-;;:::i;181061:53::-;;;;;;:::i;:::-;;:::i;181935:67::-;;;;;;:::i;:::-;;;;;;;;;;;;;;183183:58;;;;;;:::i;:::-;;;;;;;;;;;;;;198433:176;;;;;;:::i;:::-;;:::i;33928:189::-;;;;;;:::i;:::-;;:::i;182864:62::-;;;;;;:::i;:::-;;:::i;178529:::-;;;;;;183042:63;;;;;;:::i;:::-;;;;;;;;;;;;;;196473:154;;;;;;:::i;:::-;;:::i;50836:159::-;;;;;;:::i;:::-;;:::i;178662:63::-;;;;;;66052:2155;;;:::i;178845:48::-;;;;;;198842:144;198917:19;198925:10;198917:7;:19::i;:::-;198912:33;;198842:144;:::o;198912:33::-;198955:14;:23;198842:144::o;31079:321::-;31197:4;-1:-1:-1;;;;;;31234:40:0;;-1:-1:-1;;;31234:40:0;;:105;;-1:-1:-1;;;;;;;31291:48:0;;-1:-1:-1;;;31291:48:0;31234:105;:158;;;-1:-1:-1;;;;;;;;;;29839:40:0;;;31356:36;31214:178;31079:321;-1:-1:-1;;31079:321:0:o;31982:100::-;32036:13;32069:5;32062:12;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;31982:100;:::o;192142:219::-;192205:4;192242:29;192254:16;192262:7;192254;:16::i;192242:29::-;:111;;;-1:-1:-1;192288:13:0;;-1:-1:-1;;;;;192288:13:0;:31;192320:16;192328:7;192320;:16::i;:::-;192351:1;192338:10;:8;:10::i;:::-;:14;;;;:::i;:::-;192288:65;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;33418:187::-;33510:7;33530:23;33545:7;33530:14;:23::i;:::-;-1:-1:-1;33573:24:0;;;;:15;:24;;;;;;-1:-1:-1;;;;;33573:24:0;;33418:187::o;182656:61::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;33075:277::-;33156:13;33172:23;33187:7;33172:14;:23::i;:::-;33156:39;;33220:5;-1:-1:-1;;;;;33214:11:0;:2;-1:-1:-1;;;;;33214:11:0;;;33206:20;;;;;;26114:10;-1:-1:-1;;;;;33247:21:0;;;;:62;;-1:-1:-1;33272:37:0;33289:5;26114:10;33928:189;:::i;33272:37::-;33239:71;;;;;;33323:21;33332:2;33336:7;33323:8;:21::i;:::-;33145:207;33075:277;;:::o;197014:177::-;197098:19;197106:10;197098:7;:19::i;:::-;197093:33;;197014:177;:::o;197093:33::-;197138:36;:45;197014:177::o;196842:164::-;196920:19;196928:10;196920:7;:19::i;:::-;196915:33;;196842:164;:::o;196915:33::-;196960:29;:38;196842:164::o;195630:217::-;195694:7;195796:43;;195743:36;;195735:5;;:44;;;;:::i;:::-;195734:105;;;;:::i;:::-;195714:125;;195630:217;:::o;201462:879::-;201597:7;201617:260;201696:34;;201638:42;;;;:32;:42;;;;;;:92;:122;;;;-1:-1:-1;201747:13:0;;;201638:122;201617:260;;;201798:27;;;;:17;:27;;;;;;;;201848:17;201798:27;201848:7;:17::i;:::-;201840:25;;201617:260;;;201889:10;;:41;;-1:-1:-1;;;201889:41:0;;-1:-1:-1;;;;;201889:10:0;;;;:19;;:41;;201909:5;;201916:13;;201889:41;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;;201943:30:0;;;;;;:23;:30;;;;;:47;;201977:13;;201943:30;:47;;201977:13;;201943:47;:::i;:::-;;;;-1:-1:-1;;202001:38:0;;;;:28;:38;;;;;:55;;202043:13;;202001:38;:55;;202043:13;;202001:55;:::i;:::-;;;;;;;;202099:13;202067:28;;:45;;;;;;;:::i;:::-;;;;;;;;202260:15;202240:5;-1:-1:-1;;;;;202154:132:0;;202189:8;202212:13;202154:132;;;;;;;:::i;:::-;;;;;;;;-1:-1:-1;;202306:27:0;;;;:17;:27;;;;;;;201462:879;-1:-1:-1;201462:879:0:o;34184:286::-;34379:41;26114:10;34412:7;34379:18;:41::i;:::-;34371:50;;;;;;34434:28;34444:4;34450:2;34454:7;34434:9;:28::i;182448:63::-;;;;;;;;;;;;;;;;;;;;193178:151;193244:5;;-1:-1:-1;;;;;193244:5:0;193222:10;:28;193218:41;;193178:151::o;193218:41::-;193307:14;;193269:23;:35;193293:10;:8;:10::i;:::-;193269:35;;;;;;;;;;;-1:-1:-1;193269:35:0;:52;193178:151::o;181251:51::-;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;182307:63::-;;;;;;;;;;;;;;;;;;;;192369:801;192442:16;;-1:-1:-1;;;;;192442:16:0;192420:10;:39;192416:52;;192369:801::o;192416:52::-;192644:7;;:26;;;-1:-1:-1;;;192644:26:0;;;;192617:24;;-1:-1:-1;;;;;192644:7:0;;:24;;:26;;;;;;;;;;;;;;:7;:26;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;192617:53;;192686:9;192681:482;192705:1;192701;:5;192681:482;;;192728:30;192738:16;192756:1;192728:9;:30::i;:::-;192773:128;192804:1;192773:128;;;;;;;;;;;;;;;;;:12;:128::i;:::-;192941:26;;192918:54;;192930:1;;-1:-1:-1;;;;;192941:26:0;192970:1;192918:11;:54::i;:::-;-1:-1:-1;;;;;192989:29:0;;;;;;;:11;:29;;;;;;;;:37;;;;;;;;;;;;;;;;;;193075:26;;:76;;-1:-1:-1;;;193075:76:0;;;;;17279:25:1;;;193075:26:0;;;:73;;17252:18:1;;193075:76:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;193041:31;;;;:28;:31;;;;;:110;193070:1;192708:3;193070:1;192708:3;:::i;:::-;;;;192681:482;;;;192405:765;192369:801::o;195855:137::-;195909:7;195963:21;;195945:14;;195937:5;;:22;;;;:::i;34541:185::-;34679:39;34696:4;34702:2;34706:7;34679:39;;;;;;;;;;;;:16;:39::i;188914:2322::-;189184:21;189194:10;189184:9;:21::i;:::-;189179:35;;189207:7;;189179:35;189252:18;189273:13;:82;189301:8;;;;;;;;;-1:-1:-1;;;;;189301:8:0;-1:-1:-1;;;;;189301:41:0;;:43;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;189273:82:0;;;;;;;;;;;;-1:-1:-1;189273:82:0;;;-1:-1:-1;189372:9:0;;189368:59;;-1:-1:-1;189411:4:0;189368:59;189443:15;189439:62;;-1:-1:-1;189488:1:0;189439:62;189534:9;25531:14;189521:10;:32;189513:41;;;;;;189567:10;;189618:5;;189567:57;;-1:-1:-1;;;189567:57:0;;-1:-1:-1;;;;;189567:10:0;;;;:23;;:57;;189591:10;;189611:4;;189567:57;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;189635:4:0;;189680:10;;189635:56;;-1:-1:-1;;;189635:56:0;;-1:-1:-1;;;;;189635:4:0;;;;:17;;:56;;189653:10;;189673:4;;189635:56;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;189704:31;189724:10;189704:19;:31::i;:::-;189776:1;189752:21;189762:10;189752:9;:21::i;:::-;:25;189748:103;;;189794:8;;189827:10;189794:8;189813:25;;;:13;:25;;;;;;;;189794:45;;-1:-1:-1;;;189794:45:0;;-1:-1:-1;;;;;189794:8:0;;;;:18;;:45;;;;17279:25:1;;;17267:2;17252:18;;17133:177;189794:45:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;189748:103;189884:15;189902:19;:9;25531:14;;25439:114;189902:19;189884:37;;189932:21;:9;25650:19;;25668:1;25650:19;;;25561:127;189932:21;189964:30;189974:10;189986:7;189964:9;:30::i;:::-;190027:27;190040:7;190049:4;190027:12;:27::i;:::-;190106:21;;;;:12;:21;;;;;;;;:29;;;;;;;;:::i;:::-;-1:-1:-1;190146:28:0;;;;:19;:28;;;;;;:43;;;;;;;;:::i;:::-;-1:-1:-1;190200:31:0;;;;:22;:31;;;;;;;;:48;;;;;;;;:::i;:::-;-1:-1:-1;190259:26:0;;;;:17;:26;;;;;;;;:39;;;190311:17;:26;;;;;:38;;;;;;;;:::i;:::-;-1:-1:-1;190360:34:0;;;;:25;:34;;;;;;;;:47;;;190438:10;190424:25;;:13;:25;;;;;;190420:98;;190485:10;190471:25;;;;:13;:25;;;;;:35;;;190420:98;190542:10;190530:23;;;;:11;:23;;;;;;;:37;;;;;;;;;;;;;;;;190612:23;:21;:23::i;:::-;190646:27;:25;:27::i;:::-;190684:28;:26;:28::i;:::-;190778:38;190790:7;190799:10;190811:4;190778:11;:38::i;:::-;190942:21;:33;190964:10;:8;:10::i;:::-;190942:33;;;;;;;;;;;;:35;;;;;;;;;:::i;:::-;;;;-1:-1:-1;;191025:10:0;191015:21;;;;:9;:21;;;;;;;;191010:113;;191063:10;191053:21;;;;:9;:21;;;;;:28;;-1:-1:-1;;191053:28:0;191077:4;191053:28;;;191096:13;:15;;;;;;:::i;:::-;;;;;;191010:113;191163:65;;191217:10;;191163:65;;191201:14;;191163:65;:::i;:::-;;;;;;;;191194:5;191163:65;;;;;;:::i;:::-;;;;;;;;;191168:7;191177:15;191163:65;;;;;;;:::i;:::-;;;;;;;;189168:2068;;188914:2322;;;;;;;;:::o;199467:171::-;199549:19;199557:10;199549:7;:19::i;:::-;199544:33;;199467:171;:::o;199544:33::-;199587:34;:43;199467:171::o;198201:187::-;198291:19;198299:10;198291:7;:19::i;:::-;198286:33;;198201:187;:::o;198286:33::-;198329:42;:51;198201:187::o;193947:132::-;194027:13;;194003:4;;-1:-1:-1;;;;;194027:13:0;:24;194052:6;194060:10;:8;:10::i;187519:454::-;187691:16;;-1:-1:-1;;;;;187691:16:0;187669:10;:39;187665:52;;187519:454;;;:::o;187665:52::-;187729:34;187839:35;;;:26;:35;;;;;;;187894:60;;-1:-1:-1;;;187894:60:0;;187797:18;;187839:35;-1:-1:-1;;;;;187894:44:0;;;;;:60;;187866:7;;187948:5;;187894:60;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;187839:126;;;;;;;-1:-1:-1;187839:126:0;;;;;;;;;;;-1:-1:-1;;;;187519:454:0:o;199174:139::-;199241:19;199249:10;199241:7;:19::i;:::-;199236:33;;199174:139;:::o;199236:33::-;199287:18;:6;199297:7;199287:18;:::i;:::-;199279:5;:26;-1:-1:-1;199174:139:0:o;194087:123::-;194169:13;;:33;;-1:-1:-1;;;194169:33:0;;194145:4;;-1:-1:-1;;;;;194169:13:0;;:25;;:33;;194195:6;;194169:33;;;:::i;184756:454::-;184928:16;;-1:-1:-1;;;;;184928:16:0;184906:10;:39;184902:52;;184756:454;;;:::o;184902:52::-;184966:34;185076:35;;;:26;:35;;;;;;;185131:60;;-1:-1:-1;;;185131:60:0;;185034:18;;185076:35;-1:-1:-1;;;;;185131:44:0;;;;;:60;;185103:7;;185185:5;;185131:60;;;:::i;194240:118::-;194328:4;;:22;;-1:-1:-1;;;194328:22:0;;194301:7;;-1:-1:-1;;;;;194328:4:0;;:14;;:22;;194343:6;;194328:22;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;196635:169::-;196715:19;196723:10;196715:7;:19::i;:::-;196710:33;;196635:169;:::o;196710:33::-;196755:32;:41;196635:169::o;182794:63::-;;;;;;;;;;;;;;;;;;;;31704:211;31792:7;31812:13;31828:17;31837:7;31828:8;:17::i;:::-;31812:33;-1:-1:-1;;;;;;31864:19:0;;31856:28;;;;;187063:448;187233:16;;-1:-1:-1;;;;;187233:16:0;187211:10;:39;187207:52;;187063:448;;;:::o;187207:52::-;187271:34;187381:33;;;:24;:33;;;;;;;187434:58;;-1:-1:-1;;;187434:58:0;;187339:18;;187381:33;-1:-1:-1;;;;;187434:42:0;;;;;:58;;187406:7;;187486:5;;187434:58;;;:::i;31464:178::-;31552:7;-1:-1:-1;;;;;31580:19:0;;31572:28;;;;;;-1:-1:-1;;;;;;31618:16:0;;;;;:9;:16;;;;;;;31464:178::o;182587:62::-;;;;;;;;;;;;;;;;;;;;50585:103;49893:13;:11;:13::i;:::-;50650:30:::1;50677:1;50650:18;:30::i;:::-;50585:103::o:0;194984:204::-;195042:7;195143:37;;195096:30;;195083:10;;:43;;;;:::i;68215:171::-;68279:7;;:27;;-1:-1:-1;;;68279:27:0;;-1:-1:-1;;;;;68279:7:0;;;;:15;;:27;;68295:10;;68279:27;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;68271:69;;;;-1:-1:-1;;;68271:69:0;;16977:2:1;68271:69:0;;;16959:21:1;17016:2;16996:18;;;16989:30;17055:31;17035:18;;;17028:59;17104:18;;68271:69:0;;;;;;;;68351:7;:27;;-1:-1:-1;;;;;;68351:27:0;-1:-1:-1;;;;;68351:27:0;;;;;;;;;;68215:171::o;193337:469::-;-1:-1:-1;;;;;193404:20:0;;;;;;:12;:20;;;;;;;;193400:193;;;-1:-1:-1;;;;;193459:30:0;;;;;;:22;:30;;;;;;193441:14;:48;;193459:30;;193441:14;;:48;;193459:30;;193441:48;:::i;:::-;;;;-1:-1:-1;;;;;;;193504:30:0;;193537:1;193504:30;;;:22;:30;;;;;;;;:34;;;193553:12;:20;;;;;:28;;-1:-1:-1;;193553:28:0;;;193400:193;193609:19;193621:6;193609:11;:19::i;:::-;193605:194;;;193663:17;193673:6;193663:9;:17::i;:::-;193645:14;;:35;;;;;;;:::i;:::-;;;;-1:-1:-1;193728:17:0;;-1:-1:-1;193738:6:0;193728:9;:17::i;:::-;-1:-1:-1;;;;;193695:30:0;;;;;;:22;:30;;;;;;;;:50;;;;193760:12;:20;;;:27;;-1:-1:-1;;193760:27:0;193783:4;193760:27;;;193605:194;193337:469;:::o;199321:138::-;199382:19;199390:10;199382:7;:19::i;:::-;199377:33;;199321:138;:::o;199377:33::-;199433:18;:6;199443:7;199433:18;:::i;:::-;199420:10;:31;-1:-1:-1;199321:138:0:o;30585:48::-;;;;;;;;;;;;;;;;;;;;198617:189;198708:19;198716:10;198708:7;:19::i;:::-;198703:33;;198617:189;:::o;198703:33::-;198746:43;:52;198617:189::o;181121:56::-;;;;;;;;;;;;;;;;:::i;194775:201::-;194832:7;194932:36;;194886:29;;194873:10;;:42;;;;:::i;182518:62::-;;;;;;;;;;;;;;;;;;;;186145:451;186316:16;;-1:-1:-1;;;;;186316:16:0;186294:10;:39;186290:52;;186145:451;;;:::o;186290:52::-;186354:34;186464;;;:25;:34;;;;;;;186518:59;;-1:-1:-1;;;186518:59:0;;186422:18;;186464:34;-1:-1:-1;;;;;186518:43:0;;;;;:59;;186490:7;;186571:5;;186518:59;;;:::i;195408:214::-;195471:7;195572:42;;195520:35;;195512:5;;:43;;;;:::i;50000:87::-;50073:6;;-1:-1:-1;;;;;50073:6:0;;50000:87::o;182377:64::-;;;;;;;;;;;;;;;;;;;;197622:166;197702:19;197710:10;197702:7;:19::i;:::-;197697:33;;197622:166;:::o;197697:33::-;197740:31;:40;197622:166::o;32151:104::-;32207:13;32240:7;32233:14;;;;;:::i;184106:642::-;184301:16;;-1:-1:-1;;;;;184301:16:0;184279:10;:39;184275:52;;184320:7;;184275:52;184339:56;184351:7;184360:18;184380:14;184339:11;:56::i;:::-;-1:-1:-1;;;;;184408:19:0;;;;;;:11;:19;;;;;;;:33;;;;;;;;;;;;;;;;184456:17;184420:6;184456:9;:17::i;:::-;184452:70;;184495:13;:15;;;:13;:15;;;:::i;:::-;;;;;;184452:70;184532:26;184542:6;184550:7;184532:9;:26::i;:::-;-1:-1:-1;;;;;184575:21:0;;;;;;:13;:21;;;;;;184571:90;;-1:-1:-1;;;;;184618:21:0;;;;;;:13;:21;;;;;:31;;;184571:90;184677:19;184689:6;184677:11;:19::i;:::-;184673:68;;;184713:14;:16;;;:14;:16;;;:::i;:::-;;;;;;184673:68;184106:642;;;;:::o;194463:107::-;194516:7;194543:19;:9;25531:14;;25439:114;185218:457;185391:16;;-1:-1:-1;;;;;185391:16:0;185369:10;:39;185365:52;;185218:457;;;:::o;185365:52::-;185429:34;185539:36;;;:27;:36;;;;;;;185595:61;;-1:-1:-1;;;185595:61:0;;185497:18;;185539:36;-1:-1:-1;;;;;185595:45:0;;;;;:61;;185567:7;;185650:5;;185595:61;;;:::i;33677:180::-;33797:52;26114:10;33830:8;33840;33797:18;:52::i;197403:179::-;197488:19;197496:10;197488:7;:19::i;:::-;197483:33;;197403:179;:::o;197483:33::-;197528:37;:46;197403:179::o;186604:451::-;186775:16;;-1:-1:-1;;;;;186775:16:0;186753:10;:39;186749:52;;186604:451;;;:::o;186749:52::-;186813:34;186923;;;:25;:34;;;;;;;186977:59;;-1:-1:-1;;;186977:59:0;;186881:18;;186923:34;-1:-1:-1;;;;;186977:43:0;;;;;:59;;186949:7;;187030:5;;186977:59;;;:::i;194366:89::-;194434:5;;:13;;;-1:-1:-1;;;194434:13:0;;;;194407:7;;-1:-1:-1;;;;;194434:5:0;;:11;;:13;;;;;;;;;;;;;;:5;:13;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;194578:189::-;194631:7;194727:32;;194685:25;;194672:10;;:38;;;;:::i;191244:890::-;191426:21;191436:10;191426:9;:21::i;:::-;191425:22;:57;;;;191465:17;191473:8;191465:7;:17::i;:::-;-1:-1:-1;;;;;191451:31:0;:10;-1:-1:-1;;;;;191451:31:0;;;191425:57;191421:70;;;191484:7;;191421:70;191503:10;;191554:5;;191503:57;;-1:-1:-1;;;191503:57:0;;-1:-1:-1;;;;;191503:10:0;;;;:23;;:57;;191527:10;;191547:4;;191503:57;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;191571:4:0;;191616:10;;191571:56;;-1:-1:-1;;;191571:56:0;;-1:-1:-1;;;;;191571:4:0;;;;:17;;:56;;191589:10;;191609:4;;191571:56;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;191667:22:0;;;;:12;:22;;;;;;;;:30;;;;;;;;:::i;:::-;-1:-1:-1;191708:29:0;;;;:19;:29;;;;;;:44;;;;;;;;:::i;:::-;-1:-1:-1;191763:32:0;;;;:22;:32;;;;;;;;:49;;;;;;;;:::i;:::-;;191857:23;:21;:23::i;:::-;191891:27;:25;:27::i;:::-;191929:28;:26;:28::i;:::-;192043:27;;;;:17;:27;;;;;;192021:57;;192033:8;;192043:27;192021:11;:57::i;34797:273::-;34971:41;26114:10;35004:7;34971:18;:41::i;:::-;34963:50;;;;;;35024:38;35038:4;35044:2;35048:7;35057:4;35024:13;:38::i;187981:454::-;188153:16;;-1:-1:-1;;;;;188153:16:0;188131:10;:39;188127:52;;187981:454;;;:::o;188127:52::-;188191:34;188301:35;;;:26;:35;;;;;;;188356:60;;-1:-1:-1;;;188356:60:0;;188259:18;;188301:35;-1:-1:-1;;;;;188356:44:0;;;;;:60;;188328:7;;188410:5;;188356:60;;;:::i;198994:157::-;199075:19;199083:10;199075:7;:19::i;:::-;199070:33;;198994:157;:::o;199070:33::-;199113:21;:30;198994:157::o;207120:171::-;207227:13;207260:23;207275:7;207260:14;:23::i;188443:451::-;188614:16;;-1:-1:-1;;;;;188614:16:0;188592:10;:39;188588:52;;188443:451;;;:::o;188588:52::-;188652:34;188762;;;:25;:34;;;;;;;188816:59;;-1:-1:-1;;;188816:59:0;;188720:18;;188762:34;-1:-1:-1;;;;;188816:43:0;;;;;:59;;188788:7;;188869:5;;188816:59;;;:::i;182724:63::-;;;;;;;;;;;;;;;;;;;;198019:174;198103:19;198111:10;198103:7;:19::i;:::-;198098:33;;198019:174;:::o;198098:33::-;198141:35;:44;198019:174::o;195196:204::-;195257:7;195354:38;;195306:31;;195298:5;;:39;;;;:::i;197796:179::-;197882:19;197890:10;197882:7;:19::i;:::-;197877:33;;197796:179;:::o;197877:33::-;197920:38;:47;197796:179::o;196022:397::-;196102:9;25531:14;196097:2;:24;196093:37;;;196022:397;;:::o;196093:37::-;196172:11;196180:2;196172:7;:11::i;:::-;-1:-1:-1;;;;;196158:25:0;:10;-1:-1:-1;;;;;196158:25:0;;;:68;;;;-1:-1:-1;196209:16:0;;-1:-1:-1;;;;;196209:16:0;196187:10;:39;;196158:68;196140:105;;;196022:397;;:::o;196140:105::-;-1:-1:-1;;;;;196285:21:0;;196257:25;196285:21;;;:13;:21;;;;;;;;;196317:26;;;;196361:50;;196377:10;;196361:50;;;;196285:21;;196341:2;;196361:50;:::i;:::-;;;;;;;;196082:337;196022:397;;:::o;185683:454::-;185855:16;;-1:-1:-1;;;;;185855:16:0;185833:10;:39;185829:52;;185683:454;;;:::o;185829:52::-;185893:34;186003:35;;;:26;:35;;;;;;;186058:60;;-1:-1:-1;;;186058:60:0;;185961:18;;186003:35;-1:-1:-1;;;;;186058:44:0;;;;;:60;;186030:7;;186112:5;;186058:60;;;:::i;181008:46::-;;;;;;;;;;;;;;;;:::i;193830:109::-;193908:7;;:23;;-1:-1:-1;;;193908:23:0;;193884:4;;-1:-1:-1;;;;;193908:7:0;;:15;;:23;;193924:6;;193908:23;;;:::i;181061:53::-;;;;;;;;;;;;;;;;;:::i;198433:176::-;198518:19;198526:10;198518:7;:19::i;:::-;198513:33;;198433:176;:::o;198513:33::-;198556:36;:45;198433:176::o;33928:189::-;-1:-1:-1;;;;;34074:25:0;;;34050:4;34074:25;;;:18;:25;;;;;;;;:35;;;;;;;;;;;;;;;33928:189::o;182864:62::-;;;;;;;;;;;;;;;;;;;;196473:154;196547:19;196555:10;196547:7;:19::i;:::-;196542:33;;196473:154;:::o;196542:33::-;196585:25;:34;196473:154::o;50836:159::-;49893:13;:11;:13::i;:::-;-1:-1:-1;;;;;50925:22:0;::::1;50917:31;;;::::0;::::1;;50959:28;50978:8;50959:18;:28::i;66052:2155::-:0;66123:7;;:22;;;-1:-1:-1;;;66123:22:0;;;;66098;;-1:-1:-1;;;;;66123:7:0;;:20;;:22;;;;;;;;;;;;;;:7;:22;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;66098:47;;66176:14;66160:12;;:30;66156:43;;;66192:7;66052:2155::o;66156:43::-;66209:12;:29;;;66263:7;;:15;;;-1:-1:-1;;;66263:15:0;;;;-1:-1:-1;;;;;66263:7:0;;;;:13;;:15;;;;;;;;;;;;;;;:7;:15;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;66249:5;:30;;-1:-1:-1;;;;;;66249:30:0;-1:-1:-1;;;;;66249:30:0;;;;;;66311:7;;:23;;;-1:-1:-1;;;66311:23:0;;;;:7;;;;;:21;;:23;;;;;;;;;;;;;;:7;:23;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;66290:13;:45;;-1:-1:-1;;;;;;66290:45:0;-1:-1:-1;;;;;66290:45:0;;;;;;66359:7;;:14;;;-1:-1:-1;;;66359:14:0;;;;:7;;;;;:12;;:14;;;;;;;;;;;;;;:7;:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;66346:4;:28;;-1:-1:-1;;;;;;66346:28:0;-1:-1:-1;;;;;66346:28:0;;;;;;66409:7;;:23;;;-1:-1:-1;;;66409:23:0;;;;:7;;;;;:21;;:23;;;;;;;;;;;;;;:7;:23;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;66385:13;:48;;-1:-1:-1;;;;;;66385:48:0;-1:-1:-1;;;;;66385:48:0;;;;;;66468:7;;:23;;;-1:-1:-1;;;66468:23:0;;;;:7;;;;;:21;;:23;;;;;;;;;;;;;;:7;:23;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;66444:13;:48;;-1:-1:-1;;;;;;66444:48:0;-1:-1:-1;;;;;66444:48:0;;;;;;66525:7;;:21;;;-1:-1:-1;;;66525:21:0;;;;:7;;;;;:19;;:21;;;;;;;;;;;;;;:7;:21;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;66503:11;:44;;-1:-1:-1;;;;;;66503:44:0;-1:-1:-1;;;;;66503:44:0;;;;;;66575:7;;:19;;;-1:-1:-1;;;66575:19:0;;;;:7;;;;;:17;;:19;;;;;;;;;;;;;;:7;:19;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;66558:9;:37;;-1:-1:-1;;;;;;66558:37:0;-1:-1:-1;;;;;66558:37:0;;;;;;66629:7;;:18;;;-1:-1:-1;;;66629:18:0;;;;:7;;;;;:16;;:18;;;;;;;;;;;;;;:7;:18;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;66606:8;:42;;-1:-1:-1;;;;;;66606:42:0;-1:-1:-1;;;;;66606:42:0;;;;;;66688:7;;:26;;;-1:-1:-1;;;66688:26:0;;;;:7;;;;;:24;;:26;;;;;;;;;;;;;;:7;:26;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;66659:16;:56;;-1:-1:-1;;;;;;66659:56:0;-1:-1:-1;;;;;66659:56:0;;;;;;66763:7;;:30;;;-1:-1:-1;;;66763:30:0;;;;:7;;;;;:28;;:30;;;;;;;;;;;;;;:7;:30;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;66726:20;:68;;-1:-1:-1;;;;;;66726:68:0;-1:-1:-1;;;;;66726:68:0;;;;;;66839:7;;:30;;;-1:-1:-1;;;66839:30:0;;;;:7;;;;;:28;;:30;;;;;;;;;;;;;;:7;:30;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;66805:20;:65;;-1:-1:-1;;;;;;66805:65:0;-1:-1:-1;;;;;66805:65:0;;;;;;66917:7;;:31;;;-1:-1:-1;;;66917:31:0;;;;:7;;;;;:29;;:31;;;;;;;;;;;;;;:7;:31;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;66881:21;:68;;-1:-1:-1;;;;;;66881:68:0;-1:-1:-1;;;;;66881:68:0;;;;;;66990:7;;:25;;;-1:-1:-1;;;66990:25:0;;;;:7;;;;;:23;;:25;;;;;;;;;;;;;;:7;:25;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;66960:15;:56;;-1:-1:-1;;;;;;66960:56:0;-1:-1:-1;;;;;66960:56:0;;;;;;67085:7;;:35;;;-1:-1:-1;;;67085:35:0;;;;:7;;;;;:33;;:35;;;;;;;;;;;;;;:7;:35;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;67027:25;:104;;-1:-1:-1;;;;;;67027:104:0;-1:-1:-1;;;;;67027:104:0;;;;;;67180:7;;:29;;;-1:-1:-1;;;67180:29:0;;;;:7;;;;;:27;;:29;;;;;;;;;;;;;;:7;:29;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;67142:19;:68;;-1:-1:-1;;;;;;67142:68:0;-1:-1:-1;;;;;67142:68:0;;;;;;67281:7;;:36;;;-1:-1:-1;;;67281:36:0;;;;:7;;;;;:34;;:36;;;;;;;;;;;;;;:7;:36;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;67221:26;:107;;-1:-1:-1;;;;;;67221:107:0;-1:-1:-1;;;;;67221:107:0;;;;;;67379:7;;:30;;;-1:-1:-1;;;67379:30:0;;;;:7;;;;;:28;;:30;;;;;;;;;;;;;;:7;:30;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;67339:20;:71;;-1:-1:-1;;;;;;67339:71:0;-1:-1:-1;;;;;67339:71:0;;;;;;67483:7;;:39;;;-1:-1:-1;;;67483:39:0;;;;:7;;;;;:37;;:39;;;;;;;;;;;;;;:7;:39;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;67421:29;:112;;-1:-1:-1;;;;;;67421:112:0;-1:-1:-1;;;;;67421:112:0;;;;;;67600:7;;:36;;;-1:-1:-1;;;67600:36:0;;;;:7;;;;;:34;;:36;;;;;;;;;;;;;;:7;:36;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;67544:26;:103;;-1:-1:-1;;;;;;67544:103:0;-1:-1:-1;;;;;67544:103:0;;;;;;67720:7;;:34;;;-1:-1:-1;;;67720:34:0;;;;:7;;;;;:32;;:34;;;;;;;;;;;;;;:7;:34;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;67658:24;:107;;-1:-1:-1;;;;;;67658:107:0;-1:-1:-1;;;;;67658:107:0;;;;;;67812:7;;:31;;;-1:-1:-1;;;67812:31:0;;;;:7;;;;;:29;;:31;;;;;67658:24;;67812:31;;;;;:7;:31;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;67778:21;:66;;-1:-1:-1;;;;;;67778:66:0;-1:-1:-1;;;;;67778:66:0;;;;;;67890:7;;:27;;;-1:-1:-1;;;67890:27:0;;;;:7;;;;;:25;;:27;;;;;;;;;;;;;;:7;:27;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;67855:17;:63;;-1:-1:-1;;;;;;67855:63:0;-1:-1:-1;;;;;67855:63:0;;;;;;67967:7;;:30;;;-1:-1:-1;;;67967:30:0;;;;:7;;;;;:28;;:30;;;;;;;;;;;;;;:7;:30;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;67929:20;:69;;-1:-1:-1;;;;;;67929:69:0;-1:-1:-1;;;;;67929:69:0;;;;;;68042:7;;:25;;;-1:-1:-1;;;68042:25:0;;;;:7;;;;;:23;;:25;;;;;;;;;;;;;;:7;:25;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;68009:15;:59;;-1:-1:-1;;;;;;68009:59:0;-1:-1:-1;;;;;68009:59:0;;;;;;68100:7;;:20;;;-1:-1:-1;;;68100:20:0;;;;:7;;;;;:18;;:20;;;;;;;;;;;;;;:7;:20;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;68081:10;:40;;-1:-1:-1;;;;;;68081:40:0;-1:-1:-1;;;;;68081:40:0;;;;;;68145:7;;:14;;;-1:-1:-1;;;68145:14:0;;;;:7;;;;;:12;;:14;;;;;;;;;;;;;;:7;:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;68132:4;:28;;-1:-1:-1;;;;;;68132:28:0;-1:-1:-1;;;;;68132:28:0;;;;;;68184:7;;:14;;;-1:-1:-1;;;68184:14:0;;;;:7;;;;;:12;;:14;;;;;;;;;;;;;;:7;:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;68171:4;:28;;-1:-1:-1;;;;;;68171:28:0;-1:-1:-1;;;;;68171:28:0;;;;;;;;;;-1:-1:-1;66052:2155:0:o;25439:114::-;25531:14;;25439:114::o;25561:127::-;25650:19;;25668:1;25650:19;;;25561:127::o;1070:326::-;-1:-1:-1;;;;;1365:19:0;;:23;;;1070:326::o;43874:107::-;43956:16;43964:7;43956;:16::i;:::-;43948:25;;;;;43182:174;43257:24;;;;:15;:24;;;;;:29;;-1:-1:-1;;;;;;43257:29:0;-1:-1:-1;;;;;43257:29:0;;;;;;;;:24;;43311:23;43257:24;43311:14;:23::i;:::-;-1:-1:-1;;;;;43302:46:0;;;;;;;;;;;43182:174;;:::o;37049:315::-;37167:4;37184:13;37200:23;37215:7;37200:14;:23::i;:::-;37184:39;;37253:5;-1:-1:-1;;;;;37242:16:0;:7;-1:-1:-1;;;;;37242:16:0;;:65;;;;37275:32;37292:5;37299:7;37275:16;:32::i;:::-;37242:113;;;;37348:7;-1:-1:-1;;;;;37324:31:0;:20;37336:7;37324:11;:20::i;:::-;-1:-1:-1;;;;;37324:31:0;;37242:113;37234:122;37049:315;-1:-1:-1;;;;37049:315:0:o;41616:1447::-;41775:4;-1:-1:-1;;;;;41748:31:0;:23;41763:7;41748:14;:23::i;:::-;-1:-1:-1;;;;;41748:31:0;;41740:40;;;;;;-1:-1:-1;;;;;41799:16:0;;41791:25;;;;;;41998:4;-1:-1:-1;;;;;41971:31:0;:23;41986:7;41971:14;:23::i;:::-;-1:-1:-1;;;;;41971:31:0;;41963:40;;;;;;42075:24;;;;:15;:24;;;;;42068:31;;-1:-1:-1;;;;;;42068:31:0;;;42553:41;42091:7;42589:4;42553:26;:41::i;:::-;-1:-1:-1;;;;;42609:15:0;;;;;;:9;:15;;;;;:20;;-1:-1:-1;;42609:20:0;;;;;42650;:41;;;;;42690:1;42674:13;;:17;42650:41;42646:97;;;42712:13;:15;;-1:-1:-1;;42712:15:0;;;42646:97;42759:36;42783:7;42792:2;42759:23;:36::i;:::-;-1:-1:-1;;;;;42814:13:0;;;;;;:9;:13;;;;;;42810:74;;42853:13;:15;;;;;;42810:74;-1:-1:-1;;;;;42898:13:0;;;;;;;:9;:13;;;;;;;;:18;;42915:1;42898:18;;;42938:16;;;:7;:16;;;;;;:21;;-1:-1:-1;;;;;;42938:21:0;;;;;42977:27;42946:7;;42977:27;;;-1:-1:-1;;;;;;;;;;;42977:27:0;;33145:207;33075:277;;:::o;37706:110::-;37782:26;37792:2;37796:7;37782:26;;;;;;;;;;;;:9;:26::i;48796:192::-;48921:16;48929:7;48921;:16::i;:::-;48913:25;;;;;;48949:19;;;;:10;:19;;;;;;;;:31;;;;;;;;:::i;204426:2477::-;204799:37;;-1:-1:-1;;;204799:37:0;;;;;17279:25:1;;;204604:18:0;;;;-1:-1:-1;;;;;204799:30:0;;;;;17252:18:1;;204799:37:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;204799:37:0;;;;;;;;;;;;:::i;:::-;204777:19;;;;:12;:19;;;;;;;;:59;;;;:19;;:59;;;;;;:::i;:::-;-1:-1:-1;204876:68:0;;-1:-1:-1;;;204876:68:0;;;;;17279:25:1;;;-1:-1:-1;;;;;204876:37:0;;;;;17252:18:1;;204876:68:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;204876:68:0;;;;;;;;;;;;:::i;:::-;204847:26;;;;:19;:26;;;;;;:97;;;;:26;;:97;;;;;;:::i;:::-;-1:-1:-1;204987:61:0;;-1:-1:-1;;;204987:61:0;;;;;17279:25:1;;;-1:-1:-1;;;;;204987:54:0;;;;;17252:18:1;;204987:61:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;204987:61:0;;;;;;;;;;;;:::i;:::-;204955:29;;;;:22;:29;;;;;;;;:93;;;;:29;;:93;;;;;;:::i;:::-;-1:-1:-1;205094:64:0;;-1:-1:-1;;;205094:64:0;;;;;17279:25:1;;;-1:-1:-1;;;;;205094:57:0;;;;;17252:18:1;;205094:64:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;205059:32;;;;:25;:32;;;;;;;:99;;;;205196:42;-1:-1:-1;;;205196:42:0;;;;;17279:25:1;;;-1:-1:-1;;;;;205196:35:0;;;;;17252:18:1;;205196:42:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;205196:42:0;;;;;;;;;;;;:::i;:::-;205169:24;;;;:17;:24;;;;;;;;:69;;;;:24;;:69;;;;;;:::i;:::-;-1:-1:-1;205276:42:0;;-1:-1:-1;;;205276:42:0;;;;;17279:25:1;;;-1:-1:-1;;;;;205276:35:0;;;;;17252:18:1;;205276:42:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;205249:24;;;;:17;:24;;;;;;;:69;;;;205396:71;-1:-1:-1;;;205396:71:0;;;;;17279:25:1;;;-1:-1:-1;;;;;205396:64:0;;;;;17252:18:1;;205396:71:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;205354:39;;;;:32;:39;;;;;;;:113;;;;205521:72;-1:-1:-1;;;205521:72:0;;;;;17279:25:1;;;-1:-1:-1;;;;;205521:65:0;;;;;17252:18:1;;205521:72:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;205478:40;;;;:33;:40;;;;;;;:115;;;;205646:71;-1:-1:-1;;;205646:71:0;;;;;17279:25:1;;;-1:-1:-1;;;;;205646:64:0;;;;;17252:18:1;;205646:71:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;205604:39;;;;:32;:39;;;;;;;:113;;;;205769:70;-1:-1:-1;;;205769:70:0;;;;;17279:25:1;;;-1:-1:-1;;;;;205769:63:0;;;;;17252:18:1;;205769:70:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;205728:38;;;;:31;:38;;;;;;;:111;;;;205891:70;-1:-1:-1;;;205891:70:0;;;;;17279:25:1;;;-1:-1:-1;;;;;205891:63:0;;;;;17252:18:1;;205891:70:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;205850:38;;;;:31;:38;;;;;;;:111;;;;206012:69;-1:-1:-1;;;206012:69:0;;;;;17279:25:1;;;-1:-1:-1;;;;;206012:62:0;;;;;17252:18:1;;206012:69:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;205972:37;;;;:30;:37;;;;;;;:109;;;;206134:71;-1:-1:-1;;;206134:71:0;;;;;17279:25:1;;;-1:-1:-1;;;;;206134:64:0;;;;;17252:18:1;;206134:71:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;206092:39;;;;:32;:39;;;;;;;:113;;;;206258:71;-1:-1:-1;;;206258:71:0;;;;;17279:25:1;;;-1:-1:-1;;;;;206258:64:0;;;;;17252:18:1;;206258:71:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;206216:39;;;;:32;:39;;;;;;;:113;;;;206381:70;-1:-1:-1;;;206381:70:0;;;;;17279:25:1;;;-1:-1:-1;;;;;206381:63:0;;;;;17252:18:1;;206381:70:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;206340:38;;;;:31;:38;;;;;;;:111;;;;206501:66;-1:-1:-1;;;206501:66:0;;;;;17279:25:1;;;-1:-1:-1;;;;;206501:59:0;;;;;17252:18:1;;206501:66:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;206464:34;;;;:27;:34;;;;;:103;206593:1;206584:10;;206580:316;;;206650:71;;-1:-1:-1;;;206650:71:0;;;;;17279:25:1;;;-1:-1:-1;;;;;206650:64:0;;;;;17252:18:1;;206650:71:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;206611:35;;;;:28;:35;;;;;:110;;:35;;;:110;;;;;:::i;:::-;;;;-1:-1:-1;206580:316:0;;-1:-1:-1;206580:316:0;;206743:5;206752:2;206743:11;206739:157;;;206810:74;;-1:-1:-1;;;206810:74:0;;;;;17279:25:1;;;-1:-1:-1;;;;;206810:67:0;;;;;17252:18:1;;206810:74:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;206771:35;;;;:28;:35;;;;;:113;;:35;;;:113;;;;;:::i;:::-;;;;-1:-1:-1;;206739:157:0;204547:2356;;204426:2477;;;:::o;199672:256::-;199724:4;;199746:15;;-1:-1:-1;;;;;199724:4:0;;;;:13;;199746:15;199764:22;:20;:22::i;:::-;199724:63;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;199800:10:0;;199842:21;;-1:-1:-1;;;;;199800:10:0;;;;:19;;199842:21;199879:30;:28;:30::i;:::-;199800:120;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;199936:274::-;199992:4;;200014:19;;-1:-1:-1;;;;;199992:4:0;;;;:13;;200014:19;200036:26;:24;:26::i;:::-;199992:71;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;200076:10:0;;200118:25;;-1:-1:-1;;;;;200076:10:0;;;;:19;;200118:25;200159:32;:30;:32::i;200218:316::-;200275:4;;200311:20;;-1:-1:-1;;;;;200275:4:0;;;;:13;;200311:20;200347:27;:25;:27::i;:::-;200275:110;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;200398:10:0;;200440:26;;-1:-1:-1;;;;;200398:10:0;;;;:19;;200440:26;200482:33;:31;:33::i;200575:684::-;200700:13;;;200772:23;:21;:23::i;:::-;200748:47;-1:-1:-1;200837:10:0;200858:394;200869:1;200865;:5;200858:394;;;200891:7;200887:51;;;200900:38;200915:1;200918:7;200927:10;200900:14;:38::i;:::-;200963:29;200971:20;200963:7;:29::i;:::-;200955:37;;201032:129;201070:5;201094:20;201133:13;201032:19;:129::i;:::-;201191:29;;;;:17;:29;;;;;;;;201009:152;-1:-1:-1;201237:3:0;;;;:::i;:::-;;;;200858:394;;36324:117;36390:7;36417:16;;;:7;:16;;;;;;-1:-1:-1;;;;;36417:16:0;;36324:117::o;50158:96::-;26114:10;50222:7;:5;:7::i;:::-;-1:-1:-1;;;;;50222:23:0;;50214:32;;;;;51155:191;51248:6;;;-1:-1:-1;;;;;51265:17:0;;;-1:-1:-1;;;;;;51265:17:0;;;;;;;51298:40;;51248:6;;;51265:17;51248:6;;51298:40;;51229:16;;51298:40;51218:128;51155:191;:::o;43499:286::-;43654:8;-1:-1:-1;;;;;43645:17:0;:5;-1:-1:-1;;;;;43645:17:0;;;43637:26;;;;;;-1:-1:-1;;;;;43674:25:0;;;;;;;:18;:25;;;;;;;;:35;;;;;;;;;;;;;:46;;-1:-1:-1;;43674:46:0;;;;;;;;;;43736:41;;15824::1;;;43736::0;;15797:18:1;43736:41:0;;;;;;;43499:286;;;:::o;35951:259::-;36107:28;36117:4;36123:2;36127:7;36107:9;:28::i;:::-;36154:47;36177:4;36183:2;36187:7;36196:4;36154:22;:47::i;:::-;36146:56;;;;;48000:640;48089:13;48115:23;48130:7;48115:14;:23::i;:::-;48151;48177:19;;;:10;:19;;;;;48151:45;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;48207:18;48228:10;32996:9;;;;;;;;;-1:-1:-1;32996:9:0;;;32919:94;48228:10;48207:31;;48320:4;48314:18;48336:1;48314:23;48310:72;;;-1:-1:-1;48361:9:0;48000:640;-1:-1:-1;;48000:640:0:o;48310:72::-;48486:23;;:27;48482:108;;48561:4;48567:9;48544:33;;;;;;;;;:::i;:::-;;;;;;;;;;;;;48530:48;;;;48000:640;;;:::o;48482:108::-;48609:23;48624:7;48609:14;:23::i;36754:128::-;36819:4;;36843:17;36852:7;36843:8;:17::i;:::-;-1:-1:-1;;;;;36843:31:0;;;;36754:128;-1:-1:-1;;36754:128:0:o;40794:485::-;40907:9;40933:45;-1:-1:-1;;;;;40940:19:0;;;;;;:11;:19;;;;;:22;;40966:7;;40940:19;40960:1;;40940:22;;;;;;:::i;:::-;;;;;;;;;:33;40933:45;;40975:3;;;;:::i;:::-;;;;40933:45;;;-1:-1:-1;;;;;41004:17:0;;;;;;:9;:17;;;;;;:21;;41024:1;;41004:21;:::i;:::-;40999:1;:27;40991:120;;;-1:-1:-1;;;;;41073:19:0;;;;;;:11;:19;;;;;41093:5;:1;41097;41093:5;:::i;:::-;41073:26;;;;;;;;:::i;:::-;;;;;;;;;41048:11;:19;41060:6;-1:-1:-1;;;;;41048:19:0;-1:-1:-1;;;;;41048:19:0;;;;;;;;;;;;41068:1;41048:22;;;;;;;;:::i;:::-;;;;;;;;;;:51;41028:3;;;;:::i;:::-;;;;40991:120;;;-1:-1:-1;;;;;41123:19:0;;;;;;:11;:19;;;;;:25;;;;;;;:::i;:::-;;;;;;;;;;;-1:-1:-1;;41123:25:0;;;;;;;;;;;;-1:-1:-1;;;;;41165:21:0;;;;:13;:21;;;;;;:32;;41161:111;;;-1:-1:-1;;;;;41238:19:0;;;;;;:11;:19;;;;;:22;;:19;;;:22;;;;:::i;:::-;;;;;;;;;;;;;-1:-1:-1;;;;;41214:21:0;;;;:13;:21;;;;;;;:46;40896:383;40794:485;;:::o;40560:226::-;-1:-1:-1;;;;;40645:19:0;;;;;;:11;:19;;;;;;;;:33;;;;;;;;;;;;;;;;;40693:21;;;:13;:21;;;;40689:90;;-1:-1:-1;;;;;40736:21:0;;;;;:13;:21;;;;;:31;40560:226::o;38043:228::-;38172:18;38178:2;38182:7;38172:5;:18::i;:::-;38209:53;38240:1;38244:2;38248:7;38257:4;38209:22;:53::i;:::-;38201:62;;;;;202349:2069;202437:6;202433:210;;202460:37;;;;:32;:37;;;;;:39;;;;;;:::i;:::-;;;;-1:-1:-1;;202514:32:0;;;;:27;:32;;;;;:34;;;;;;:::i;:::-;;;;-1:-1:-1;;202563:31:0;;;;:26;:31;;;;;;;:45;;;;;;;;;;;;;;-1:-1:-1;202349:2069:0:o;202433:210::-;202659:1;202664;202659:6;202655:212;;;202682:38;;;;:33;:38;;;;;:40;;;;;;:::i;:::-;;;;-1:-1:-1;;202737:32:0;;;;:27;:32;;;;;:34;;;;;;:::i;:::-;;;;-1:-1:-1;;202786:32:0;;;;:27;:32;;;;;;;:46;;;;;;;;;;;;;;-1:-1:-1;202349:2069:0:o;202655:212::-;202883:1;202888;202883:6;202879:210;;;202906:37;;;;:32;:37;;;;;:39;;;;;;:::i;:::-;;;;-1:-1:-1;;202960:32:0;;;;:27;:32;;;;;:34;;;;;;:::i;:::-;;;;-1:-1:-1;;203009:31:0;;;;:26;:31;;;;;;;:45;;;;;;;;;;;;;;-1:-1:-1;202349:2069:0:o;202879:210::-;203105:1;203110;203105:6;203101:208;;;203128:36;;;;:31;:36;;;;;:38;;;;;;:::i;:::-;;;;-1:-1:-1;;203181:32:0;;;;:27;:32;;;;;:34;;;;;;:::i;:::-;;;;-1:-1:-1;;203230:30:0;;;;:25;:30;;;;;;;:44;;;;;;;;;;;;;;-1:-1:-1;202349:2069:0:o;203101:208::-;203325:1;203330;203325:6;203321:208;;;203348:36;;;;:31;:36;;;;;:38;;;;;;:::i;:::-;;;;-1:-1:-1;;203401:32:0;;;;:27;:32;;;;;:34;;;;;;:::i;:::-;;;;-1:-1:-1;;203450:30:0;;;;:25;:30;;;;;;;:44;;;;;;;;;;;;;;-1:-1:-1;202349:2069:0:o;203321:208::-;203545:1;203550;203545:6;203541:206;;;203568:35;;;;:30;:35;;;;;:37;;;;;;:::i;:::-;;;;-1:-1:-1;;203620:32:0;;;;:27;:32;;;;;:34;;;;;;:::i;:::-;;;;-1:-1:-1;;203669:29:0;;;;:24;:29;;;;;;;:43;;;;;;;;;;;;;;-1:-1:-1;202349:2069:0:o;203541:206::-;203763:1;203768;203763:6;203759:210;;;203786:37;;;;:32;:37;;;;;:39;;;;;;:::i;:::-;;;;-1:-1:-1;;203840:32:0;;;;:27;:32;;;;;:34;;;;;;:::i;:::-;;;;-1:-1:-1;;203889:31:0;;;;:26;:31;;;;;;;:45;;;;;;;;;;;;;;-1:-1:-1;202349:2069:0:o;203759:210::-;203985:1;203990;203985:6;203981:210;;;204008:37;;;;:32;:37;;;;;:39;;;;;;:::i;:::-;;;;-1:-1:-1;;204062:32:0;;;;:27;:32;;;;;:34;;;;;;:::i;:::-;;;;-1:-1:-1;;204111:31:0;;;;:26;:31;;;;;;;:45;;;;;;;;;;;;;;-1:-1:-1;202349:2069:0:o;203981:210::-;204207:1;204212;204207:6;204203:208;;;204230:36;;;;:31;:36;;;;;:38;;;;;;:::i;:::-;;;;-1:-1:-1;;204283:32:0;;;;:27;:32;;;;;:34;;;;;;:::i;:::-;;;;-1:-1:-1;;204332:30:0;;;;:25;:30;;;;;;;:44;;;;;;;;;;;;;;-1:-1:-1;202349:2069:0:o;44545:934::-;44699:4;44720:15;:2;-1:-1:-1;;;;;44720:13:0;;:15::i;:::-;44716:756;;;44773:174;;-1:-1:-1;;;44773:174:0;;-1:-1:-1;;;;;44773:36:0;;;;;:174;;26114:10;;44867:4;;44894:7;;44924:4;;44773:174;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;44773:174:0;;;;;;;;-1:-1:-1;;44773:174:0;;;;;;;;;;;;:::i;:::-;;;44752:665;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;45130:13:0;;45126:276;;45173:8;;;45126:276;45352:6;45346:13;45337:6;45333:2;45329:15;45322:38;44752:665;-1:-1:-1;;;;;;45011:51:0;-1:-1:-1;;;45011:51:0;;-1:-1:-1;45004:58:0;;44716:756;-1:-1:-1;45456:4:0;44545:934;;;;;;:::o;32326:344::-;32415:13;32441:23;32456:7;32441:14;:23::i;:::-;32477:21;32501:10;32996:9;;;;;;;;;-1:-1:-1;32996:9:0;;;32919:94;32501:10;32477:34;;32566:1;32548:7;32542:21;:25;:120;;;;;;;;;;;;;;;;;32611:7;32620:18;:7;:16;:18::i;:::-;32594:45;;;;;;;;;:::i;:::-;;;;;;;;;;;;;32542:120;32522:140;32326:344;-1:-1:-1;;;32326:344:0:o;38607:829::-;-1:-1:-1;;;;;38687:16:0;;38679:25;;;;;;38724:16;38732:7;38724;:16::i;:::-;38723:17;38715:26;;;;;;38898:16;38906:7;38898;:16::i;:::-;38897:17;38889:26;;;;;;-1:-1:-1;;;;;39257:13:0;;;;;;:9;:13;;;;;;;;:18;;39274:1;39257:18;;;39299:16;;;:7;:16;;;;;;:21;;-1:-1:-1;;;;;;39299:21:0;;;;;39338:33;39307:7;;39257:13;;-1:-1:-1;;;;;;;;;;;39338:33:0;39257:13;;39338:33;192681:482;192405:765;192369:801::o;10203:716::-;10259:13;10310:14;10327:17;10338:5;10327:10;:17::i;:::-;10347:1;10327:21;10310:38;;10363:20;10397:6;-1:-1:-1;;;;;10386:18:0;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;10386:18:0;-1:-1:-1;10363:41:0;-1:-1:-1;10528:28:0;;;10544:2;10528:28;10585:288;-1:-1:-1;;10617:5:0;-1:-1:-1;;;10754:2:0;10743:14;;10738:30;10617:5;10725:44;10815:2;10806:11;;;-1:-1:-1;10840:10:0;10836:21;;10852:5;;10836:21;10585:288;;;-1:-1:-1;10894:6:0;10203:716;-1:-1:-1;;;10203:716:0:o;22176:948::-;22229:7;;-1:-1:-1;;;22307:17:0;;22303:106;;-1:-1:-1;;;22345:17:0;;;-1:-1:-1;22391:2:0;22381:12;22303:106;-1:-1:-1;;;22427:5:0;:17;22423:106;;-1:-1:-1;;;22465:17:0;;;-1:-1:-1;22511:2:0;22501:12;22423:106;22556:8;22547:5;:17;22543:106;;22594:8;22585:17;;;-1:-1:-1;22631:2:0;22621:12;22543:106;22676:7;22667:5;:16;22663:103;;22713:7;22704:16;;;-1:-1:-1;22749:1:0;22739:11;22663:103;22793:7;22784:5;:16;22780:103;;22830:7;22821:16;;;-1:-1:-1;22866:1:0;22856:11;22780:103;22910:7;22901:5;:16;22897:103;;22947:7;22938:16;;;-1:-1:-1;22983:1:0;22973:11;22897:103;23027:7;23018:5;:16;23014:68;;23065:1;23055:11;23110:6;22176:948;-1:-1:-1;;22176:948:0:o;-1:-1:-1:-;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;:::o;:::-;;;;;;;;;;;;;;;14:336:1;78:5;107:52;123:35;151:6;123:35;:::i;:::-;107:52;:::i;:::-;98:61;;182:6;175:5;168:21;222:3;213:6;208:3;204:16;201:25;198:45;;;239:1;236;229:12;198:45;288:6;283:3;276:4;269:5;265:16;252:43;342:1;335:4;326:6;319:5;315:18;311:29;304:40;14:336;;;;;:::o;355:221::-;398:5;451:3;444:4;436:6;432:17;428:27;418:55;;469:1;466;459:12;418:55;491:79;566:3;557:6;544:20;537:4;529:6;525:17;491:79;:::i;581:247::-;640:6;693:2;681:9;672:7;668:23;664:32;661:52;;;709:1;706;699:12;661:52;748:9;735:23;767:31;792:5;767:31;:::i;833:251::-;903:6;956:2;944:9;935:7;931:23;927:32;924:52;;;972:1;969;962:12;924:52;1004:9;998:16;1023:31;1048:5;1023:31;:::i;1089:388::-;1157:6;1165;1218:2;1206:9;1197:7;1193:23;1189:32;1186:52;;;1234:1;1231;1224:12;1186:52;1273:9;1260:23;1292:31;1317:5;1292:31;:::i;:::-;1342:5;-1:-1:-1;1399:2:1;1384:18;;1371:32;1412:33;1371:32;1412:33;:::i;:::-;1464:7;1454:17;;;1089:388;;;;;:::o;1482:456::-;1559:6;1567;1575;1628:2;1616:9;1607:7;1603:23;1599:32;1596:52;;;1644:1;1641;1634:12;1596:52;1683:9;1670:23;1702:31;1727:5;1702:31;:::i;:::-;1752:5;-1:-1:-1;1809:2:1;1794:18;;1781:32;1822:33;1781:32;1822:33;:::i;:::-;1482:456;;1874:7;;-1:-1:-1;;;1928:2:1;1913:18;;;;1900:32;;1482:456::o;1943:794::-;2038:6;2046;2054;2062;2115:3;2103:9;2094:7;2090:23;2086:33;2083:53;;;2132:1;2129;2122:12;2083:53;2171:9;2158:23;2190:31;2215:5;2190:31;:::i;:::-;2240:5;-1:-1:-1;2297:2:1;2282:18;;2269:32;2310:33;2269:32;2310:33;:::i;:::-;2362:7;-1:-1:-1;2416:2:1;2401:18;;2388:32;;-1:-1:-1;2471:2:1;2456:18;;2443:32;-1:-1:-1;;;;;2487:30:1;;2484:50;;;2530:1;2527;2520:12;2484:50;2553:22;;2606:4;2598:13;;2594:27;-1:-1:-1;2584:55:1;;2635:1;2632;2625:12;2584:55;2658:73;2723:7;2718:2;2705:16;2700:2;2696;2692:11;2658:73;:::i;:::-;2648:83;;;1943:794;;;;;;;:::o;2742:382::-;2807:6;2815;2868:2;2856:9;2847:7;2843:23;2839:32;2836:52;;;2884:1;2881;2874:12;2836:52;2923:9;2910:23;2942:31;2967:5;2942:31;:::i;:::-;2992:5;-1:-1:-1;3049:2:1;3034:18;;3021:32;3062:30;3021:32;3062:30;:::i;3129:315::-;3197:6;3205;3258:2;3246:9;3237:7;3233:23;3229:32;3226:52;;;3274:1;3271;3264:12;3226:52;3313:9;3300:23;3332:31;3357:5;3332:31;:::i;:::-;3382:5;3434:2;3419:18;;;;3406:32;;-1:-1:-1;;;3129:315:1:o;3449:525::-;3535:6;3543;3551;3559;3612:3;3600:9;3591:7;3587:23;3583:33;3580:53;;;3629:1;3626;3619:12;3580:53;3668:9;3655:23;3687:31;3712:5;3687:31;:::i;:::-;3737:5;-1:-1:-1;3789:2:1;3774:18;;3761:32;;-1:-1:-1;3845:2:1;3830:18;;3817:32;3858:33;3817:32;3858:33;:::i;:::-;3449:525;;;;-1:-1:-1;3910:7:1;;3964:2;3949:18;3936:32;;-1:-1:-1;;3449:525:1:o;3979:383::-;4056:6;4064;4072;4125:2;4113:9;4104:7;4100:23;4096:32;4093:52;;;4141:1;4138;4131:12;4093:52;4180:9;4167:23;4199:31;4224:5;4199:31;:::i;:::-;4249:5;4301:2;4286:18;;4273:32;;-1:-1:-1;4352:2:1;4337:18;;;4324:32;;3979:383;-1:-1:-1;;;3979:383:1:o;4367:245::-;4434:6;4487:2;4475:9;4466:7;4462:23;4458:32;4455:52;;;4503:1;4500;4493:12;4455:52;4535:9;4529:16;4554:28;4576:5;4554:28;:::i;4617:245::-;4675:6;4728:2;4716:9;4707:7;4703:23;4699:32;4696:52;;;4744:1;4741;4734:12;4696:52;4783:9;4770:23;4802:30;4826:5;4802:30;:::i;4867:249::-;4936:6;4989:2;4977:9;4968:7;4964:23;4960:32;4957:52;;;5005:1;5002;4995:12;4957:52;5037:9;5031:16;5056:30;5080:5;5056:30;:::i;9546:635::-;9626:6;9679:2;9667:9;9658:7;9654:23;9650:32;9647:52;;;9695:1;9692;9685:12;9647:52;9722:16;;-1:-1:-1;;;;;9750:30:1;;9747:50;;;9793:1;9790;9783:12;9747:50;9816:22;;9869:4;9861:13;;9857:27;-1:-1:-1;9847:55:1;;9898:1;9895;9888:12;9847:55;9927:2;9921:9;9952:48;9968:31;9996:2;9968:31;:::i;9952:48::-;10023:2;10016:5;10009:17;10063:7;10058:2;10053;10049;10045:11;10041:20;10038:33;10035:53;;;10084:1;10081;10074:12;10035:53;10097:54;10148:2;10143;10136:5;10132:14;10127:2;10123;10119:11;10097:54;:::i;:::-;10170:5;9546:635;-1:-1:-1;;;;;9546:635:1:o;10186:1283::-;10349:6;10357;10365;10373;10381;10389;10397;10450:3;10438:9;10429:7;10425:23;10421:33;10418:53;;;10467:1;10464;10457:12;10418:53;10494:23;;-1:-1:-1;;;;;10566:14:1;;;10563:34;;;10593:1;10590;10583:12;10563:34;10616:50;10658:7;10649:6;10638:9;10634:22;10616:50;:::i;:::-;10606:60;;10719:2;10708:9;10704:18;10691:32;10675:48;;10748:2;10738:8;10735:16;10732:36;;;10764:1;10761;10754:12;10732:36;10787:52;10831:7;10820:8;10809:9;10805:24;10787:52;:::i;:::-;10777:62;;10892:2;10881:9;10877:18;10864:32;10848:48;;10921:2;10911:8;10908:16;10905:36;;;10937:1;10934;10927:12;10905:36;10960:52;11004:7;10993:8;10982:9;10978:24;10960:52;:::i;:::-;10950:62;;11065:2;11054:9;11050:18;11037:32;11021:48;;11094:2;11084:8;11081:16;11078:36;;;11110:1;11107;11100:12;11078:36;11133:52;11177:7;11166:8;11155:9;11151:24;11133:52;:::i;:::-;11123:62;;11238:3;11227:9;11223:19;11210:33;11194:49;;11268:2;11258:8;11255:16;11252:36;;;11284:1;11281;11274:12;11252:36;;11307:52;11351:7;11340:8;11329:9;11325:24;11307:52;:::i;:::-;11297:62;;;11406:3;11395:9;11391:19;11378:33;11368:43;;11458:3;11447:9;11443:19;11430:33;11420:43;;10186:1283;;;;;;;;;;:::o;11474:812::-;11590:6;11598;11606;11614;11667:3;11655:9;11646:7;11642:23;11638:33;11635:53;;;11684:1;11681;11674:12;11635:53;11711:23;;-1:-1:-1;;;;;11783:14:1;;;11780:34;;;11810:1;11807;11800:12;11780:34;11833:50;11875:7;11866:6;11855:9;11851:22;11833:50;:::i;:::-;11823:60;;11936:2;11925:9;11921:18;11908:32;11892:48;;11965:2;11955:8;11952:16;11949:36;;;11981:1;11978;11971:12;11949:36;12004:52;12048:7;12037:8;12026:9;12022:24;12004:52;:::i;:::-;11994:62;;12109:2;12098:9;12094:18;12081:32;12065:48;;12138:2;12128:8;12125:16;12122:36;;;12154:1;12151;12144:12;12122:36;;12177:52;12221:7;12210:8;12199:9;12195:24;12177:52;:::i;:::-;11474:812;;;;-1:-1:-1;12167:62:1;;12276:2;12261:18;12248:32;;-1:-1:-1;;;11474:812:1:o;12291:180::-;12350:6;12403:2;12391:9;12382:7;12378:23;12374:32;12371:52;;;12419:1;12416;12409:12;12371:52;-1:-1:-1;12442:23:1;;12291:180;-1:-1:-1;12291:180:1:o;12476:184::-;12546:6;12599:2;12587:9;12578:7;12574:23;12570:32;12567:52;;;12615:1;12612;12605:12;12567:52;-1:-1:-1;12638:16:1;;12476:184;-1:-1:-1;12476:184:1:o;12665:248::-;12733:6;12741;12794:2;12782:9;12773:7;12769:23;12765:32;12762:52;;;12810:1;12807;12800:12;12762:52;-1:-1:-1;;12833:23:1;;;12903:2;12888:18;;;12875:32;;-1:-1:-1;12665:248:1:o;12918:383::-;12995:6;13003;13011;13064:2;13052:9;13043:7;13039:23;13035:32;13032:52;;;13080:1;13077;13070:12;13032:52;13116:9;13103:23;13093:33;;13173:2;13162:9;13158:18;13145:32;13135:42;;13227:2;13216:9;13212:18;13199:32;13240:31;13265:5;13240:31;:::i;:::-;13290:5;13280:15;;;12918:383;;;;;:::o;13306:257::-;13347:3;13385:5;13379:12;13412:6;13407:3;13400:19;13428:63;13484:6;13477:4;13472:3;13468:14;13461:4;13454:5;13450:16;13428:63;:::i;:::-;13545:2;13524:15;-1:-1:-1;;13520:29:1;13511:39;;;;13552:4;13507:50;;13306:257;-1:-1:-1;;13306:257:1:o;13568:276::-;13699:3;13737:6;13731:13;13753:53;13799:6;13794:3;13787:4;13779:6;13775:17;13753:53;:::i;:::-;13822:16;;;;;13568:276;-1:-1:-1;;13568:276:1:o;13849:470::-;14028:3;14066:6;14060:13;14082:53;14128:6;14123:3;14116:4;14108:6;14104:17;14082:53;:::i;:::-;14198:13;;14157:16;;;;14220:57;14198:13;14157:16;14254:4;14242:17;;14220:57;:::i;:::-;14293:20;;13849:470;-1:-1:-1;;;;13849:470:1:o;14324:203::-;-1:-1:-1;;;;;14488:32:1;;;;14470:51;;14458:2;14443:18;;14324:203::o;14532:375::-;-1:-1:-1;;;;;14790:15:1;;;14772:34;;14842:15;;;;14837:2;14822:18;;14815:43;14889:2;14874:18;;14867:34;;;;14722:2;14707:18;;14532:375::o;14912:488::-;-1:-1:-1;;;;;15181:15:1;;;15163:34;;15233:15;;15228:2;15213:18;;15206:43;15280:2;15265:18;;15258:34;;;15328:3;15323:2;15308:18;;15301:31;;;15106:4;;15349:45;;15374:19;;15366:6;15349:45;:::i;:::-;15341:53;14912:488;-1:-1:-1;;;;;;14912:488:1:o;15405:274::-;-1:-1:-1;;;;;15597:32:1;;;;15579:51;;15661:2;15646:18;;15639:34;15567:2;15552:18;;15405:274::o;16551:219::-;16700:2;16689:9;16682:21;16663:4;16720:44;16760:2;16749:9;16745:18;16737:6;16720:44;:::i;17315:248::-;17489:25;;;17545:2;17530:18;;17523:34;17477:2;17462:18;;17315:248::o;17568:275::-;17639:2;17633:9;17704:2;17685:13;;-1:-1:-1;;17681:27:1;17669:40;;-1:-1:-1;;;;;17724:34:1;;17760:22;;;17721:62;17718:88;;;17786:18;;:::i;:::-;17822:2;17815:22;17568:275;;-1:-1:-1;17568:275:1:o;17848:186::-;17896:4;-1:-1:-1;;;;;17918:30:1;;17915:56;;;17951:18;;:::i;:::-;-1:-1:-1;18017:2:1;17996:15;-1:-1:-1;;17992:29:1;18023:4;17988:40;;17848:186::o;18039:128::-;18079:3;18110:1;18106:6;18103:1;18100:13;18097:39;;;18116:18;;:::i;:::-;-1:-1:-1;18152:9:1;;18039:128::o;18172:217::-;18212:1;18238;18228:132;;18282:10;18277:3;18273:20;18270:1;18263:31;18317:4;18314:1;18307:15;18345:4;18342:1;18335:15;18228:132;-1:-1:-1;18374:9:1;;18172:217::o;18394:168::-;18434:7;18500:1;18496;18492:6;18488:14;18485:1;18482:21;18477:1;18470:9;18463:17;18459:45;18456:71;;;18507:18;;:::i;:::-;-1:-1:-1;18547:9:1;;18394:168::o;18567:125::-;18607:4;18635:1;18632;18629:8;18626:34;;;18640:18;;:::i;:::-;-1:-1:-1;18677:9:1;;18567:125::o;18697:258::-;18769:1;18779:113;18793:6;18790:1;18787:13;18779:113;;;18869:11;;;18863:18;18850:11;;;18843:39;18815:2;18808:10;18779:113;;;18910:6;18907:1;18904:13;18901:48;;;-1:-1:-1;;18945:1:1;18927:16;;18920:27;18697:258::o;18960:380::-;19039:1;19035:12;;;;19082;;;19103:61;;19157:4;19149:6;19145:17;19135:27;;19103:61;19210:2;19202:6;19199:14;19179:18;19176:38;19173:161;;;19256:10;19251:3;19247:20;19244:1;19237:31;19291:4;19288:1;19281:15;19319:4;19316:1;19309:15;19173:161;;18960:380;;;:::o;19345:135::-;19384:3;-1:-1:-1;;19405:17:1;;19402:43;;;19425:18;;:::i;:::-;-1:-1:-1;19472:1:1;19461:13;;19345:135::o;19485:127::-;19546:10;19541:3;19537:20;19534:1;19527:31;19577:4;19574:1;19567:15;19601:4;19598:1;19591:15;19749:127;19810:10;19805:3;19801:20;19798:1;19791:31;19841:4;19838:1;19831:15;19865:4;19862:1;19855:15;19881:127;19942:10;19937:3;19933:20;19930:1;19923:31;19973:4;19970:1;19963:15;19997:4;19994:1;19987:15;20013:127;20074:10;20069:3;20065:20;20062:1;20055:31;20105:4;20102:1;20095:15;20129:4;20126:1;20119:15;20145:131;-1:-1:-1;;;;;20220:31:1;;20210:42;;20200:70;;20266:1;20263;20256:12;20281:118;20367:5;20360:13;20353:21;20346:5;20343:32;20333:60;;20389:1;20386;20379:12;20404:131;-1:-1:-1;;;;;;20478:32:1;;20468:43;;20458:71;;20525:1;20522;20515:12

Swarm Source

ipfs://868a46f6c1de263806a361876d5eb1974f5af51d22d0d93939ad068c17c9105e

Block Transaction Gas Used Reward
view all blocks collator

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.