ETH Price: $2,552.89 (+0.37%)

Transaction Decoder

Block:
21100384 at Nov-02-2024 01:54:11 PM +UTC
Transaction Fee:
0.000551244720806094 ETH $1.41
Gas Used:
76,038 Gas / 7.249595213 Gwei

Emitted Events:

273 wQUIL.Transfer( from=0x0000000000000000000000000000000000000000, to=[Sender] 0xa0cf651a9157211e89b15d3a44ee2bea384a0451, value=151075874300 )
274 wQUIL.TokensMintedWithSignature( signer=0xE35F2aAE...D59c7cACC, mintedTo=[Sender] 0xa0cf651a9157211e89b15d3a44ee2bea384a0451, mintRequest=[{name:to, type:address, order:1, indexed:false, value:0xA0Cf651A9157211E89b15d3a44EE2bEA384a0451, valueString:0xA0Cf651A9157211E89b15d3a44EE2bEA384a0451}, {name:primarySaleRecipient, type:address, order:2, indexed:false, value:0xE35F2aAE042640D56c50b3c1B90bAf7D59c7cACC, valueString:0xE35F2aAE042640D56c50b3c1B90bAf7D59c7cACC}, {name:quantity, type:uint256, order:3, indexed:false, value:151075874300, valueString:151075874300}, {name:price, type:uint256, order:4, indexed:false, value:0, valueString:0}, {name:currency, type:address, order:5, indexed:false, value:0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, valueString:0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE}, {name:validityStartTimestamp, type:uint128, order:6, indexed:false, value:0, valueString:0}, {name:validityEndTimestamp, type:uint128, order:7, indexed:false, value:1731160400, valueString:1731160400}, {name:uid, type:bytes32, order:8, indexed:false, value:1081D9CE60FF7D92872E92F795981C6C5C7F0141319C1249426C9D4DE7EBC217, valueString:1081D9CE60FF7D92872E92F795981C6C5C7F0141319C1249426C9D4DE7EBC217}] )

Account State Difference:

  Address   Before After State Difference Code
0x8143182a...98866945D
(beaverbuild)
14.587173891579597469 Eth14.587251399738741685 Eth0.000077508159144216
0xA0Cf651A...A384a0451
0.014681156672435936 Eth
Nonce: 1
0.014129911951629842 Eth
Nonce: 2
0.000551244720806094

Execution Trace

wQUIL.mintWithSignature( _req=[{name:to, type:address, order:1, indexed:false, value:0xA0Cf651A9157211E89b15d3a44EE2bEA384a0451, valueString:0xA0Cf651A9157211E89b15d3a44EE2bEA384a0451}, {name:primarySaleRecipient, type:address, order:2, indexed:false, value:0xE35F2aAE042640D56c50b3c1B90bAf7D59c7cACC, valueString:0xE35F2aAE042640D56c50b3c1B90bAf7D59c7cACC}, {name:quantity, type:uint256, order:3, indexed:false, value:151075874300, valueString:151075874300}, {name:price, type:uint256, order:4, indexed:false, value:0, valueString:0}, {name:currency, type:address, order:5, indexed:false, value:0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, valueString:0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE}, {name:validityStartTimestamp, type:uint128, order:6, indexed:false, value:0, valueString:0}, {name:validityEndTimestamp, type:uint128, order:7, indexed:false, value:1731160400, valueString:1731160400}, {name:uid, type:bytes32, order:8, indexed:false, value:1081D9CE60FF7D92872E92F795981C6C5C7F0141319C1249426C9D4DE7EBC217, valueString:1081D9CE60FF7D92872E92F795981C6C5C7F0141319C1249426C9D4DE7EBC217}], _signature=0xCD7AF199D22F0E20875284E84031A0D1819EDF46C9819065148A16A82D775B6B6DD9B69766D3C0CF3F19A1F3EF9243F578B53A97ABDC7C78CFAB919B86B999BE1C ) => ( signer=0xE35F2aAE042640D56c50b3c1B90bAf7D59c7cACC )
  • Null: 0x000...001.692d5f13( )
    // SPDX-License-Identifier: Apache-2.0
    pragma solidity ^0.8.0;
    /// @author thirdweb
    import "../external-deps/openzeppelin/token/ERC20/extensions/ERC20Permit.sol";
    import "../extension/ContractMetadata.sol";
    import "../extension/Multicall.sol";
    import "../extension/Ownable.sol";
    import "../extension/interface/IMintableERC20.sol";
    import "../extension/interface/IBurnableERC20.sol";
    /**
     *  The `ERC20Base` smart contract implements the ERC20 standard.
     *  It includes the following additions to standard ERC20 logic:
     *
     *      - Ability to mint & burn tokens via the provided `mint` & `burn` functions.
     *
     *      - Ownership of the contract, with the ability to restrict certain functions to
     *        only be called by the contract's owner.
     *
     *      - Multicall capability to perform multiple actions atomically
     *
     *      - EIP 2612 compliance: See {ERC20-permit} method, which can be used to change an account's ERC20 allowance by
     *                             presenting a message signed by the account.
     */
    contract ERC20Base is ContractMetadata, Multicall, Ownable, ERC20Permit, IMintableERC20, IBurnableERC20 {
        /*//////////////////////////////////////////////////////////////
                                Constructor
        //////////////////////////////////////////////////////////////*/
        constructor(address _defaultAdmin, string memory _name, string memory _symbol) ERC20Permit(_name, _symbol) {
            _setupOwner(_defaultAdmin);
        }
        /*//////////////////////////////////////////////////////////////
                                Minting logic
        //////////////////////////////////////////////////////////////*/
        /**
         *  @notice          Lets an authorized address mint tokens to a recipient.
         *  @dev             The logic in the `_canMint` function determines whether the caller is authorized to mint tokens.
         *
         *  @param _to       The recipient of the tokens to mint.
         *  @param _amount   Quantity of tokens to mint.
         */
        function mintTo(address _to, uint256 _amount) public virtual {
            require(_canMint(), "Not authorized to mint.");
            require(_amount != 0, "Minting zero tokens.");
            _mint(_to, _amount);
        }
        /**
         *  @notice          Lets an owner a given amount of their tokens.
         *  @dev             Caller should own the `_amount` of tokens.
         *
         *  @param _amount   The number of tokens to burn.
         */
        function burn(uint256 _amount) external virtual {
            require(balanceOf(msg.sender) >= _amount, "not enough balance");
            _burn(msg.sender, _amount);
        }
        /**
         *  @notice          Lets an owner burn a given amount of an account's tokens.
         *  @dev             `_account` should own the `_amount` of tokens.
         *
         *  @param _account  The account to burn tokens from.
         *  @param _amount   The number of tokens to burn.
         */
        function burnFrom(address _account, uint256 _amount) external virtual override {
            require(_canBurn(), "Not authorized to burn.");
            require(balanceOf(_account) >= _amount, "not enough balance");
            uint256 decreasedAllowance = allowance(_account, msg.sender) - _amount;
            _approve(_account, msg.sender, 0);
            _approve(_account, msg.sender, decreasedAllowance);
            _burn(_account, _amount);
        }
        /*//////////////////////////////////////////////////////////////
                            Internal (overrideable) functions
        //////////////////////////////////////////////////////////////*/
        /// @dev Returns whether contract metadata can be set in the given execution context.
        function _canSetContractURI() internal view virtual override returns (bool) {
            return msg.sender == owner();
        }
        /// @dev Returns whether tokens can be minted in the given execution context.
        function _canMint() internal view virtual returns (bool) {
            return msg.sender == owner();
        }
        /// @dev Returns whether tokens can be burned in the given execution context.
        function _canBurn() internal view virtual returns (bool) {
            return msg.sender == owner();
        }
        /// @dev Returns whether owner can be set in the given execution context.
        function _canSetOwner() internal view virtual override returns (bool) {
            return msg.sender == owner();
        }
        /// @notice Returns the sender in the given execution context.
        function _msgSender() internal view override(Multicall, Context) returns (address) {
            return msg.sender;
        }
    }
    // SPDX-License-Identifier: Apache-2.0
    pragma solidity ^0.8.0;
    /// @author thirdweb
    import "./ERC20Base.sol";
    import "../extension/PrimarySale.sol";
    import { SignatureMintERC20 } from "../extension/SignatureMintERC20.sol";
    import { ReentrancyGuard } from "../extension/upgradeable/ReentrancyGuard.sol";
    import { CurrencyTransferLib } from "../lib/CurrencyTransferLib.sol";
    /**
     *      BASE:      ERC20
     *      EXTENSION: SignatureMintERC20
     *
     *  The `ERC20SignatureMint` contract uses the `ERC20Base` contract, along with the `SignatureMintERC20` extension.
     *
     *  The 'signature minting' mechanism in the `SignatureMintERC20` extension uses EIP 712, and is a way for a contract
     *  admin to authorize an external party's request to mint tokens on the admin's contract. At a high level, this means
     *  you can authorize some external party to mint tokens on your contract, and specify what exactly will be minted by
     *  that external party.
     *
     */
    contract ERC20SignatureMint is ERC20Base, PrimarySale, SignatureMintERC20, ReentrancyGuard {
        /*//////////////////////////////////////////////////////////////
                                Constructor
        //////////////////////////////////////////////////////////////*/
        constructor(
            address _defaultAdmin,
            string memory _name,
            string memory _symbol,
            address _primarySaleRecipient
        ) ERC20Base(_defaultAdmin, _name, _symbol) {
            _setupPrimarySaleRecipient(_primarySaleRecipient);
        }
        /*//////////////////////////////////////////////////////////////
                            Signature minting logic
        //////////////////////////////////////////////////////////////*/
        /**
         *  @notice           Mints tokens according to the provided mint request.
         *
         *  @param _req       The payload / mint request.
         *  @param _signature The signature produced by an account signing the mint request.
         */
        function mintWithSignature(
            MintRequest calldata _req,
            bytes calldata _signature
        ) external payable virtual nonReentrant returns (address signer) {
            require(_req.quantity > 0, "Minting zero tokens.");
            // Verify and process payload.
            signer = _processRequest(_req, _signature);
            address receiver = _req.to;
            // Collect price
            _collectPriceOnClaim(_req.primarySaleRecipient, _req.currency, _req.price);
            // Mint tokens.
            _mint(receiver, _req.quantity);
            emit TokensMintedWithSignature(signer, receiver, _req);
        }
        /*//////////////////////////////////////////////////////////////
                                Internal functions
        //////////////////////////////////////////////////////////////*/
        /// @dev Returns whether a given address is authorized to sign mint requests.
        function _canSignMintRequest(address _signer) internal view virtual override returns (bool) {
            return _signer == owner();
        }
        /// @dev Returns whether primary sale recipient can be set in the given execution context.
        function _canSetPrimarySaleRecipient() internal view virtual override returns (bool) {
            return msg.sender == owner();
        }
        /// @dev Collects and distributes the primary sale value of tokens being claimed.
        function _collectPriceOnClaim(address _primarySaleRecipient, address _currency, uint256 _price) internal virtual {
            if (_price == 0) {
                require(msg.value == 0, "!Value");
                return;
            }
            if (_currency == CurrencyTransferLib.NATIVE_TOKEN) {
                require(msg.value == _price, "Must send total price.");
            } else {
                require(msg.value == 0, "msg value not zero");
            }
            address saleRecipient = _primarySaleRecipient == address(0) ? primarySaleRecipient() : _primarySaleRecipient;
            CurrencyTransferLib.transferCurrency(_currency, msg.sender, saleRecipient, _price);
        }
    }
    // SPDX-License-Identifier: Apache-2.0
    pragma solidity ^0.8.0;
    /**
     * @title ERC20 interface
     * @dev see https://github.com/ethereum/EIPs/issues/20
     */
    interface IERC20 {
        function totalSupply() external view returns (uint256);
        function balanceOf(address who) external view returns (uint256);
        function allowance(address owner, address spender) external view returns (uint256);
        function transfer(address to, uint256 value) external returns (bool);
        function approve(address spender, uint256 value) external returns (bool);
        function transferFrom(address from, address to, uint256 value) external returns (bool);
        event Transfer(address indexed from, address indexed to, uint256 value);
        event Approval(address indexed owner, address indexed spender, uint256 value);
    }
    // SPDX-License-Identifier: Apache-2.0
    pragma solidity ^0.8.0;
    /**
     * @title ERC20Metadata interface
     * @dev see https://github.com/ethereum/EIPs/issues/20
     */
    interface IERC20Metadata {
        function name() external view returns (string memory);
        function symbol() external view returns (string memory);
        function decimals() external view returns (uint8);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-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.
     */
    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].
         */
        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: Apache-2.0
    pragma solidity ^0.8.0;
    /// @author thirdweb
    import "./interface/IContractMetadata.sol";
    /**
     *  @title   Contract Metadata
     *  @notice  Thirdweb's `ContractMetadata` is a contract extension for any base contracts. It lets you set a metadata URI
     *           for you contract.
     *           Additionally, `ContractMetadata` is necessary for NFT contracts that want royalties to get distributed on OpenSea.
     */
    abstract contract ContractMetadata is IContractMetadata {
        /// @dev The sender is not authorized to perform the action
        error ContractMetadataUnauthorized();
        /// @notice Returns the contract metadata URI.
        string public override contractURI;
        /**
         *  @notice         Lets a contract admin set the URI for contract-level metadata.
         *  @dev            Caller should be authorized to setup contractURI, e.g. contract admin.
         *                  See {_canSetContractURI}.
         *                  Emits {ContractURIUpdated Event}.
         *
         *  @param _uri     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
         */
        function setContractURI(string memory _uri) external override {
            if (!_canSetContractURI()) {
                revert ContractMetadataUnauthorized();
            }
            _setupContractURI(_uri);
        }
        /// @dev Lets a contract admin set the URI for contract-level metadata.
        function _setupContractURI(string memory _uri) internal {
            string memory prevURI = contractURI;
            contractURI = _uri;
            emit ContractURIUpdated(prevURI, _uri);
        }
        /// @dev Returns whether contract metadata can be set in the given execution context.
        function _canSetContractURI() internal view virtual returns (bool);
    }
    // SPDX-License-Identifier: Apache-2.0
    pragma solidity ^0.8.0;
    /// @author thirdweb
    interface IBurnableERC20 {
        /**
         * @dev Destroys `amount` tokens from the caller.
         *
         * See {ERC20-_burn}.
         */
        function burn(uint256 amount) external;
        /**
         * @dev Destroys `amount` tokens from `account`, deducting from the caller's
         * allowance.
         *
         * See {ERC20-_burn} and {ERC20-allowance}.
         *
         * Requirements:
         *
         * - the caller must have allowance for ``accounts``'s tokens of at least
         * `amount`.
         */
        function burnFrom(address account, uint256 amount) external;
    }
    // SPDX-License-Identifier: Apache-2.0
    pragma solidity ^0.8.0;
    /// @author thirdweb
    /**
     *  Thirdweb's `ContractMetadata` is a contract extension for any base contracts. It lets you set a metadata URI
     *  for you contract.
     *
     *  Additionally, `ContractMetadata` is necessary for NFT contracts that want royalties to get distributed on OpenSea.
     */
    interface IContractMetadata {
        /// @dev Returns the metadata URI of the contract.
        function contractURI() external view returns (string memory);
        /**
         *  @dev Sets contract URI for the storefront-level metadata of the contract.
         *       Only module admin can call this function.
         */
        function setContractURI(string calldata _uri) external;
        /// @dev Emitted when the contract URI is updated.
        event ContractURIUpdated(string prevURI, string newURI);
    }
    // SPDX-License-Identifier: Apache-2.0
    pragma solidity ^0.8.0;
    /// @author thirdweb
    interface IMintableERC20 {
        /// @dev Emitted when tokens are minted with `mintTo`
        event TokensMinted(address indexed mintedTo, uint256 quantityMinted);
        /**
         * @dev Creates `amount` new tokens for `to`.
         *
         * See {ERC20-_mint}.
         *
         * Requirements:
         *
         * - the caller must have the `MINTER_ROLE`.
         */
        function mintTo(address to, uint256 amount) external;
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    /// @author thirdweb
    /**
     * @dev Provides a function to batch together multiple calls in a single external call.
     *
     * _Available since v4.1._
     */
    interface IMulticall {
        /**
         * @dev Receives and executes a batch of function calls on this contract.
         */
        function multicall(bytes[] calldata data) external returns (bytes[] memory results);
    }
    // SPDX-License-Identifier: Apache-2.0
    pragma solidity ^0.8.0;
    /// @author thirdweb
    /**
     *  Thirdweb's `Ownable` is a contract extension to be used with any base contract. It exposes functions for setting and reading
     *  who the 'owner' of the inheriting smart contract is, and lets the inheriting contract perform conditional logic that uses
     *  information about who the contract's owner is.
     */
    interface IOwnable {
        /// @dev Returns the owner of the contract.
        function owner() external view returns (address);
        /// @dev Lets a module admin set a new owner for the contract. The new owner must be a module admin.
        function setOwner(address _newOwner) external;
        /// @dev Emitted when a new Owner is set.
        event OwnerUpdated(address indexed prevOwner, address indexed newOwner);
    }
    // SPDX-License-Identifier: Apache-2.0
    pragma solidity ^0.8.0;
    /// @author thirdweb
    /**
     *  Thirdweb's `Primary` is a contract extension to be used with any base contract. It exposes functions for setting and reading
     *  the recipient of primary sales, and lets the inheriting contract perform conditional logic that uses information about
     *  primary sales, if desired.
     */
    interface IPrimarySale {
        /// @dev The adress that receives all primary sales value.
        function primarySaleRecipient() external view returns (address);
        /// @dev Lets a module admin set the default recipient of all primary sales.
        function setPrimarySaleRecipient(address _saleRecipient) external;
        /// @dev Emitted when a new sale recipient is set.
        event PrimarySaleRecipientUpdated(address indexed recipient);
    }
    // SPDX-License-Identifier: Apache-2.0
    pragma solidity ^0.8.0;
    /// @author thirdweb
    /**
     *  The 'signature minting' mechanism used in thirdweb Token smart contracts is a way for a contract admin to authorize an external party's
     *  request to mint tokens on the admin's contract.
     *
     *  At a high level, this means you can authorize some external party to mint tokens on your contract, and specify what exactly will be
     *  minted by that external party.
     */
    interface ISignatureMintERC20 {
        /**
         *  @notice The body of a request to mint tokens.
         *
         *  @param to The receiver of the tokens to mint.
         *  @param primarySaleRecipient The recipient of the minted token's primary sales proceeds.
         *  @param quantity The quantity of tokens to mint.
         *  @param pricePerToken The price to pay per quantity of tokens minted.
         *  @param currency The currency in which to pay the price per token minted.
         *  @param validityStartTimestamp The unix timestamp after which the payload is valid.
         *  @param validityEndTimestamp The unix timestamp at which the payload expires.
         *  @param uid A unique identifier for the payload.
         */
        struct MintRequest {
            address to;
            address primarySaleRecipient;
            uint256 quantity;
            uint256 price;
            address currency;
            uint128 validityStartTimestamp;
            uint128 validityEndTimestamp;
            bytes32 uid;
        }
        /// @dev Emitted when tokens are minted.
        event TokensMintedWithSignature(address indexed signer, address indexed mintedTo, MintRequest mintRequest);
        /**
         *  @notice Verifies that a mint request is signed by an account holding
         *          MINTER_ROLE (at the time of the function call).
         *
         *  @param req The payload / mint request.
         *  @param signature The signature produced by an account signing the mint request.
         *
         *  returns (success, signer) Result of verification and the recovered address.
         */
        function verify(
            MintRequest calldata req,
            bytes calldata signature
        ) external view returns (bool success, address signer);
        /**
         *  @notice Mints tokens according to the provided mint request.
         *
         *  @param req The payload / mint request.
         *  @param signature The signature produced by an account signing the mint request.
         */
        function mintWithSignature(
            MintRequest calldata req,
            bytes calldata signature
        ) external payable returns (address signer);
    }
    // SPDX-License-Identifier: Apache 2.0
    pragma solidity ^0.8.0;
    /// @author thirdweb
    import "../lib/Address.sol";
    import "./interface/IMulticall.sol";
    /**
     * @dev Provides a function to batch together multiple calls in a single external call.
     *
     * _Available since v4.1._
     */
    contract Multicall is IMulticall {
        /**
         *  @notice Receives and executes a batch of function calls on this contract.
         *  @dev Receives and executes a batch of function calls on this contract.
         *
         *  @param data The bytes data that makes up the batch of function calls to execute.
         *  @return results The bytes data that makes up the result of the batch of function calls executed.
         */
        function multicall(bytes[] calldata data) external returns (bytes[] memory results) {
            results = new bytes[](data.length);
            address sender = _msgSender();
            bool isForwarder = msg.sender != sender;
            for (uint256 i = 0; i < data.length; i++) {
                if (isForwarder) {
                    results[i] = Address.functionDelegateCall(address(this), abi.encodePacked(data[i], sender));
                } else {
                    results[i] = Address.functionDelegateCall(address(this), data[i]);
                }
            }
            return results;
        }
        /// @notice Returns the sender in the given execution context.
        function _msgSender() internal view virtual returns (address) {
            return msg.sender;
        }
    }
    // SPDX-License-Identifier: Apache-2.0
    pragma solidity ^0.8.0;
    /// @author thirdweb
    import "./interface/IOwnable.sol";
    /**
     *  @title   Ownable
     *  @notice  Thirdweb's `Ownable` is a contract extension to be used with any base contract. It exposes functions for setting and reading
     *           who the 'owner' of the inheriting smart contract is, and lets the inheriting contract perform conditional logic that uses
     *           information about who the contract's owner is.
     */
    abstract contract Ownable is IOwnable {
        /// @dev The sender is not authorized to perform the action
        error OwnableUnauthorized();
        /// @dev Owner of the contract (purpose: OpenSea compatibility)
        address private _owner;
        /// @dev Reverts if caller is not the owner.
        modifier onlyOwner() {
            if (msg.sender != _owner) {
                revert OwnableUnauthorized();
            }
            _;
        }
        /**
         *  @notice Returns the owner of the contract.
         */
        function owner() public view override returns (address) {
            return _owner;
        }
        /**
         *  @notice Lets an authorized wallet set a new owner for the contract.
         *  @param _newOwner The address to set as the new owner of the contract.
         */
        function setOwner(address _newOwner) external override {
            if (!_canSetOwner()) {
                revert OwnableUnauthorized();
            }
            _setupOwner(_newOwner);
        }
        /// @dev Lets a contract admin set a new owner for the contract. The new owner must be a contract admin.
        function _setupOwner(address _newOwner) internal {
            address _prevOwner = _owner;
            _owner = _newOwner;
            emit OwnerUpdated(_prevOwner, _newOwner);
        }
        /// @dev Returns whether owner can be set in the given execution context.
        function _canSetOwner() internal view virtual returns (bool);
    }
    // SPDX-License-Identifier: Apache-2.0
    pragma solidity ^0.8.0;
    /// @author thirdweb
    import "./interface/IPrimarySale.sol";
    /**
     *  @title   Primary Sale
     *  @notice  Thirdweb's `PrimarySale` is a contract extension to be used with any base contract. It exposes functions for setting and reading
     *           the recipient of primary sales, and lets the inheriting contract perform conditional logic that uses information about
     *           primary sales, if desired.
     */
    abstract contract PrimarySale is IPrimarySale {
        /// @dev The sender is not authorized to perform the action
        error PrimarySaleUnauthorized();
        /// @dev The recipient is invalid
        error PrimarySaleInvalidRecipient(address recipient);
        /// @dev The address that receives all primary sales value.
        address private recipient;
        /// @dev Returns primary sale recipient address.
        function primarySaleRecipient() public view override returns (address) {
            return recipient;
        }
        /**
         *  @notice         Updates primary sale recipient.
         *  @dev            Caller should be authorized to set primary sales info.
         *                  See {_canSetPrimarySaleRecipient}.
         *                  Emits {PrimarySaleRecipientUpdated Event}; See {_setupPrimarySaleRecipient}.
         *
         *  @param _saleRecipient   Address to be set as new recipient of primary sales.
         */
        function setPrimarySaleRecipient(address _saleRecipient) external override {
            if (!_canSetPrimarySaleRecipient()) {
                revert PrimarySaleUnauthorized();
            }
            _setupPrimarySaleRecipient(_saleRecipient);
        }
        /// @dev Lets a contract admin set the recipient for all primary sales.
        function _setupPrimarySaleRecipient(address _saleRecipient) internal {
            if (_saleRecipient == address(0)) {
                revert PrimarySaleInvalidRecipient(_saleRecipient);
            }
            recipient = _saleRecipient;
            emit PrimarySaleRecipientUpdated(_saleRecipient);
        }
        /// @dev Returns whether primary sale recipient can be set in the given execution context.
        function _canSetPrimarySaleRecipient() internal view virtual returns (bool);
    }
    // SPDX-License-Identifier: Apache-2.0
    pragma solidity ^0.8.0;
    /// @author thirdweb
    import "./interface/ISignatureMintERC20.sol";
    import "../external-deps/openzeppelin/utils/cryptography/EIP712.sol";
    abstract contract SignatureMintERC20 is EIP712, ISignatureMintERC20 {
        using ECDSA for bytes32;
        bytes32 private constant TYPEHASH =
            keccak256(
                "MintRequest(address to,address primarySaleRecipient,uint256 quantity,uint256 price,address currency,uint128 validityStartTimestamp,uint128 validityEndTimestamp,bytes32 uid)"
            );
        /// @dev Mapping from mint request UID => whether the mint request is processed.
        mapping(bytes32 => bool) private minted;
        constructor() EIP712("SignatureMintERC20", "1") {}
        /// @dev Verifies that a mint request is signed by an account holding MINTER_ROLE (at the time of the function call).
        function verify(
            MintRequest calldata _req,
            bytes calldata _signature
        ) public view override returns (bool success, address signer) {
            signer = _recoverAddress(_req, _signature);
            success = !minted[_req.uid] && _canSignMintRequest(signer);
        }
        /// @dev Returns whether a given address is authorized to sign mint requests.
        function _canSignMintRequest(address _signer) internal view virtual returns (bool);
        /// @dev Verifies a mint request and marks the request as minted.
        function _processRequest(MintRequest calldata _req, bytes calldata _signature) internal returns (address signer) {
            bool success;
            (success, signer) = verify(_req, _signature);
            require(success, "Invalid request");
            require(
                _req.validityStartTimestamp <= block.timestamp && block.timestamp <= _req.validityEndTimestamp,
                "Request expired"
            );
            require(_req.to != address(0), "recipient undefined");
            require(_req.quantity > 0, "0 qty");
            minted[_req.uid] = true;
        }
        /// @dev Returns the address of the signer of the mint request.
        function _recoverAddress(MintRequest calldata _req, bytes calldata _signature) internal view returns (address) {
            return _hashTypedDataV4(keccak256(_encodeRequest(_req))).recover(_signature);
        }
        /// @dev Resolves 'stack too deep' error in `recoverAddress`.
        function _encodeRequest(MintRequest calldata _req) internal pure returns (bytes memory) {
            return
                abi.encode(
                    TYPEHASH,
                    _req.to,
                    _req.primarySaleRecipient,
                    _req.quantity,
                    _req.price,
                    _req.currency,
                    _req.validityStartTimestamp,
                    _req.validityEndTimestamp,
                    _req.uid
                );
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol)
    pragma solidity ^0.8.0;
    library ReentrancyGuardStorage {
        /// @custom:storage-location erc7201:reentrancy.guard.storage
        /// @dev keccak256(abi.encode(uint256(keccak256("reentrancy.guard.storage")) - 1)) & ~bytes32(uint256(0xff))
        bytes32 public constant REENTRANCY_GUARD_STORAGE_POSITION =
            0x1d281c488dae143b6ea4122e80c65059929950b9c32f17fc57be22089d9c3b00;
        struct Data {
            uint256 _status;
        }
        function data() internal pure returns (Data storage data_) {
            bytes32 position = REENTRANCY_GUARD_STORAGE_POSITION;
            assembly {
                data_.slot := position
            }
        }
    }
    abstract contract ReentrancyGuard {
        uint256 private constant _NOT_ENTERED = 1;
        uint256 private constant _ENTERED = 2;
        constructor() {
            _reentrancyGuardStorage()._status = _NOT_ENTERED;
        }
        /**
         * @dev Prevents a contract from calling itself, directly or indirectly.
         */
        modifier nonReentrant() {
            // On the first call to nonReentrant, _notEntered will be true
            require(_reentrancyGuardStorage()._status != _ENTERED, "ReentrancyGuard: reentrant call");
            // Any calls to nonReentrant after this point will fail
            _reentrancyGuardStorage()._status = _ENTERED;
            _;
            // By storing the original value once again, a refund is triggered (see
            // https://eips.ethereum.org/EIPS/eip-2200)
            _reentrancyGuardStorage()._status = _NOT_ENTERED;
        }
        /// @dev Returns the ReentrancyGuard storage.
        function _reentrancyGuardStorage() internal pure returns (ReentrancyGuardStorage.Data storage data) {
            data = ReentrancyGuardStorage.data();
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/ERC20.sol)
    pragma solidity ^0.8.0;
    import "../../../../eip/interface/IERC20.sol";
    import "../../../../eip/interface/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.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
     * to implement supply mechanisms].
     *
     * 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}.
         *
         * The default value of {decimals} is 18. To select a different value for
         * {decimals} you should overload it.
         *
         * All two of these values are immutable: they can only be set once during
         * construction.
         */
        constructor(string memory name_, string memory symbol_) {
            _name = name_;
            _symbol = symbol_;
        }
        /**
         * @dev Returns the name of the token.
         */
        function name() public view virtual override returns (string memory) {
            return _name;
        }
        /**
         * @dev Returns the symbol of the token, usually a shorter version of the
         * name.
         */
        function symbol() public view virtual override returns (string memory) {
            return _symbol;
        }
        /**
         * @dev Returns the number of decimals used to get its user representation.
         * For example, if `decimals` equals `2`, a balance of `505` tokens should
         * be displayed to a user as `5.05` (`505 / 10 ** 2`).
         *
         * Tokens usually opt for a value of 18, imitating the relationship between
         * Ether and Wei. This is the value {ERC20} uses, unless this function is
         * overridden;
         *
         * NOTE: This information is only used for _display_ purposes: it in
         * no way affects any of the arithmetic of the contract, including
         * {IERC20-balanceOf} and {IERC20-transfer}.
         */
        function decimals() public view virtual override returns (uint8) {
            return 18;
        }
        /**
         * @dev See {IERC20-totalSupply}.
         */
        function totalSupply() public view virtual override returns (uint256) {
            return _totalSupply;
        }
        /**
         * @dev See {IERC20-balanceOf}.
         */
        function balanceOf(address account) public view virtual override returns (uint256) {
            return _balances[account];
        }
        /**
         * @dev See {IERC20-transfer}.
         *
         * Requirements:
         *
         * - `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, _allowances[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 = _allowances[owner][spender];
            require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
            unchecked {
                _approve(owner, spender, currentAllowance - subtractedValue);
            }
            return true;
        }
        /**
         * @dev Moves `amount` of tokens from `sender` to `recipient`.
         *
         * This internal function is equivalent to {transfer}, and can be used to
         * e.g. implement automatic token fees, slashing mechanisms, etc.
         *
         * Emits a {Transfer} event.
         *
         * Requirements:
         *
         * - `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;
            }
            _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;
            _balances[account] += amount;
            emit Transfer(address(0), account, amount);
            _afterTokenTransfer(address(0), account, amount);
        }
        /**
         * @dev Destroys `amount` tokens from `account`, reducing the
         * total supply.
         *
         * Emits a {Transfer} event with `to` set to the zero address.
         *
         * Requirements:
         *
         * - `account` cannot be the zero address.
         * - `account` must have at least `amount` tokens.
         */
        function _burn(address account, uint256 amount) internal virtual {
            require(account != address(0), "ERC20: burn from the zero address");
            _beforeTokenTransfer(account, address(0), amount);
            uint256 accountBalance = _balances[account];
            require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
            unchecked {
                _balances[account] = accountBalance - amount;
            }
            _totalSupply -= amount;
            emit Transfer(account, address(0), amount);
            _afterTokenTransfer(account, address(0), amount);
        }
        /**
         * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
         *
         * This internal function is equivalent to `approve`, and can be used to
         * e.g. set automatic allowances for certain subsystems, etc.
         *
         * Emits an {Approval} event.
         *
         * Requirements:
         *
         * - `owner` cannot be the zero address.
         * - `spender` cannot be the zero address.
         */
        function _approve(address owner, address spender, uint256 amount) internal virtual {
            require(owner != address(0), "ERC20: approve from the zero address");
            require(spender != address(0), "ERC20: approve to the zero address");
            _allowances[owner][spender] = amount;
            emit Approval(owner, spender, amount);
        }
        /**
         * @dev Spend `amount` form the allowance of `owner` toward `spender`.
         *
         * 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 v4.4.1 (token/ERC20/extensions/draft-ERC20Permit.sol)
    pragma solidity ^0.8.0;
    import "../../../../../eip/interface/IERC20Permit.sol";
    import "../ERC20.sol";
    import "../../../utils/cryptography/EIP712.sol";
    import "../../../utils/cryptography/ECDSA.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 {
        using Counters for Counters.Counter;
        mapping(address => Counters.Counter) private _nonces;
        // solhint-disable-next-line var-name-mixedcase
        bytes32 private immutable _CACHED_DOMAIN_SEPARATOR;
        // solhint-disable-next-line var-name-mixedcase
        uint256 private immutable _CACHED_CHAIN_ID;
        // solhint-disable-next-line var-name-mixedcase
        address private immutable _CACHED_THIS;
        // solhint-disable-next-line var-name-mixedcase
        bytes32 private immutable _PERMIT_TYPEHASH =
            keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
        /**
         * @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_, string memory symbol_) ERC20(name_, symbol_) {
            _CACHED_CHAIN_ID = block.chainid;
            _CACHED_THIS = address(this);
            _CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator();
        }
        /**
         * @dev See {IERC20Permit-permit}.
         */
        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 = ECDSA.toTypedDataHash(DOMAIN_SEPARATOR(), structHash);
            address signer = ECDSA.recover(hash, v, r, s);
            require(signer == owner, "ERC20Permit: invalid signature");
            _approve(owner, spender, value);
        }
        /**
         * @dev See {IERC20Permit-nonces}.
         */
        function nonces(address owner) public view virtual override returns (uint256) {
            return _nonces[owner].current();
        }
        /**
         * @dev See {IERC20Permit-DOMAIN_SEPARATOR}.
         */
        // solhint-disable-next-line func-name-mixedcase
        function DOMAIN_SEPARATOR() public view override returns (bytes32) {
            if (address(this) == _CACHED_THIS && block.chainid == _CACHED_CHAIN_ID) {
                return _CACHED_DOMAIN_SEPARATOR;
            } else {
                return _buildDomainSeparator();
            }
        }
        function _buildDomainSeparator() private view returns (bytes32) {
            return
                keccak256(
                    abi.encode(
                        keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                        keccak256(bytes(name())),
                        keccak256("1"),
                        block.chainid,
                        address(this)
                    )
                );
        }
        /**
         * @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/utils/SafeERC20.sol)
    pragma solidity ^0.8.0;
    import "../../../../../eip/interface/IERC20.sol";
    import { Address } from "../../../../../lib/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;
        function safeTransfer(IERC20 token, address to, uint256 value) internal {
            _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
        }
        function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
            _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
        }
        /**
         * @dev Deprecated. This function has issues similar to the ones found in
         * {IERC20-approve}, and its usage is discouraged.
         *
         * Whenever possible, use {safeIncreaseAllowance} and
         * {safeDecreaseAllowance} instead.
         */
        function safeApprove(IERC20 token, address spender, uint256 value) internal {
            // safeApprove should only be called when setting an initial allowance,
            // or when resetting it to zero. To increase and decrease it, use
            // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
            require(
                (value == 0) || (token.allowance(address(this), spender) == 0),
                "SafeERC20: approve from non-zero to non-zero allowance"
            );
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
        }
        function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
            uint256 newAllowance = token.allowance(address(this), spender) + value;
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
        }
        function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
            unchecked {
                uint256 oldAllowance = token.allowance(address(this), spender);
                require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
                uint256 newAllowance = oldAllowance - value;
                _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
            }
        }
        /**
         * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
         * on the return value: the return value is optional (but if data is returned, it must not be false).
         * @param token The token targeted by the call.
         * @param data The call data (encoded using abi.encode or one of its variants).
         */
        function _callOptionalReturn(IERC20 token, bytes memory data) private {
            // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
            // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
            // the target address contains contract code and also asserts for success in the low-level call.
            bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
            if (returndata.length > 0) {
                // Return data is optional
                require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (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;
        }
    }
    // 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.8.0) (utils/cryptography/ECDSA.sol)
    pragma solidity ^0.8.0;
    import "../../../../lib/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 v4.4.1 (utils/cryptography/draft-EIP712.sol)
    pragma solidity ^0.8.0;
    import "./ECDSA.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].
     *
     * _Available since v3.4._
     */
    abstract contract EIP712 {
        /* solhint-disable var-name-mixedcase */
        // 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 _CACHED_DOMAIN_SEPARATOR;
        uint256 private immutable _CACHED_CHAIN_ID;
        address private immutable _CACHED_THIS;
        bytes32 private immutable _HASHED_NAME;
        bytes32 private immutable _HASHED_VERSION;
        bytes32 private immutable _TYPE_HASH;
        /* solhint-enable var-name-mixedcase */
        /**
         * @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) {
            bytes32 hashedName = keccak256(bytes(name));
            bytes32 hashedVersion = keccak256(bytes(version));
            bytes32 typeHash = keccak256(
                "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
            );
            _HASHED_NAME = hashedName;
            _HASHED_VERSION = hashedVersion;
            _CACHED_CHAIN_ID = block.chainid;
            _CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(typeHash, hashedName, hashedVersion);
            _CACHED_THIS = address(this);
            _TYPE_HASH = typeHash;
        }
        /**
         * @dev Returns the domain separator for the current chain.
         */
        function _domainSeparatorV4() internal view returns (bytes32) {
            if (address(this) == _CACHED_THIS && block.chainid == _CACHED_CHAIN_ID) {
                return _CACHED_DOMAIN_SEPARATOR;
            } else {
                return _buildDomainSeparator(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION);
            }
        }
        function _buildDomainSeparator(
            bytes32 typeHash,
            bytes32 nameHash,
            bytes32 versionHash
        ) private view returns (bytes32) {
            return keccak256(abi.encode(typeHash, nameHash, versionHash, 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);
        }
    }
    // SPDX-License-Identifier: Apache-2.0
    pragma solidity ^0.8.0;
    interface IWETH {
        function deposit() external payable;
        function withdraw(uint256 amount) external;
        function transfer(address to, uint256 value) external returns (bool);
    }
    // SPDX-License-Identifier: Apache-2.0
    pragma solidity ^0.8.1;
    /// @author thirdweb, OpenZeppelin Contracts (v4.9.0)
    /**
     * @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: Apache-2.0
    pragma solidity ^0.8.0;
    /// @author thirdweb
    // Helper interfaces
    import { IWETH } from "../infra/interface/IWETH.sol";
    import { SafeERC20, IERC20 } from "../external-deps/openzeppelin/token/ERC20/utils/SafeERC20.sol";
    library CurrencyTransferLib {
        using SafeERC20 for IERC20;
        error CurrencyTransferLibMismatchedValue(uint256 expected, uint256 actual);
        error CurrencyTransferLibFailedNativeTransfer(address recipient, uint256 value);
        /// @dev The address interpreted as native token of the chain.
        address public constant NATIVE_TOKEN = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
        /// @dev Transfers a given amount of currency.
        function transferCurrency(address _currency, address _from, address _to, uint256 _amount) internal {
            if (_amount == 0) {
                return;
            }
            if (_currency == NATIVE_TOKEN) {
                safeTransferNativeToken(_to, _amount);
            } else {
                safeTransferERC20(_currency, _from, _to, _amount);
            }
        }
        /// @dev Transfers a given amount of currency. (With native token wrapping)
        function transferCurrencyWithWrapper(
            address _currency,
            address _from,
            address _to,
            uint256 _amount,
            address _nativeTokenWrapper
        ) internal {
            if (_amount == 0) {
                return;
            }
            if (_currency == NATIVE_TOKEN) {
                if (_from == address(this)) {
                    // withdraw from weth then transfer withdrawn native token to recipient
                    IWETH(_nativeTokenWrapper).withdraw(_amount);
                    safeTransferNativeTokenWithWrapper(_to, _amount, _nativeTokenWrapper);
                } else if (_to == address(this)) {
                    // store native currency in weth
                    if (_amount != msg.value) {
                        revert CurrencyTransferLibMismatchedValue(msg.value, _amount);
                    }
                    IWETH(_nativeTokenWrapper).deposit{ value: _amount }();
                } else {
                    safeTransferNativeTokenWithWrapper(_to, _amount, _nativeTokenWrapper);
                }
            } else {
                safeTransferERC20(_currency, _from, _to, _amount);
            }
        }
        /// @dev Transfer `amount` of ERC20 token from `from` to `to`.
        function safeTransferERC20(address _currency, address _from, address _to, uint256 _amount) internal {
            if (_from == _to) {
                return;
            }
            if (_from == address(this)) {
                IERC20(_currency).safeTransfer(_to, _amount);
            } else {
                IERC20(_currency).safeTransferFrom(_from, _to, _amount);
            }
        }
        /// @dev Transfers `amount` of native token to `to`.
        function safeTransferNativeToken(address to, uint256 value) internal {
            // solhint-disable avoid-low-level-calls
            // slither-disable-next-line low-level-calls
            (bool success, ) = to.call{ value: value }("");
            if (!success) {
                revert CurrencyTransferLibFailedNativeTransfer(to, value);
            }
        }
        /// @dev Transfers `amount` of native token to `to`. (With native token wrapping)
        function safeTransferNativeTokenWithWrapper(address to, uint256 value, address _nativeTokenWrapper) internal {
            // solhint-disable avoid-low-level-calls
            // slither-disable-next-line low-level-calls
            (bool success, ) = to.call{ value: value }("");
            if (!success) {
                IWETH(_nativeTokenWrapper).deposit{ value: value }();
                IERC20(_nativeTokenWrapper).safeTransfer(to, value);
            }
        }
    }
    // SPDX-License-Identifier: Apache-2.0
    pragma solidity ^0.8.0;
    /// @author thirdweb
    /**
     * @dev String operations.
     */
    library Strings {
        bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
        /**
         * @dev Converts a `uint256` to its ASCII `string` decimal representation.
         */
        function toString(uint256 value) internal pure returns (string memory) {
            // Inspired by OraclizeAPI's implementation - MIT licence
            // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
            if (value == 0) {
                return "0";
            }
            uint256 temp = value;
            uint256 digits;
            while (temp != 0) {
                digits++;
                temp /= 10;
            }
            bytes memory buffer = new bytes(digits);
            while (value != 0) {
                digits -= 1;
                buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
                value /= 10;
            }
            return string(buffer);
        }
        /**
         * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
         */
        function toHexString(uint256 value) internal pure returns (string memory) {
            if (value == 0) {
                return "0x00";
            }
            uint256 temp = value;
            uint256 length = 0;
            while (temp != 0) {
                length++;
                temp >>= 8;
            }
            return toHexString(value, length);
        }
        /**
         * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
         */
        function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
            bytes memory buffer = new bytes(2 * length + 2);
            buffer[0] = "0";
            buffer[1] = "x";
            for (uint256 i = 2 * length + 1; i > 1; --i) {
                buffer[i] = _HEX_SYMBOLS[value & 0xf];
                value >>= 4;
            }
            require(value == 0, "Strings: hex length insufficient");
            return string(buffer);
        }
        /// @dev Returns the hexadecimal representation of `value`.
        /// The output is prefixed with "0x", encoded using 2 hexadecimal digits per byte,
        /// and the alphabets are capitalized conditionally according to
        /// https://eips.ethereum.org/EIPS/eip-55
        function toHexStringChecksummed(address value) internal pure returns (string memory str) {
            str = toHexString(value);
            /// @solidity memory-safe-assembly
            assembly {
                let mask := shl(6, div(not(0), 255)) // `0b010000000100000000 ...`
                let o := add(str, 0x22)
                let hashed := and(keccak256(o, 40), mul(34, mask)) // `0b10001000 ... `
                let t := shl(240, 136) // `0b10001000 << 240`
                for {
                    let i := 0
                } 1 {
                } {
                    mstore(add(i, i), mul(t, byte(i, hashed)))
                    i := add(i, 1)
                    if eq(i, 20) {
                        break
                    }
                }
                mstore(o, xor(mload(o), shr(1, and(mload(0x00), and(mload(o), mask)))))
                o := add(o, 0x20)
                mstore(o, xor(mload(o), shr(1, and(mload(0x20), and(mload(o), mask)))))
            }
        }
        /// @dev Returns the hexadecimal representation of `value`.
        /// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte.
        function toHexString(address value) internal pure returns (string memory str) {
            str = toHexStringNoPrefix(value);
            /// @solidity memory-safe-assembly
            assembly {
                let strLength := add(mload(str), 2) // Compute the length.
                mstore(str, 0x3078) // Write the "0x" prefix.
                str := sub(str, 2) // Move the pointer.
                mstore(str, strLength) // Write the length.
            }
        }
        /// @dev Returns the hexadecimal representation of `value`.
        /// The output is encoded using 2 hexadecimal digits per byte.
        function toHexStringNoPrefix(address value) internal pure returns (string memory str) {
            /// @solidity memory-safe-assembly
            assembly {
                str := mload(0x40)
                // Allocate the memory.
                // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,
                // 0x02 bytes for the prefix, and 0x28 bytes for the digits.
                // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x28) is 0x80.
                mstore(0x40, add(str, 0x80))
                // Store "0123456789abcdef" in scratch space.
                mstore(0x0f, 0x30313233343536373839616263646566)
                str := add(str, 2)
                mstore(str, 40)
                let o := add(str, 0x20)
                mstore(add(o, 40), 0)
                value := shl(96, value)
                // We write the string from rightmost digit to leftmost digit.
                // The following is essentially a do-while loop that also handles the zero case.
                for {
                    let i := 0
                } 1 {
                } {
                    let p := add(o, add(i, i))
                    let temp := byte(i, value)
                    mstore8(add(p, 1), mload(and(temp, 15)))
                    mstore8(p, mload(shr(4, temp)))
                    i := add(i, 1)
                    if eq(i, 20) {
                        break
                    }
                }
            }
        }
        /// @dev Returns the hex encoded string from the raw bytes.
        /// The output is encoded using 2 hexadecimal digits per byte.
        function toHexString(bytes memory raw) internal pure returns (string memory str) {
            str = toHexStringNoPrefix(raw);
            /// @solidity memory-safe-assembly
            assembly {
                let strLength := add(mload(str), 2) // Compute the length.
                mstore(str, 0x3078) // Write the "0x" prefix.
                str := sub(str, 2) // Move the pointer.
                mstore(str, strLength) // Write the length.
            }
        }
        /// @dev Returns the hex encoded string from the raw bytes.
        /// The output is encoded using 2 hexadecimal digits per byte.
        function toHexStringNoPrefix(bytes memory raw) internal pure returns (string memory str) {
            /// @solidity memory-safe-assembly
            assembly {
                let length := mload(raw)
                str := add(mload(0x40), 2) // Skip 2 bytes for the optional prefix.
                mstore(str, add(length, length)) // Store the length of the output.
                // Store "0123456789abcdef" in scratch space.
                mstore(0x0f, 0x30313233343536373839616263646566)
                let o := add(str, 0x20)
                let end := add(raw, length)
                for {
                } iszero(eq(raw, end)) {
                } {
                    raw := add(raw, 1)
                    mstore8(add(o, 1), mload(and(mload(raw), 15)))
                    mstore8(o, mload(and(shr(4, mload(raw)), 15)))
                    o := add(o, 2)
                }
                mstore(o, 0) // Zeroize the slot after the string.
                mstore(0x40, add(o, 0x20)) // Allocate the memory.
            }
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    import "@thirdweb-dev/contracts/base/ERC20SignatureMint.sol";
    contract wQUIL is ERC20SignatureMint {
          constructor(
            address _defaultAdmin,
            string memory _name,
            string memory _symbol,
            address _primarySaleRecipient
        )
            ERC20SignatureMint(
                _defaultAdmin,
                _name,
                _symbol,
                _primarySaleRecipient
            )
        {}
        function decimals() public view override returns (uint8) {
            return 8;
        }
    }