ETH Price: $2,679.64 (+5.73%)
Gas: 7.55 Gwei

Transaction Decoder

Block:
19351329 at Mar-03-2024 01:22:11 AM +UTC
Transaction Fee:
0.003136935376195947 ETH $8.41
Gas Used:
79,593 Gas / 39.412201779 Gwei

Emitted Events:

187 Claims.FundsClaimed( by=[Sender] 0xe86b734e0e49ec7a4c8f5fa5671e7a795548ec21, round=21, token=0xEeeeeEee...eeeeeEEeE, amount=3609730000000000000 )
188 TetherToken.Transfer( from=[Receiver] Claims, to=[Sender] 0xe86b734e0e49ec7a4c8f5fa5671e7a795548ec21, value=1428965000 )
189 Claims.FundsClaimed( by=[Sender] 0xe86b734e0e49ec7a4c8f5fa5671e7a795548ec21, round=21, token=TetherToken, amount=1428965000 )

Account State Difference:

  Address   Before After State Difference Code
0x2cb19740...64EF28d4c 425.189048994246971246 Eth421.579318994246971246 Eth3.60973
(Titan Builder)
58.85110663570712171 Eth58.85112096244712171 Eth0.00001432674
0xdAC17F95...13D831ec7
0xE86b734e...95548eC21
0.964788228590060304 Eth
Nonce: 37
4.571381293213864357 Eth
Nonce: 38
3.606593064623804053

Execution Trace

Claims.claim( round=21, tokens=[0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, 0xdAC17F958D2ee523a2206206994597C13D831ec7] )
  • PreSaleDop.rounds( 21 ) => ( startTime=1708664400, endTime=1709218800, price=60000000000000000 )
  • ETH 3.60973 0xe86b734e0e49ec7a4c8f5fa5671e7a795548ec21.CALL( )
  • TetherToken.transfer( _to=0xE86b734e0e49Ec7a4c8F5FA5671e7A795548eC21, _value=1428965000 )
    File 1 of 3: Claims
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (access/AccessControl.sol)
    pragma solidity ^0.8.20;
    import {IAccessControl} from "./IAccessControl.sol";
    import {Context} from "../utils/Context.sol";
    import {ERC165} from "../utils/introspection/ERC165.sol";
    /**
     * @dev Contract module that allows children to implement role-based access
     * control mechanisms. This is a lightweight version that doesn't allow enumerating role
     * members except through off-chain means by accessing the contract event logs. Some
     * applications may benefit from on-chain enumerability, for those cases see
     * {AccessControlEnumerable}.
     *
     * Roles are referred to by their `bytes32` identifier. These should be exposed
     * in the external API and be unique. The best way to achieve this is by
     * using `public constant` hash digests:
     *
     * ```solidity
     * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
     * ```
     *
     * Roles can be used to represent a set of permissions. To restrict access to a
     * function call, use {hasRole}:
     *
     * ```solidity
     * function foo() public {
     *     require(hasRole(MY_ROLE, msg.sender));
     *     ...
     * }
     * ```
     *
     * Roles can be granted and revoked dynamically via the {grantRole} and
     * {revokeRole} functions. Each role has an associated admin role, and only
     * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
     *
     * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
     * that only accounts with this role will be able to grant or revoke other
     * roles. More complex role relationships can be created by using
     * {_setRoleAdmin}.
     *
     * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
     * grant and revoke this role. Extra precautions should be taken to secure
     * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
     * to enforce additional security measures for this role.
     */
    abstract contract AccessControl is Context, IAccessControl, ERC165 {
        struct RoleData {
            mapping(address account => bool) hasRole;
            bytes32 adminRole;
        }
        mapping(bytes32 role => RoleData) private _roles;
        bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
        /**
         * @dev Modifier that checks that an account has a specific role. Reverts
         * with an {AccessControlUnauthorizedAccount} error including the required role.
         */
        modifier onlyRole(bytes32 role) {
            _checkRole(role);
            _;
        }
        /**
         * @dev See {IERC165-supportsInterface}.
         */
        function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
            return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
        }
        /**
         * @dev Returns `true` if `account` has been granted `role`.
         */
        function hasRole(bytes32 role, address account) public view virtual returns (bool) {
            return _roles[role].hasRole[account];
        }
        /**
         * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`
         * is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.
         */
        function _checkRole(bytes32 role) internal view virtual {
            _checkRole(role, _msgSender());
        }
        /**
         * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
         * is missing `role`.
         */
        function _checkRole(bytes32 role, address account) internal view virtual {
            if (!hasRole(role, account)) {
                revert AccessControlUnauthorizedAccount(account, role);
            }
        }
        /**
         * @dev Returns the admin role that controls `role`. See {grantRole} and
         * {revokeRole}.
         *
         * To change a role's admin, use {_setRoleAdmin}.
         */
        function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {
            return _roles[role].adminRole;
        }
        /**
         * @dev Grants `role` to `account`.
         *
         * If `account` had not been already granted `role`, emits a {RoleGranted}
         * event.
         *
         * Requirements:
         *
         * - the caller must have ``role``'s admin role.
         *
         * May emit a {RoleGranted} event.
         */
        function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
            _grantRole(role, account);
        }
        /**
         * @dev Revokes `role` from `account`.
         *
         * If `account` had been granted `role`, emits a {RoleRevoked} event.
         *
         * Requirements:
         *
         * - the caller must have ``role``'s admin role.
         *
         * May emit a {RoleRevoked} event.
         */
        function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
            _revokeRole(role, account);
        }
        /**
         * @dev Revokes `role` from the calling account.
         *
         * Roles are often managed via {grantRole} and {revokeRole}: this function's
         * purpose is to provide a mechanism for accounts to lose their privileges
         * if they are compromised (such as when a trusted device is misplaced).
         *
         * If the calling account had been revoked `role`, emits a {RoleRevoked}
         * event.
         *
         * Requirements:
         *
         * - the caller must be `callerConfirmation`.
         *
         * May emit a {RoleRevoked} event.
         */
        function renounceRole(bytes32 role, address callerConfirmation) public virtual {
            if (callerConfirmation != _msgSender()) {
                revert AccessControlBadConfirmation();
            }
            _revokeRole(role, callerConfirmation);
        }
        /**
         * @dev Sets `adminRole` as ``role``'s admin role.
         *
         * Emits a {RoleAdminChanged} event.
         */
        function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
            bytes32 previousAdminRole = getRoleAdmin(role);
            _roles[role].adminRole = adminRole;
            emit RoleAdminChanged(role, previousAdminRole, adminRole);
        }
        /**
         * @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
         *
         * Internal function without access restriction.
         *
         * May emit a {RoleGranted} event.
         */
        function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
            if (!hasRole(role, account)) {
                _roles[role].hasRole[account] = true;
                emit RoleGranted(role, account, _msgSender());
                return true;
            } else {
                return false;
            }
        }
        /**
         * @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked.
         *
         * Internal function without access restriction.
         *
         * May emit a {RoleRevoked} event.
         */
        function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
            if (hasRole(role, account)) {
                _roles[role].hasRole[account] = false;
                emit RoleRevoked(role, account, _msgSender());
                return true;
            } else {
                return false;
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (access/IAccessControl.sol)
    pragma solidity ^0.8.20;
    /**
     * @dev External interface of AccessControl declared to support ERC165 detection.
     */
    interface IAccessControl {
        /**
         * @dev The `account` is missing a role.
         */
        error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);
        /**
         * @dev The caller of a function is not the expected one.
         *
         * NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.
         */
        error AccessControlBadConfirmation();
        /**
         * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
         *
         * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
         * {RoleAdminChanged} not being emitted signaling this.
         */
        event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
        /**
         * @dev Emitted when `account` is granted `role`.
         *
         * `sender` is the account that originated the contract call, an admin role
         * bearer except when using {AccessControl-_setupRole}.
         */
        event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
        /**
         * @dev Emitted when `account` is revoked `role`.
         *
         * `sender` is the account that originated the contract call:
         *   - if using `revokeRole`, it is the admin role bearer
         *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
         */
        event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
        /**
         * @dev Returns `true` if `account` has been granted `role`.
         */
        function hasRole(bytes32 role, address account) external view returns (bool);
        /**
         * @dev Returns the admin role that controls `role`. See {grantRole} and
         * {revokeRole}.
         *
         * To change a role's admin, use {AccessControl-_setRoleAdmin}.
         */
        function getRoleAdmin(bytes32 role) external view returns (bytes32);
        /**
         * @dev Grants `role` to `account`.
         *
         * If `account` had not been already granted `role`, emits a {RoleGranted}
         * event.
         *
         * Requirements:
         *
         * - the caller must have ``role``'s admin role.
         */
        function grantRole(bytes32 role, address account) external;
        /**
         * @dev Revokes `role` from `account`.
         *
         * If `account` had been granted `role`, emits a {RoleRevoked} event.
         *
         * Requirements:
         *
         * - the caller must have ``role``'s admin role.
         */
        function revokeRole(bytes32 role, address account) external;
        /**
         * @dev Revokes `role` from the calling account.
         *
         * Roles are often managed via {grantRole} and {revokeRole}: this function's
         * purpose is to provide a mechanism for accounts to lose their privileges
         * if they are compromised (such as when a trusted device is misplaced).
         *
         * If the calling account had been granted `role`, emits a {RoleRevoked}
         * event.
         *
         * Requirements:
         *
         * - the caller must be `callerConfirmation`.
         */
        function renounceRole(bytes32 role, address callerConfirmation) external;
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)
    pragma solidity ^0.8.20;
    /**
     * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
     * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
     *
     * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
     * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
     * need to send a transaction, and thus is not required to hold Ether at all.
     *
     * ==== Security Considerations
     *
     * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
     * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
     * considered as an intention to spend the allowance in any specific way. The second is that because permits have
     * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
     * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
     * generally recommended is:
     *
     * ```solidity
     * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
     *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
     *     doThing(..., value);
     * }
     *
     * function doThing(..., uint256 value) public {
     *     token.safeTransferFrom(msg.sender, address(this), value);
     *     ...
     * }
     * ```
     *
     * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
     * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
     * {SafeERC20-safeTransferFrom}).
     *
     * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
     * contracts should have entry points that don't rely on permit.
     */
    interface IERC20Permit {
        /**
         * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
         * given ``owner``'s signed approval.
         *
         * IMPORTANT: The same issues {IERC20-approve} has related to transaction
         * ordering also apply here.
         *
         * Emits an {Approval} event.
         *
         * Requirements:
         *
         * - `spender` cannot be the zero address.
         * - `deadline` must be a timestamp in the future.
         * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
         * over the EIP712-formatted function arguments.
         * - the signature must use ``owner``'s current nonce (see {nonces}).
         *
         * For more information on the signature format, see the
         * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
         * section].
         *
         * CAUTION: See Security Considerations above.
         */
        function permit(
            address owner,
            address spender,
            uint256 value,
            uint256 deadline,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) external;
        /**
         * @dev Returns the current nonce for `owner`. This value must be
         * included whenever a signature is generated for {permit}.
         *
         * Every successful call to {permit} increases ``owner``'s nonce by one. This
         * prevents a signature from being used multiple times.
         */
        function nonces(address owner) external view returns (uint256);
        /**
         * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
         */
        // solhint-disable-next-line func-name-mixedcase
        function DOMAIN_SEPARATOR() external view returns (bytes32);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
    pragma solidity ^0.8.20;
    /**
     * @dev Interface of the ERC20 standard as defined in the EIP.
     */
    interface IERC20 {
        /**
         * @dev Emitted when `value` tokens are moved from one account (`from`) to
         * another (`to`).
         *
         * Note that `value` may be zero.
         */
        event Transfer(address indexed from, address indexed to, uint256 value);
        /**
         * @dev Emitted when the allowance of a `spender` for an `owner` is set by
         * a call to {approve}. `value` is the new allowance.
         */
        event Approval(address indexed owner, address indexed spender, uint256 value);
        /**
         * @dev Returns the value of tokens in existence.
         */
        function totalSupply() external view returns (uint256);
        /**
         * @dev Returns the value of tokens owned by `account`.
         */
        function balanceOf(address account) external view returns (uint256);
        /**
         * @dev Moves a `value` amount of tokens from the caller's account to `to`.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * Emits a {Transfer} event.
         */
        function transfer(address to, uint256 value) external returns (bool);
        /**
         * @dev Returns the remaining number of tokens that `spender` will be
         * allowed to spend on behalf of `owner` through {transferFrom}. This is
         * zero by default.
         *
         * This value changes when {approve} or {transferFrom} are called.
         */
        function allowance(address owner, address spender) external view returns (uint256);
        /**
         * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
         * caller's tokens.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * IMPORTANT: Beware that changing an allowance with this method brings the risk
         * that someone may use both the old and the new allowance by unfortunate
         * transaction ordering. One possible solution to mitigate this race
         * condition is to first reduce the spender's allowance to 0 and set the
         * desired value afterwards:
         * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
         *
         * Emits an {Approval} event.
         */
        function approve(address spender, uint256 value) external returns (bool);
        /**
         * @dev Moves a `value` amount of tokens from `from` to `to` using the
         * allowance mechanism. `value` is then deducted from the caller's
         * allowance.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * Emits a {Transfer} event.
         */
        function transferFrom(address from, address to, uint256 value) external returns (bool);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)
    pragma solidity ^0.8.20;
    import {IERC20} from "../IERC20.sol";
    import {IERC20Permit} from "../extensions/IERC20Permit.sol";
    import {Address} from "../../../utils/Address.sol";
    /**
     * @title SafeERC20
     * @dev Wrappers around ERC20 operations that throw on failure (when the token
     * contract returns false). Tokens that return no value (and instead revert or
     * throw on failure) are also supported, non-reverting calls are assumed to be
     * successful.
     * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
     * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
     */
    library SafeERC20 {
        using Address for address;
        /**
         * @dev An operation with an ERC20 token failed.
         */
        error SafeERC20FailedOperation(address token);
        /**
         * @dev Indicates a failed `decreaseAllowance` request.
         */
        error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
        /**
         * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
         * non-reverting calls are assumed to be successful.
         */
        function safeTransfer(IERC20 token, address to, uint256 value) internal {
            _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
        }
        /**
         * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
         * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
         */
        function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
            _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
        }
        /**
         * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
         * non-reverting calls are assumed to be successful.
         */
        function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
            uint256 oldAllowance = token.allowance(address(this), spender);
            forceApprove(token, spender, oldAllowance + value);
        }
        /**
         * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
         * value, non-reverting calls are assumed to be successful.
         */
        function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
            unchecked {
                uint256 currentAllowance = token.allowance(address(this), spender);
                if (currentAllowance < requestedDecrease) {
                    revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
                }
                forceApprove(token, spender, currentAllowance - requestedDecrease);
            }
        }
        /**
         * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
         * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
         * to be set to zero before setting it to a non-zero value, such as USDT.
         */
        function forceApprove(IERC20 token, address spender, uint256 value) internal {
            bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
            if (!_callOptionalReturnBool(token, approvalCall)) {
                _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
                _callOptionalReturn(token, approvalCall);
            }
        }
        /**
         * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
         * on the return value: the return value is optional (but if data is returned, it must not be false).
         * @param token The token targeted by the call.
         * @param data The call data (encoded using abi.encode or one of its variants).
         */
        function _callOptionalReturn(IERC20 token, bytes memory data) private {
            // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
            // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
            // the target address contains contract code and also asserts for success in the low-level call.
            bytes memory returndata = address(token).functionCall(data);
            if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
                revert SafeERC20FailedOperation(address(token));
            }
        }
        /**
         * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
         * on the return value: the return value is optional (but if data is returned, it must not be false).
         * @param token The token targeted by the call.
         * @param data The call data (encoded using abi.encode or one of its variants).
         *
         * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
         */
        function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
            // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
            // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
            // and not revert is the subcall reverts.
            (bool success, bytes memory returndata) = address(token).call(data);
            return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
    pragma solidity ^0.8.20;
    /**
     * @dev Collection of functions related to the address type
     */
    library Address {
        /**
         * @dev The ETH balance of the account is not enough to perform the operation.
         */
        error AddressInsufficientBalance(address account);
        /**
         * @dev There's no code at `target` (it is not a contract).
         */
        error AddressEmptyCode(address target);
        /**
         * @dev A call to an address target failed. The target may have reverted.
         */
        error FailedInnerCall();
        /**
         * @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://consensys.net/diligence/blog/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.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
         */
        function sendValue(address payable recipient, uint256 amount) internal {
            if (address(this).balance < amount) {
                revert AddressInsufficientBalance(address(this));
            }
            (bool success, ) = recipient.call{value: amount}("");
            if (!success) {
                revert FailedInnerCall();
            }
        }
        /**
         * @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 or custom error, it is bubbled
         * up by this function (like regular Solidity function calls). However, if
         * the call reverted with no returned reason, this function reverts with a
         * {FailedInnerCall} error.
         *
         * 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.
         */
        function functionCall(address target, bytes memory data) internal returns (bytes memory) {
            return functionCallWithValue(target, data, 0);
        }
        /**
         * @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`.
         */
        function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
            if (address(this).balance < value) {
                revert AddressInsufficientBalance(address(this));
            }
            (bool success, bytes memory returndata) = target.call{value: value}(data);
            return verifyCallResultFromTarget(target, success, returndata);
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but performing a static call.
         */
        function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
            (bool success, bytes memory returndata) = target.staticcall(data);
            return verifyCallResultFromTarget(target, success, returndata);
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but performing a delegate call.
         */
        function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
            (bool success, bytes memory returndata) = target.delegatecall(data);
            return verifyCallResultFromTarget(target, success, returndata);
        }
        /**
         * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
         * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
         * unsuccessful call.
         */
        function verifyCallResultFromTarget(
            address target,
            bool success,
            bytes memory returndata
        ) internal view returns (bytes memory) {
            if (!success) {
                _revert(returndata);
            } else {
                // only check if target is a contract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                if (returndata.length == 0 && target.code.length == 0) {
                    revert AddressEmptyCode(target);
                }
                return returndata;
            }
        }
        /**
         * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
         * revert reason or with a default {FailedInnerCall} error.
         */
        function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
            if (!success) {
                _revert(returndata);
            } else {
                return returndata;
            }
        }
        /**
         * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
         */
        function _revert(bytes memory returndata) 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 FailedInnerCall();
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/Context.sol)
    pragma solidity ^0.8.20;
    /**
     * @dev Provides information about the current execution context, including the
     * sender of the transaction and its data. While these are generally available
     * via msg.sender and msg.data, they should not be accessed in such a direct
     * manner, since when dealing with meta-transactions the account sending and
     * paying for execution may not be the actual sender (as far as an application
     * is concerned).
     *
     * This contract is only required for intermediate, library-like contracts.
     */
    abstract contract Context {
        function _msgSender() internal view virtual returns (address) {
            return msg.sender;
        }
        function _msgData() internal view virtual returns (bytes calldata) {
            return msg.data;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol)
    pragma solidity ^0.8.20;
    import {IERC165} from "./IERC165.sol";
    /**
     * @dev Implementation of the {IERC165} interface.
     *
     * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
     * for the additional interface id that will be supported. For example:
     *
     * ```solidity
     * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
     *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
     * }
     * ```
     */
    abstract contract ERC165 is IERC165 {
        /**
         * @dev See {IERC165-supportsInterface}.
         */
        function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
            return interfaceId == type(IERC165).interfaceId;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)
    pragma solidity ^0.8.20;
    /**
     * @dev Interface of the ERC165 standard, as defined in the
     * https://eips.ethereum.org/EIPS/eip-165[EIP].
     *
     * Implementers can declare support of contract interfaces, which can then be
     * queried by others ({ERC165Checker}).
     *
     * For an implementation, see {ERC165}.
     */
    interface IERC165 {
        /**
         * @dev Returns true if this contract implements the interface defined by
         * `interfaceId`. See the corresponding
         * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
         * to learn more about how these ids are created.
         *
         * This function call must use less than 30 000 gas.
         */
        function supportsInterface(bytes4 interfaceId) external view returns (bool);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)
    pragma solidity ^0.8.20;
    /**
     * @dev Contract module that helps prevent reentrant calls to a function.
     *
     * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
     * available, which can be applied to functions to make sure there are no nested
     * (reentrant) calls to them.
     *
     * Note that because there is a single `nonReentrant` guard, functions marked as
     * `nonReentrant` may not call one another. This can be worked around by making
     * those functions `private`, and then adding `external` `nonReentrant` entry
     * points to them.
     *
     * TIP: If you would like to learn more about reentrancy and alternative ways
     * to protect against it, check out our blog post
     * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
     */
    abstract contract ReentrancyGuard {
        // Booleans are more expensive than uint256 or any type that takes up a full
        // word because each write operation emits an extra SLOAD to first read the
        // slot's contents, replace the bits taken up by the boolean, and then write
        // back. This is the compiler's defense against contract upgrades and
        // pointer aliasing, and it cannot be disabled.
        // The values being non-zero value makes deployment a bit more expensive,
        // but in exchange the refund on every call to nonReentrant will be lower in
        // amount. Since refunds are capped to a percentage of the total
        // transaction's gas, it is best to keep them low in cases like this one, to
        // increase the likelihood of the full refund coming into effect.
        uint256 private constant NOT_ENTERED = 1;
        uint256 private constant ENTERED = 2;
        uint256 private _status;
        /**
         * @dev Unauthorized reentrant call.
         */
        error ReentrancyGuardReentrantCall();
        constructor() {
            _status = NOT_ENTERED;
        }
        /**
         * @dev Prevents a contract from calling itself, directly or indirectly.
         * Calling a `nonReentrant` function from another `nonReentrant`
         * function is not supported. It is possible to prevent this from happening
         * by making the `nonReentrant` function external, and making it call a
         * `private` function that does the actual work.
         */
        modifier nonReentrant() {
            _nonReentrantBefore();
            _;
            _nonReentrantAfter();
        }
        function _nonReentrantBefore() private {
            // On the first call to nonReentrant, _status will be NOT_ENTERED
            if (_status == ENTERED) {
                revert ReentrancyGuardReentrantCall();
            }
            // Any calls to nonReentrant after this point will fail
            _status = ENTERED;
        }
        function _nonReentrantAfter() private {
            // By storing the original value once again, a refund is triggered (see
            // https://eips.ethereum.org/EIPS/eip-2200)
            _status = NOT_ENTERED;
        }
        /**
         * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
         * `nonReentrant` function in the call stack.
         */
        function _reentrancyGuardEntered() internal view returns (bool) {
            return _status == ENTERED;
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.22;
    import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
    import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";
    import {Address} from "@openzeppelin/contracts/utils/Address.sol";
    import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
    import {IPreSaleDop} from "./IPreSaleDop.sol";
    import "./Common.sol";
    /// @title Claims contract
    /// @notice Implements the claiming of the leader's commissons
    contract Claims is AccessControl, ReentrancyGuard {
        using SafeERC20 for IERC20;
        using Address for address payable;
        /// @notice Thrown when input array length is zero
        error InvalidData();
        /// @notice Thrown when claiming before round ends
        error RoundNotEnded();
        /// @notice Thrown when round is not Enabled
        error RoundNotEnabled();
        /// @notice Thrown when CommissionsManager wants to setClaim while claim enable
        error WaitForRoundDisable();
        /// @notice Returns the identifier of the CommissionsManager role
        bytes32 public constant COMMISSIONS_MANAGER =
            keccak256("COMMISSIONS_MANAGER");
        /// @notice Returns the identifier of the AdminRole role
        bytes32 public constant ADMIN_ROLE = keccak256("ADMIN");
        /// @notice Returns the address of PreSaleDop contract
        IPreSaleDop public presaleDop;
        /// @member token The token address
        /// @member amount The token amount
        struct ClaimInfo {
            IERC20 token;
            uint256 amount;
        }
        /// @notice Stores the amount of token in a round of the user
        mapping(address => mapping(uint32 => mapping(IERC20 => uint256)))
            public pendingClaims;
        /// @notice Stores the enabled/disabled status of a round
        mapping(uint32 => bool) public isEnabled;
        /// @dev Emitted when claim amount is set for the addresses
        event ClaimSet(
            address[] indexed to,
            uint32 indexed round,
            IERC20 token,
            uint256 amount
        );
        /// @dev Emitted when claim amount is claimed
        event FundsClaimed(
            address indexed by,
            uint32 indexed round,
            IERC20 token,
            uint256 amount
        );
        /// @dev Emitted when claim access changes for the round
        event RoundEnableUpdated(bool oldAccess, bool newAccess);
        /// @dev Emitted when dop preSale contract is updated
        event PresaleDopUpdated(address oldpresaleDop, address newpresaleDop);
        /// @dev Constructor.
        constructor() {
            _setRoleAdmin(ADMIN_ROLE, ADMIN_ROLE);
            _setRoleAdmin(COMMISSIONS_MANAGER, ADMIN_ROLE);
            _grantRole(ADMIN_ROLE, msg.sender);
        }
        /// @notice Updates token amounts to addresses in a given round
        /// @param to The array of claimants
        /// @param round The round number
        /// @param claims The tokens and amount to claim
        function setClaim(
            address[] calldata to,
            uint32 round,
            ClaimInfo[][] memory claims
        ) external onlyRole(COMMISSIONS_MANAGER) {
            if (isEnabled[round]) {
                revert WaitForRoundDisable();
            }
            uint256 toLength = to.length;
            if (toLength == 0) {
                revert InvalidData();
            }
            if (toLength != claims.length) {
                revert ArrayLengthMismatch();
            }
            for (uint256 i; i < toLength; ++i) {
                mapping(IERC20 => uint256) storage claimInfo = pendingClaims[to[i]][
                    round
                ];
                for (uint256 j = 0; j < claims[i].length; j++) {
                    ClaimInfo memory amount = claims[i][j];
                    claimInfo[amount.token] = amount.amount;
                    emit ClaimSet({
                        to: to,
                        round: round,
                        token: amount.token,
                        amount: amount.amount
                    });
                }
            }
        }
        /// @notice Claims the amount in a given round
        /// @param round The round in which you want to claim
        /// @param tokens The addresses of the token to be claimed
        function claim(
            uint32 round,
            IERC20[] calldata tokens
        ) external nonReentrant {
            _checkRoundAndTime(round);
            mapping(IERC20 => uint256) storage claimInfo = pendingClaims[
                msg.sender
            ][round];
            for (uint256 i = 0; i < tokens.length; ++i) {
                IERC20 token = tokens[i];
                uint256 amount = claimInfo[token];
                if (amount == 0) {
                    continue;
                }
                delete claimInfo[token];
                if (token == ETH) {
                    payable(msg.sender).sendValue(amount);
                } else {
                    token.safeTransfer(msg.sender, amount);
                }
                emit FundsClaimed({
                    by: msg.sender,
                    round: round,
                    token: token,
                    amount: amount
                });
            }
        }
        /// @notice Purchases Dop Token with claim amounts
        /// @param round The round in which user will purchase
        /// @param deadline The deadline of the signature
        /// @param tokenPrices The current prices of the tokens in 10 decimals
        /// @param tokens The address of the tokens
        /// @param amounts The Investment amounts
        /// @param v The `v` signature parameter
        /// @param r The `r` signature parameter
        /// @param s The `s` signature parameter
        function purchaseWithClaim(
            uint32 round,
            uint256 deadline,
            uint8[] calldata normalizationFactors,
            uint256[] calldata tokenPrices,
            IERC20[] calldata tokens,
            uint256[] calldata amounts,
            uint256[] calldata minAmountsDop,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) external nonReentrant {
            _checkRoundAndTime(round);
            if (normalizationFactors.length == 0) {
                revert ZeroLengthArray();
            }
            if (
                normalizationFactors.length != tokenPrices.length ||
                tokenPrices.length != tokens.length ||
                tokens.length != amounts.length ||
                amounts.length != minAmountsDop.length
            ) {
                revert ArrayLengthMismatch();
            }
            _verifyPurchaseWithClaim(
                msg.sender,
                round,
                deadline,
                tokenPrices,
                normalizationFactors,
                tokens,
                amounts,
                v,
                r,
                s
            );
            mapping(IERC20 => uint256) storage info = pendingClaims[msg.sender][
                round
            ];
            for (uint256 i = 0; i < tokens.length; ++i) {
                IERC20 token = tokens[i];
                uint256 amountToInvest = amounts[i];
                uint8 normalizationFactor = normalizationFactors[i];
                uint256 minAmountDop = minAmountsDop[i];
                uint256 amount = info[token];
                if (amount == 0) {
                    continue;
                }
                if (amountToInvest > amount) {
                    continue;
                }
                delete info[token];
                uint256 remainingAmount = amount - amountToInvest;
                if (token == ETH) {
                    presaleDop.purchaseWithClaim{value: amountToInvest}(
                        ETH,
                        0,
                        normalizationFactor,
                        amountToInvest,
                        minAmountDop,
                        msg.sender,
                        round
                    );
                } else {
                    // address presaleContract = address(presaleDop);
                    uint256 allowance = token.allowance(
                        address(this),
                        address(presaleDop)
                    );
                    if (allowance < amountToInvest) {
                        token.forceApprove(address(presaleDop), type(uint256).max);
                    }
                    presaleDop.purchaseWithClaim(
                        token,
                        tokenPrices[i],
                        normalizationFactor,
                        amountToInvest,
                        minAmountDop,
                        msg.sender,
                        round
                    );
                }
                if (remainingAmount > 0) {
                    if (token == ETH) {
                        payable(msg.sender).sendValue(remainingAmount);
                    } else {
                        token.safeTransfer(msg.sender, remainingAmount);
                    }
                    emit FundsClaimed({
                        by: msg.sender,
                        round: round,
                        token: token,
                        amount: remainingAmount
                    });
                }
            }
        }
        // The tokenPrices,tokens are provided externally and therefore have to be verified by the trusted presale contract
        function _verifyPurchaseWithClaim(
            address by,
            uint32 round,
            uint256 deadline,
            uint256[] calldata tokenPrices,
            uint8[] calldata normalizationFactors,
            IERC20[] calldata tokens,
            uint256[] calldata amounts,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) private {
            presaleDop.verifyPurchaseWithClaim(
                by,
                round,
                deadline,
                tokenPrices,
                normalizationFactors,
                tokens,
                amounts,
                v,
                r,
                s
            );
        }
        /// @notice Verifies round and time
        function _checkRoundAndTime(uint32 round) internal view {
            if (!isEnabled[round]) {
                revert RoundNotEnabled();
            }
            (, uint256 endTime, ) = presaleDop.rounds(round);
            if (block.timestamp < endTime) {
                revert RoundNotEnded();
            }
        }
        /// @notice Changes PresaleDop contract address
        /// @param presaleDopAddress The address of the new PreSaleDop contract
        function updatePreSaleAddress(
            IPreSaleDop presaleDopAddress
        ) external onlyRole(ADMIN_ROLE) {
            if (address(presaleDopAddress) == address(0)) {
                revert ZeroAddress();
            }
            if (presaleDop == presaleDopAddress) {
                revert IdenticalValue();
            }
            emit PresaleDopUpdated({
                oldpresaleDop: address(presaleDop),
                newpresaleDop: address(presaleDopAddress)
            });
            presaleDop = presaleDopAddress;
        }
        /// @notice Changes the Claim access of the contract
        /// @param round The round number of which access is changed
        /// @param status The access status of the round
        function enableClaims(
            uint32 round,
            bool status
        ) public onlyRole(COMMISSIONS_MANAGER) {
            bool oldAccess = isEnabled[round];
            if (oldAccess == status) {
                revert IdenticalValue();
            }
            emit RoundEnableUpdated({oldAccess: oldAccess, newAccess: status});
            isEnabled[round] = status;
        }
        receive() external payable {}
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.22;
    import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    /// @notice Thrown when updating an address with zero address
    error ZeroAddress();
    /// @notice Thrown when updating with an array of no values
    error ZeroLengthArray();
    /// @notice Thrown when updating with the same value as previously stored
    error IdenticalValue();
    /// @notice Thrown when two array lengths does not match
    error ArrayLengthMismatch();
    /// @dev The address of the Ethereum
    IERC20 constant ETH = IERC20(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.22;
    import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    import {IRounds} from "./IRounds.sol";
    interface IPreSaleDop is IRounds {
        /// @notice Purchases Dop token with claim amount
        /// @param token The address of investment token
        /// @param tokenPrice The current price of token in 10 decimals
        /// @param referenceNormalizationFactor The value to handle decimals
        /// @param amount The investment amount
        /// @param minAmountDop The minimum amount of dop recipient will get
        /// @param recipient The address of the recipient
        /// @param round The round in which user will purchase
        function purchaseWithClaim(
            IERC20 token,
            uint256 tokenPrice,
            uint8 referenceNormalizationFactor,
            uint256 amount,
            uint256 minAmountDop,
            address recipient,
            uint32 round
        ) external payable;
        /// @notice The helper function which verifies signature, signed by signerWallet, reverts if invalidSignature
        function verifyPurchaseWithClaim(
            address recipient,
            uint32 round,
            uint256 deadline,
            uint256[] calldata tokenPrices,
            uint8[] calldata normalizationFactors,
            IERC20[] calldata tokens,
            uint256[] calldata amounts,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) external;
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.22;
    interface IRounds {
        /// @notice Returns the round details of the round numberz
        function rounds(
            uint32 round
        ) external view returns (uint256 startTime, uint256 endTime, uint256 price);
    }
    

    File 2 of 3: TetherToken
    pragma solidity ^0.4.17;
    
    /**
     * @title SafeMath
     * @dev Math operations with safety checks that throw on error
     */
    library SafeMath {
        function mul(uint256 a, uint256 b) internal pure returns (uint256) {
            if (a == 0) {
                return 0;
            }
            uint256 c = a * b;
            assert(c / a == b);
            return c;
        }
    
        function div(uint256 a, uint256 b) internal pure returns (uint256) {
            // assert(b > 0); // Solidity automatically throws when dividing by 0
            uint256 c = a / b;
            // assert(a == b * c + a % b); // There is no case in which this doesn't hold
            return c;
        }
    
        function sub(uint256 a, uint256 b) internal pure returns (uint256) {
            assert(b <= a);
            return a - b;
        }
    
        function add(uint256 a, uint256 b) internal pure returns (uint256) {
            uint256 c = a + b;
            assert(c >= a);
            return c;
        }
    }
    
    /**
     * @title Ownable
     * @dev The Ownable contract has an owner address, and provides basic authorization control
     * functions, this simplifies the implementation of "user permissions".
     */
    contract Ownable {
        address public owner;
    
        /**
          * @dev The Ownable constructor sets the original `owner` of the contract to the sender
          * account.
          */
        function Ownable() public {
            owner = msg.sender;
        }
    
        /**
          * @dev Throws if called by any account other than the owner.
          */
        modifier onlyOwner() {
            require(msg.sender == owner);
            _;
        }
    
        /**
        * @dev Allows the current owner to transfer control of the contract to a newOwner.
        * @param newOwner The address to transfer ownership to.
        */
        function transferOwnership(address newOwner) public onlyOwner {
            if (newOwner != address(0)) {
                owner = newOwner;
            }
        }
    
    }
    
    /**
     * @title ERC20Basic
     * @dev Simpler version of ERC20 interface
     * @dev see https://github.com/ethereum/EIPs/issues/20
     */
    contract ERC20Basic {
        uint public _totalSupply;
        function totalSupply() public constant returns (uint);
        function balanceOf(address who) public constant returns (uint);
        function transfer(address to, uint value) public;
        event Transfer(address indexed from, address indexed to, uint value);
    }
    
    /**
     * @title ERC20 interface
     * @dev see https://github.com/ethereum/EIPs/issues/20
     */
    contract ERC20 is ERC20Basic {
        function allowance(address owner, address spender) public constant returns (uint);
        function transferFrom(address from, address to, uint value) public;
        function approve(address spender, uint value) public;
        event Approval(address indexed owner, address indexed spender, uint value);
    }
    
    /**
     * @title Basic token
     * @dev Basic version of StandardToken, with no allowances.
     */
    contract BasicToken is Ownable, ERC20Basic {
        using SafeMath for uint;
    
        mapping(address => uint) public balances;
    
        // additional variables for use if transaction fees ever became necessary
        uint public basisPointsRate = 0;
        uint public maximumFee = 0;
    
        /**
        * @dev Fix for the ERC20 short address attack.
        */
        modifier onlyPayloadSize(uint size) {
            require(!(msg.data.length < size + 4));
            _;
        }
    
        /**
        * @dev transfer token for a specified address
        * @param _to The address to transfer to.
        * @param _value The amount to be transferred.
        */
        function transfer(address _to, uint _value) public onlyPayloadSize(2 * 32) {
            uint fee = (_value.mul(basisPointsRate)).div(10000);
            if (fee > maximumFee) {
                fee = maximumFee;
            }
            uint sendAmount = _value.sub(fee);
            balances[msg.sender] = balances[msg.sender].sub(_value);
            balances[_to] = balances[_to].add(sendAmount);
            if (fee > 0) {
                balances[owner] = balances[owner].add(fee);
                Transfer(msg.sender, owner, fee);
            }
            Transfer(msg.sender, _to, sendAmount);
        }
    
        /**
        * @dev Gets the balance of the specified address.
        * @param _owner The address to query the the balance of.
        * @return An uint representing the amount owned by the passed address.
        */
        function balanceOf(address _owner) public constant returns (uint balance) {
            return balances[_owner];
        }
    
    }
    
    /**
     * @title Standard ERC20 token
     *
     * @dev Implementation of the basic standard token.
     * @dev https://github.com/ethereum/EIPs/issues/20
     * @dev Based oncode by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
     */
    contract StandardToken is BasicToken, ERC20 {
    
        mapping (address => mapping (address => uint)) public allowed;
    
        uint public constant MAX_UINT = 2**256 - 1;
    
        /**
        * @dev Transfer tokens from one address to another
        * @param _from address The address which you want to send tokens from
        * @param _to address The address which you want to transfer to
        * @param _value uint the amount of tokens to be transferred
        */
        function transferFrom(address _from, address _to, uint _value) public onlyPayloadSize(3 * 32) {
            var _allowance = allowed[_from][msg.sender];
    
            // Check is not needed because sub(_allowance, _value) will already throw if this condition is not met
            // if (_value > _allowance) throw;
    
            uint fee = (_value.mul(basisPointsRate)).div(10000);
            if (fee > maximumFee) {
                fee = maximumFee;
            }
            if (_allowance < MAX_UINT) {
                allowed[_from][msg.sender] = _allowance.sub(_value);
            }
            uint sendAmount = _value.sub(fee);
            balances[_from] = balances[_from].sub(_value);
            balances[_to] = balances[_to].add(sendAmount);
            if (fee > 0) {
                balances[owner] = balances[owner].add(fee);
                Transfer(_from, owner, fee);
            }
            Transfer(_from, _to, sendAmount);
        }
    
        /**
        * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
        * @param _spender The address which will spend the funds.
        * @param _value The amount of tokens to be spent.
        */
        function approve(address _spender, uint _value) public onlyPayloadSize(2 * 32) {
    
            // To change the approve amount you first have to reduce the addresses`
            //  allowance to zero by calling `approve(_spender, 0)` if it is not
            //  already 0 to mitigate the race condition described here:
            //  https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
            require(!((_value != 0) && (allowed[msg.sender][_spender] != 0)));
    
            allowed[msg.sender][_spender] = _value;
            Approval(msg.sender, _spender, _value);
        }
    
        /**
        * @dev Function to check the amount of tokens than an owner allowed to a spender.
        * @param _owner address The address which owns the funds.
        * @param _spender address The address which will spend the funds.
        * @return A uint specifying the amount of tokens still available for the spender.
        */
        function allowance(address _owner, address _spender) public constant returns (uint remaining) {
            return allowed[_owner][_spender];
        }
    
    }
    
    
    /**
     * @title Pausable
     * @dev Base contract which allows children to implement an emergency stop mechanism.
     */
    contract Pausable is Ownable {
      event Pause();
      event Unpause();
    
      bool public paused = false;
    
    
      /**
       * @dev Modifier to make a function callable only when the contract is not paused.
       */
      modifier whenNotPaused() {
        require(!paused);
        _;
      }
    
      /**
       * @dev Modifier to make a function callable only when the contract is paused.
       */
      modifier whenPaused() {
        require(paused);
        _;
      }
    
      /**
       * @dev called by the owner to pause, triggers stopped state
       */
      function pause() onlyOwner whenNotPaused public {
        paused = true;
        Pause();
      }
    
      /**
       * @dev called by the owner to unpause, returns to normal state
       */
      function unpause() onlyOwner whenPaused public {
        paused = false;
        Unpause();
      }
    }
    
    contract BlackList is Ownable, BasicToken {
    
        /////// Getters to allow the same blacklist to be used also by other contracts (including upgraded Tether) ///////
        function getBlackListStatus(address _maker) external constant returns (bool) {
            return isBlackListed[_maker];
        }
    
        function getOwner() external constant returns (address) {
            return owner;
        }
    
        mapping (address => bool) public isBlackListed;
        
        function addBlackList (address _evilUser) public onlyOwner {
            isBlackListed[_evilUser] = true;
            AddedBlackList(_evilUser);
        }
    
        function removeBlackList (address _clearedUser) public onlyOwner {
            isBlackListed[_clearedUser] = false;
            RemovedBlackList(_clearedUser);
        }
    
        function destroyBlackFunds (address _blackListedUser) public onlyOwner {
            require(isBlackListed[_blackListedUser]);
            uint dirtyFunds = balanceOf(_blackListedUser);
            balances[_blackListedUser] = 0;
            _totalSupply -= dirtyFunds;
            DestroyedBlackFunds(_blackListedUser, dirtyFunds);
        }
    
        event DestroyedBlackFunds(address _blackListedUser, uint _balance);
    
        event AddedBlackList(address _user);
    
        event RemovedBlackList(address _user);
    
    }
    
    contract UpgradedStandardToken is StandardToken{
        // those methods are called by the legacy contract
        // and they must ensure msg.sender to be the contract address
        function transferByLegacy(address from, address to, uint value) public;
        function transferFromByLegacy(address sender, address from, address spender, uint value) public;
        function approveByLegacy(address from, address spender, uint value) public;
    }
    
    contract TetherToken is Pausable, StandardToken, BlackList {
    
        string public name;
        string public symbol;
        uint public decimals;
        address public upgradedAddress;
        bool public deprecated;
    
        //  The contract can be initialized with a number of tokens
        //  All the tokens are deposited to the owner address
        //
        // @param _balance Initial supply of the contract
        // @param _name Token Name
        // @param _symbol Token symbol
        // @param _decimals Token decimals
        function TetherToken(uint _initialSupply, string _name, string _symbol, uint _decimals) public {
            _totalSupply = _initialSupply;
            name = _name;
            symbol = _symbol;
            decimals = _decimals;
            balances[owner] = _initialSupply;
            deprecated = false;
        }
    
        // Forward ERC20 methods to upgraded contract if this one is deprecated
        function transfer(address _to, uint _value) public whenNotPaused {
            require(!isBlackListed[msg.sender]);
            if (deprecated) {
                return UpgradedStandardToken(upgradedAddress).transferByLegacy(msg.sender, _to, _value);
            } else {
                return super.transfer(_to, _value);
            }
        }
    
        // Forward ERC20 methods to upgraded contract if this one is deprecated
        function transferFrom(address _from, address _to, uint _value) public whenNotPaused {
            require(!isBlackListed[_from]);
            if (deprecated) {
                return UpgradedStandardToken(upgradedAddress).transferFromByLegacy(msg.sender, _from, _to, _value);
            } else {
                return super.transferFrom(_from, _to, _value);
            }
        }
    
        // Forward ERC20 methods to upgraded contract if this one is deprecated
        function balanceOf(address who) public constant returns (uint) {
            if (deprecated) {
                return UpgradedStandardToken(upgradedAddress).balanceOf(who);
            } else {
                return super.balanceOf(who);
            }
        }
    
        // Forward ERC20 methods to upgraded contract if this one is deprecated
        function approve(address _spender, uint _value) public onlyPayloadSize(2 * 32) {
            if (deprecated) {
                return UpgradedStandardToken(upgradedAddress).approveByLegacy(msg.sender, _spender, _value);
            } else {
                return super.approve(_spender, _value);
            }
        }
    
        // Forward ERC20 methods to upgraded contract if this one is deprecated
        function allowance(address _owner, address _spender) public constant returns (uint remaining) {
            if (deprecated) {
                return StandardToken(upgradedAddress).allowance(_owner, _spender);
            } else {
                return super.allowance(_owner, _spender);
            }
        }
    
        // deprecate current contract in favour of a new one
        function deprecate(address _upgradedAddress) public onlyOwner {
            deprecated = true;
            upgradedAddress = _upgradedAddress;
            Deprecate(_upgradedAddress);
        }
    
        // deprecate current contract if favour of a new one
        function totalSupply() public constant returns (uint) {
            if (deprecated) {
                return StandardToken(upgradedAddress).totalSupply();
            } else {
                return _totalSupply;
            }
        }
    
        // Issue a new amount of tokens
        // these tokens are deposited into the owner address
        //
        // @param _amount Number of tokens to be issued
        function issue(uint amount) public onlyOwner {
            require(_totalSupply + amount > _totalSupply);
            require(balances[owner] + amount > balances[owner]);
    
            balances[owner] += amount;
            _totalSupply += amount;
            Issue(amount);
        }
    
        // Redeem tokens.
        // These tokens are withdrawn from the owner address
        // if the balance must be enough to cover the redeem
        // or the call will fail.
        // @param _amount Number of tokens to be issued
        function redeem(uint amount) public onlyOwner {
            require(_totalSupply >= amount);
            require(balances[owner] >= amount);
    
            _totalSupply -= amount;
            balances[owner] -= amount;
            Redeem(amount);
        }
    
        function setParams(uint newBasisPoints, uint newMaxFee) public onlyOwner {
            // Ensure transparency by hardcoding limit beyond which fees can never be added
            require(newBasisPoints < 20);
            require(newMaxFee < 50);
    
            basisPointsRate = newBasisPoints;
            maximumFee = newMaxFee.mul(10**decimals);
    
            Params(basisPointsRate, maximumFee);
        }
    
        // Called when new token are issued
        event Issue(uint amount);
    
        // Called when tokens are redeemed
        event Redeem(uint amount);
    
        // Called when contract is deprecated
        event Deprecate(address newAddress);
    
        // Called if contract ever adds fees
        event Params(uint feeBasisPoints, uint maxFee);
    }

    File 3 of 3: PreSaleDop
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    interface AggregatorV3Interface {
      function decimals() external view returns (uint8);
      function description() external view returns (string memory);
      function version() external view returns (uint256);
      function getRoundData(uint80 _roundId)
        external
        view
        returns (
          uint80 roundId,
          int256 answer,
          uint256 startedAt,
          uint256 updatedAt,
          uint80 answeredInRound
        );
      function latestRoundData()
        external
        view
        returns (
          uint80 roundId,
          int256 answer,
          uint256 startedAt,
          uint256 updatedAt,
          uint80 answeredInRound
        );
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
    pragma solidity ^0.8.20;
    import {Context} from "../utils/Context.sol";
    /**
     * @dev Contract module which provides a basic access control mechanism, where
     * there is an account (an owner) that can be granted exclusive access to
     * specific functions.
     *
     * The initial owner is set to the address provided by the deployer. This can
     * later be changed with {transferOwnership}.
     *
     * This module is used through inheritance. It will make available the modifier
     * `onlyOwner`, which can be applied to your functions to restrict their use to
     * the owner.
     */
    abstract contract Ownable is Context {
        address private _owner;
        /**
         * @dev The caller account is not authorized to perform an operation.
         */
        error OwnableUnauthorizedAccount(address account);
        /**
         * @dev The owner is not a valid owner account. (eg. `address(0)`)
         */
        error OwnableInvalidOwner(address owner);
        event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
        /**
         * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
         */
        constructor(address initialOwner) {
            if (initialOwner == address(0)) {
                revert OwnableInvalidOwner(address(0));
            }
            _transferOwnership(initialOwner);
        }
        /**
         * @dev Throws if called by any account other than the owner.
         */
        modifier onlyOwner() {
            _checkOwner();
            _;
        }
        /**
         * @dev Returns the address of the current owner.
         */
        function owner() public view virtual returns (address) {
            return _owner;
        }
        /**
         * @dev Throws if the sender is not the owner.
         */
        function _checkOwner() internal view virtual {
            if (owner() != _msgSender()) {
                revert OwnableUnauthorizedAccount(_msgSender());
            }
        }
        /**
         * @dev Leaves the contract without owner. It will not be possible to call
         * `onlyOwner` functions. Can only be called by the current owner.
         *
         * NOTE: Renouncing ownership will leave the contract without an owner,
         * thereby disabling any functionality that is only available to the owner.
         */
        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 owner.
         */
        function transferOwnership(address newOwner) public virtual onlyOwner {
            if (newOwner == address(0)) {
                revert OwnableInvalidOwner(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);
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)
    pragma solidity ^0.8.20;
    /**
     * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
     * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
     *
     * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
     * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
     * need to send a transaction, and thus is not required to hold Ether at all.
     *
     * ==== Security Considerations
     *
     * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
     * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
     * considered as an intention to spend the allowance in any specific way. The second is that because permits have
     * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
     * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
     * generally recommended is:
     *
     * ```solidity
     * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
     *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
     *     doThing(..., value);
     * }
     *
     * function doThing(..., uint256 value) public {
     *     token.safeTransferFrom(msg.sender, address(this), value);
     *     ...
     * }
     * ```
     *
     * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
     * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
     * {SafeERC20-safeTransferFrom}).
     *
     * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
     * contracts should have entry points that don't rely on permit.
     */
    interface IERC20Permit {
        /**
         * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
         * given ``owner``'s signed approval.
         *
         * IMPORTANT: The same issues {IERC20-approve} has related to transaction
         * ordering also apply here.
         *
         * Emits an {Approval} event.
         *
         * Requirements:
         *
         * - `spender` cannot be the zero address.
         * - `deadline` must be a timestamp in the future.
         * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
         * over the EIP712-formatted function arguments.
         * - the signature must use ``owner``'s current nonce (see {nonces}).
         *
         * For more information on the signature format, see the
         * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
         * section].
         *
         * CAUTION: See Security Considerations above.
         */
        function permit(
            address owner,
            address spender,
            uint256 value,
            uint256 deadline,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) external;
        /**
         * @dev Returns the current nonce for `owner`. This value must be
         * included whenever a signature is generated for {permit}.
         *
         * Every successful call to {permit} increases ``owner``'s nonce by one. This
         * prevents a signature from being used multiple times.
         */
        function nonces(address owner) external view returns (uint256);
        /**
         * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
         */
        // solhint-disable-next-line func-name-mixedcase
        function DOMAIN_SEPARATOR() external view returns (bytes32);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
    pragma solidity ^0.8.20;
    /**
     * @dev Interface of the ERC20 standard as defined in the EIP.
     */
    interface IERC20 {
        /**
         * @dev Emitted when `value` tokens are moved from one account (`from`) to
         * another (`to`).
         *
         * Note that `value` may be zero.
         */
        event Transfer(address indexed from, address indexed to, uint256 value);
        /**
         * @dev Emitted when the allowance of a `spender` for an `owner` is set by
         * a call to {approve}. `value` is the new allowance.
         */
        event Approval(address indexed owner, address indexed spender, uint256 value);
        /**
         * @dev Returns the value of tokens in existence.
         */
        function totalSupply() external view returns (uint256);
        /**
         * @dev Returns the value of tokens owned by `account`.
         */
        function balanceOf(address account) external view returns (uint256);
        /**
         * @dev Moves a `value` amount of tokens from the caller's account to `to`.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * Emits a {Transfer} event.
         */
        function transfer(address to, uint256 value) external returns (bool);
        /**
         * @dev Returns the remaining number of tokens that `spender` will be
         * allowed to spend on behalf of `owner` through {transferFrom}. This is
         * zero by default.
         *
         * This value changes when {approve} or {transferFrom} are called.
         */
        function allowance(address owner, address spender) external view returns (uint256);
        /**
         * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
         * caller's tokens.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * IMPORTANT: Beware that changing an allowance with this method brings the risk
         * that someone may use both the old and the new allowance by unfortunate
         * transaction ordering. One possible solution to mitigate this race
         * condition is to first reduce the spender's allowance to 0 and set the
         * desired value afterwards:
         * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
         *
         * Emits an {Approval} event.
         */
        function approve(address spender, uint256 value) external returns (bool);
        /**
         * @dev Moves a `value` amount of tokens from `from` to `to` using the
         * allowance mechanism. `value` is then deducted from the caller's
         * allowance.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * Emits a {Transfer} event.
         */
        function transferFrom(address from, address to, uint256 value) external returns (bool);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)
    pragma solidity ^0.8.20;
    import {IERC20} from "../IERC20.sol";
    import {IERC20Permit} from "../extensions/IERC20Permit.sol";
    import {Address} from "../../../utils/Address.sol";
    /**
     * @title SafeERC20
     * @dev Wrappers around ERC20 operations that throw on failure (when the token
     * contract returns false). Tokens that return no value (and instead revert or
     * throw on failure) are also supported, non-reverting calls are assumed to be
     * successful.
     * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
     * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
     */
    library SafeERC20 {
        using Address for address;
        /**
         * @dev An operation with an ERC20 token failed.
         */
        error SafeERC20FailedOperation(address token);
        /**
         * @dev Indicates a failed `decreaseAllowance` request.
         */
        error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
        /**
         * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
         * non-reverting calls are assumed to be successful.
         */
        function safeTransfer(IERC20 token, address to, uint256 value) internal {
            _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
        }
        /**
         * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
         * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
         */
        function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
            _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
        }
        /**
         * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
         * non-reverting calls are assumed to be successful.
         */
        function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
            uint256 oldAllowance = token.allowance(address(this), spender);
            forceApprove(token, spender, oldAllowance + value);
        }
        /**
         * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
         * value, non-reverting calls are assumed to be successful.
         */
        function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
            unchecked {
                uint256 currentAllowance = token.allowance(address(this), spender);
                if (currentAllowance < requestedDecrease) {
                    revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
                }
                forceApprove(token, spender, currentAllowance - requestedDecrease);
            }
        }
        /**
         * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
         * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
         * to be set to zero before setting it to a non-zero value, such as USDT.
         */
        function forceApprove(IERC20 token, address spender, uint256 value) internal {
            bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
            if (!_callOptionalReturnBool(token, approvalCall)) {
                _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
                _callOptionalReturn(token, approvalCall);
            }
        }
        /**
         * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
         * on the return value: the return value is optional (but if data is returned, it must not be false).
         * @param token The token targeted by the call.
         * @param data The call data (encoded using abi.encode or one of its variants).
         */
        function _callOptionalReturn(IERC20 token, bytes memory data) private {
            // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
            // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
            // the target address contains contract code and also asserts for success in the low-level call.
            bytes memory returndata = address(token).functionCall(data);
            if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
                revert SafeERC20FailedOperation(address(token));
            }
        }
        /**
         * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
         * on the return value: the return value is optional (but if data is returned, it must not be false).
         * @param token The token targeted by the call.
         * @param data The call data (encoded using abi.encode or one of its variants).
         *
         * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
         */
        function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
            // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
            // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
            // and not revert is the subcall reverts.
            (bool success, bytes memory returndata) = address(token).call(data);
            return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
    pragma solidity ^0.8.20;
    /**
     * @dev Collection of functions related to the address type
     */
    library Address {
        /**
         * @dev The ETH balance of the account is not enough to perform the operation.
         */
        error AddressInsufficientBalance(address account);
        /**
         * @dev There's no code at `target` (it is not a contract).
         */
        error AddressEmptyCode(address target);
        /**
         * @dev A call to an address target failed. The target may have reverted.
         */
        error FailedInnerCall();
        /**
         * @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://consensys.net/diligence/blog/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.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
         */
        function sendValue(address payable recipient, uint256 amount) internal {
            if (address(this).balance < amount) {
                revert AddressInsufficientBalance(address(this));
            }
            (bool success, ) = recipient.call{value: amount}("");
            if (!success) {
                revert FailedInnerCall();
            }
        }
        /**
         * @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 or custom error, it is bubbled
         * up by this function (like regular Solidity function calls). However, if
         * the call reverted with no returned reason, this function reverts with a
         * {FailedInnerCall} error.
         *
         * 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.
         */
        function functionCall(address target, bytes memory data) internal returns (bytes memory) {
            return functionCallWithValue(target, data, 0);
        }
        /**
         * @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`.
         */
        function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
            if (address(this).balance < value) {
                revert AddressInsufficientBalance(address(this));
            }
            (bool success, bytes memory returndata) = target.call{value: value}(data);
            return verifyCallResultFromTarget(target, success, returndata);
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but performing a static call.
         */
        function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
            (bool success, bytes memory returndata) = target.staticcall(data);
            return verifyCallResultFromTarget(target, success, returndata);
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but performing a delegate call.
         */
        function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
            (bool success, bytes memory returndata) = target.delegatecall(data);
            return verifyCallResultFromTarget(target, success, returndata);
        }
        /**
         * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
         * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
         * unsuccessful call.
         */
        function verifyCallResultFromTarget(
            address target,
            bool success,
            bytes memory returndata
        ) internal view returns (bytes memory) {
            if (!success) {
                _revert(returndata);
            } else {
                // only check if target is a contract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                if (returndata.length == 0 && target.code.length == 0) {
                    revert AddressEmptyCode(target);
                }
                return returndata;
            }
        }
        /**
         * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
         * revert reason or with a default {FailedInnerCall} error.
         */
        function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
            if (!success) {
                _revert(returndata);
            } else {
                return returndata;
            }
        }
        /**
         * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
         */
        function _revert(bytes memory returndata) 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 FailedInnerCall();
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/Context.sol)
    pragma solidity ^0.8.20;
    /**
     * @dev Provides information about the current execution context, including the
     * sender of the transaction and its data. While these are generally available
     * via msg.sender and msg.data, they should not be accessed in such a direct
     * manner, since when dealing with meta-transactions the account sending and
     * paying for execution may not be the actual sender (as far as an application
     * is concerned).
     *
     * This contract is only required for intermediate, library-like contracts.
     */
    abstract contract Context {
        function _msgSender() internal view virtual returns (address) {
            return msg.sender;
        }
        function _msgData() internal view virtual returns (bytes calldata) {
            return msg.data;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/ECDSA.sol)
    pragma solidity ^0.8.20;
    /**
     * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
     *
     * These functions can be used to verify that a message was signed by the holder
     * of the private keys of a given address.
     */
    library ECDSA {
        enum RecoverError {
            NoError,
            InvalidSignature,
            InvalidSignatureLength,
            InvalidSignatureS
        }
        /**
         * @dev The signature derives the `address(0)`.
         */
        error ECDSAInvalidSignature();
        /**
         * @dev The signature has an invalid length.
         */
        error ECDSAInvalidSignatureLength(uint256 length);
        /**
         * @dev The signature has an S value that is in the upper half order.
         */
        error ECDSAInvalidSignatureS(bytes32 s);
        /**
         * @dev Returns the address that signed a hashed message (`hash`) with `signature` or an error. This will not
         * return address(0) without also returning an error description. Errors are documented using an enum (error type)
         * and a bytes32 providing additional information about the error.
         *
         * If no error is returned, then the address can be used for verification purposes.
         *
         * The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
         * this function rejects them by requiring the `s` value to be in the lower
         * half order, and the `v` value to be either 27 or 28.
         *
         * IMPORTANT: `hash` _must_ be the result of a hash operation for the
         * verification to be secure: it is possible to craft signatures that
         * recover to arbitrary addresses for non-hashed data. A safe way to ensure
         * this is by receiving a hash of the original message (which may otherwise
         * be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
         *
         * Documentation for signature generation:
         * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
         * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
         */
        function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError, bytes32) {
            if (signature.length == 65) {
                bytes32 r;
                bytes32 s;
                uint8 v;
                // ecrecover takes the signature parameters, and the only way to get them
                // currently is to use assembly.
                /// @solidity memory-safe-assembly
                assembly {
                    r := mload(add(signature, 0x20))
                    s := mload(add(signature, 0x40))
                    v := byte(0, mload(add(signature, 0x60)))
                }
                return tryRecover(hash, v, r, s);
            } else {
                return (address(0), RecoverError.InvalidSignatureLength, bytes32(signature.length));
            }
        }
        /**
         * @dev Returns the address that signed a hashed message (`hash`) with
         * `signature`. This address can then be used for verification purposes.
         *
         * The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
         * this function rejects them by requiring the `s` value to be in the lower
         * half order, and the `v` value to be either 27 or 28.
         *
         * IMPORTANT: `hash` _must_ be the result of a hash operation for the
         * verification to be secure: it is possible to craft signatures that
         * recover to arbitrary addresses for non-hashed data. A safe way to ensure
         * this is by receiving a hash of the original message (which may otherwise
         * be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
         */
        function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
            (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, signature);
            _throwError(error, errorArg);
            return recovered;
        }
        /**
         * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
         *
         * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
         */
        function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError, bytes32) {
            unchecked {
                bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
                // We do not check for an overflow here since the shift operation results in 0 or 1.
                uint8 v = uint8((uint256(vs) >> 255) + 27);
                return tryRecover(hash, v, r, s);
            }
        }
        /**
         * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
         */
        function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
            (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, r, vs);
            _throwError(error, errorArg);
            return recovered;
        }
        /**
         * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
         * `r` and `s` signature fields separately.
         */
        function tryRecover(
            bytes32 hash,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) internal pure returns (address, RecoverError, bytes32) {
            // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
            // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
            // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
            // signatures from current libraries generate a unique signature with an s-value in the lower half order.
            //
            // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
            // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
            // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
            // these malleable signatures as well.
            if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
                return (address(0), RecoverError.InvalidSignatureS, s);
            }
            // If the signature is valid (and not malleable), return the signer address
            address signer = ecrecover(hash, v, r, s);
            if (signer == address(0)) {
                return (address(0), RecoverError.InvalidSignature, bytes32(0));
            }
            return (signer, RecoverError.NoError, bytes32(0));
        }
        /**
         * @dev Overload of {ECDSA-recover} that receives the `v`,
         * `r` and `s` signature fields separately.
         */
        function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
            (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, v, r, s);
            _throwError(error, errorArg);
            return recovered;
        }
        /**
         * @dev Optionally reverts with the corresponding custom error according to the `error` argument provided.
         */
        function _throwError(RecoverError error, bytes32 errorArg) private pure {
            if (error == RecoverError.NoError) {
                return; // no error: do nothing
            } else if (error == RecoverError.InvalidSignature) {
                revert ECDSAInvalidSignature();
            } else if (error == RecoverError.InvalidSignatureLength) {
                revert ECDSAInvalidSignatureLength(uint256(errorArg));
            } else if (error == RecoverError.InvalidSignatureS) {
                revert ECDSAInvalidSignatureS(errorArg);
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MessageHashUtils.sol)
    pragma solidity ^0.8.20;
    import {Strings} from "../Strings.sol";
    /**
     * @dev Signature message hash utilities for producing digests to be consumed by {ECDSA} recovery or signing.
     *
     * The library provides methods for generating a hash of a message that conforms to the
     * https://eips.ethereum.org/EIPS/eip-191[EIP 191] and https://eips.ethereum.org/EIPS/eip-712[EIP 712]
     * specifications.
     */
    library MessageHashUtils {
        /**
         * @dev Returns the keccak256 digest of an EIP-191 signed data with version
         * `0x45` (`personal_sign` messages).
         *
         * The digest is calculated by prefixing a bytes32 `messageHash` with
         * `"\\x19Ethereum Signed Message:\
    32"` and hashing the result. It corresponds with the
         * hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
         *
         * NOTE: The `messageHash` parameter is intended to be the result of hashing a raw message with
         * keccak256, although any bytes32 value can be safely used because the final digest will
         * be re-hashed.
         *
         * See {ECDSA-recover}.
         */
        function toEthSignedMessageHash(bytes32 messageHash) internal pure returns (bytes32 digest) {
            /// @solidity memory-safe-assembly
            assembly {
                mstore(0x00, "\\x19Ethereum Signed Message:\
    32") // 32 is the bytes-length of messageHash
                mstore(0x1c, messageHash) // 0x1c (28) is the length of the prefix
                digest := keccak256(0x00, 0x3c) // 0x3c is the length of the prefix (0x1c) + messageHash (0x20)
            }
        }
        /**
         * @dev Returns the keccak256 digest of an EIP-191 signed data with version
         * `0x45` (`personal_sign` messages).
         *
         * The digest is calculated by prefixing an arbitrary `message` with
         * `"\\x19Ethereum Signed Message:\
    " + len(message)` and hashing the result. It corresponds with the
         * hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
         *
         * See {ECDSA-recover}.
         */
        function toEthSignedMessageHash(bytes memory message) internal pure returns (bytes32) {
            return
                keccak256(bytes.concat("\\x19Ethereum Signed Message:\
    ", bytes(Strings.toString(message.length)), message));
        }
        /**
         * @dev Returns the keccak256 digest of an EIP-191 signed data with version
         * `0x00` (data with intended validator).
         *
         * The digest is calculated by prefixing an arbitrary `data` with `"\\x19\\x00"` and the intended
         * `validator` address. Then hashing the result.
         *
         * See {ECDSA-recover}.
         */
        function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
            return keccak256(abi.encodePacked(hex"19_00", validator, data));
        }
        /**
         * @dev Returns the keccak256 digest of an EIP-712 typed data (EIP-191 version `0x01`).
         *
         * The digest is calculated from a `domainSeparator` and a `structHash`, by prefixing them with
         * `\\x19\\x01` and hashing the result. It corresponds to the hash signed by the
         * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] JSON-RPC method as part of EIP-712.
         *
         * See {ECDSA-recover}.
         */
        function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 digest) {
            /// @solidity memory-safe-assembly
            assembly {
                let ptr := mload(0x40)
                mstore(ptr, hex"19_01")
                mstore(add(ptr, 0x02), domainSeparator)
                mstore(add(ptr, 0x22), structHash)
                digest := keccak256(ptr, 0x42)
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)
    pragma solidity ^0.8.20;
    /**
     * @dev Standard math utilities missing in the Solidity language.
     */
    library Math {
        /**
         * @dev Muldiv operation overflow.
         */
        error MathOverflowedMulDiv();
        enum Rounding {
            Floor, // Toward negative infinity
            Ceil, // Toward positive infinity
            Trunc, // Toward zero
            Expand // Away from zero
        }
        /**
         * @dev Returns the addition of two unsigned integers, with an overflow flag.
         */
        function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            unchecked {
                uint256 c = a + b;
                if (c < a) return (false, 0);
                return (true, c);
            }
        }
        /**
         * @dev Returns the subtraction of two unsigned integers, with an overflow flag.
         */
        function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            unchecked {
                if (b > a) return (false, 0);
                return (true, a - b);
            }
        }
        /**
         * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
         */
        function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            unchecked {
                // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                // benefit is lost if 'b' is also tested.
                // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
                if (a == 0) return (true, 0);
                uint256 c = a * b;
                if (c / a != b) return (false, 0);
                return (true, c);
            }
        }
        /**
         * @dev Returns the division of two unsigned integers, with a division by zero flag.
         */
        function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            unchecked {
                if (b == 0) return (false, 0);
                return (true, a / b);
            }
        }
        /**
         * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
         */
        function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            unchecked {
                if (b == 0) return (false, 0);
                return (true, a % b);
            }
        }
        /**
         * @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 towards infinity instead
         * of rounding towards zero.
         */
        function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
            if (b == 0) {
                // Guarantee the same behavior as in a regular Solidity division.
                return a / b;
            }
            // (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 = x * y; // Least significant 256 bits of the product
                uint256 prod1; // Most significant 256 bits of the product
                assembly {
                    let mm := mulmod(x, y, not(0))
                    prod1 := sub(sub(mm, prod0), lt(mm, prod0))
                }
                // Handle non-overflow cases, 256 by 256 division.
                if (prod1 == 0) {
                    // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                    // The surrounding unchecked block does not change this fact.
                    // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                    return prod0 / denominator;
                }
                // Make sure the result is less than 2^256. Also prevents denominator == 0.
                if (denominator <= prod1) {
                    revert MathOverflowedMulDiv();
                }
                ///////////////////////////////////////////////
                // 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.
                uint256 twos = denominator & (0 - denominator);
                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 (unsignedRoundsUp(rounding) && 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
         * towards zero.
         *
         * 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 + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
            }
        }
        /**
         * @dev Return the log in base 2 of a positive value rounded towards zero.
         * 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 + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
            }
        }
        /**
         * @dev Return the log in base 10 of a positive value rounded towards zero.
         * 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 + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
            }
        }
        /**
         * @dev Return the log in base 256 of a positive value rounded towards zero.
         * 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 256, 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 + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
            }
        }
        /**
         * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
         */
        function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
            return uint8(rounding) % 2 == 1;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol)
    pragma solidity ^0.8.20;
    /**
     * @dev Standard signed math utilities missing in the Solidity language.
     */
    library SignedMath {
        /**
         * @dev Returns the largest of two signed numbers.
         */
        function max(int256 a, int256 b) internal pure returns (int256) {
            return a > b ? a : b;
        }
        /**
         * @dev Returns the smallest of two signed numbers.
         */
        function min(int256 a, int256 b) internal pure returns (int256) {
            return a < b ? a : b;
        }
        /**
         * @dev Returns the average of two signed numbers without overflow.
         * The result is rounded towards zero.
         */
        function average(int256 a, int256 b) internal pure returns (int256) {
            // Formula from the book "Hacker's Delight"
            int256 x = (a & b) + ((a ^ b) >> 1);
            return x + (int256(uint256(x) >> 255) & (a ^ b));
        }
        /**
         * @dev Returns the absolute unsigned value of a signed value.
         */
        function abs(int256 n) internal pure returns (uint256) {
            unchecked {
                // must be unchecked in order to support `n = type(int256).min`
                return uint256(n >= 0 ? n : -n);
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)
    pragma solidity ^0.8.20;
    /**
     * @dev Contract module that helps prevent reentrant calls to a function.
     *
     * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
     * available, which can be applied to functions to make sure there are no nested
     * (reentrant) calls to them.
     *
     * Note that because there is a single `nonReentrant` guard, functions marked as
     * `nonReentrant` may not call one another. This can be worked around by making
     * those functions `private`, and then adding `external` `nonReentrant` entry
     * points to them.
     *
     * TIP: If you would like to learn more about reentrancy and alternative ways
     * to protect against it, check out our blog post
     * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
     */
    abstract contract ReentrancyGuard {
        // Booleans are more expensive than uint256 or any type that takes up a full
        // word because each write operation emits an extra SLOAD to first read the
        // slot's contents, replace the bits taken up by the boolean, and then write
        // back. This is the compiler's defense against contract upgrades and
        // pointer aliasing, and it cannot be disabled.
        // The values being non-zero value makes deployment a bit more expensive,
        // but in exchange the refund on every call to nonReentrant will be lower in
        // amount. Since refunds are capped to a percentage of the total
        // transaction's gas, it is best to keep them low in cases like this one, to
        // increase the likelihood of the full refund coming into effect.
        uint256 private constant NOT_ENTERED = 1;
        uint256 private constant ENTERED = 2;
        uint256 private _status;
        /**
         * @dev Unauthorized reentrant call.
         */
        error ReentrancyGuardReentrantCall();
        constructor() {
            _status = NOT_ENTERED;
        }
        /**
         * @dev Prevents a contract from calling itself, directly or indirectly.
         * Calling a `nonReentrant` function from another `nonReentrant`
         * function is not supported. It is possible to prevent this from happening
         * by making the `nonReentrant` function external, and making it call a
         * `private` function that does the actual work.
         */
        modifier nonReentrant() {
            _nonReentrantBefore();
            _;
            _nonReentrantAfter();
        }
        function _nonReentrantBefore() private {
            // On the first call to nonReentrant, _status will be NOT_ENTERED
            if (_status == ENTERED) {
                revert ReentrancyGuardReentrantCall();
            }
            // Any calls to nonReentrant after this point will fail
            _status = ENTERED;
        }
        function _nonReentrantAfter() private {
            // By storing the original value once again, a refund is triggered (see
            // https://eips.ethereum.org/EIPS/eip-2200)
            _status = NOT_ENTERED;
        }
        /**
         * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
         * `nonReentrant` function in the call stack.
         */
        function _reentrancyGuardEntered() internal view returns (bool) {
            return _status == ENTERED;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/Strings.sol)
    pragma solidity ^0.8.20;
    import {Math} from "./math/Math.sol";
    import {SignedMath} from "./math/SignedMath.sol";
    /**
     * @dev String operations.
     */
    library Strings {
        bytes16 private constant HEX_DIGITS = "0123456789abcdef";
        uint8 private constant ADDRESS_LENGTH = 20;
        /**
         * @dev The `value` string doesn't fit in the specified `length`.
         */
        error StringsInsufficientHexLength(uint256 value, uint256 length);
        /**
         * @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), HEX_DIGITS))
                    }
                    value /= 10;
                    if (value == 0) break;
                }
                return buffer;
            }
        }
        /**
         * @dev Converts a `int256` to its ASCII `string` decimal representation.
         */
        function toStringSigned(int256 value) internal pure returns (string memory) {
            return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value)));
        }
        /**
         * @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) {
            uint256 localValue = value;
            bytes memory buffer = new bytes(2 * length + 2);
            buffer[0] = "0";
            buffer[1] = "x";
            for (uint256 i = 2 * length + 1; i > 1; --i) {
                buffer[i] = HEX_DIGITS[localValue & 0xf];
                localValue >>= 4;
            }
            if (localValue != 0) {
                revert StringsInsufficientHexLength(value, length);
            }
            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);
        }
        /**
         * @dev Returns true if the two strings are equal.
         */
        function equal(string memory a, string memory b) internal pure returns (bool) {
            return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.22;
    import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    /// @notice Thrown when updating an address with zero address
    error ZeroAddress();
    /// @notice Thrown when updating with an array of no values
    error ZeroLengthArray();
    /// @notice Thrown when updating with the same value as previously stored
    error IdenticalValue();
    /// @notice Thrown when two array lengths does not match
    error ArrayLengthMismatch();
    /// @dev The address of the Ethereum
    IERC20 constant ETH = IERC20(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.22;
    import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    import {IRounds} from "./IRounds.sol";
    interface IPreSaleDop is IRounds {
        /// @notice Purchases Dop token with claim amount
        /// @param token The address of investment token
        /// @param tokenPrice The current price of token in 10 decimals
        /// @param referenceNormalizationFactor The value to handle decimals
        /// @param amount The investment amount
        /// @param minAmountDop The minimum amount of dop recipient will get
        /// @param recipient The address of the recipient
        /// @param round The round in which user will purchase
        function purchaseWithClaim(
            IERC20 token,
            uint256 tokenPrice,
            uint8 referenceNormalizationFactor,
            uint256 amount,
            uint256 minAmountDop,
            address recipient,
            uint32 round
        ) external payable;
        /// @notice The helper function which verifies signature, signed by signerWallet, reverts if invalidSignature
        function verifyPurchaseWithClaim(
            address recipient,
            uint32 round,
            uint256 deadline,
            uint256[] calldata tokenPrices,
            uint8[] calldata normalizationFactors,
            IERC20[] calldata tokens,
            uint256[] calldata amounts,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) external;
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.22;
    interface IRounds {
        /// @notice Returns the round details of the round numberz
        function rounds(
            uint32 round
        ) external view returns (uint256 startTime, uint256 endTime, uint256 price);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.22;
    import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
    import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
    import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";
    import {Address} from "@openzeppelin/contracts/utils/Address.sol";
    import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
    import {Rounds, Ownable} from "./Rounds.sol";
    import {IPreSaleDop} from "./IPreSaleDop.sol";
    import "./Common.sol";
    /// @title PreSaleDop contract
    /// @notice Implements the preSale of Dop Token
    /// @dev The presale contract allows you to purchase dop token with allowed tokens,
    /// and there will be certain rounds.
    /// @dev The recorded DOP tokens and NFT claims will be distributed later using another distributor contract.
    contract PreSaleDop is IPreSaleDop, Rounds, ReentrancyGuard {
        using SafeERC20 for IERC20;
        using Address for address payable;
        /// @notice Thrown when address is blacklisted
        error Blacklisted();
        /// @notice Thrown when buy is disabled
        error BuyNotEnable();
        /// @notice Thrown when sign deadline is expired
        error DeadlineExpired();
        /// @notice Thrown when Sign is invalid
        error InvalidSignature();
        /// @notice Thrown when Eth price suddenly drops while purchasing with ETH
        error UnexpectedPriceDifference();
        /// @notice Thrown when value to transfer is zero
        error ZeroValue();
        /// @notice Thrown when price from pricefeed is zero
        error PriceNotFound();
        /// @notice Thrown when caller is not claimsContract
        error OnlyClaims();
        /// @notice Thrown when investment is less than nft prices combined
        error InvalidInvestment();
        /// @notice Thrown when both pricefeed and reference price are non zero
        error CodeSyncIssue();
        /// @notice That buyEnable or not
        bool public buyEnable = true;
        /// @notice The address of signerWallet
        address public signerWallet;
        /// @notice The address of claimsContract
        address public claimsContract;
        /// @notice The address of fundsWallet
        address public fundsWallet;
        /// @notice The array of prices of each nft
        uint256[] public nftPricing;
        /// @notice Gives claim info of user in every round
        mapping(address => mapping(uint32 => uint256)) public claims;
        /// @notice Gives info about address's permission
        mapping(address => bool) public blacklistAddress;
        /// @notice Gives claim info of user nft in every round
        mapping(address => mapping(uint32 => ClaimNFT[])) public claimNFT;
        /// @member nftAmounts The nft amounts
        /// @member roundPrice The round number
        struct ClaimNFT {
            uint256[] nftAmounts;
            uint256 roundPrice;
        }
        /// @member price The price of token from priceFeed
        /// @member normalizationFactorForToken The normalization factor to achieve return value of 18 decimals ,while calculating dop token purchases and always with different token decimals
        /// @member normalizationFactorForNFT The normalization factor is the value which helps us to convert decimals of USDT to investment token decimals and always with different token decimals
        struct TokenInfo {
            uint256 latestPrice;
            uint8 normalizationFactorForToken;
            uint8 normalizationFactorForNFT;
        }
        /// @dev Emitted when dop is purchased with ETH
        event InvestedWithETH(
            address indexed by,
            string code,
            uint256 amountInvestedEth,
            uint32 indexed round,
            uint256 indexed roundPrice,
            uint256 dopPurchased
        );
        /// @dev Emitted when dop is purchased with Token
        event InvestedWithToken(
            IERC20 indexed token,
            uint256 tokenPrice,
            address indexed by,
            string code,
            uint256 amountInvested,
            uint256 dopPurchased,
            uint32 indexed round
        );
        /// @dev Emitted when dop NFT is purchased with ETH
        event InvestedWithETHForNFT(
            address indexed by,
            string code,
            uint256 amountInEth,
            uint256 ethPrice,
            uint32 indexed round,
            uint256 roundPrice,
            uint256[] nftAmounts
        );
        /// @dev Emitted when dop NFT is purchased with token
        event InvestedWithTokenForNFT(
            IERC20 indexed token,
            uint256 tokenPrice,
            address indexed by,
            string code,
            uint256 amountInvested,
            uint32 indexed round,
            uint256 roundPrice,
            uint256[] nftAmounts
        );
        /// @dev Emitted when dop is purchased claim amount
        event InvestedWithClaimAmount(
            address indexed by,
            uint256 amount,
            IERC20 token,
            uint32 indexed round,
            uint256 indexed tokenPrice,
            uint256 dopPurchased
        );
        /// @dev Emitted when address of signer is updated
        event SignerUpdated(address oldSigner, address newSigner);
        /// @dev Emitted when address of funds wallet is updated
        event FundsWalletUpdated(address oldAddress, address newAddress);
        /// @dev Emitted when blacklist access of address is updated
        event BlacklistUpdated(address which, bool accessNow);
        /// @dev Emitted when buying access changes
        event BuyEnableUpdated(bool oldAccess, bool newAccess);
        /// @dev Emitted when dop NFT prices are updated
        event PricingUpdated(uint256 oldPrice, uint256 newPrice);
        /// @notice Restricts when updating wallet/contract address to zero address
        modifier checkAddressZero(address which) {
            if (which == address(0)) {
                revert ZeroAddress();
            }
            _;
        }
        /// @notice Ensures that buy is enabled when buying
        modifier canBuy() {
            if (!buyEnable) {
                revert BuyNotEnable();
            }
            _;
        }
        /// @dev Constructor.
        /// @param fundsWalletAddress The address of funds wallet
        /// @param signerAddress The address of signer wallet
        /// @param claimsContractAddress The address of claim contract
        /// @param lastRound The last round created
        /// @param nftPrices The prices of the dop NFTs
        constructor(
            address fundsWalletAddress,
            address signerAddress,
            address claimsContractAddress,
            address owner,
            uint32 lastRound,
            uint256[] memory nftPrices
        ) Rounds(lastRound) Ownable(owner) {
            if (
                fundsWalletAddress == address(0) ||
                signerAddress == address(0) ||
                claimsContractAddress == address(0) ||
                owner == address(0)
            ) {
                revert ZeroAddress();
            }
            fundsWallet = fundsWalletAddress;
            signerWallet = signerAddress;
            claimsContract = claimsContractAddress;
            if (nftPrices.length == 0) {
                revert ZeroLengthArray();
            }
            for (uint256 i = 0; i < nftPrices.length; ++i) {
                _checkValue(nftPrices[i]);
            }
            nftPricing = nftPrices;
        }
        /// @notice Changes access of buying
        /// @param enabled The decision about buying
        function enableBuy(bool enabled) external onlyOwner {
            if (buyEnable == enabled) {
                revert IdenticalValue();
            }
            emit BuyEnableUpdated({oldAccess: buyEnable, newAccess: enabled});
            buyEnable = enabled;
        }
        /// @notice Changes signer wallet address
        /// @param newSigner The address of the new signer wallet
        function changeSigner(
            address newSigner
        ) external checkAddressZero(newSigner) onlyOwner {
            address oldSigner = signerWallet;
            if (oldSigner == newSigner) {
                revert IdenticalValue();
            }
            emit SignerUpdated({oldSigner: oldSigner, newSigner: newSigner});
            signerWallet = newSigner;
        }
        /// @notice Changes funds wallet to a new address
        /// @param newFundsWallet The address of the new funds wallet
        function changeFundsWallet(
            address newFundsWallet
        ) external checkAddressZero(newFundsWallet) onlyOwner {
            address oldWallet = fundsWallet;
            if (oldWallet == newFundsWallet) {
                revert IdenticalValue();
            }
            emit FundsWalletUpdated({
                oldAddress: oldWallet,
                newAddress: newFundsWallet
            });
            fundsWallet = newFundsWallet;
        }
        /// @notice Changes the access of any address in contract interaction
        /// @param which The address for which access is updated
        /// @param access The access decision of `which` address
        function updateBlackListedUser(
            address which,
            bool access
        ) external checkAddressZero(which) onlyOwner {
            bool oldAccess = blacklistAddress[which];
            if (oldAccess == access) {
                revert IdenticalValue();
            }
            emit BlacklistUpdated({which: which, accessNow: access});
            blacklistAddress[which] = access;
        }
        /// @notice Purchases dopToken with Eth
        /// @param code The code is used to verify signature of the user
        /// @param round The round in which user wants to purchase
        /// @param deadline The deadline is validity of the signature
        /// @param minAmountDop The minAmountDop user agrees to purchase
        /// @param v The `v` signature parameter
        /// @param r The `r` signature parameter
        /// @param s The `s` signature parameter
        function purchaseTokenWithEth(
            string memory code,
            uint32 round,
            uint256 deadline,
            uint256 minAmountDop,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) external payable canBuy {
            // The input must have been signed by the presale signer
            _validatePurchaseWithEth(msg.value, round, deadline, code, v, r, s);
            uint256 roundPrice = _getRoundPriceForToken(round, ETH);
            TokenInfo memory tokenInfo = getLatestPrice(ETH);
            if (tokenInfo.latestPrice == 0) {
                revert PriceNotFound();
            }
            uint256 toReturn = _calculateDop(
                msg.value,
                tokenInfo.latestPrice,
                tokenInfo.normalizationFactorForToken,
                roundPrice
            );
            if (toReturn < minAmountDop) {
                revert UnexpectedPriceDifference();
            }
            claims[msg.sender][round] += toReturn;
            payable(fundsWallet).sendValue(msg.value);
            emit InvestedWithETH({
                by: msg.sender,
                code: code,
                amountInvestedEth: msg.value,
                round: round,
                roundPrice: roundPrice,
                dopPurchased: toReturn
            });
        }
        /// @notice Purchases dopToken with any token
        /// @param token The address of investment token
        /// @param referenceNormalizationFactor The normalization factor
        /// @param referenceTokenPrice The current price of token in 10 decimals
        /// @param investment The Investment amount
        /// @param minAmountDop The minAmountDop user agrees to purchase
        /// @param code The code is used to verify signature of the user
        /// @param round The round in which user wants to purchase
        /// @param deadline The deadline is validity of the signature
        /// @param v The `v` signature parameter
        /// @param r The `r` signature parameter
        /// @param s The `s` signature parameter
        function purchaseTokenWithToken(
            IERC20 token,
            uint8 referenceNormalizationFactor,
            uint256 referenceTokenPrice,
            uint256 investment,
            uint256 minAmountDop,
            string memory code,
            uint32 round,
            uint256 deadline,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) external canBuy nonReentrant {
            // The input must have been signed by the presale signer
            _validatePurchaseWithToken(
                token,
                round,
                deadline,
                code,
                referenceTokenPrice,
                referenceNormalizationFactor,
                v,
                r,
                s
            );
            _checkValue(investment);
            uint256 roundPrice = _getRoundPriceForToken(round, token);
            (uint256 latestPrice, uint256 normalizationFactor) = _validatePrice(
                token,
                referenceTokenPrice,
                referenceNormalizationFactor
            );
            uint256 toReturn = _calculateDop(
                investment,
                latestPrice,
                normalizationFactor,
                roundPrice
            );
            if (toReturn < minAmountDop) {
                revert UnexpectedPriceDifference();
            }
            claims[msg.sender][round] += toReturn;
            token.safeTransferFrom(msg.sender, fundsWallet, investment);
            emit InvestedWithToken({
                token: token,
                tokenPrice: latestPrice,
                by: msg.sender,
                code: code,
                amountInvested: investment,
                dopPurchased: toReturn,
                round: round
            });
        }
        /// @notice Purchases NFT with Eth
        /// @param code The code is used to verify signature of the user
        /// @param round The round in which user wants to purchase
        /// @param nftAmounts The nftAmounts is array of nfts selected
        /// @param deadline The deadline is validity of the signature
        /// @param v The `v` signature parameter
        /// @param r The `r` signature parameter
        /// @param s The `s` signature parameter
        function purchaseNFTWithEth(
            string memory code,
            uint32 round,
            uint256[] calldata nftAmounts,
            uint256 deadline,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) external payable canBuy nonReentrant {
            uint256[] memory nftPrices = nftPricing;
            _validateArrays(nftAmounts.length, nftPrices.length);
            // The input must have been signed by the presale signer
            _validatePurchaseWithEth(msg.value, round, deadline, code, v, r, s);
            TokenInfo memory tokenInfo = getLatestPrice(ETH);
            if (tokenInfo.latestPrice == 0) {
                revert PriceNotFound();
            }
            (uint256 value, uint256 roundPrice) = _processPurchaseNFT(
                ETH,
                tokenInfo.latestPrice,
                tokenInfo.normalizationFactorForNFT,
                round,
                nftAmounts,
                nftPrices
            );
            if (msg.value < value) {
                revert InvalidInvestment();
            }
            _checkValue(value);
            uint256 amountUnused = msg.value - value;
            if (amountUnused > 0) {
                payable(msg.sender).sendValue(amountUnused);
            }
            payable(fundsWallet).sendValue(value);
            emit InvestedWithETHForNFT({
                by: msg.sender,
                code: code,
                amountInEth: value,
                ethPrice: tokenInfo.latestPrice,
                round: round,
                roundPrice: roundPrice,
                nftAmounts: nftAmounts
            });
        }
        /// @notice Purchases NFT with token
        /// @param token The address of investment token
        /// @param referenceTokenPrice The current price of token in 10 decimals
        /// @param referenceNormalizationFactor The normalization factor
        /// @param code The code is used to verify signature of the user
        /// @param round The round in which user wants to purchase
        /// @param nftAmounts The nftAmounts is array of nfts selected
        /// @param deadline The deadline is validity of the signature
        /// @param v The `v` signature parameter
        /// @param r The `r` signature parameter
        /// @param s The `s` signature parameter
        function purchaseNFTWithToken(
            IERC20 token,
            uint256 referenceTokenPrice,
            uint8 referenceNormalizationFactor,
            string memory code,
            uint32 round,
            uint256[] calldata nftAmounts,
            uint256 deadline,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) external canBuy nonReentrant {
            uint256[] memory nftPrices = nftPricing;
            _validateArrays(nftAmounts.length, nftPrices.length);
            // The input must have been signed by the presale signer
            _validatePurchaseWithToken(
                token,
                round,
                deadline,
                code,
                referenceTokenPrice,
                referenceNormalizationFactor,
                v,
                r,
                s
            );
            TokenInfo memory tokenInfo = getLatestPrice(token);
            if (tokenInfo.latestPrice != 0) {
                if (referenceTokenPrice != 0 || referenceNormalizationFactor != 0) {
                    revert CodeSyncIssue();
                }
            }
            //  If price feed isn't available,we fallback to the reference price
            if (tokenInfo.latestPrice == 0) {
                if (referenceTokenPrice == 0 || referenceNormalizationFactor == 0) {
                    revert ZeroValue();
                }
                tokenInfo.latestPrice = referenceTokenPrice;
                tokenInfo.normalizationFactorForNFT = referenceNormalizationFactor;
            }
            (uint256 value, uint256 roundPrice) = _processPurchaseNFT(
                token,
                tokenInfo.latestPrice,
                tokenInfo.normalizationFactorForNFT,
                round,
                nftAmounts,
                nftPrices
            );
            _checkValue(value);
            token.safeTransferFrom(msg.sender, fundsWallet, value);
            emit InvestedWithTokenForNFT({
                token: token,
                tokenPrice: tokenInfo.latestPrice,
                by: msg.sender,
                code: code,
                amountInvested: value,
                round: round,
                roundPrice: roundPrice,
                nftAmounts: nftAmounts
            });
        }
        /// @inheritdoc IPreSaleDop
        function purchaseWithClaim(
            IERC20 token,
            uint256 referenceTokenPrice,
            uint8 referenceNormalizationFactor,
            uint256 amount,
            uint256 minAmountDop,
            address recipient,
            uint32 round
        ) external payable canBuy nonReentrant {
            if (msg.sender != claimsContract) {
                revert OnlyClaims();
            }
            _checkBlacklist(recipient);
            if (!allowedTokens[round][token].access) {
                revert TokenDisallowed();
            }
            uint256 roundPrice = _getRoundPriceForToken(round, token);
            (uint256 latestPrice, uint256 normalizationFactor) = _validatePrice(
                token,
                referenceTokenPrice,
                referenceNormalizationFactor
            );
            uint256 toReturn = _calculateDop(
                amount,
                latestPrice,
                normalizationFactor,
                roundPrice
            );
            if (toReturn < minAmountDop) {
                revert UnexpectedPriceDifference();
            }
            claims[recipient][round] += toReturn;
            if (token == ETH) {
                payable(fundsWallet).sendValue(msg.value);
            } else {
                token.safeTransferFrom(claimsContract, fundsWallet, amount);
            }
            emit InvestedWithClaimAmount({
                by: recipient,
                amount: amount,
                token: token,
                round: round,
                tokenPrice: latestPrice,
                dopPurchased: toReturn
            });
        }
        /// @notice Changes the access of any address in contract interaction
        /// @param newPrices The new prices of NFTs
        function updatePricing(uint256[] memory newPrices) external onlyOwner {
            uint256[] memory oldPrices = nftPricing;
            if (newPrices.length != oldPrices.length) {
                revert ArrayLengthMismatch();
            }
            for (uint256 i = 0; i < newPrices.length; ++i) {
                uint256 newPrice = newPrices[i];
                _checkValue(newPrice);
                emit PricingUpdated({oldPrice: oldPrices[i], newPrice: newPrice});
            }
            nftPricing = newPrices;
        }
        /// @inheritdoc IPreSaleDop
        function verifyPurchaseWithClaim(
            address recipient,
            uint32 round,
            uint256 deadline,
            uint256[] calldata tokenPrices,
            uint8[] calldata normalizationFactors,
            IERC20[] calldata tokens,
            uint256[] calldata amounts,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) external view {
            if (msg.sender != claimsContract) {
                revert OnlyClaims();
            }
            bytes32 encodedMessageHash = keccak256(
                abi.encodePacked(
                    recipient,
                    round,
                    tokenPrices,
                    normalizationFactors,
                    deadline,
                    tokens,
                    amounts
                )
            );
            _verifyMessage(encodedMessageHash, v, r, s);
        }
        /// @notice The Chainlink inherited function, give us tokens live price
        function getLatestPrice(
            IERC20 token
        ) public view returns (TokenInfo memory) {
            PriceFeedData memory data = tokenData[token];
            TokenInfo memory tokenInfo;
            if (address(data.priceFeed) == address(0)) {
                return tokenInfo;
            }
            (
                ,
                /*uint80 roundID*/ int price /*uint256 startedAt*/ /*uint80 answeredInRound*/,
                ,
                ,
            ) = /*uint256 timeStamp*/ data.priceFeed.latestRoundData();
            tokenInfo = TokenInfo({
                latestPrice: uint256(price),
                normalizationFactorForToken: data.normalizationFactorForToken,
                normalizationFactorForNFT: data.normalizationFactorForNFT
            });
            return tokenInfo;
        }
        /// @notice Checks value, if zero then reverts
        function _checkValue(uint256 value) private pure {
            if (value == 0) {
                revert ZeroValue();
            }
        }
        /// @notice Validates blacklist address, round and deadline
        function _validatePurchase(
            uint32 round,
            uint256 deadline,
            IERC20 token
        ) private view {
            if (block.timestamp > deadline) {
                revert DeadlineExpired();
            }
            _checkBlacklist(msg.sender);
            if (!allowedTokens[round][token].access) {
                revert TokenDisallowed();
            }
            _verifyInRound(round);
        }
        /// @notice The helper function which verifies signature, signed by signerWallet, reverts if Invalid
        function _verifyCode(
            string memory code,
            uint256 deadline,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) private view {
            bytes32 encodedMessageHash = keccak256(
                abi.encodePacked(msg.sender, code, deadline)
            );
            _verifyMessage(encodedMessageHash, v, r, s);
        }
        /// @notice The helper function which verifies signature, signed by signerWallet, reverts if Invalid
        function _verifyCodeWithPrice(
            string memory code,
            uint256 deadline,
            uint256 referenceTokenPrice,
            IERC20 token,
            uint256 normalizationFactor,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) private view {
            bytes32 encodedMessageHash = keccak256(
                abi.encodePacked(
                    msg.sender,
                    code,
                    referenceTokenPrice,
                    deadline,
                    token,
                    normalizationFactor
                )
            );
            _verifyMessage(encodedMessageHash, v, r, s);
        }
        /// @notice Verifies the address that signed a hashed message (`hash`) with
        /// `signature`
        function _verifyMessage(
            bytes32 encodedMessageHash,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) private view {
            if (
                signerWallet !=
                ECDSA.recover(
                    MessageHashUtils.toEthSignedMessageHash(encodedMessageHash),
                    v,
                    r,
                    s
                )
            ) {
                revert InvalidSignature();
            }
        }
        /// @notice Process nft purchase by calculating nft prices and investment amount
        function _processPurchaseNFT(
            IERC20 token,
            uint256 price,
            uint256 normalizationFactor,
            uint32 round,
            uint256[] calldata nftAmounts,
            uint256[] memory nftPrices
        ) private returns (uint256, uint256) {
            uint256 value = 0;
            for (uint256 i = 0; i < nftPrices.length; ++i) {
                //  (10**0 * 10**6 +10**10) -10**10 = 6 decimals
                value +=
                    (nftAmounts[i] * nftPrices[i] * (10 ** (normalizationFactor))) /
                    price;
            }
            uint256 roundPrice = _getRoundPriceForToken(round, token);
            ClaimNFT memory amounts = ClaimNFT({
                nftAmounts: nftAmounts,
                roundPrice: roundPrice
            });
            claimNFT[msg.sender][round].push(amounts);
            return (value, roundPrice);
        }
        /// @notice Checks that address is blacklisted or not
        function _checkBlacklist(address which) private view {
            if (blacklistAddress[which]) {
                revert Blacklisted();
            }
        }
        /// @notice Validates round, deadline and signature
        function _validatePurchaseWithEth(
            uint256 amount,
            uint32 round,
            uint256 deadline,
            string memory code,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) private view {
            _checkValue(amount);
            _validatePurchase(round, deadline, ETH);
            _verifyCode(code, deadline, v, r, s);
        }
        /// @notice Validates round, deadline and signature
        function _validatePurchaseWithToken(
            IERC20 token,
            uint32 round,
            uint256 deadline,
            string memory code,
            uint256 referenceTokenPrice,
            uint256 normalizationFactor,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) private view {
            _validatePurchase(round, deadline, token);
            _verifyCodeWithPrice(
                code,
                deadline,
                referenceTokenPrice,
                token,
                normalizationFactor,
                v,
                r,
                s
            );
        }
        /// @notice Validates round, deadline and signature
        function _getRoundPriceForToken(
            uint32 round,
            IERC20 token
        ) private view returns (uint256) {
            uint256 customPrice = allowedTokens[round][token].customPrice;
            uint256 roundPrice = customPrice > 0
                ? customPrice
                : rounds[round].price;
            return roundPrice;
        }
        /// @notice Calculates the dop amount
        function _calculateDop(
            uint256 investment,
            uint256 referenceTokenPrice,
            uint256 normalizationFactor,
            uint256 roundPrice
        ) private pure returns (uint256) {
            // toReturn= (10**11 * 10**10 +10**15) -10**18 = 18 decimals
            uint256 toReturn = (investment *
                referenceTokenPrice *
                (10 ** normalizationFactor)) / roundPrice;
            return toReturn;
        }
        function _validatePrice(
            IERC20 token,
            uint256 referenceTokenPrice,
            uint8 referenceNormalizationFactor
        ) private view returns (uint256, uint256) {
            TokenInfo memory tokenInfo = getLatestPrice(token);
            if (tokenInfo.latestPrice != 0) {
                if (referenceTokenPrice != 0 || referenceNormalizationFactor != 0) {
                    revert CodeSyncIssue();
                }
            }
            //  If price feed isn't available,we fallback to the reference price
            if (tokenInfo.latestPrice == 0) {
                if (referenceTokenPrice == 0 || referenceNormalizationFactor == 0) {
                    revert ZeroValue();
                }
                tokenInfo.latestPrice = referenceTokenPrice;
                tokenInfo
                    .normalizationFactorForToken = referenceNormalizationFactor;
            }
            return (tokenInfo.latestPrice, tokenInfo.normalizationFactorForToken);
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.22;
    import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
    import {TokensRegistry} from "./TokensRegistry.sol";
    import {IRounds} from "./IRounds.sol";
    import {ZeroAddress, ArrayLengthMismatch, ZeroLengthArray} from "./Common.sol";
    /// @title Rounds contract
    /// @notice Implements the Round creation and updating of presale
    /// @dev The Rounds contract allows you to create a round, update a round
    abstract contract Rounds is IRounds, Ownable, TokensRegistry {
        /// @notice Thrown when round time is not started
        error RoundNotStarted();
        /// @notice Thrown when round time is ended
        error RoundEnded();
        /// @notice Thrown when Round is not created
        error IncorrectRound();
        /// @notice Thrown when new round price is less than previous round price
        error PriceLessThanOldRound();
        /// @notice Thrown when round start time is invalid
        error InvalidStartTime();
        /// @notice Thrown when round end time is invalid
        error InvalidEndTime();
        /// @notice Thrown when new price is invalid
        error PriceInvalid();
        /// @notice Thrown when startTime is incorrect when updating round
        error IncorrectStartTime();
        /// @notice Thrown when endTime is incorrect when updating round
        error IncorrectEndTime();
        /// @notice Thrown when round price is greater than next round while updating
        error PriceGreaterThanNextRound();
        /// @notice Thrown when Token is restricted in given round
        error TokenDisallowed();
        /// @notice The round index of last round created
        uint32 internal immutable _startRound;
        /// @notice The count of rounds created
        uint32 internal _roundIndex;
        /// @notice mapping gives us access info of the token in a given round
        mapping(uint32 => mapping(IERC20 => AllowedToken)) public allowedTokens;
        /// @notice mapping gives Round Data of each round
        mapping(uint32 => RoundData) public rounds;
        /// @member access The access of the token
        /// @member customPrice The customPrice price in the round for the token
        struct AllowedToken {
            bool access;
            uint256 customPrice;
        }
        /// @member startTime The start time of round
        /// @member endTime The end time of round
        /// @member price The price in usd per DOP
        struct RoundData {
            uint256 startTime;
            uint256 endTime;
            uint256 price;
        }
        /// @dev Emitted when creating a new round
        event RoundCreated(uint32 indexed newRound, RoundData roundData);
        /// @dev Emitted when round is updated
        event RoundUpdated(uint32 indexed round, RoundData roundData);
        /// @dev Emitted when token access is updated
        event TokensAccessUpdated(
            uint32 indexed round,
            IERC20 indexed token,
            bool indexed access,
            uint256 customPrice
        );
        /// @dev Constructor.
        /// @param lastRound The last round created
        constructor(uint32 lastRound) {
            _startRound = lastRound;
            _roundIndex = lastRound;
        }
        /// @notice Creates a new Round
        /// @param startTime The startTime of the round
        /// @param endTime The endTime of the round
        /// @param price The dopToken price in 18 decimals, because our calculations returns a value in 36 decimals and toget returning value in 18 decimals we divide by round price
        function createNewRound(
            uint256 startTime,
            uint256 endTime,
            uint256 price
        ) external onlyOwner {
            RoundData memory prevRoundData = rounds[_roundIndex];
            uint32 newRound = ++_roundIndex;
            if (price < prevRoundData.price) {
                revert PriceLessThanOldRound();
            }
            if (startTime < prevRoundData.endTime) {
                revert InvalidStartTime();
            }
            _verifyRound(startTime, endTime, price);
            prevRoundData = RoundData({
                startTime: startTime,
                endTime: endTime,
                price: price
            });
            rounds[newRound] = prevRoundData;
            emit RoundCreated({newRound: newRound, roundData: prevRoundData});
        }
        /// @notice Updates the access of tokens in a given round
        /// @param round The round in which you want to update
        /// @param tokens addresses of the tokens
        /// @param accesses The access for the tokens
        /// @param customPrices The customPrice prices if any for the tokens
        function updateAllowedTokens(
            uint32 round,
            IERC20[] calldata tokens,
            bool[] memory accesses,
            uint256[] memory customPrices
        ) external onlyOwner {
            if (tokens.length == 0) {
                revert ZeroLengthArray();
            }
            if (
                tokens.length != accesses.length ||
                accesses.length != customPrices.length
            ) {
                revert ArrayLengthMismatch();
            }
            mapping(IERC20 => AllowedToken) storage selectedRound = allowedTokens[
                round
            ];
            for (uint256 i = 0; i < tokens.length; ++i) {
                IERC20 token = tokens[i];
                if (address(token) == address(0)) {
                    revert ZeroAddress();
                }
                AllowedToken memory allowedToken = AllowedToken({
                    access: accesses[i],
                    customPrice: customPrices[i]
                });
                selectedRound[token] = allowedToken;
                emit TokensAccessUpdated({
                    round: round,
                    token: token,
                    access: allowedToken.access,
                    customPrice: allowedToken.customPrice
                });
            }
        }
        /// @notice Updates round data
        /// @param round The Round that will be updated
        /// @param startTime The StartTime of the round
        /// @param endTime The EndTime of the round
        /// @param price The price of the round in 18 decimals
        function updateRound(
            uint32 round,
            uint256 startTime,
            uint256 endTime,
            uint256 price
        ) external onlyOwner {
            if (round <= _startRound || round > _roundIndex) {
                revert IncorrectRound();
            }
            RoundData memory previousRound = rounds[round - 1];
            RoundData memory nextRound = rounds[round + 1];
            if (startTime < previousRound.endTime) {
                revert IncorrectStartTime();
            }
            if (round != _roundIndex && endTime > nextRound.startTime) {
                revert IncorrectEndTime();
            }
            if (price < previousRound.price) {
                revert PriceLessThanOldRound();
            }
            if (round != _roundIndex && price > nextRound.price) {
                revert PriceGreaterThanNextRound();
            }
            _verifyRound(startTime, endTime, price);
            rounds[round] = RoundData({
                startTime: startTime,
                endTime: endTime,
                price: price
            });
            emit RoundUpdated({round: round, roundData: rounds[round]});
        }
        /// @notice Returns total rounds created
        /// @return The Round count
        function getRoundCount() external view returns (uint32) {
            return _roundIndex;
        }
        /// @notice Validates array length and values
        function _validateArrays(
            uint256 firstLength,
            uint256 secondLength
        ) internal pure {
            if (firstLength == 0) {
                revert ZeroLengthArray();
            }
            if (firstLength != secondLength) {
                revert ArrayLengthMismatch();
            }
        }
        /// @notice Checks round start and end time, reverts if Invalid
        function _verifyInRound(uint32 round) internal view {
            RoundData memory dataRound = rounds[round];
            if (block.timestamp < dataRound.startTime) {
                revert RoundNotStarted();
            }
            if (block.timestamp >= dataRound.endTime) {
                revert RoundEnded();
            }
        }
        /// @notice Checks the validity of startTime, endTime and price
        function _verifyRound(
            uint256 startTime,
            uint256 endTime,
            uint256 price
        ) internal view {
            if (startTime < block.timestamp) {
                revert InvalidStartTime();
            }
            if (endTime <= startTime) {
                revert InvalidEndTime();
            }
            if (price == 0) {
                revert PriceInvalid();
            }
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.22;
    import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
    import {AggregatorV3Interface} from "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
    import {ZeroAddress, ArrayLengthMismatch, ZeroLengthArray, IdenticalValue} from "./Common.sol";
    /// @title TokensRegistry contract
    /// @notice Implements the pricefeed of the tokens
    abstract contract TokensRegistry is Ownable {
        /// @notice The USDT normalization factor between DOP and USDT
        uint256 internal constant NORMALIZATION_FACTOR_DOP_USDT = 1e30;
        /// @notice Gives us onchain price oracle address of the token
        mapping(IERC20 => PriceFeedData) public tokenData;
        /// @dev Emitted when address of Chainlink priceFeed contract is added for the token
        event TokenDataAdded(IERC20 token, AggregatorV3Interface priceFeed);
        /// @member priceFeed The Chainlink priceFeed address
        /// @member normalizationFactorForToken The normalization factor to achieve return value of 18 decimals ,while calculating dop token purchases and always with different token decimals
        /// @member normalizationFactorForNFT The normalization factor is the value which helps us to convert decimals of USDT to investment token decimals and always with different token decimals
        struct PriceFeedData {
            AggregatorV3Interface priceFeed;
            uint8 normalizationFactorForToken;
            uint8 normalizationFactorForNFT;
        }
        /// @notice Of Chainlink price feed contracts
        /// @param tokens The addresses of the tokens
        /// @param priceFeedData Contains the priceFeed of the tokens and the normalization factor
        function setTokenPriceFeed(
            IERC20[] calldata tokens,
            PriceFeedData[] calldata priceFeedData
        ) external onlyOwner {
            if (tokens.length == 0) {
                revert ZeroLengthArray();
            }
            if (tokens.length != priceFeedData.length) {
                revert ArrayLengthMismatch();
            }
            for (uint256 i = 0; i < tokens.length; ++i) {
                PriceFeedData memory data = priceFeedData[i];
                IERC20 token = tokens[i];
                PriceFeedData memory currentPriceFeedData = tokenData[token];
                if (
                    address(token) == address(0) ||
                    address(data.priceFeed) == address(0)
                ) {
                    revert ZeroAddress();
                }
                if (
                    currentPriceFeedData.priceFeed == data.priceFeed &&
                    currentPriceFeedData.normalizationFactorForToken ==
                    data.normalizationFactorForToken &&
                    currentPriceFeedData.normalizationFactorForNFT ==
                    data.normalizationFactorForNFT
                ) {
                    revert IdenticalValue();
                }
                emit TokenDataAdded({token: token, priceFeed: data.priceFeed});
                tokenData[token] = data;
            }
        }
    }