ETH Price: $2,638.84 (+3.90%)

Transaction Decoder

Block:
20093710 at Jun-15-2024 12:44:59 AM +UTC
Transaction Fee:
0.00042022000809858 ETH $1.11
Gas Used:
107,970 Gas / 3.892007114 Gwei

Emitted Events:

58 AethirToken.Transfer( from=[Receiver] Distributor1, to=[Sender] 0xda750e574f12d650952943ea389663f0917f67fd, value=128757721345234468864 )
59 Distributor1.AirdropClaimed( account=[Sender] 0xda750e574f12d650952943ea389663f0917f67fd, amount=128757721345234468864 )

Account State Difference:

  Address   Before After State Difference Code
(Fee Recipient: 0x684c...b14)
0.467796086109146954 Eth0.467850071109146954 Eth0.000053985
0x7ca50Eb6...fa34707ca
0xbe0Ed413...7E6c5226B
0xda750e57...0917F67fd
0.000643765828617169 Eth
Nonce: 3
0.000223545820518589 Eth
Nonce: 4
0.00042022000809858

Execution Trace

Distributor1.claim( )
  • AethirToken.balanceOf( account=0x7ca50Eb6f717843a134aACcA58eBF09fa34707ca ) => ( 324348722726365268928249856 )
  • Null: 0x000...001.49f3b2e4( )
  • AethirToken.transfer( to=0xda750e574F12d650952943ea389663f0917F67fd, amount=128757721345234468864 ) => ( True )
    File 1 of 2: Distributor1
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.25;
    import "@solady/src/tokens/ERC20.sol";
    import "@solady/src/utils/MerkleProofLib.sol";
    import "@solady/src/utils/ECDSA.sol";
    import "@openzeppelin/contracts/access/Ownable2Step.sol";
    //   ____ _ _
    //  / ___| (_) __ _ _   _  ___
    // | |   | | |/ _` | | | |/ _ \\
    // | |___| | | (_| | |_| |  __/
    //  \\____|_|_|\\__, |\\__,_|\\___|        _               _
    // |  _ \\(_)___| |_|_ __(_) |__  _   _| |_ ___  _ __  / |
    // | | | | / __| __| '__| | '_ \\| | | | __/ _ \\| '__| | |
    // | |_| | \\__ \\ |_| |  | | |_) | |_| | || (_) | |    | |
    // |____/|_|___/\\__|_|  |_|_.__/ \\__,_|\\__\\___/|_|    |_|
    /// @title Distributor1
    /// @notice Clique Airdrop contract (Mekle + ECDSA)
    /// @author Clique (@Clique2046)
    /// @author Eillo (@0xEillo)
    contract Distributor1 is Ownable2Step {
        // token to be airdroppped
        address public token;
        // address signing the claims
        address public signer;
        // root of the merkle tree
        bytes32 public claimRoot;
        // whether the airdrop is active
        bool public active = false;
        // fee to claim
        uint256 public fee;
        // mapping of addresses to whether they have claimed
        mapping(address => bool) public claimed;
        // errors
        error InsufficientBalance();
        error AlreadyClaimed();
        error InvalidSignature();
        error InvalidMerkleProof();
        error NotActive();
        error ZeroAddress();
        error InsufficientFee();
        error MerkleRootNotSet();
        event AirdropClaimed(address indexed account, uint256 amount);
        modifier feeCheck() {
            if (msg.value < fee) revert InsufficientFee();
            _;
        }
        /// @notice Construct a new Claim contract
        /// @param _signer address that can sign messages
        /// @param _token address of the token that will be claimed
        constructor(address _signer, address _token) Ownable(msg.sender) {
            if (_token == address(0)) revert ZeroAddress();
            signer = _signer;
            token = _token;
        }
        /// @notice Set the signer
        /// @param _signer address that can sign messages
        function setSigner(address _signer) external onlyOwner {
            signer = _signer;
        }
        /// @notice Set the claim root
        /// @param _claimRoot root of the merkle tree
        function setClaimRoot(bytes32 _claimRoot) external onlyOwner {
            claimRoot = _claimRoot;
        }
        /// @notice Set the fee
        /// @param _fee fee to claim
        function setFee(uint256 _fee) external onlyOwner {
            fee = _fee;
        }
        /// @notice Withdraw ETH from the contract
        /// @param receiver address to receive the tokens
        function withdrawETH(address receiver) external onlyOwner {
            payable(receiver).transfer(address(this).balance);
        }
        /// @notice Withdraw tokens from the contract
        /// @param receiver address to receive the tokens
        /// @param amount amount of tokens to withdraw
        function withdrawTokens(
            address receiver,
            uint256 amount
        ) external onlyOwner {
            ERC20(token).transfer(receiver, amount);
        }
        /// @notice Toggle the active state
        function toggleActive() external onlyOwner {
            if (claimRoot == bytes32(0)) revert MerkleRootNotSet();
            active = !active;
        }
        /// @notice Claim airdrop tokens. Checks for both merkle proof
        //          and signature validation
        /// @param _proof merkle proof of the claim
        /// @param _signature signature of the claim
        /// @param _amount amount of tokens to claim
        function claim(
            bytes32[] calldata _proof,
            bytes calldata _signature,
            uint256 _amount
        ) external payable feeCheck {
            if (ERC20(token).balanceOf(address(this)) < _amount)
                revert InsufficientBalance();
            if (claimed[msg.sender]) revert AlreadyClaimed();
            if (!active) revert NotActive();
            claimed[msg.sender] = true;
            _rootCheck(_proof, _amount);
            _signatureCheck(_amount, _signature);
            ERC20(token).transfer(msg.sender, _amount);
            emit AirdropClaimed(msg.sender, _amount);
        }
        /// @notice Internal function to check the merkle proof
        /// @param _proof merkle proof of the claim
        /// @param _amount amount of tokens to claim
        function _rootCheck(
            bytes32[] calldata _proof,
            uint256 _amount
        ) internal view {
            bytes32 leaf = keccak256(abi.encodePacked(msg.sender, _amount));
            if (!MerkleProofLib.verify(_proof, claimRoot, leaf))
                revert InvalidMerkleProof();
        }
        /// @notice Internal function to check the signature
        /// @param _amount amount of tokens to claim
        /// @param _signature signature of the claim
        function _signatureCheck(
            uint256 _amount,
            bytes calldata _signature
        ) internal view {
            if (_signature.length == 0) revert InvalidSignature();
            bytes32 messageHash = keccak256(
                abi.encodePacked(msg.sender, _amount, address(this), block.chainid)
            );
            bytes32 prefixedHash = ECDSA.toEthSignedMessageHash(messageHash);
            address recoveredSigner = ECDSA.recoverCalldata(
                prefixedHash,
                _signature
            );
            if (recoveredSigner != signer) revert InvalidSignature();
        }
        /// @notice Receive function to accept ETH directly to the contract
        receive() external payable {}
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.4;
    /// @notice Simple ERC20 + EIP-2612 implementation.
    /// @author Solady (https://github.com/vectorized/solady/blob/main/src/tokens/ERC20.sol)
    /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
    /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol)
    ///
    /// @dev Note:
    /// - The ERC20 standard allows minting and transferring to and from the zero address,
    ///   minting and transferring zero tokens, as well as self-approvals.
    ///   For performance, this implementation WILL NOT revert for such actions.
    ///   Please add any checks with overrides if desired.
    /// - The `permit` function uses the ecrecover precompile (0x1).
    ///
    /// If you are overriding:
    /// - NEVER violate the ERC20 invariant:
    ///   the total sum of all balances must be equal to `totalSupply()`.
    /// - Check that the overridden function is actually used in the function you want to
    ///   change the behavior of. Much of the code has been manually inlined for performance.
    abstract contract ERC20 {
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                       CUSTOM ERRORS                        */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
        /// @dev The total supply has overflowed.
        error TotalSupplyOverflow();
        /// @dev The allowance has overflowed.
        error AllowanceOverflow();
        /// @dev The allowance has underflowed.
        error AllowanceUnderflow();
        /// @dev Insufficient balance.
        error InsufficientBalance();
        /// @dev Insufficient allowance.
        error InsufficientAllowance();
        /// @dev The permit is invalid.
        error InvalidPermit();
        /// @dev The permit has expired.
        error PermitExpired();
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                           EVENTS                           */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
        /// @dev Emitted when `amount` tokens is transferred from `from` to `to`.
        event Transfer(address indexed from, address indexed to, uint256 amount);
        /// @dev Emitted when `amount` tokens is approved by `owner` to be used by `spender`.
        event Approval(address indexed owner, address indexed spender, uint256 amount);
        /// @dev `keccak256(bytes("Transfer(address,address,uint256)"))`.
        uint256 private constant _TRANSFER_EVENT_SIGNATURE =
            0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef;
        /// @dev `keccak256(bytes("Approval(address,address,uint256)"))`.
        uint256 private constant _APPROVAL_EVENT_SIGNATURE =
            0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925;
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                          STORAGE                           */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
        /// @dev The storage slot for the total supply.
        uint256 private constant _TOTAL_SUPPLY_SLOT = 0x05345cdf77eb68f44c;
        /// @dev The balance slot of `owner` is given by:
        /// ```
        ///     mstore(0x0c, _BALANCE_SLOT_SEED)
        ///     mstore(0x00, owner)
        ///     let balanceSlot := keccak256(0x0c, 0x20)
        /// ```
        uint256 private constant _BALANCE_SLOT_SEED = 0x87a211a2;
        /// @dev The allowance slot of (`owner`, `spender`) is given by:
        /// ```
        ///     mstore(0x20, spender)
        ///     mstore(0x0c, _ALLOWANCE_SLOT_SEED)
        ///     mstore(0x00, owner)
        ///     let allowanceSlot := keccak256(0x0c, 0x34)
        /// ```
        uint256 private constant _ALLOWANCE_SLOT_SEED = 0x7f5e9f20;
        /// @dev The nonce slot of `owner` is given by:
        /// ```
        ///     mstore(0x0c, _NONCES_SLOT_SEED)
        ///     mstore(0x00, owner)
        ///     let nonceSlot := keccak256(0x0c, 0x20)
        /// ```
        uint256 private constant _NONCES_SLOT_SEED = 0x38377508;
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                         CONSTANTS                          */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
        /// @dev `(_NONCES_SLOT_SEED << 16) | 0x1901`.
        uint256 private constant _NONCES_SLOT_SEED_WITH_SIGNATURE_PREFIX = 0x383775081901;
        /// @dev `keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")`.
        bytes32 private constant _DOMAIN_TYPEHASH =
            0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f;
        /// @dev `keccak256("1")`.
        bytes32 private constant _VERSION_HASH =
            0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6;
        /// @dev `keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)")`.
        bytes32 private constant _PERMIT_TYPEHASH =
            0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                       ERC20 METADATA                       */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
        /// @dev Returns the name of the token.
        function name() public view virtual returns (string memory);
        /// @dev Returns the symbol of the token.
        function symbol() public view virtual returns (string memory);
        /// @dev Returns the decimals places of the token.
        function decimals() public view virtual returns (uint8) {
            return 18;
        }
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                           ERC20                            */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
        /// @dev Returns the amount of tokens in existence.
        function totalSupply() public view virtual returns (uint256 result) {
            /// @solidity memory-safe-assembly
            assembly {
                result := sload(_TOTAL_SUPPLY_SLOT)
            }
        }
        /// @dev Returns the amount of tokens owned by `owner`.
        function balanceOf(address owner) public view virtual returns (uint256 result) {
            /// @solidity memory-safe-assembly
            assembly {
                mstore(0x0c, _BALANCE_SLOT_SEED)
                mstore(0x00, owner)
                result := sload(keccak256(0x0c, 0x20))
            }
        }
        /// @dev Returns the amount of tokens that `spender` can spend on behalf of `owner`.
        function allowance(address owner, address spender)
            public
            view
            virtual
            returns (uint256 result)
        {
            /// @solidity memory-safe-assembly
            assembly {
                mstore(0x20, spender)
                mstore(0x0c, _ALLOWANCE_SLOT_SEED)
                mstore(0x00, owner)
                result := sload(keccak256(0x0c, 0x34))
            }
        }
        /// @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
        ///
        /// Emits a {Approval} event.
        function approve(address spender, uint256 amount) public virtual returns (bool) {
            /// @solidity memory-safe-assembly
            assembly {
                // Compute the allowance slot and store the amount.
                mstore(0x20, spender)
                mstore(0x0c, _ALLOWANCE_SLOT_SEED)
                mstore(0x00, caller())
                sstore(keccak256(0x0c, 0x34), amount)
                // Emit the {Approval} event.
                mstore(0x00, amount)
                log3(0x00, 0x20, _APPROVAL_EVENT_SIGNATURE, caller(), shr(96, mload(0x2c)))
            }
            return true;
        }
        /// @dev Transfer `amount` tokens from the caller to `to`.
        ///
        /// Requirements:
        /// - `from` must at least have `amount`.
        ///
        /// Emits a {Transfer} event.
        function transfer(address to, uint256 amount) public virtual returns (bool) {
            _beforeTokenTransfer(msg.sender, to, amount);
            /// @solidity memory-safe-assembly
            assembly {
                // Compute the balance slot and load its value.
                mstore(0x0c, _BALANCE_SLOT_SEED)
                mstore(0x00, caller())
                let fromBalanceSlot := keccak256(0x0c, 0x20)
                let fromBalance := sload(fromBalanceSlot)
                // Revert if insufficient balance.
                if gt(amount, fromBalance) {
                    mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
                    revert(0x1c, 0x04)
                }
                // Subtract and store the updated balance.
                sstore(fromBalanceSlot, sub(fromBalance, amount))
                // Compute the balance slot of `to`.
                mstore(0x00, to)
                let toBalanceSlot := keccak256(0x0c, 0x20)
                // Add and store the updated balance of `to`.
                // Will not overflow because the sum of all user balances
                // cannot exceed the maximum uint256 value.
                sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
                // Emit the {Transfer} event.
                mstore(0x20, amount)
                log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, caller(), shr(96, mload(0x0c)))
            }
            _afterTokenTransfer(msg.sender, to, amount);
            return true;
        }
        /// @dev Transfers `amount` tokens from `from` to `to`.
        ///
        /// Note: Does not update the allowance if it is the maximum uint256 value.
        ///
        /// Requirements:
        /// - `from` must at least have `amount`.
        /// - The caller must have at least `amount` of allowance to transfer the tokens of `from`.
        ///
        /// Emits a {Transfer} event.
        function transferFrom(address from, address to, uint256 amount) public virtual returns (bool) {
            _beforeTokenTransfer(from, to, amount);
            /// @solidity memory-safe-assembly
            assembly {
                let from_ := shl(96, from)
                // Compute the allowance slot and load its value.
                mstore(0x20, caller())
                mstore(0x0c, or(from_, _ALLOWANCE_SLOT_SEED))
                let allowanceSlot := keccak256(0x0c, 0x34)
                let allowance_ := sload(allowanceSlot)
                // If the allowance is not the maximum uint256 value.
                if add(allowance_, 1) {
                    // Revert if the amount to be transferred exceeds the allowance.
                    if gt(amount, allowance_) {
                        mstore(0x00, 0x13be252b) // `InsufficientAllowance()`.
                        revert(0x1c, 0x04)
                    }
                    // Subtract and store the updated allowance.
                    sstore(allowanceSlot, sub(allowance_, amount))
                }
                // Compute the balance slot and load its value.
                mstore(0x0c, or(from_, _BALANCE_SLOT_SEED))
                let fromBalanceSlot := keccak256(0x0c, 0x20)
                let fromBalance := sload(fromBalanceSlot)
                // Revert if insufficient balance.
                if gt(amount, fromBalance) {
                    mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
                    revert(0x1c, 0x04)
                }
                // Subtract and store the updated balance.
                sstore(fromBalanceSlot, sub(fromBalance, amount))
                // Compute the balance slot of `to`.
                mstore(0x00, to)
                let toBalanceSlot := keccak256(0x0c, 0x20)
                // Add and store the updated balance of `to`.
                // Will not overflow because the sum of all user balances
                // cannot exceed the maximum uint256 value.
                sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
                // Emit the {Transfer} event.
                mstore(0x20, amount)
                log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, from_), shr(96, mload(0x0c)))
            }
            _afterTokenTransfer(from, to, amount);
            return true;
        }
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                          EIP-2612                          */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
        /// @dev For more performance, override to return the constant value
        /// of `keccak256(bytes(name()))` if `name()` will never change.
        function _constantNameHash() internal view virtual returns (bytes32 result) {}
        /// @dev Returns the current nonce for `owner`.
        /// This value is used to compute the signature for EIP-2612 permit.
        function nonces(address owner) public view virtual returns (uint256 result) {
            /// @solidity memory-safe-assembly
            assembly {
                // Compute the nonce slot and load its value.
                mstore(0x0c, _NONCES_SLOT_SEED)
                mstore(0x00, owner)
                result := sload(keccak256(0x0c, 0x20))
            }
        }
        /// @dev Sets `value` as the allowance of `spender` over the tokens of `owner`,
        /// authorized by a signed approval by `owner`.
        ///
        /// Emits a {Approval} event.
        function permit(
            address owner,
            address spender,
            uint256 value,
            uint256 deadline,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) public virtual {
            bytes32 nameHash = _constantNameHash();
            //  We simply calculate it on-the-fly to allow for cases where the `name` may change.
            if (nameHash == bytes32(0)) nameHash = keccak256(bytes(name()));
            /// @solidity memory-safe-assembly
            assembly {
                // Revert if the block timestamp is greater than `deadline`.
                if gt(timestamp(), deadline) {
                    mstore(0x00, 0x1a15a3cc) // `PermitExpired()`.
                    revert(0x1c, 0x04)
                }
                let m := mload(0x40) // Grab the free memory pointer.
                // Clean the upper 96 bits.
                owner := shr(96, shl(96, owner))
                spender := shr(96, shl(96, spender))
                // Compute the nonce slot and load its value.
                mstore(0x0e, _NONCES_SLOT_SEED_WITH_SIGNATURE_PREFIX)
                mstore(0x00, owner)
                let nonceSlot := keccak256(0x0c, 0x20)
                let nonceValue := sload(nonceSlot)
                // Prepare the domain separator.
                mstore(m, _DOMAIN_TYPEHASH)
                mstore(add(m, 0x20), nameHash)
                mstore(add(m, 0x40), _VERSION_HASH)
                mstore(add(m, 0x60), chainid())
                mstore(add(m, 0x80), address())
                mstore(0x2e, keccak256(m, 0xa0))
                // Prepare the struct hash.
                mstore(m, _PERMIT_TYPEHASH)
                mstore(add(m, 0x20), owner)
                mstore(add(m, 0x40), spender)
                mstore(add(m, 0x60), value)
                mstore(add(m, 0x80), nonceValue)
                mstore(add(m, 0xa0), deadline)
                mstore(0x4e, keccak256(m, 0xc0))
                // Prepare the ecrecover calldata.
                mstore(0x00, keccak256(0x2c, 0x42))
                mstore(0x20, and(0xff, v))
                mstore(0x40, r)
                mstore(0x60, s)
                let t := staticcall(gas(), 1, 0, 0x80, 0x20, 0x20)
                // If the ecrecover fails, the returndatasize will be 0x00,
                // `owner` will be checked if it equals the hash at 0x00,
                // which evaluates to false (i.e. 0), and we will revert.
                // If the ecrecover succeeds, the returndatasize will be 0x20,
                // `owner` will be compared against the returned address at 0x20.
                if iszero(eq(mload(returndatasize()), owner)) {
                    mstore(0x00, 0xddafbaef) // `InvalidPermit()`.
                    revert(0x1c, 0x04)
                }
                // Increment and store the updated nonce.
                sstore(nonceSlot, add(nonceValue, t)) // `t` is 1 if ecrecover succeeds.
                // Compute the allowance slot and store the value.
                // The `owner` is already at slot 0x20.
                mstore(0x40, or(shl(160, _ALLOWANCE_SLOT_SEED), spender))
                sstore(keccak256(0x2c, 0x34), value)
                // Emit the {Approval} event.
                log3(add(m, 0x60), 0x20, _APPROVAL_EVENT_SIGNATURE, owner, spender)
                mstore(0x40, m) // Restore the free memory pointer.
                mstore(0x60, 0) // Restore the zero pointer.
            }
        }
        /// @dev Returns the EIP-712 domain separator for the EIP-2612 permit.
        function DOMAIN_SEPARATOR() public view virtual returns (bytes32 result) {
            bytes32 nameHash = _constantNameHash();
            //  We simply calculate it on-the-fly to allow for cases where the `name` may change.
            if (nameHash == bytes32(0)) nameHash = keccak256(bytes(name()));
            /// @solidity memory-safe-assembly
            assembly {
                let m := mload(0x40) // Grab the free memory pointer.
                mstore(m, _DOMAIN_TYPEHASH)
                mstore(add(m, 0x20), nameHash)
                mstore(add(m, 0x40), _VERSION_HASH)
                mstore(add(m, 0x60), chainid())
                mstore(add(m, 0x80), address())
                result := keccak256(m, 0xa0)
            }
        }
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                  INTERNAL MINT FUNCTIONS                   */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
        /// @dev Mints `amount` tokens to `to`, increasing the total supply.
        ///
        /// Emits a {Transfer} event.
        function _mint(address to, uint256 amount) internal virtual {
            _beforeTokenTransfer(address(0), to, amount);
            /// @solidity memory-safe-assembly
            assembly {
                let totalSupplyBefore := sload(_TOTAL_SUPPLY_SLOT)
                let totalSupplyAfter := add(totalSupplyBefore, amount)
                // Revert if the total supply overflows.
                if lt(totalSupplyAfter, totalSupplyBefore) {
                    mstore(0x00, 0xe5cfe957) // `TotalSupplyOverflow()`.
                    revert(0x1c, 0x04)
                }
                // Store the updated total supply.
                sstore(_TOTAL_SUPPLY_SLOT, totalSupplyAfter)
                // Compute the balance slot and load its value.
                mstore(0x0c, _BALANCE_SLOT_SEED)
                mstore(0x00, to)
                let toBalanceSlot := keccak256(0x0c, 0x20)
                // Add and store the updated balance.
                sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
                // Emit the {Transfer} event.
                mstore(0x20, amount)
                log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, 0, shr(96, mload(0x0c)))
            }
            _afterTokenTransfer(address(0), to, amount);
        }
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                  INTERNAL BURN FUNCTIONS                   */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
        /// @dev Burns `amount` tokens from `from`, reducing the total supply.
        ///
        /// Emits a {Transfer} event.
        function _burn(address from, uint256 amount) internal virtual {
            _beforeTokenTransfer(from, address(0), amount);
            /// @solidity memory-safe-assembly
            assembly {
                // Compute the balance slot and load its value.
                mstore(0x0c, _BALANCE_SLOT_SEED)
                mstore(0x00, from)
                let fromBalanceSlot := keccak256(0x0c, 0x20)
                let fromBalance := sload(fromBalanceSlot)
                // Revert if insufficient balance.
                if gt(amount, fromBalance) {
                    mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
                    revert(0x1c, 0x04)
                }
                // Subtract and store the updated balance.
                sstore(fromBalanceSlot, sub(fromBalance, amount))
                // Subtract and store the updated total supply.
                sstore(_TOTAL_SUPPLY_SLOT, sub(sload(_TOTAL_SUPPLY_SLOT), amount))
                // Emit the {Transfer} event.
                mstore(0x00, amount)
                log3(0x00, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, shl(96, from)), 0)
            }
            _afterTokenTransfer(from, address(0), amount);
        }
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                INTERNAL TRANSFER FUNCTIONS                 */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
        /// @dev Moves `amount` of tokens from `from` to `to`.
        function _transfer(address from, address to, uint256 amount) internal virtual {
            _beforeTokenTransfer(from, to, amount);
            /// @solidity memory-safe-assembly
            assembly {
                let from_ := shl(96, from)
                // Compute the balance slot and load its value.
                mstore(0x0c, or(from_, _BALANCE_SLOT_SEED))
                let fromBalanceSlot := keccak256(0x0c, 0x20)
                let fromBalance := sload(fromBalanceSlot)
                // Revert if insufficient balance.
                if gt(amount, fromBalance) {
                    mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
                    revert(0x1c, 0x04)
                }
                // Subtract and store the updated balance.
                sstore(fromBalanceSlot, sub(fromBalance, amount))
                // Compute the balance slot of `to`.
                mstore(0x00, to)
                let toBalanceSlot := keccak256(0x0c, 0x20)
                // Add and store the updated balance of `to`.
                // Will not overflow because the sum of all user balances
                // cannot exceed the maximum uint256 value.
                sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
                // Emit the {Transfer} event.
                mstore(0x20, amount)
                log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, from_), shr(96, mload(0x0c)))
            }
            _afterTokenTransfer(from, to, amount);
        }
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                INTERNAL ALLOWANCE FUNCTIONS                */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
        /// @dev Updates the allowance of `owner` for `spender` based on spent `amount`.
        function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
            /// @solidity memory-safe-assembly
            assembly {
                // Compute the allowance slot and load its value.
                mstore(0x20, spender)
                mstore(0x0c, _ALLOWANCE_SLOT_SEED)
                mstore(0x00, owner)
                let allowanceSlot := keccak256(0x0c, 0x34)
                let allowance_ := sload(allowanceSlot)
                // If the allowance is not the maximum uint256 value.
                if add(allowance_, 1) {
                    // Revert if the amount to be transferred exceeds the allowance.
                    if gt(amount, allowance_) {
                        mstore(0x00, 0x13be252b) // `InsufficientAllowance()`.
                        revert(0x1c, 0x04)
                    }
                    // Subtract and store the updated allowance.
                    sstore(allowanceSlot, sub(allowance_, amount))
                }
            }
        }
        /// @dev Sets `amount` as the allowance of `spender` over the tokens of `owner`.
        ///
        /// Emits a {Approval} event.
        function _approve(address owner, address spender, uint256 amount) internal virtual {
            /// @solidity memory-safe-assembly
            assembly {
                let owner_ := shl(96, owner)
                // Compute the allowance slot and store the amount.
                mstore(0x20, spender)
                mstore(0x0c, or(owner_, _ALLOWANCE_SLOT_SEED))
                sstore(keccak256(0x0c, 0x34), amount)
                // Emit the {Approval} event.
                mstore(0x00, amount)
                log3(0x00, 0x20, _APPROVAL_EVENT_SIGNATURE, shr(96, owner_), shr(96, mload(0x2c)))
            }
        }
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                     HOOKS TO OVERRIDE                      */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
        /// @dev Hook that is called before any transfer of tokens.
        /// This includes minting and burning.
        function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}
        /// @dev Hook that is called after any transfer of tokens.
        /// This includes minting and burning.
        function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.4;
    /// @notice Gas optimized verification of proof of inclusion for a leaf in a Merkle tree.
    /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/MerkleProofLib.sol)
    /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/MerkleProofLib.sol)
    /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/MerkleProof.sol)
    library MerkleProofLib {
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*            MERKLE PROOF VERIFICATION OPERATIONS            */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
        /// @dev Returns whether `leaf` exists in the Merkle tree with `root`, given `proof`.
        function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf)
            internal
            pure
            returns (bool isValid)
        {
            /// @solidity memory-safe-assembly
            assembly {
                if mload(proof) {
                    // Initialize `offset` to the offset of `proof` elements in memory.
                    let offset := add(proof, 0x20)
                    // Left shift by 5 is equivalent to multiplying by 0x20.
                    let end := add(offset, shl(5, mload(proof)))
                    // Iterate over proof elements to compute root hash.
                    for {} 1 {} {
                        // Slot of `leaf` in scratch space.
                        // If the condition is true: 0x20, otherwise: 0x00.
                        let scratch := shl(5, gt(leaf, mload(offset)))
                        // Store elements to hash contiguously in scratch space.
                        // Scratch space is 64 bytes (0x00 - 0x3f) and both elements are 32 bytes.
                        mstore(scratch, leaf)
                        mstore(xor(scratch, 0x20), mload(offset))
                        // Reuse `leaf` to store the hash to reduce stack operations.
                        leaf := keccak256(0x00, 0x40)
                        offset := add(offset, 0x20)
                        if iszero(lt(offset, end)) { break }
                    }
                }
                isValid := eq(leaf, root)
            }
        }
        /// @dev Returns whether `leaf` exists in the Merkle tree with `root`, given `proof`.
        function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf)
            internal
            pure
            returns (bool isValid)
        {
            /// @solidity memory-safe-assembly
            assembly {
                if proof.length {
                    // Left shift by 5 is equivalent to multiplying by 0x20.
                    let end := add(proof.offset, shl(5, proof.length))
                    // Initialize `offset` to the offset of `proof` in the calldata.
                    let offset := proof.offset
                    // Iterate over proof elements to compute root hash.
                    for {} 1 {} {
                        // Slot of `leaf` in scratch space.
                        // If the condition is true: 0x20, otherwise: 0x00.
                        let scratch := shl(5, gt(leaf, calldataload(offset)))
                        // Store elements to hash contiguously in scratch space.
                        // Scratch space is 64 bytes (0x00 - 0x3f) and both elements are 32 bytes.
                        mstore(scratch, leaf)
                        mstore(xor(scratch, 0x20), calldataload(offset))
                        // Reuse `leaf` to store the hash to reduce stack operations.
                        leaf := keccak256(0x00, 0x40)
                        offset := add(offset, 0x20)
                        if iszero(lt(offset, end)) { break }
                    }
                }
                isValid := eq(leaf, root)
            }
        }
        /// @dev Returns whether all `leaves` exist in the Merkle tree with `root`,
        /// given `proof` and `flags`.
        ///
        /// Note:
        /// - Breaking the invariant `flags.length == (leaves.length - 1) + proof.length`
        ///   will always return false.
        /// - The sum of the lengths of `proof` and `leaves` must never overflow.
        /// - Any non-zero word in the `flags` array is treated as true.
        /// - The memory offset of `proof` must be non-zero
        ///   (i.e. `proof` is not pointing to the scratch space).
        function verifyMultiProof(
            bytes32[] memory proof,
            bytes32 root,
            bytes32[] memory leaves,
            bool[] memory flags
        ) internal pure returns (bool isValid) {
            // Rebuilds the root by consuming and producing values on a queue.
            // The queue starts with the `leaves` array, and goes into a `hashes` array.
            // After the process, the last element on the queue is verified
            // to be equal to the `root`.
            //
            // The `flags` array denotes whether the sibling
            // should be popped from the queue (`flag == true`), or
            // should be popped from the `proof` (`flag == false`).
            /// @solidity memory-safe-assembly
            assembly {
                // Cache the lengths of the arrays.
                let leavesLength := mload(leaves)
                let proofLength := mload(proof)
                let flagsLength := mload(flags)
                // Advance the pointers of the arrays to point to the data.
                leaves := add(0x20, leaves)
                proof := add(0x20, proof)
                flags := add(0x20, flags)
                // If the number of flags is correct.
                for {} eq(add(leavesLength, proofLength), add(flagsLength, 1)) {} {
                    // For the case where `proof.length + leaves.length == 1`.
                    if iszero(flagsLength) {
                        // `isValid = (proof.length == 1 ? proof[0] : leaves[0]) == root`.
                        isValid := eq(mload(xor(leaves, mul(xor(proof, leaves), proofLength))), root)
                        break
                    }
                    // The required final proof offset if `flagsLength` is not zero, otherwise zero.
                    let proofEnd := add(proof, shl(5, proofLength))
                    // We can use the free memory space for the queue.
                    // We don't need to allocate, since the queue is temporary.
                    let hashesFront := mload(0x40)
                    // Copy the leaves into the hashes.
                    // Sometimes, a little memory expansion costs less than branching.
                    // Should cost less, even with a high free memory offset of 0x7d00.
                    leavesLength := shl(5, leavesLength)
                    for { let i := 0 } iszero(eq(i, leavesLength)) { i := add(i, 0x20) } {
                        mstore(add(hashesFront, i), mload(add(leaves, i)))
                    }
                    // Compute the back of the hashes.
                    let hashesBack := add(hashesFront, leavesLength)
                    // This is the end of the memory for the queue.
                    // We recycle `flagsLength` to save on stack variables (sometimes save gas).
                    flagsLength := add(hashesBack, shl(5, flagsLength))
                    for {} 1 {} {
                        // Pop from `hashes`.
                        let a := mload(hashesFront)
                        // Pop from `hashes`.
                        let b := mload(add(hashesFront, 0x20))
                        hashesFront := add(hashesFront, 0x40)
                        // If the flag is false, load the next proof,
                        // else, pops from the queue.
                        if iszero(mload(flags)) {
                            // Loads the next proof.
                            b := mload(proof)
                            proof := add(proof, 0x20)
                            // Unpop from `hashes`.
                            hashesFront := sub(hashesFront, 0x20)
                        }
                        // Advance to the next flag.
                        flags := add(flags, 0x20)
                        // Slot of `a` in scratch space.
                        // If the condition is true: 0x20, otherwise: 0x00.
                        let scratch := shl(5, gt(a, b))
                        // Hash the scratch space and push the result onto the queue.
                        mstore(scratch, a)
                        mstore(xor(scratch, 0x20), b)
                        mstore(hashesBack, keccak256(0x00, 0x40))
                        hashesBack := add(hashesBack, 0x20)
                        if iszero(lt(hashesBack, flagsLength)) { break }
                    }
                    isValid :=
                        and(
                            // Checks if the last value in the queue is same as the root.
                            eq(mload(sub(hashesBack, 0x20)), root),
                            // And whether all the proofs are used, if required.
                            eq(proofEnd, proof)
                        )
                    break
                }
            }
        }
        /// @dev Returns whether all `leaves` exist in the Merkle tree with `root`,
        /// given `proof` and `flags`.
        ///
        /// Note:
        /// - Breaking the invariant `flags.length == (leaves.length - 1) + proof.length`
        ///   will always return false.
        /// - Any non-zero word in the `flags` array is treated as true.
        /// - The calldata offset of `proof` must be non-zero
        ///   (i.e. `proof` is from a regular Solidity function with a 4-byte selector).
        function verifyMultiProofCalldata(
            bytes32[] calldata proof,
            bytes32 root,
            bytes32[] calldata leaves,
            bool[] calldata flags
        ) internal pure returns (bool isValid) {
            // Rebuilds the root by consuming and producing values on a queue.
            // The queue starts with the `leaves` array, and goes into a `hashes` array.
            // After the process, the last element on the queue is verified
            // to be equal to the `root`.
            //
            // The `flags` array denotes whether the sibling
            // should be popped from the queue (`flag == true`), or
            // should be popped from the `proof` (`flag == false`).
            /// @solidity memory-safe-assembly
            assembly {
                // If the number of flags is correct.
                for {} eq(add(leaves.length, proof.length), add(flags.length, 1)) {} {
                    // For the case where `proof.length + leaves.length == 1`.
                    if iszero(flags.length) {
                        // `isValid = (proof.length == 1 ? proof[0] : leaves[0]) == root`.
                        // forgefmt: disable-next-item
                        isValid := eq(
                            calldataload(
                                xor(leaves.offset, mul(xor(proof.offset, leaves.offset), proof.length))
                            ),
                            root
                        )
                        break
                    }
                    // The required final proof offset if `flagsLength` is not zero, otherwise zero.
                    let proofEnd := add(proof.offset, shl(5, proof.length))
                    // We can use the free memory space for the queue.
                    // We don't need to allocate, since the queue is temporary.
                    let hashesFront := mload(0x40)
                    // Copy the leaves into the hashes.
                    // Sometimes, a little memory expansion costs less than branching.
                    // Should cost less, even with a high free memory offset of 0x7d00.
                    calldatacopy(hashesFront, leaves.offset, shl(5, leaves.length))
                    // Compute the back of the hashes.
                    let hashesBack := add(hashesFront, shl(5, leaves.length))
                    // This is the end of the memory for the queue.
                    // We recycle `flagsLength` to save on stack variables (sometimes save gas).
                    flags.length := add(hashesBack, shl(5, flags.length))
                    // We don't need to make a copy of `proof.offset` or `flags.offset`,
                    // as they are pass-by-value (this trick may not always save gas).
                    for {} 1 {} {
                        // Pop from `hashes`.
                        let a := mload(hashesFront)
                        // Pop from `hashes`.
                        let b := mload(add(hashesFront, 0x20))
                        hashesFront := add(hashesFront, 0x40)
                        // If the flag is false, load the next proof,
                        // else, pops from the queue.
                        if iszero(calldataload(flags.offset)) {
                            // Loads the next proof.
                            b := calldataload(proof.offset)
                            proof.offset := add(proof.offset, 0x20)
                            // Unpop from `hashes`.
                            hashesFront := sub(hashesFront, 0x20)
                        }
                        // Advance to the next flag offset.
                        flags.offset := add(flags.offset, 0x20)
                        // Slot of `a` in scratch space.
                        // If the condition is true: 0x20, otherwise: 0x00.
                        let scratch := shl(5, gt(a, b))
                        // Hash the scratch space and push the result onto the queue.
                        mstore(scratch, a)
                        mstore(xor(scratch, 0x20), b)
                        mstore(hashesBack, keccak256(0x00, 0x40))
                        hashesBack := add(hashesBack, 0x20)
                        if iszero(lt(hashesBack, flags.length)) { break }
                    }
                    isValid :=
                        and(
                            // Checks if the last value in the queue is same as the root.
                            eq(mload(sub(hashesBack, 0x20)), root),
                            // And whether all the proofs are used, if required.
                            eq(proofEnd, proof.offset)
                        )
                    break
                }
            }
        }
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                   EMPTY CALLDATA HELPERS                   */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
        /// @dev Returns an empty calldata bytes32 array.
        function emptyProof() internal pure returns (bytes32[] calldata proof) {
            /// @solidity memory-safe-assembly
            assembly {
                proof.length := 0
            }
        }
        /// @dev Returns an empty calldata bytes32 array.
        function emptyLeaves() internal pure returns (bytes32[] calldata leaves) {
            /// @solidity memory-safe-assembly
            assembly {
                leaves.length := 0
            }
        }
        /// @dev Returns an empty calldata bool array.
        function emptyFlags() internal pure returns (bool[] calldata flags) {
            /// @solidity memory-safe-assembly
            assembly {
                flags.length := 0
            }
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.4;
    /// @notice Gas optimized ECDSA wrapper.
    /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/ECDSA.sol)
    /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/ECDSA.sol)
    /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/ECDSA.sol)
    ///
    /// @dev Note:
    /// - The recovery functions use the ecrecover precompile (0x1).
    /// - As of Solady version 0.0.68, the `recover` variants will revert upon recovery failure.
    ///   This is for more safety by default.
    ///   Use the `tryRecover` variants if you need to get the zero address back
    ///   upon recovery failure instead.
    /// - As of Solady version 0.0.134, all `bytes signature` variants accept both
    ///   regular 65-byte `(r, s, v)` and EIP-2098 `(r, vs)` short form signatures.
    ///   See: https://eips.ethereum.org/EIPS/eip-2098
    ///   This is for calldata efficiency on smart accounts prevalent on L2s.
    ///
    /// WARNING! Do NOT use signatures as unique identifiers:
    /// - Use a nonce in the digest to prevent replay attacks on the same contract.
    /// - Use EIP-712 for the digest to prevent replay attacks across different chains and contracts.
    ///   EIP-712 also enables readable signing of typed data for better user safety.
    /// This implementation does NOT check if a signature is non-malleable.
    library ECDSA {
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                        CUSTOM ERRORS                       */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
        /// @dev The signature is invalid.
        error InvalidSignature();
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                    RECOVERY OPERATIONS                     */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
        /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
        function recover(bytes32 hash, bytes memory signature) internal view returns (address result) {
            /// @solidity memory-safe-assembly
            assembly {
                result := 1
                let m := mload(0x40) // Cache the free memory pointer.
                for {} 1 {} {
                    mstore(0x00, hash)
                    mstore(0x40, mload(add(signature, 0x20))) // `r`.
                    if eq(mload(signature), 64) {
                        let vs := mload(add(signature, 0x40))
                        mstore(0x20, add(shr(255, vs), 27)) // `v`.
                        mstore(0x60, shr(1, shl(1, vs))) // `s`.
                        break
                    }
                    if eq(mload(signature), 65) {
                        mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`.
                        mstore(0x60, mload(add(signature, 0x40))) // `s`.
                        break
                    }
                    result := 0
                    break
                }
                result :=
                    mload(
                        staticcall(
                            gas(), // Amount of gas left for the transaction.
                            result, // Address of `ecrecover`.
                            0x00, // Start of input.
                            0x80, // Size of input.
                            0x01, // Start of output.
                            0x20 // Size of output.
                        )
                    )
                // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                if iszero(returndatasize()) {
                    mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
                    revert(0x1c, 0x04)
                }
                mstore(0x60, 0) // Restore the zero slot.
                mstore(0x40, m) // Restore the free memory pointer.
            }
        }
        /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
        function recoverCalldata(bytes32 hash, bytes calldata signature)
            internal
            view
            returns (address result)
        {
            /// @solidity memory-safe-assembly
            assembly {
                result := 1
                let m := mload(0x40) // Cache the free memory pointer.
                mstore(0x00, hash)
                for {} 1 {} {
                    if eq(signature.length, 64) {
                        let vs := calldataload(add(signature.offset, 0x20))
                        mstore(0x20, add(shr(255, vs), 27)) // `v`.
                        mstore(0x40, calldataload(signature.offset)) // `r`.
                        mstore(0x60, shr(1, shl(1, vs))) // `s`.
                        break
                    }
                    if eq(signature.length, 65) {
                        mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`.
                        calldatacopy(0x40, signature.offset, 0x40) // Copy `r` and `s`.
                        break
                    }
                    result := 0
                    break
                }
                result :=
                    mload(
                        staticcall(
                            gas(), // Amount of gas left for the transaction.
                            result, // Address of `ecrecover`.
                            0x00, // Start of input.
                            0x80, // Size of input.
                            0x01, // Start of output.
                            0x20 // Size of output.
                        )
                    )
                // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                if iszero(returndatasize()) {
                    mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
                    revert(0x1c, 0x04)
                }
                mstore(0x60, 0) // Restore the zero slot.
                mstore(0x40, m) // Restore the free memory pointer.
            }
        }
        /// @dev Recovers the signer's address from a message digest `hash`,
        /// and the EIP-2098 short form signature defined by `r` and `vs`.
        function recover(bytes32 hash, bytes32 r, bytes32 vs) internal view returns (address result) {
            /// @solidity memory-safe-assembly
            assembly {
                let m := mload(0x40) // Cache the free memory pointer.
                mstore(0x00, hash)
                mstore(0x20, add(shr(255, vs), 27)) // `v`.
                mstore(0x40, r)
                mstore(0x60, shr(1, shl(1, vs))) // `s`.
                result :=
                    mload(
                        staticcall(
                            gas(), // Amount of gas left for the transaction.
                            1, // Address of `ecrecover`.
                            0x00, // Start of input.
                            0x80, // Size of input.
                            0x01, // Start of output.
                            0x20 // Size of output.
                        )
                    )
                // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                if iszero(returndatasize()) {
                    mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
                    revert(0x1c, 0x04)
                }
                mstore(0x60, 0) // Restore the zero slot.
                mstore(0x40, m) // Restore the free memory pointer.
            }
        }
        /// @dev Recovers the signer's address from a message digest `hash`,
        /// and the signature defined by `v`, `r`, `s`.
        function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s)
            internal
            view
            returns (address result)
        {
            /// @solidity memory-safe-assembly
            assembly {
                let m := mload(0x40) // Cache the free memory pointer.
                mstore(0x00, hash)
                mstore(0x20, and(v, 0xff))
                mstore(0x40, r)
                mstore(0x60, s)
                result :=
                    mload(
                        staticcall(
                            gas(), // Amount of gas left for the transaction.
                            1, // Address of `ecrecover`.
                            0x00, // Start of input.
                            0x80, // Size of input.
                            0x01, // Start of output.
                            0x20 // Size of output.
                        )
                    )
                // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                if iszero(returndatasize()) {
                    mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
                    revert(0x1c, 0x04)
                }
                mstore(0x60, 0) // Restore the zero slot.
                mstore(0x40, m) // Restore the free memory pointer.
            }
        }
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                   TRY-RECOVER OPERATIONS                   */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
        // WARNING!
        // These functions will NOT revert upon recovery failure.
        // Instead, they will return the zero address upon recovery failure.
        // It is critical that the returned address is NEVER compared against
        // a zero address (e.g. an uninitialized address variable).
        /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
        function tryRecover(bytes32 hash, bytes memory signature)
            internal
            view
            returns (address result)
        {
            /// @solidity memory-safe-assembly
            assembly {
                result := 1
                let m := mload(0x40) // Cache the free memory pointer.
                for {} 1 {} {
                    mstore(0x00, hash)
                    mstore(0x40, mload(add(signature, 0x20))) // `r`.
                    if eq(mload(signature), 64) {
                        let vs := mload(add(signature, 0x40))
                        mstore(0x20, add(shr(255, vs), 27)) // `v`.
                        mstore(0x60, shr(1, shl(1, vs))) // `s`.
                        break
                    }
                    if eq(mload(signature), 65) {
                        mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`.
                        mstore(0x60, mload(add(signature, 0x40))) // `s`.
                        break
                    }
                    result := 0
                    break
                }
                pop(
                    staticcall(
                        gas(), // Amount of gas left for the transaction.
                        result, // Address of `ecrecover`.
                        0x00, // Start of input.
                        0x80, // Size of input.
                        0x40, // Start of output.
                        0x20 // Size of output.
                    )
                )
                mstore(0x60, 0) // Restore the zero slot.
                // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                result := mload(xor(0x60, returndatasize()))
                mstore(0x40, m) // Restore the free memory pointer.
            }
        }
        /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
        function tryRecoverCalldata(bytes32 hash, bytes calldata signature)
            internal
            view
            returns (address result)
        {
            /// @solidity memory-safe-assembly
            assembly {
                result := 1
                let m := mload(0x40) // Cache the free memory pointer.
                mstore(0x00, hash)
                for {} 1 {} {
                    if eq(signature.length, 64) {
                        let vs := calldataload(add(signature.offset, 0x20))
                        mstore(0x20, add(shr(255, vs), 27)) // `v`.
                        mstore(0x40, calldataload(signature.offset)) // `r`.
                        mstore(0x60, shr(1, shl(1, vs))) // `s`.
                        break
                    }
                    if eq(signature.length, 65) {
                        mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`.
                        calldatacopy(0x40, signature.offset, 0x40) // Copy `r` and `s`.
                        break
                    }
                    result := 0
                    break
                }
                pop(
                    staticcall(
                        gas(), // Amount of gas left for the transaction.
                        result, // Address of `ecrecover`.
                        0x00, // Start of input.
                        0x80, // Size of input.
                        0x40, // Start of output.
                        0x20 // Size of output.
                    )
                )
                mstore(0x60, 0) // Restore the zero slot.
                // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                result := mload(xor(0x60, returndatasize()))
                mstore(0x40, m) // Restore the free memory pointer.
            }
        }
        /// @dev Recovers the signer's address from a message digest `hash`,
        /// and the EIP-2098 short form signature defined by `r` and `vs`.
        function tryRecover(bytes32 hash, bytes32 r, bytes32 vs)
            internal
            view
            returns (address result)
        {
            /// @solidity memory-safe-assembly
            assembly {
                let m := mload(0x40) // Cache the free memory pointer.
                mstore(0x00, hash)
                mstore(0x20, add(shr(255, vs), 27)) // `v`.
                mstore(0x40, r)
                mstore(0x60, shr(1, shl(1, vs))) // `s`.
                pop(
                    staticcall(
                        gas(), // Amount of gas left for the transaction.
                        1, // Address of `ecrecover`.
                        0x00, // Start of input.
                        0x80, // Size of input.
                        0x40, // Start of output.
                        0x20 // Size of output.
                    )
                )
                mstore(0x60, 0) // Restore the zero slot.
                // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                result := mload(xor(0x60, returndatasize()))
                mstore(0x40, m) // Restore the free memory pointer.
            }
        }
        /// @dev Recovers the signer's address from a message digest `hash`,
        /// and the signature defined by `v`, `r`, `s`.
        function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s)
            internal
            view
            returns (address result)
        {
            /// @solidity memory-safe-assembly
            assembly {
                let m := mload(0x40) // Cache the free memory pointer.
                mstore(0x00, hash)
                mstore(0x20, and(v, 0xff))
                mstore(0x40, r)
                mstore(0x60, s)
                pop(
                    staticcall(
                        gas(), // Amount of gas left for the transaction.
                        1, // Address of `ecrecover`.
                        0x00, // Start of input.
                        0x80, // Size of input.
                        0x40, // Start of output.
                        0x20 // Size of output.
                    )
                )
                mstore(0x60, 0) // Restore the zero slot.
                // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                result := mload(xor(0x60, returndatasize()))
                mstore(0x40, m) // Restore the free memory pointer.
            }
        }
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                     HASHING OPERATIONS                     */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
        /// @dev Returns an Ethereum Signed Message, created from a `hash`.
        /// This produces a hash corresponding to the one signed with the
        /// [`eth_sign`](https://eth.wiki/json-rpc/API#eth_sign)
        /// JSON-RPC method as part of EIP-191.
        function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 result) {
            /// @solidity memory-safe-assembly
            assembly {
                mstore(0x20, hash) // Store into scratch space for keccak256.
                mstore(0x00, "\\x00\\x00\\x00\\x00\\x19Ethereum Signed Message:\
    32") // 28 bytes.
                result := keccak256(0x04, 0x3c) // `32 * 2 - (32 - 28) = 60 = 0x3c`.
            }
        }
        /// @dev Returns an Ethereum Signed Message, created from `s`.
        /// This produces a hash corresponding to the one signed with the
        /// [`eth_sign`](https://eth.wiki/json-rpc/API#eth_sign)
        /// JSON-RPC method as part of EIP-191.
        /// Note: Supports lengths of `s` up to 999999 bytes.
        function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32 result) {
            /// @solidity memory-safe-assembly
            assembly {
                let sLength := mload(s)
                let o := 0x20
                mstore(o, "\\x19Ethereum Signed Message:\
    ") // 26 bytes, zero-right-padded.
                mstore(0x00, 0x00)
                // Convert the `s.length` to ASCII decimal representation: `base10(s.length)`.
                for { let temp := sLength } 1 {} {
                    o := sub(o, 1)
                    mstore8(o, add(48, mod(temp, 10)))
                    temp := div(temp, 10)
                    if iszero(temp) { break }
                }
                let n := sub(0x3a, o) // Header length: `26 + 32 - o`.
                // Throw an out-of-offset error (consumes all gas) if the header exceeds 32 bytes.
                returndatacopy(returndatasize(), returndatasize(), gt(n, 0x20))
                mstore(s, or(mload(0x00), mload(n))) // Temporarily store the header.
                result := keccak256(add(s, sub(0x20, n)), add(n, sLength))
                mstore(s, sLength) // Restore the length.
            }
        }
        /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
        /*                   EMPTY CALLDATA HELPERS                   */
        /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
        /// @dev Returns an empty calldata bytes.
        function emptySignature() internal pure returns (bytes calldata signature) {
            /// @solidity memory-safe-assembly
            assembly {
                signature.length := 0
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable2Step.sol)
    pragma solidity ^0.8.20;
    import {Ownable} from "./Ownable.sol";
    /**
     * @dev Contract module which provides access control mechanism, where
     * there is an account (an owner) that can be granted exclusive access to
     * specific functions.
     *
     * The initial owner is specified at deployment time in the constructor for `Ownable`. This
     * can later be changed with {transferOwnership} and {acceptOwnership}.
     *
     * This module is used through inheritance. It will make available all functions
     * from parent (Ownable).
     */
    abstract contract Ownable2Step is Ownable {
        address private _pendingOwner;
        event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);
        /**
         * @dev Returns the address of the pending owner.
         */
        function pendingOwner() public view virtual returns (address) {
            return _pendingOwner;
        }
        /**
         * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
         * Can only be called by the current owner.
         */
        function transferOwnership(address newOwner) public virtual override onlyOwner {
            _pendingOwner = newOwner;
            emit OwnershipTransferStarted(owner(), newOwner);
        }
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
         * Internal function without access restriction.
         */
        function _transferOwnership(address newOwner) internal virtual override {
            delete _pendingOwner;
            super._transferOwnership(newOwner);
        }
        /**
         * @dev The new owner accepts the ownership transfer.
         */
        function acceptOwnership() public virtual {
            address sender = _msgSender();
            if (pendingOwner() != sender) {
                revert OwnableUnauthorizedAccount(sender);
            }
            _transferOwnership(sender);
        }
    }
    // 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.1) (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;
        }
        function _contextSuffixLength() internal view virtual returns (uint256) {
            return 0;
        }
    }
    

    File 2 of 2: AethirToken
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)
    pragma solidity ^0.8.0;
    import "../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.
     *
     * By default, the owner account will be the one that deploys the contract. 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;
        event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
        /**
         * @dev Initializes the contract setting the deployer as the initial owner.
         */
        constructor() {
            _transferOwnership(_msgSender());
        }
        /**
         * @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 {
            require(owner() == _msgSender(), "Ownable: caller is not the owner");
        }
        /**
         * @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 {
            require(newOwner != address(0), "Ownable: new owner is the zero address");
            _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 v4.9.0) (interfaces/IERC5267.sol)
    pragma solidity ^0.8.0;
    interface IERC5267 {
        /**
         * @dev MAY be emitted to signal that the domain could have changed.
         */
        event EIP712DomainChanged();
        /**
         * @dev returns the fields and values that describe the domain separator used by this contract for EIP-712
         * signature.
         */
        function eip712Domain()
            external
            view
            returns (
                bytes1 fields,
                string memory name,
                string memory version,
                uint256 chainId,
                address verifyingContract,
                bytes32 salt,
                uint256[] memory extensions
            );
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/ERC20.sol)
    pragma solidity ^0.8.0;
    import "./IERC20.sol";
    import "./extensions/IERC20Metadata.sol";
    import "../../utils/Context.sol";
    /**
     * @dev Implementation of the {IERC20} interface.
     *
     * This implementation is agnostic to the way tokens are created. This means
     * that a supply mechanism has to be added in a derived contract using {_mint}.
     * For a generic mechanism see {ERC20PresetMinterPauser}.
     *
     * TIP: For a detailed writeup see our guide
     * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
     * to implement supply mechanisms].
     *
     * The default value of {decimals} is 18. To change this, you should override
     * this function so it returns a different value.
     *
     * We have followed general OpenZeppelin Contracts guidelines: functions revert
     * instead returning `false` on failure. This behavior is nonetheless
     * conventional and does not conflict with the expectations of ERC20
     * applications.
     *
     * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
     * This allows applications to reconstruct the allowance for all accounts just
     * by listening to said events. Other implementations of the EIP may not emit
     * these events, as it isn't required by the specification.
     *
     * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
     * functions have been added to mitigate the well-known issues around setting
     * allowances. See {IERC20-approve}.
     */
    contract ERC20 is Context, IERC20, IERC20Metadata {
        mapping(address => uint256) private _balances;
        mapping(address => mapping(address => uint256)) private _allowances;
        uint256 private _totalSupply;
        string private _name;
        string private _symbol;
        /**
         * @dev Sets the values for {name} and {symbol}.
         *
         * All two of these values are immutable: they can only be set once during
         * construction.
         */
        constructor(string memory name_, string memory symbol_) {
            _name = name_;
            _symbol = symbol_;
        }
        /**
         * @dev Returns the name of the token.
         */
        function name() public view virtual override returns (string memory) {
            return _name;
        }
        /**
         * @dev Returns the symbol of the token, usually a shorter version of the
         * name.
         */
        function symbol() public view virtual override returns (string memory) {
            return _symbol;
        }
        /**
         * @dev Returns the number of decimals used to get its user representation.
         * For example, if `decimals` equals `2`, a balance of `505` tokens should
         * be displayed to a user as `5.05` (`505 / 10 ** 2`).
         *
         * Tokens usually opt for a value of 18, imitating the relationship between
         * Ether and Wei. This is the default value returned by this function, unless
         * it's overridden.
         *
         * NOTE: This information is only used for _display_ purposes: it in
         * no way affects any of the arithmetic of the contract, including
         * {IERC20-balanceOf} and {IERC20-transfer}.
         */
        function decimals() public view virtual override returns (uint8) {
            return 18;
        }
        /**
         * @dev See {IERC20-totalSupply}.
         */
        function totalSupply() public view virtual override returns (uint256) {
            return _totalSupply;
        }
        /**
         * @dev See {IERC20-balanceOf}.
         */
        function balanceOf(address account) public view virtual override returns (uint256) {
            return _balances[account];
        }
        /**
         * @dev See {IERC20-transfer}.
         *
         * Requirements:
         *
         * - `to` cannot be the zero address.
         * - the caller must have a balance of at least `amount`.
         */
        function transfer(address to, uint256 amount) public virtual override returns (bool) {
            address owner = _msgSender();
            _transfer(owner, to, amount);
            return true;
        }
        /**
         * @dev See {IERC20-allowance}.
         */
        function allowance(address owner, address spender) public view virtual override returns (uint256) {
            return _allowances[owner][spender];
        }
        /**
         * @dev See {IERC20-approve}.
         *
         * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
         * `transferFrom`. This is semantically equivalent to an infinite approval.
         *
         * Requirements:
         *
         * - `spender` cannot be the zero address.
         */
        function approve(address spender, uint256 amount) public virtual override returns (bool) {
            address owner = _msgSender();
            _approve(owner, spender, amount);
            return true;
        }
        /**
         * @dev See {IERC20-transferFrom}.
         *
         * Emits an {Approval} event indicating the updated allowance. This is not
         * required by the EIP. See the note at the beginning of {ERC20}.
         *
         * NOTE: Does not update the allowance if the current allowance
         * is the maximum `uint256`.
         *
         * Requirements:
         *
         * - `from` and `to` cannot be the zero address.
         * - `from` must have a balance of at least `amount`.
         * - the caller must have allowance for ``from``'s tokens of at least
         * `amount`.
         */
        function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) {
            address spender = _msgSender();
            _spendAllowance(from, spender, amount);
            _transfer(from, to, amount);
            return true;
        }
        /**
         * @dev Atomically increases the allowance granted to `spender` by the caller.
         *
         * This is an alternative to {approve} that can be used as a mitigation for
         * problems described in {IERC20-approve}.
         *
         * Emits an {Approval} event indicating the updated allowance.
         *
         * Requirements:
         *
         * - `spender` cannot be the zero address.
         */
        function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
            address owner = _msgSender();
            _approve(owner, spender, allowance(owner, spender) + addedValue);
            return true;
        }
        /**
         * @dev Atomically decreases the allowance granted to `spender` by the caller.
         *
         * This is an alternative to {approve} that can be used as a mitigation for
         * problems described in {IERC20-approve}.
         *
         * Emits an {Approval} event indicating the updated allowance.
         *
         * Requirements:
         *
         * - `spender` cannot be the zero address.
         * - `spender` must have allowance for the caller of at least
         * `subtractedValue`.
         */
        function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
            address owner = _msgSender();
            uint256 currentAllowance = allowance(owner, spender);
            require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
            unchecked {
                _approve(owner, spender, currentAllowance - subtractedValue);
            }
            return true;
        }
        /**
         * @dev Moves `amount` of tokens from `from` to `to`.
         *
         * This internal function is equivalent to {transfer}, and can be used to
         * e.g. implement automatic token fees, slashing mechanisms, etc.
         *
         * Emits a {Transfer} event.
         *
         * Requirements:
         *
         * - `from` cannot be the zero address.
         * - `to` cannot be the zero address.
         * - `from` must have a balance of at least `amount`.
         */
        function _transfer(address from, address to, uint256 amount) internal virtual {
            require(from != address(0), "ERC20: transfer from the zero address");
            require(to != address(0), "ERC20: transfer to the zero address");
            _beforeTokenTransfer(from, to, amount);
            uint256 fromBalance = _balances[from];
            require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
            unchecked {
                _balances[from] = fromBalance - amount;
                // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
                // decrementing then incrementing.
                _balances[to] += amount;
            }
            emit Transfer(from, to, amount);
            _afterTokenTransfer(from, to, amount);
        }
        /** @dev Creates `amount` tokens and assigns them to `account`, increasing
         * the total supply.
         *
         * Emits a {Transfer} event with `from` set to the zero address.
         *
         * Requirements:
         *
         * - `account` cannot be the zero address.
         */
        function _mint(address account, uint256 amount) internal virtual {
            require(account != address(0), "ERC20: mint to the zero address");
            _beforeTokenTransfer(address(0), account, amount);
            _totalSupply += amount;
            unchecked {
                // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
                _balances[account] += amount;
            }
            emit Transfer(address(0), account, amount);
            _afterTokenTransfer(address(0), account, amount);
        }
        /**
         * @dev Destroys `amount` tokens from `account`, reducing the
         * total supply.
         *
         * Emits a {Transfer} event with `to` set to the zero address.
         *
         * Requirements:
         *
         * - `account` cannot be the zero address.
         * - `account` must have at least `amount` tokens.
         */
        function _burn(address account, uint256 amount) internal virtual {
            require(account != address(0), "ERC20: burn from the zero address");
            _beforeTokenTransfer(account, address(0), amount);
            uint256 accountBalance = _balances[account];
            require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
            unchecked {
                _balances[account] = accountBalance - amount;
                // Overflow not possible: amount <= accountBalance <= totalSupply.
                _totalSupply -= amount;
            }
            emit Transfer(account, address(0), amount);
            _afterTokenTransfer(account, address(0), amount);
        }
        /**
         * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
         *
         * This internal function is equivalent to `approve`, and can be used to
         * e.g. set automatic allowances for certain subsystems, etc.
         *
         * Emits an {Approval} event.
         *
         * Requirements:
         *
         * - `owner` cannot be the zero address.
         * - `spender` cannot be the zero address.
         */
        function _approve(address owner, address spender, uint256 amount) internal virtual {
            require(owner != address(0), "ERC20: approve from the zero address");
            require(spender != address(0), "ERC20: approve to the zero address");
            _allowances[owner][spender] = amount;
            emit Approval(owner, spender, amount);
        }
        /**
         * @dev Updates `owner` s allowance for `spender` based on spent `amount`.
         *
         * Does not update the allowance amount in case of infinite allowance.
         * Revert if not enough allowance is available.
         *
         * Might emit an {Approval} event.
         */
        function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
            uint256 currentAllowance = allowance(owner, spender);
            if (currentAllowance != type(uint256).max) {
                require(currentAllowance >= amount, "ERC20: insufficient allowance");
                unchecked {
                    _approve(owner, spender, currentAllowance - amount);
                }
            }
        }
        /**
         * @dev Hook that is called before any transfer of tokens. This includes
         * minting and burning.
         *
         * Calling conditions:
         *
         * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
         * will be transferred to `to`.
         * - when `from` is zero, `amount` tokens will be minted for `to`.
         * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
         * - `from` and `to` are never both zero.
         *
         * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
         */
        function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}
        /**
         * @dev Hook that is called after any transfer of tokens. This includes
         * minting and burning.
         *
         * Calling conditions:
         *
         * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
         * has been transferred to `to`.
         * - when `from` is zero, `amount` tokens have been minted for `to`.
         * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
         * - `from` and `to` are never both zero.
         *
         * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
         */
        function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
    pragma solidity ^0.8.0;
    /**
     * @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 amount of tokens in existence.
         */
        function totalSupply() external view returns (uint256);
        /**
         * @dev Returns the amount of tokens owned by `account`.
         */
        function balanceOf(address account) external view returns (uint256);
        /**
         * @dev Moves `amount` tokens from the caller's account to `to`.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * Emits a {Transfer} event.
         */
        function transfer(address to, uint256 amount) external returns (bool);
        /**
         * @dev Returns the remaining number of tokens that `spender` will be
         * allowed to spend on behalf of `owner` through {transferFrom}. This is
         * zero by default.
         *
         * This value changes when {approve} or {transferFrom} are called.
         */
        function allowance(address owner, address spender) external view returns (uint256);
        /**
         * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * IMPORTANT: Beware that changing an allowance with this method brings the risk
         * that someone may use both the old and the new allowance by unfortunate
         * transaction ordering. One possible solution to mitigate this race
         * condition is to first reduce the spender's allowance to 0 and set the
         * desired value afterwards:
         * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
         *
         * Emits an {Approval} event.
         */
        function approve(address spender, uint256 amount) external returns (bool);
        /**
         * @dev Moves `amount` tokens from `from` to `to` using the
         * allowance mechanism. `amount` is then deducted from the caller's
         * allowance.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * Emits a {Transfer} event.
         */
        function transferFrom(address from, address to, uint256 amount) external returns (bool);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.9.4) (token/ERC20/extensions/ERC20Permit.sol)
    pragma solidity ^0.8.0;
    import "./IERC20Permit.sol";
    import "../ERC20.sol";
    import "../../../utils/cryptography/ECDSA.sol";
    import "../../../utils/cryptography/EIP712.sol";
    import "../../../utils/Counters.sol";
    /**
     * @dev Implementation 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.
     *
     * _Available since v3.4._
     */
    abstract contract ERC20Permit is ERC20, IERC20Permit, EIP712 {
        using Counters for Counters.Counter;
        mapping(address => Counters.Counter) private _nonces;
        // solhint-disable-next-line var-name-mixedcase
        bytes32 private constant _PERMIT_TYPEHASH =
            keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
        /**
         * @dev In previous versions `_PERMIT_TYPEHASH` was declared as `immutable`.
         * However, to ensure consistency with the upgradeable transpiler, we will continue
         * to reserve a slot.
         * @custom:oz-renamed-from _PERMIT_TYPEHASH
         */
        // solhint-disable-next-line var-name-mixedcase
        bytes32 private _PERMIT_TYPEHASH_DEPRECATED_SLOT;
        /**
         * @dev Initializes the {EIP712} domain separator using the `name` parameter, and setting `version` to `"1"`.
         *
         * It's a good idea to use the same `name` that is defined as the ERC20 token name.
         */
        constructor(string memory name) EIP712(name, "1") {}
        /**
         * @inheritdoc IERC20Permit
         */
        function permit(
            address owner,
            address spender,
            uint256 value,
            uint256 deadline,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) public virtual override {
            require(block.timestamp <= deadline, "ERC20Permit: expired deadline");
            bytes32 structHash = keccak256(abi.encode(_PERMIT_TYPEHASH, owner, spender, value, _useNonce(owner), deadline));
            bytes32 hash = _hashTypedDataV4(structHash);
            address signer = ECDSA.recover(hash, v, r, s);
            require(signer == owner, "ERC20Permit: invalid signature");
            _approve(owner, spender, value);
        }
        /**
         * @inheritdoc IERC20Permit
         */
        function nonces(address owner) public view virtual override returns (uint256) {
            return _nonces[owner].current();
        }
        /**
         * @inheritdoc IERC20Permit
         */
        // solhint-disable-next-line func-name-mixedcase
        function DOMAIN_SEPARATOR() external view override returns (bytes32) {
            return _domainSeparatorV4();
        }
        /**
         * @dev "Consume a nonce": return the current value and increment.
         *
         * _Available since v4.1._
         */
        function _useNonce(address owner) internal virtual returns (uint256 current) {
            Counters.Counter storage nonce = _nonces[owner];
            current = nonce.current();
            nonce.increment();
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
    pragma solidity ^0.8.0;
    import "../IERC20.sol";
    /**
     * @dev Interface for the optional metadata functions from the ERC20 standard.
     *
     * _Available since v4.1._
     */
    interface IERC20Metadata is IERC20 {
        /**
         * @dev Returns the name of the token.
         */
        function name() external view returns (string memory);
        /**
         * @dev Returns the symbol of the token.
         */
        function symbol() external view returns (string memory);
        /**
         * @dev Returns the decimals places of the token.
         */
        function decimals() external view returns (uint8);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.9.4) (token/ERC20/extensions/IERC20Permit.sol)
    pragma solidity ^0.8.0;
    /**
     * @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 v4.9.3) (token/ERC20/utils/SafeERC20.sol)
    pragma solidity ^0.8.0;
    import "../IERC20.sol";
    import "../extensions/IERC20Permit.sol";
    import "../../../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 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.encodeWithSelector(token.transfer.selector, 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.encodeWithSelector(token.transferFrom.selector, from, to, value));
        }
        /**
         * @dev Deprecated. This function has issues similar to the ones found in
         * {IERC20-approve}, and its usage is discouraged.
         *
         * Whenever possible, use {safeIncreaseAllowance} and
         * {safeDecreaseAllowance} instead.
         */
        function safeApprove(IERC20 token, address spender, uint256 value) internal {
            // safeApprove should only be called when setting an initial allowance,
            // or when resetting it to zero. To increase and decrease it, use
            // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
            require(
                (value == 0) || (token.allowance(address(this), spender) == 0),
                "SafeERC20: approve from non-zero to non-zero allowance"
            );
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
        }
        /**
         * @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);
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
        }
        /**
         * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
         * non-reverting calls are assumed to be successful.
         */
        function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
            unchecked {
                uint256 oldAllowance = token.allowance(address(this), spender);
                require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
                _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
            }
        }
        /**
         * @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.encodeWithSelector(token.approve.selector, spender, value);
            if (!_callOptionalReturnBool(token, approvalCall)) {
                _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
                _callOptionalReturn(token, approvalCall);
            }
        }
        /**
         * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
         * Revert on invalid signature.
         */
        function safePermit(
            IERC20Permit token,
            address owner,
            address spender,
            uint256 value,
            uint256 deadline,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) internal {
            uint256 nonceBefore = token.nonces(owner);
            token.permit(owner, spender, value, deadline, v, r, s);
            uint256 nonceAfter = token.nonces(owner);
            require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
        }
        /**
         * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
         * on the return value: the return value is optional (but if data is returned, it must not be false).
         * @param token The token targeted by the call.
         * @param data The call data (encoded using abi.encode or one of its variants).
         */
        function _callOptionalReturn(IERC20 token, bytes memory data) private {
            // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
            // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
            // the target address contains contract code and also asserts for success in the low-level call.
            bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
            require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
        /**
         * @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.isContract(address(token));
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
    pragma solidity ^0.8.1;
    /**
     * @dev Collection of functions related to the address type
     */
    library Address {
        /**
         * @dev Returns true if `account` is a contract.
         *
         * [IMPORTANT]
         * ====
         * It is unsafe to assume that an address for which this function returns
         * false is an externally-owned account (EOA) and not a contract.
         *
         * Among others, `isContract` will return false for the following
         * types of addresses:
         *
         *  - an externally-owned account
         *  - a contract in construction
         *  - an address where a contract will be created
         *  - an address where a contract lived, but was destroyed
         *
         * Furthermore, `isContract` will also return true if the target contract within
         * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
         * which only has an effect at the end of a transaction.
         * ====
         *
         * [IMPORTANT]
         * ====
         * You shouldn't rely on `isContract` to protect against flash loan attacks!
         *
         * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
         * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
         * constructor.
         * ====
         */
        function isContract(address account) internal view returns (bool) {
            // This method relies on extcodesize/address.code.length, which returns 0
            // for contracts in construction, since the code is only stored at the end
            // of the constructor execution.
            return account.code.length > 0;
        }
        /**
         * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
         * `recipient`, forwarding all available gas and reverting on errors.
         *
         * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
         * of certain opcodes, possibly making contracts go over the 2300 gas limit
         * imposed by `transfer`, making them unable to receive funds via
         * `transfer`. {sendValue} removes this limitation.
         *
         * https://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.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
         */
        function sendValue(address payable recipient, uint256 amount) internal {
            require(address(this).balance >= amount, "Address: insufficient balance");
            (bool success, ) = recipient.call{value: amount}("");
            require(success, "Address: unable to send value, recipient may have reverted");
        }
        /**
         * @dev Performs a Solidity function call using a low level `call`. A
         * plain `call` is an unsafe replacement for a function call: use this
         * function instead.
         *
         * If `target` reverts with a revert reason, it is bubbled up by this
         * function (like regular Solidity function calls).
         *
         * Returns the raw returned data. To convert to the expected return value,
         * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
         *
         * Requirements:
         *
         * - `target` must be a contract.
         * - calling `target` with `data` must not revert.
         *
         * _Available since v3.1._
         */
        function functionCall(address target, bytes memory data) internal returns (bytes memory) {
            return functionCallWithValue(target, data, 0, "Address: low-level call failed");
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
         * `errorMessage` as a fallback revert reason when `target` reverts.
         *
         * _Available since v3.1._
         */
        function functionCall(
            address target,
            bytes memory data,
            string memory errorMessage
        ) internal returns (bytes memory) {
            return functionCallWithValue(target, data, 0, errorMessage);
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but also transferring `value` wei to `target`.
         *
         * Requirements:
         *
         * - the calling contract must have an ETH balance of at least `value`.
         * - the called Solidity function must be `payable`.
         *
         * _Available since v3.1._
         */
        function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
            return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
        }
        /**
         * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
         * with `errorMessage` as a fallback revert reason when `target` reverts.
         *
         * _Available since v3.1._
         */
        function functionCallWithValue(
            address target,
            bytes memory data,
            uint256 value,
            string memory errorMessage
        ) internal returns (bytes memory) {
            require(address(this).balance >= value, "Address: insufficient balance for call");
            (bool success, bytes memory returndata) = target.call{value: value}(data);
            return verifyCallResultFromTarget(target, success, returndata, errorMessage);
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but performing a static call.
         *
         * _Available since v3.3._
         */
        function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
            return functionStaticCall(target, data, "Address: low-level static call failed");
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
         * but performing a static call.
         *
         * _Available since v3.3._
         */
        function functionStaticCall(
            address target,
            bytes memory data,
            string memory errorMessage
        ) internal view returns (bytes memory) {
            (bool success, bytes memory returndata) = target.staticcall(data);
            return verifyCallResultFromTarget(target, success, returndata, errorMessage);
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but performing a delegate call.
         *
         * _Available since v3.4._
         */
        function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
            return functionDelegateCall(target, data, "Address: low-level delegate call failed");
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
         * but performing a delegate call.
         *
         * _Available since v3.4._
         */
        function functionDelegateCall(
            address target,
            bytes memory data,
            string memory errorMessage
        ) internal returns (bytes memory) {
            (bool success, bytes memory returndata) = target.delegatecall(data);
            return verifyCallResultFromTarget(target, success, returndata, errorMessage);
        }
        /**
         * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
         * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
         *
         * _Available since v4.8._
         */
        function verifyCallResultFromTarget(
            address target,
            bool success,
            bytes memory returndata,
            string memory errorMessage
        ) internal view returns (bytes memory) {
            if (success) {
                if (returndata.length == 0) {
                    // only check isContract if the call was successful and the return data is empty
                    // otherwise we already know that it was a contract
                    require(isContract(target), "Address: call to non-contract");
                }
                return returndata;
            } else {
                _revert(returndata, errorMessage);
            }
        }
        /**
         * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
         * revert reason or using the provided one.
         *
         * _Available since v4.3._
         */
        function verifyCallResult(
            bool success,
            bytes memory returndata,
            string memory errorMessage
        ) internal pure returns (bytes memory) {
            if (success) {
                return returndata;
            } else {
                _revert(returndata, errorMessage);
            }
        }
        function _revert(bytes memory returndata, string memory errorMessage) private pure {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly
                /// @solidity memory-safe-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol)
    pragma solidity ^0.8.0;
    /**
     * @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;
        }
        function _contextSuffixLength() internal view virtual returns (uint256) {
            return 0;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (utils/Counters.sol)
    pragma solidity ^0.8.0;
    /**
     * @title Counters
     * @author Matt Condon (@shrugs)
     * @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number
     * of elements in a mapping, issuing ERC721 ids, or counting request ids.
     *
     * Include with `using Counters for Counters.Counter;`
     */
    library Counters {
        struct Counter {
            // This variable should never be directly accessed by users of the library: interactions must be restricted to
            // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
            // this feature: see https://github.com/ethereum/solidity/issues/4637
            uint256 _value; // default: 0
        }
        function current(Counter storage counter) internal view returns (uint256) {
            return counter._value;
        }
        function increment(Counter storage counter) internal {
            unchecked {
                counter._value += 1;
            }
        }
        function decrement(Counter storage counter) internal {
            uint256 value = counter._value;
            require(value > 0, "Counter: decrement overflow");
            unchecked {
                counter._value = value - 1;
            }
        }
        function reset(Counter storage counter) internal {
            counter._value = 0;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.9.0) (utils/ShortStrings.sol)
    pragma solidity ^0.8.8;
    import "./StorageSlot.sol";
    // | string  | 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA   |
    // | length  | 0x                                                              BB |
    type ShortString is bytes32;
    /**
     * @dev This library provides functions to convert short memory strings
     * into a `ShortString` type that can be used as an immutable variable.
     *
     * Strings of arbitrary length can be optimized using this library if
     * they are short enough (up to 31 bytes) by packing them with their
     * length (1 byte) in a single EVM word (32 bytes). Additionally, a
     * fallback mechanism can be used for every other case.
     *
     * Usage example:
     *
     * ```solidity
     * contract Named {
     *     using ShortStrings for *;
     *
     *     ShortString private immutable _name;
     *     string private _nameFallback;
     *
     *     constructor(string memory contractName) {
     *         _name = contractName.toShortStringWithFallback(_nameFallback);
     *     }
     *
     *     function name() external view returns (string memory) {
     *         return _name.toStringWithFallback(_nameFallback);
     *     }
     * }
     * ```
     */
    library ShortStrings {
        // Used as an identifier for strings longer than 31 bytes.
        bytes32 private constant _FALLBACK_SENTINEL = 0x00000000000000000000000000000000000000000000000000000000000000FF;
        error StringTooLong(string str);
        error InvalidShortString();
        /**
         * @dev Encode a string of at most 31 chars into a `ShortString`.
         *
         * This will trigger a `StringTooLong` error is the input string is too long.
         */
        function toShortString(string memory str) internal pure returns (ShortString) {
            bytes memory bstr = bytes(str);
            if (bstr.length > 31) {
                revert StringTooLong(str);
            }
            return ShortString.wrap(bytes32(uint256(bytes32(bstr)) | bstr.length));
        }
        /**
         * @dev Decode a `ShortString` back to a "normal" string.
         */
        function toString(ShortString sstr) internal pure returns (string memory) {
            uint256 len = byteLength(sstr);
            // using `new string(len)` would work locally but is not memory safe.
            string memory str = new string(32);
            /// @solidity memory-safe-assembly
            assembly {
                mstore(str, len)
                mstore(add(str, 0x20), sstr)
            }
            return str;
        }
        /**
         * @dev Return the length of a `ShortString`.
         */
        function byteLength(ShortString sstr) internal pure returns (uint256) {
            uint256 result = uint256(ShortString.unwrap(sstr)) & 0xFF;
            if (result > 31) {
                revert InvalidShortString();
            }
            return result;
        }
        /**
         * @dev Encode a string into a `ShortString`, or write it to storage if it is too long.
         */
        function toShortStringWithFallback(string memory value, string storage store) internal returns (ShortString) {
            if (bytes(value).length < 32) {
                return toShortString(value);
            } else {
                StorageSlot.getStringSlot(store).value = value;
                return ShortString.wrap(_FALLBACK_SENTINEL);
            }
        }
        /**
         * @dev Decode a string that was encoded to `ShortString` or written to storage using {setWithFallback}.
         */
        function toStringWithFallback(ShortString value, string storage store) internal pure returns (string memory) {
            if (ShortString.unwrap(value) != _FALLBACK_SENTINEL) {
                return toString(value);
            } else {
                return store;
            }
        }
        /**
         * @dev Return the length of a string that was encoded to `ShortString` or written to storage using {setWithFallback}.
         *
         * WARNING: This will return the "byte length" of the string. This may not reflect the actual length in terms of
         * actual characters as the UTF-8 encoding of a single character can span over multiple bytes.
         */
        function byteLengthWithFallback(ShortString value, string storage store) internal view returns (uint256) {
            if (ShortString.unwrap(value) != _FALLBACK_SENTINEL) {
                return byteLength(value);
            } else {
                return bytes(store).length;
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.9.0) (utils/StorageSlot.sol)
    // This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
    pragma solidity ^0.8.0;
    /**
     * @dev Library for reading and writing primitive types to specific storage slots.
     *
     * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
     * This library helps with reading and writing to such slots without the need for inline assembly.
     *
     * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
     *
     * Example usage to set ERC1967 implementation slot:
     * ```solidity
     * contract ERC1967 {
     *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
     *
     *     function _getImplementation() internal view returns (address) {
     *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
     *     }
     *
     *     function _setImplementation(address newImplementation) internal {
     *         require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
     *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
     *     }
     * }
     * ```
     *
     * _Available since v4.1 for `address`, `bool`, `bytes32`, `uint256`._
     * _Available since v4.9 for `string`, `bytes`._
     */
    library StorageSlot {
        struct AddressSlot {
            address value;
        }
        struct BooleanSlot {
            bool value;
        }
        struct Bytes32Slot {
            bytes32 value;
        }
        struct Uint256Slot {
            uint256 value;
        }
        struct StringSlot {
            string value;
        }
        struct BytesSlot {
            bytes value;
        }
        /**
         * @dev Returns an `AddressSlot` with member `value` located at `slot`.
         */
        function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
            /// @solidity memory-safe-assembly
            assembly {
                r.slot := slot
            }
        }
        /**
         * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
         */
        function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
            /// @solidity memory-safe-assembly
            assembly {
                r.slot := slot
            }
        }
        /**
         * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
         */
        function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
            /// @solidity memory-safe-assembly
            assembly {
                r.slot := slot
            }
        }
        /**
         * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
         */
        function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
            /// @solidity memory-safe-assembly
            assembly {
                r.slot := slot
            }
        }
        /**
         * @dev Returns an `StringSlot` with member `value` located at `slot`.
         */
        function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
            /// @solidity memory-safe-assembly
            assembly {
                r.slot := slot
            }
        }
        /**
         * @dev Returns an `StringSlot` representation of the string storage pointer `store`.
         */
        function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
            /// @solidity memory-safe-assembly
            assembly {
                r.slot := store.slot
            }
        }
        /**
         * @dev Returns an `BytesSlot` with member `value` located at `slot`.
         */
        function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
            /// @solidity memory-safe-assembly
            assembly {
                r.slot := slot
            }
        }
        /**
         * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
         */
        function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
            /// @solidity memory-safe-assembly
            assembly {
                r.slot := store.slot
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)
    pragma solidity ^0.8.0;
    import "./math/Math.sol";
    import "./math/SignedMath.sol";
    /**
     * @dev String operations.
     */
    library Strings {
        bytes16 private constant _SYMBOLS = "0123456789abcdef";
        uint8 private constant _ADDRESS_LENGTH = 20;
        /**
         * @dev Converts a `uint256` to its ASCII `string` decimal representation.
         */
        function toString(uint256 value) internal pure returns (string memory) {
            unchecked {
                uint256 length = Math.log10(value) + 1;
                string memory buffer = new string(length);
                uint256 ptr;
                /// @solidity memory-safe-assembly
                assembly {
                    ptr := add(buffer, add(32, length))
                }
                while (true) {
                    ptr--;
                    /// @solidity memory-safe-assembly
                    assembly {
                        mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                    }
                    value /= 10;
                    if (value == 0) break;
                }
                return buffer;
            }
        }
        /**
         * @dev Converts a `int256` to its ASCII `string` decimal representation.
         */
        function toString(int256 value) internal pure returns (string memory) {
            return string(abi.encodePacked(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) {
            bytes memory buffer = new bytes(2 * length + 2);
            buffer[0] = "0";
            buffer[1] = "x";
            for (uint256 i = 2 * length + 1; i > 1; --i) {
                buffer[i] = _SYMBOLS[value & 0xf];
                value >>= 4;
            }
            require(value == 0, "Strings: hex length insufficient");
            return string(buffer);
        }
        /**
         * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
         */
        function toHexString(address addr) internal pure returns (string memory) {
            return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
        }
        /**
         * @dev Returns true if the two strings are equal.
         */
        function equal(string memory a, string memory b) internal pure returns (bool) {
            return keccak256(bytes(a)) == keccak256(bytes(b));
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/ECDSA.sol)
    pragma solidity ^0.8.0;
    import "../Strings.sol";
    /**
     * @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,
            InvalidSignatureV // Deprecated in v4.8
        }
        function _throwError(RecoverError error) private pure {
            if (error == RecoverError.NoError) {
                return; // no error: do nothing
            } else if (error == RecoverError.InvalidSignature) {
                revert("ECDSA: invalid signature");
            } else if (error == RecoverError.InvalidSignatureLength) {
                revert("ECDSA: invalid signature length");
            } else if (error == RecoverError.InvalidSignatureS) {
                revert("ECDSA: invalid signature 's' value");
            }
        }
        /**
         * @dev Returns the address that signed a hashed message (`hash`) with
         * `signature` or error string. This address can then be used for verification purposes.
         *
         * The `ecrecover` EVM opcode 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 {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]
         *
         * _Available since v4.3._
         */
        function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
            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);
            }
        }
        /**
         * @dev Returns the address that signed a hashed message (`hash`) with
         * `signature`. This address can then be used for verification purposes.
         *
         * The `ecrecover` EVM opcode 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 {toEthSignedMessageHash} on it.
         */
        function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
            (address recovered, RecoverError error) = tryRecover(hash, signature);
            _throwError(error);
            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]
         *
         * _Available since v4.3._
         */
        function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError) {
            bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
            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.
         *
         * _Available since v4.2._
         */
        function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
            (address recovered, RecoverError error) = tryRecover(hash, r, vs);
            _throwError(error);
            return recovered;
        }
        /**
         * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
         * `r` and `s` signature fields separately.
         *
         * _Available since v4.3._
         */
        function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address, RecoverError) {
            // 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);
            }
            // 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);
            }
            return (signer, RecoverError.NoError);
        }
        /**
         * @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) = tryRecover(hash, v, r, s);
            _throwError(error);
            return recovered;
        }
        /**
         * @dev Returns an Ethereum Signed Message, created from a `hash`. This
         * produces hash corresponding to the one signed with the
         * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
         * JSON-RPC method as part of EIP-191.
         *
         * See {recover}.
         */
        function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 message) {
            // 32 is the length in bytes of hash,
            // enforced by the type signature above
            /// @solidity memory-safe-assembly
            assembly {
                mstore(0x00, "\\x19Ethereum Signed Message:\
    32")
                mstore(0x1c, hash)
                message := keccak256(0x00, 0x3c)
            }
        }
        /**
         * @dev Returns an Ethereum Signed Message, created from `s`. This
         * produces hash corresponding to the one signed with the
         * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
         * JSON-RPC method as part of EIP-191.
         *
         * See {recover}.
         */
        function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
            return keccak256(abi.encodePacked("\\x19Ethereum Signed Message:\
    ", Strings.toString(s.length), s));
        }
        /**
         * @dev Returns an Ethereum Signed Typed Data, created from a
         * `domainSeparator` and a `structHash`. This produces hash corresponding
         * to the one signed with the
         * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
         * JSON-RPC method as part of EIP-712.
         *
         * See {recover}.
         */
        function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 data) {
            /// @solidity memory-safe-assembly
            assembly {
                let ptr := mload(0x40)
                mstore(ptr, "\\x19\\x01")
                mstore(add(ptr, 0x02), domainSeparator)
                mstore(add(ptr, 0x22), structHash)
                data := keccak256(ptr, 0x42)
            }
        }
        /**
         * @dev Returns an Ethereum Signed Data with intended validator, created from a
         * `validator` and `data` according to the version 0 of EIP-191.
         *
         * See {recover}.
         */
        function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
            return keccak256(abi.encodePacked("\\x19\\x00", validator, data));
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/EIP712.sol)
    pragma solidity ^0.8.8;
    import "./ECDSA.sol";
    import "../ShortStrings.sol";
    import "../../interfaces/IERC5267.sol";
    /**
     * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
     *
     * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
     * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
     * they need in their contracts using a combination of `abi.encode` and `keccak256`.
     *
     * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
     * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
     * ({_hashTypedDataV4}).
     *
     * The implementation of the domain separator was designed to be as efficient as possible while still properly updating
     * the chain id to protect against replay attacks on an eventual fork of the chain.
     *
     * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
     * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
     *
     * NOTE: In the upgradeable version of this contract, the cached values will correspond to the address, and the domain
     * separator of the implementation contract. This will cause the `_domainSeparatorV4` function to always rebuild the
     * separator from the immutable values, which is cheaper than accessing a cached version in cold storage.
     *
     * _Available since v3.4._
     *
     * @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment
     */
    abstract contract EIP712 is IERC5267 {
        using ShortStrings for *;
        bytes32 private constant _TYPE_HASH =
            keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
        // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to
        // invalidate the cached domain separator if the chain id changes.
        bytes32 private immutable _cachedDomainSeparator;
        uint256 private immutable _cachedChainId;
        address private immutable _cachedThis;
        bytes32 private immutable _hashedName;
        bytes32 private immutable _hashedVersion;
        ShortString private immutable _name;
        ShortString private immutable _version;
        string private _nameFallback;
        string private _versionFallback;
        /**
         * @dev Initializes the domain separator and parameter caches.
         *
         * The meaning of `name` and `version` is specified in
         * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
         *
         * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
         * - `version`: the current major version of the signing domain.
         *
         * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
         * contract upgrade].
         */
        constructor(string memory name, string memory version) {
            _name = name.toShortStringWithFallback(_nameFallback);
            _version = version.toShortStringWithFallback(_versionFallback);
            _hashedName = keccak256(bytes(name));
            _hashedVersion = keccak256(bytes(version));
            _cachedChainId = block.chainid;
            _cachedDomainSeparator = _buildDomainSeparator();
            _cachedThis = address(this);
        }
        /**
         * @dev Returns the domain separator for the current chain.
         */
        function _domainSeparatorV4() internal view returns (bytes32) {
            if (address(this) == _cachedThis && block.chainid == _cachedChainId) {
                return _cachedDomainSeparator;
            } else {
                return _buildDomainSeparator();
            }
        }
        function _buildDomainSeparator() private view returns (bytes32) {
            return keccak256(abi.encode(_TYPE_HASH, _hashedName, _hashedVersion, block.chainid, address(this)));
        }
        /**
         * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
         * function returns the hash of the fully encoded EIP712 message for this domain.
         *
         * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
         *
         * ```solidity
         * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
         *     keccak256("Mail(address to,string contents)"),
         *     mailTo,
         *     keccak256(bytes(mailContents))
         * )));
         * address signer = ECDSA.recover(digest, signature);
         * ```
         */
        function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
            return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash);
        }
        /**
         * @dev See {EIP-5267}.
         *
         * _Available since v4.9._
         */
        function eip712Domain()
            public
            view
            virtual
            override
            returns (
                bytes1 fields,
                string memory name,
                string memory version,
                uint256 chainId,
                address verifyingContract,
                bytes32 salt,
                uint256[] memory extensions
            )
        {
            return (
                hex"0f", // 01111
                _name.toStringWithFallback(_nameFallback),
                _version.toStringWithFallback(_versionFallback),
                block.chainid,
                address(this),
                bytes32(0),
                new uint256[](0)
            );
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev Standard math utilities missing in the Solidity language.
     */
    library Math {
        enum Rounding {
            Down, // Toward negative infinity
            Up, // Toward infinity
            Zero // Toward zero
        }
        /**
         * @dev Returns the largest of two numbers.
         */
        function max(uint256 a, uint256 b) internal pure returns (uint256) {
            return a > b ? a : b;
        }
        /**
         * @dev Returns the smallest of two numbers.
         */
        function min(uint256 a, uint256 b) internal pure returns (uint256) {
            return a < b ? a : b;
        }
        /**
         * @dev Returns the average of two numbers. The result is rounded towards
         * zero.
         */
        function average(uint256 a, uint256 b) internal pure returns (uint256) {
            // (a + b) / 2 can overflow.
            return (a & b) + (a ^ b) / 2;
        }
        /**
         * @dev Returns the ceiling of the division of two numbers.
         *
         * This differs from standard division with `/` in that it rounds up instead
         * of rounding down.
         */
        function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
            // (a + b - 1) / b can overflow on addition, so we distribute.
            return a == 0 ? 0 : (a - 1) / b + 1;
        }
        /**
         * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
         * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
         * with further edits by Uniswap Labs also under MIT license.
         */
        function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
            unchecked {
                // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
                // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
                // variables such that product = prod1 * 2^256 + prod0.
                uint256 prod0; // Least significant 256 bits of the product
                uint256 prod1; // Most significant 256 bits of the product
                assembly {
                    let mm := mulmod(x, y, not(0))
                    prod0 := mul(x, y)
                    prod1 := sub(sub(mm, prod0), lt(mm, prod0))
                }
                // Handle non-overflow cases, 256 by 256 division.
                if (prod1 == 0) {
                    // 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.
                require(denominator > prod1, "Math: mulDiv overflow");
                ///////////////////////////////////////////////
                // 512 by 256 division.
                ///////////////////////////////////////////////
                // Make division exact by subtracting the remainder from [prod1 prod0].
                uint256 remainder;
                assembly {
                    // Compute remainder using mulmod.
                    remainder := mulmod(x, y, denominator)
                    // Subtract 256 bit number from 512 bit number.
                    prod1 := sub(prod1, gt(remainder, prod0))
                    prod0 := sub(prod0, remainder)
                }
                // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
                // See https://cs.stackexchange.com/q/138556/92363.
                // Does not overflow because the denominator cannot be zero at this stage in the function.
                uint256 twos = denominator & (~denominator + 1);
                assembly {
                    // Divide denominator by twos.
                    denominator := div(denominator, twos)
                    // Divide [prod1 prod0] by twos.
                    prod0 := div(prod0, twos)
                    // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                    twos := add(div(sub(0, twos), twos), 1)
                }
                // Shift in bits from prod1 into prod0.
                prod0 |= prod1 * twos;
                // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
                // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
                // four bits. That is, denominator * inv = 1 mod 2^4.
                uint256 inverse = (3 * denominator) ^ 2;
                // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
                // in modular arithmetic, doubling the correct bits in each step.
                inverse *= 2 - denominator * inverse; // inverse mod 2^8
                inverse *= 2 - denominator * inverse; // inverse mod 2^16
                inverse *= 2 - denominator * inverse; // inverse mod 2^32
                inverse *= 2 - denominator * inverse; // inverse mod 2^64
                inverse *= 2 - denominator * inverse; // inverse mod 2^128
                inverse *= 2 - denominator * inverse; // inverse mod 2^256
                // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
                // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
                // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
                // is no longer required.
                result = prod0 * inverse;
                return result;
            }
        }
        /**
         * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
         */
        function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
            uint256 result = mulDiv(x, y, denominator);
            if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
                result += 1;
            }
            return result;
        }
        /**
         * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
         *
         * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
         */
        function sqrt(uint256 a) internal pure returns (uint256) {
            if (a == 0) {
                return 0;
            }
            // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
            //
            // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
            // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
            //
            // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
            // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
            // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
            //
            // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
            uint256 result = 1 << (log2(a) >> 1);
            // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
            // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
            // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
            // into the expected uint128 result.
            unchecked {
                result = (result + a / result) >> 1;
                result = (result + a / result) >> 1;
                result = (result + a / result) >> 1;
                result = (result + a / result) >> 1;
                result = (result + a / result) >> 1;
                result = (result + a / result) >> 1;
                result = (result + a / result) >> 1;
                return min(result, a / result);
            }
        }
        /**
         * @notice Calculates sqrt(a), following the selected rounding direction.
         */
        function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
            unchecked {
                uint256 result = sqrt(a);
                return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
            }
        }
        /**
         * @dev Return the log in base 2, rounded down, of a positive value.
         * Returns 0 if given 0.
         */
        function log2(uint256 value) internal pure returns (uint256) {
            uint256 result = 0;
            unchecked {
                if (value >> 128 > 0) {
                    value >>= 128;
                    result += 128;
                }
                if (value >> 64 > 0) {
                    value >>= 64;
                    result += 64;
                }
                if (value >> 32 > 0) {
                    value >>= 32;
                    result += 32;
                }
                if (value >> 16 > 0) {
                    value >>= 16;
                    result += 16;
                }
                if (value >> 8 > 0) {
                    value >>= 8;
                    result += 8;
                }
                if (value >> 4 > 0) {
                    value >>= 4;
                    result += 4;
                }
                if (value >> 2 > 0) {
                    value >>= 2;
                    result += 2;
                }
                if (value >> 1 > 0) {
                    result += 1;
                }
            }
            return result;
        }
        /**
         * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
         * Returns 0 if given 0.
         */
        function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
            unchecked {
                uint256 result = log2(value);
                return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
            }
        }
        /**
         * @dev Return the log in base 10, rounded down, of a positive value.
         * Returns 0 if given 0.
         */
        function log10(uint256 value) internal pure returns (uint256) {
            uint256 result = 0;
            unchecked {
                if (value >= 10 ** 64) {
                    value /= 10 ** 64;
                    result += 64;
                }
                if (value >= 10 ** 32) {
                    value /= 10 ** 32;
                    result += 32;
                }
                if (value >= 10 ** 16) {
                    value /= 10 ** 16;
                    result += 16;
                }
                if (value >= 10 ** 8) {
                    value /= 10 ** 8;
                    result += 8;
                }
                if (value >= 10 ** 4) {
                    value /= 10 ** 4;
                    result += 4;
                }
                if (value >= 10 ** 2) {
                    value /= 10 ** 2;
                    result += 2;
                }
                if (value >= 10 ** 1) {
                    result += 1;
                }
            }
            return result;
        }
        /**
         * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
         * Returns 0 if given 0.
         */
        function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
            unchecked {
                uint256 result = log10(value);
                return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
            }
        }
        /**
         * @dev Return the log in base 256, rounded down, of a positive value.
         * Returns 0 if given 0.
         *
         * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
         */
        function log256(uint256 value) internal pure returns (uint256) {
            uint256 result = 0;
            unchecked {
                if (value >> 128 > 0) {
                    value >>= 128;
                    result += 16;
                }
                if (value >> 64 > 0) {
                    value >>= 64;
                    result += 8;
                }
                if (value >> 32 > 0) {
                    value >>= 32;
                    result += 4;
                }
                if (value >> 16 > 0) {
                    value >>= 16;
                    result += 2;
                }
                if (value >> 8 > 0) {
                    result += 1;
                }
            }
            return result;
        }
        /**
         * @dev Return the log in base 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 + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)
    pragma solidity ^0.8.0;
    /**
     * @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
    pragma solidity =0.8.18;
    import {
        ERC20,
        ERC20Permit
    } from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";
    import {
        SafeERC20
    } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
    import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
    /**
     * @title AethirToken
     * @dev This contract is an implementation of the ERC20 token standard with additional features.
     * The admin multisig wallet can manage addresses from the whitelist, update the maximum allowed token amount for a whitelisted address.
     * The contract also includes a mint function that can be called by the admin multisig wallet to mint tokens to the contract itself.
     */
    contract AethirToken is ERC20Permit, Ownable {
        // Events that the contract emits when changes are made
        event WhitelistedAdded(address account, uint256 maxAmount);
        event WhitelistedRemoved(address account);
        event WhitelistedMaxAmountUpdated(address account, uint256 newMaxAmount);
        event WhitelistedAddressUpdated(address account, address newAddress);
        // Constant that defines the maximum supply of tokens
        uint256 public constant MAX_SUPPLY = 42_000_000_000 * 10 ** 18;
        // Mappings that keep track of the allowed and transferred amounts for each address
        mapping(address => uint256) public allowedAmount;
        mapping(address => uint256) public transferredAmount;
        // Variable that keeps track of the remain amount of tokens that have been whitelisted
        uint256 public remainWhitelisted;
        // Variable that keeps track of the amount of tokens that have been transferred out
        uint256 public totalTransferred;
        /**
         * @dev Contract constructor.
         *
         * This constructor sets the name and symbol of the token using the ERC20
         * constructor, and sets the name of the permit using the ERC20Permit
         * constructor. It also transfers the ownership of the contract to the
         * provided admin multisig wallet.
         *
         * @param _adminMultisigWallet The address of the admin multisig wallet that will own the contract.
         */
        constructor(
            address _adminMultisigWallet
        ) ERC20("Cethir Token", "CTH") ERC20Permit("CTH") {
            transferOwnership(_adminMultisigWallet);
        }
        /**
         * @dev Mints a specified amount of tokens and store inside the token contract.
         *
         * This function can only be called by the admin multisig wallet. It mints
         * a specified amount of tokens and assigns them to the contract itself,
         * increasing the total supply.
         *
         * Requirements:
         *
         * - The total supply of tokens after the minting operation must not exceed the maximum supply.
         *
         * @param amount The amount of tokens to mint.
         */
        function mint(uint256 amount) public onlyOwner {
            require(
                totalSupply() + amount <= MAX_SUPPLY,
                "Cannot mint more than max supply"
            );
            _mint(address(this), amount);
        }
        /**
         * @dev Transfers tokens to a whitelisted address.
         *
         * This function can only be called by the admin multisig wallet. It transfers
         * a specified amount of tokens to a given address, provided that the address
         * is whitelisted and the amount does not exceed the maximum allowed amount
         * for that address.
         *
         * Requirements:
         *
         * - `to` must be a whitelisted address.
         * - The sum of `amount` and the previously transferred amount to `to` must not exceed the maximum allowed amount for `to`.
         *
         * @param to The address to transfer tokens to.
         * @param amount The amount of tokens to transfer.
         */
        function transferToWhitelisted(
            address to,
            uint256 amount
        ) public onlyOwner {
            require(
                transferredAmount[to] + amount <= allowedAmount[to],
                "Cannot transfer more than max allowed amount"
            );
            transferredAmount[to] += amount;
            totalTransferred += amount;
            remainWhitelisted -= amount;
            SafeERC20.safeTransfer(this, to, amount);
        }
        /**
         * @dev Adds an address to the whitelist and sets its maximum allowed token amount.
         *
         * Requirements:
         *
         * - `account` cannot be the zero address.
         * - `account` must not already be whitelisted.
         * - `maxAmount` must be greater than 0.
         *
         * Emits an {WhitelistedAdded} event.
         *
         * @param account The address to add to the whitelist.
         * @param maxAmount The maximum amount of tokens that the address is allowed to hold.
         */
        function addWhitelisted(
            address account,
            uint256 maxAmount
        ) public onlyOwner {
            require(account != address(0), "Account is the zero address");
            require(account != address(this), "Account is the token address");
            require(allowedAmount[account] == 0, "Account was whitelisted");
            require(maxAmount > 0, "Max amount must be greater than 0");
            require(
                remainWhitelisted + totalTransferred + maxAmount <= MAX_SUPPLY,
                "Cannot whitelist more than max supply"
            );
            emit WhitelistedAdded(account, maxAmount);
            remainWhitelisted += maxAmount;
            allowedAmount[account] = maxAmount;
        }
        /**
         * @dev Removes an address from the whitelist.
         *
         * This function can only be called by the admin multisig wallet. It removes
         * a given address from the whitelist by setting its maximum allowed token
         * amount to 0.
         *
         * Requirements:
         *
         * - `account` must be a whitelisted address.
         *
         * Emits a {WhitelistedRemoved} event.
         *
         * @param account The address to remove from the whitelist.
         */
        function removeWhitelisted(address account) public onlyOwner {
            require(allowedAmount[account] > 0, "Account is not whitelisted");
            emit WhitelistedRemoved(account);
            remainWhitelisted -=
                allowedAmount[account] -
                transferredAmount[account];
            allowedAmount[account] = 0;
            transferredAmount[account] = 0;
        }
        /**
         * @dev Updates the maximum allowed token amount for a whitelisted address.
         *
         * This function can only be called by the admin multisig wallet. It updates
         * the maximum allowed token amount for a given address, provided that the address
         * is whitelisted and the new maximum amount is greater than 0.
         *
         * Requirements:
         *
         * - `account` must be a whitelisted address.
         * - `newMaxAmount` must be greater than 0.
         *
         * Emits an {WhitelistedMaxAmountUpdated} event.
         *
         * @param account The address to update the maximum allowed token amount for.
         * @param newMaxAmount The new maximum amount of tokens that the address is allowed to hold.
         */
        function updateWhitelistedMaxAmount(
            address account,
            uint256 newMaxAmount
        ) public onlyOwner {
            require(allowedAmount[account] > 0, "Account is not whitelisted");
            require(newMaxAmount > 0, "Max amount must be greater than 0");
            require(
                newMaxAmount >= transferredAmount[account],
                "Max amount must not less than transferred amount"
            );
            require(
                remainWhitelisted + totalTransferred + newMaxAmount <=
                    MAX_SUPPLY + allowedAmount[account],
                "Cannot whitelist more than max supply"
            );
            emit WhitelistedMaxAmountUpdated(account, newMaxAmount);
            remainWhitelisted =
                remainWhitelisted +
                newMaxAmount -
                allowedAmount[account];
            allowedAmount[account] = newMaxAmount;
        }
        /**
         * @dev Updates the whitelisted address.
         *
         * This function can only be called by the admin multisig wallet. It updates
         * the whitelisted address to a new address, provided that the original address
         * is whitelisted, and the new address is not already whitelisted.
         *
         * The function also transfers the allowed and transferred amounts from the
         * original address to the new address, and resets the allowed and transferred
         * amounts for the original address to 0.
         *
         * Requirements:
         *
         * - `account` must be a whitelisted address.
         * - `newAddress` cannot be the zero address.
         * - `newAddress` must not already be whitelisted.
         *
         * Emits an {WhitelistedAddressUpdated} event.
         *
         * @param account The original address to update.
         * @param newAddress The new address to update to.
         */
        function updateWhitelistedAddress(
            address account,
            address newAddress
        ) public onlyOwner {
            require(newAddress != address(0), "New address is the zero address");
            require(allowedAmount[account] > 0, "Account is not whitelisted");
            require(allowedAmount[newAddress] == 0, "New address was whitelisted");
            emit WhitelistedAddressUpdated(account, newAddress);
            allowedAmount[newAddress] = allowedAmount[account];
            transferredAmount[newAddress] = transferredAmount[account];
            allowedAmount[account] = 0;
            transferredAmount[account] = 0;
        }
        function _afterTokenTransfer(
            address from,
            address to,
            uint256 amount
        ) internal override {
            super._afterTokenTransfer(from, to, amount);
            // Allow any wallet addresses to transfer token back to the contract
            // If the wallet address is whitelisted, the transfered amount will be updated
            if (to == address(this) && from != address(0)) {
                totalTransferred -= amount;
                if (transferredAmount[from] >= amount) {
                    transferredAmount[from] -= amount;
                } else {
                    transferredAmount[from] = 0;
                }
            }
        }
    }