ETH Price: $2,156.76 (+2.65%)

Transaction Decoder

Block:
14504027 at Apr-02-2022 02:06:44 AM +UTC
Transaction Fee:
0.018033939898619316 ETH $38.89
Gas Used:
341,556 Gas / 52.799364961 Gwei

Emitted Events:

Account State Difference:

  Address   Before After State Difference Code
(2Miners: PPLNS)
5,918.896072134009595912 Eth5,918.896926024009595912 Eth0.00085389
0x0cD925f2...552d7d837
0x9338AB84...c21b6F82e
0.083587532236674008 Eth
Nonce: 10
0.065553592338054692 Eth
Nonce: 11
0.018033939898619316

Execution Trace

HoldersLoot.mintClaim( tokenIndices=[9, 5, 1, 4], mintAmounts=[1, 1, 1, 1], networkName=1, contractAddress=0x0cD925f268678d03057D184Ded76d98552d7d837, nonce=1, signature=0x61B056E77AC26C4663BC305BA07CF39F51AE309E52B6DF269C4E19D2DD7A250835AA2F59CB68A1522CE38D175436F8120A565A9D1ED56B074B7ED7476998EE641C )
  • Null: 0x000...001.ebf00cf1( )
    //SPDX-License-Identifier: MIT
    pragma solidity ^0.8.7;
    // Openzeppelin Contracts
    import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";
    import "@openzeppelin/contracts/token/ERC1155/extensions/ERC1155Burnable.sol";
    import "@openzeppelin/contracts/token/ERC1155/extensions/ERC1155Supply.sol";
    import "@openzeppelin/contracts/access/AccessControl.sol";
    import "@openzeppelin/contracts/security/Pausable.sol";
    import "@openzeppelin/contracts/utils/Strings.sol";
    // PlaySide Contracts
    import "./SignatureVerify.sol";
    import "./LootItems.sol";
    import "./Roles.sol";
    import "./Items.sol";
    import "./Settings.sol";
    import "./ErrorCodes.sol";
    contract HoldersLoot is
        ERC1155,
        AccessControl,
        Pausable,
        ERC1155Supply,
    \tERC1155Burnable,
        SignatureVerify,
        Settings,
        LootItems
    {
        using Strings for uint256;
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // *                    CONTRACT SETTINGS
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        /// @dev The name of this contract.
        ///     This will get pulled by clients and name this contract using this string.
        string public constant name = "BEANS HOLDERS LOOT - Dumb Ways to Die";
        /// @dev The symbol of this contract ( When referenced in short hand )
        ///     This will get pulled by clients and symbol this contract using this string.
        string public constant symbol = "DWTD_HOLDERS_LOOT";
    \t/// @dev Keeps track of all of the minting calls that a certain address has 
    \t/// \tminted
        /// address => (tokenID => balance)
    \tmapping(address => mapping(uint256 => uint256)) accountsMintedCount;
        constructor()
            ERC1155(
                "https://dwtd.playsidestudios-devel.com/loot/founders/metadata/{id}.json"
            )
        {}
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // *                        URI
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        /// @dev Sets a new base URI for all tokens.
        /// @param newuri: The new URI of all tokens.
        /// This should be a directory holding all the json data for each token
        //  This is unused currently as each token is using an array to map each tokens URI
        function setURI(string memory newuri) public onlyRole(Roles.ROLE_SAFE) {
            _setURI(newuri);
        }
        /// @dev Returns the URI for a specfic token ID. Returns from an array that was filled
        ///     in by the admin for each specifc item.
        /// @param tokenID: The token ID that should be returned.
        function uri(uint256 tokenID)
            public
            view
            virtual
            override
            returns (string memory) {
            return LootItems.lootItemList[tokenID].Uri;
        }
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // *                    PAUSE
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        function setPause() public onlyRole(Roles.ROLE_SAFE) {
            _pause();
        }
        function unpause() public onlyRole(Roles.ROLE_SAFE) {
            _unpause();
        }
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // *                        MINT
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        /// @dev Mints target tokens and amounts from the daily store array.
        /// Will fail if 'Settings.dailyStoreActive' is set to false.
        /// @param tokenIndicies: An array of tokens that the user is requesting to mint.
        //      example: [1,2,3] will mint token IDs : 1,2,3.
        /// @param mintAmounts: The amount respectivly of each token that the user is requesting to mint.
        //      example: [1,2,1] will mint token IDs [(TokenID: 1 Amount: 1), (TokenID: 2 Amount: 2),(TokenID: 3 Amount: 1)]
        function mintDailyStore(
            // Token Params
            uint256[] calldata tokenIndices,
            uint256[] calldata mintAmounts
        ) public payable whenNotPaused {
            // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            // *            ENSURE THE PUBLIC SALE IS ACTIVE
            // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            if (!Settings.dailyStoreActive) revert ErrorCodes.DailyStoreInactive();
            // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            // *          CHECK ITEM EXISTS IN DAILY STORE
            // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            // Loop Through All The Requested Token Indicies
            for (
                uint256 index_TokenIndicies = 0;
                index_TokenIndicies < tokenIndices.length;
                index_TokenIndicies++
            ) {
                bool indexExists = false;
                // Check that each of them are in the daily store, if not, error out
                for (
                    uint256 index_DailyStore = 0;
                    index_DailyStore < dailyStore.length;
                    index_DailyStore++
                ) {
                    // Check if this index matches one in the daily store
                    if (
                        tokenIndices[index_TokenIndicies] ==
                        LootItems.dailyStore[index_DailyStore]
                    ) {
                        indexExists = true;
                        break;
                    }
                }
                // If not, revert this transaction
                if (indexExists == false) revert ErrorCodes.NotInDailyStore();
            }
            // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            // *              CHECK ALL REQUIRES
            // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            CheckRequires(tokenIndices, mintAmounts, true);
            // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            // *                 FINALLY MINT
            // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            _mintBatch(msg.sender, tokenIndices, mintAmounts, "");
        }
        /// @dev This is a nonpayable function that will allow a user to claim a token that they are whitelisted to claim.
        /// Will fail if the signature wasnt signed with the correct data and by a signer address on server side.
        /// @param tokenIndicies: An array of tokens that the user is requesting to mint.
        //      example: [1,2,3] will mint token IDs : 1,2,3.
        /// @param mintAmounts: The amount respectivly of each token that the user is requesting to mint.
        //      example: [1,2,1] will mint token IDs [(TokenID: 1 Amount: 1), (TokenID: 2 Amount: 2),(TokenID: 3 Amount: 1)].
        /// @param networkName: The network name that this contract is on. Removes the ability for users to create a dummy contract on a test network
        ///     and sign using that contract.
        /// @param contractAddress: Similar to the network name this ensures that this data was signed using this contract address removing the ability
        ///     for users to sign using a different contract.
        /// @param nonce: This removes the ability to repeat attack this contract in order to control the amount of tokens a user can get.
        /// @param signature: All of the data that is sent through this function is also signed by a signer address and the user and hashed
        ///     out to this param. This signature will be reveresed engineered to ensure that the data is the same as what was signed by the server.
        function mintClaim(
            // Token Params
            uint256[] calldata tokenIndices,
            uint256[] calldata mintAmounts,
            // Signature Params
            uint256 networkName,
            address contractAddress,
            uint256 nonce,
            bytes memory signature
        ) public whenNotPaused {
    \t\t// Check not paused
    \t\tif(Settings.claimActive == false) revert ErrorCodes.ClaimMintDisabled();
            // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            // *              CHECK IF MESSAGE WAS SIGNED
            // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            bool isOwner = (msg.sender == Settings.signerAddress);
            if ((isOwner == false)) {
                // Verify Signature
                if (
                    !verify(
                        Settings.signerAddress,
                        msg.sender,
                        tokenIndices,
                        mintAmounts,
                        networkName,
                        contractAddress,
                        nonce,
                        signature
                    )
                ) revert ErrorCodes.SignatureVerify();
            }
    \t\t// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            // *              CHECK CORRECT NETWORK ID
            // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    \t\t// Check that the network passed into the contract was the correct network
    \t\t// \tDoing this after the verify as that ensures that the network passed in matches
    \t\t// \tthe signed data from the server
    \t\tif(networkName != Settings.blockchain) revert ErrorCodes.NetworkMissmatch();
    \t\t// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            // *              CHECK CORRECT CONTRACT ADDRESS
            // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    \t\tif(contractAddress != address(this)) revert ErrorCodes.IncorrectContractSignature(); 
            // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            // *              CHECK ALL REQUIRES
            // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            CheckRequires(tokenIndices, mintAmounts, false);
    \t\t// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            // *                 ADD TO MAPPING
            // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    \t\tCacheMintRequests(tokenIndices, mintAmounts);
            // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            // *                 FINALLY MINT
            // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            _mintBatch(msg.sender, tokenIndices, mintAmounts, "");
        }
        /// @dev Checks all of the requires in order to allow a user to mint a token.
        /// @param tokenIndicies: An array of tokens that the user is requesting to mint.
        //      example: [1,2,3] will mint token IDs : 1,2,3.
        /// @param mintAmounts: The amount respectivly of each token that the user is requesting to mint.
        //      example: [1,2,1] will mint token IDs [(TokenID: 1 Amount: 1), (TokenID: 2 Amount: 2),(TokenID: 3 Amount: 1)]
        /// @param shouldCheckCost: A bool that controls if this function should check the cost of the message matches the cost
        ///     of all of of the tokens that a user is requesting.
        function CheckRequires(
            uint256[] calldata tokenIndices,
            uint256[] calldata mintAmounts,
            bool shouldCheckCost
        ) internal view {
            // Check the length of each array
            if (tokenIndices.length <= 0 || tokenIndices.length != mintAmounts.length)
                revert ErrorCodes.ArrayMissmatch();
            uint256 _totalCost = 0;
            for (uint256 index = 0; index < tokenIndices.length; index++) {
                // Get Source Data
                uint256 tokenID = tokenIndices[index];
                LootItems.LootItem memory _sourceData = LootItems.getLootItem(
                    tokenID
                );
                // Get the total supply of the token index above the current index
                uint256 supplyCount = totalSupply(tokenID);
                // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                // *                    CACHE VARIABLES
                // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                // Cache the desired mint amount
                uint256 mintAmount = mintAmounts[index];
                // Get the cost of this item that is being minted and add it to the total cost
                if (shouldCheckCost) {
                    _totalCost =
                        _totalCost +
                        (
                            _sourceData.IsOnSale
                                ? (_sourceData.WeiSaleCost \t* mintAmount)
                                : (_sourceData.WeiCost \t\t* mintAmount)
                        );
                }
                // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                // *              FINALLY VALIDATE SETTINGS
                // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                // Has to mint at least 1 item
                if (mintAmount <= 0) revert ErrorCodes.RequestedIncorrectAmount();
                // Check has enough supply to mint
                if (supplyCount + mintAmount > _sourceData.TotalSupply)
                    revert ErrorCodes.SoldOut();
                // Check for the entire cost of this transaction
                if (shouldCheckCost && msg.value < _totalCost)
                    revert ErrorCodes.InsufficientFunds(_totalCost, msg.value);
            }
        }
    \tfunction CacheMintRequests(uint256[] calldata tokenIndices, uint256[] calldata mintAmounts) internal
    \t{
    \t\tuint256 tokenIndicesLength \t= tokenIndices.length;
    \t\t// Loop through all token indicies and mint amounts and add them into the mapping
    \t\tfor(uint256 tokenIndex = 0; tokenIndex < tokenIndicesLength; tokenIndex++)
    \t\t{
    \t\t\t// Get the current token index
    \t\t\tuint256 tokenID = tokenIndices[tokenIndex];
    \t\t\t// Get the current mint amount for this token index
    \t\t\tuint256 mintAmount = mintAmounts[tokenIndex];
    \t\t\t// Calculate the new mint amount from what they have already minted + requested mint amount
    \t\t\tuint256 newMintAmount = accountsMintedCount[msg.sender][tokenID] + mintAmount;
    \t\t\t// Check that this item exists in our database
    \t\t\tLootItems.LootItem memory sourceData = LootItems.getLootItem(tokenID);
    \t\t\t// If the item doesnt exist, revert transaction
    \t\t\tif(sourceData.exists == false) revert ErrorCodes.LootItem_ItemDoesntExist();
    \t\t\t// Check the new mint amount against the settings for this item
    \t\t\tif(newMintAmount > sourceData.MaxMintPerUser) {
    \t\t\t\trevert ErrorCodes.LootItem_MaxMintError();
    \t\t\t}
    \t\t\t// Add to mapping of this address and this token index
    \t\t\taccountsMintedCount[msg.sender][tokenID] = newMintAmount;
    \t\t}
    \t}
    \t/// @dev Withdraws all the funds 
    \tfunction withdraw() public onlyRole(Roles.ROLE_SAFE) {
        \t(bool os, ) = payable(0x1e9C6144c06Bb4B21586E11bb9d0D526Dc590C9d).call{value: address(this).balance}("");
        \trequire(os);
    \t}
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // *      ERC1155 OVERRIDE ( Used for supply tracking )
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        function _beforeTokenTransfer(
            address operator,
            address from,
            address to,
            uint256[] memory ids,
            uint256[] memory amounts,
            bytes memory data
        ) internal override(ERC1155, ERC1155Supply) whenNotPaused {
            super._beforeTokenTransfer(operator, from, to, ids, amounts, data);
        }
        // The following function override is required by Solidity.
        function supportsInterface(bytes4 interfaceId)
            public
            view
            override(ERC1155, AccessControl)
            returns (bool)
        {
            return super.supportsInterface(interfaceId);
        }
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (token/ERC1155/ERC1155.sol)
    pragma solidity ^0.8.0;
    import "./IERC1155.sol";
    import "./IERC1155Receiver.sol";
    import "./extensions/IERC1155MetadataURI.sol";
    import "../../utils/Address.sol";
    import "../../utils/Context.sol";
    import "../../utils/introspection/ERC165.sol";
    /**
     * @dev Implementation of the basic standard multi-token.
     * See https://eips.ethereum.org/EIPS/eip-1155
     * Originally based on code by Enjin: https://github.com/enjin/erc-1155
     *
     * _Available since v3.1._
     */
    contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI {
        using Address for address;
        // Mapping from token ID to account balances
        mapping(uint256 => mapping(address => uint256)) private _balances;
        // Mapping from account to operator approvals
        mapping(address => mapping(address => bool)) private _operatorApprovals;
        // Used as the URI for all token types by relying on ID substitution, e.g. https://token-cdn-domain/{id}.json
        string private _uri;
        /**
         * @dev See {_setURI}.
         */
        constructor(string memory uri_) {
            _setURI(uri_);
        }
        /**
         * @dev See {IERC165-supportsInterface}.
         */
        function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
            return
                interfaceId == type(IERC1155).interfaceId ||
                interfaceId == type(IERC1155MetadataURI).interfaceId ||
                super.supportsInterface(interfaceId);
        }
        /**
         * @dev See {IERC1155MetadataURI-uri}.
         *
         * This implementation returns the same URI for *all* token types. It relies
         * on the token type ID substitution mechanism
         * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP].
         *
         * Clients calling this function must replace the `\\{id\\}` substring with the
         * actual token type ID.
         */
        function uri(uint256) public view virtual override returns (string memory) {
            return _uri;
        }
        /**
         * @dev See {IERC1155-balanceOf}.
         *
         * Requirements:
         *
         * - `account` cannot be the zero address.
         */
        function balanceOf(address account, uint256 id) public view virtual override returns (uint256) {
            require(account != address(0), "ERC1155: balance query for the zero address");
            return _balances[id][account];
        }
        /**
         * @dev See {IERC1155-balanceOfBatch}.
         *
         * Requirements:
         *
         * - `accounts` and `ids` must have the same length.
         */
        function balanceOfBatch(address[] memory accounts, uint256[] memory ids)
            public
            view
            virtual
            override
            returns (uint256[] memory)
        {
            require(accounts.length == ids.length, "ERC1155: accounts and ids length mismatch");
            uint256[] memory batchBalances = new uint256[](accounts.length);
            for (uint256 i = 0; i < accounts.length; ++i) {
                batchBalances[i] = balanceOf(accounts[i], ids[i]);
            }
            return batchBalances;
        }
        /**
         * @dev See {IERC1155-setApprovalForAll}.
         */
        function setApprovalForAll(address operator, bool approved) public virtual override {
            _setApprovalForAll(_msgSender(), operator, approved);
        }
        /**
         * @dev See {IERC1155-isApprovedForAll}.
         */
        function isApprovedForAll(address account, address operator) public view virtual override returns (bool) {
            return _operatorApprovals[account][operator];
        }
        /**
         * @dev See {IERC1155-safeTransferFrom}.
         */
        function safeTransferFrom(
            address from,
            address to,
            uint256 id,
            uint256 amount,
            bytes memory data
        ) public virtual override {
            require(
                from == _msgSender() || isApprovedForAll(from, _msgSender()),
                "ERC1155: caller is not owner nor approved"
            );
            _safeTransferFrom(from, to, id, amount, data);
        }
        /**
         * @dev See {IERC1155-safeBatchTransferFrom}.
         */
        function safeBatchTransferFrom(
            address from,
            address to,
            uint256[] memory ids,
            uint256[] memory amounts,
            bytes memory data
        ) public virtual override {
            require(
                from == _msgSender() || isApprovedForAll(from, _msgSender()),
                "ERC1155: transfer caller is not owner nor approved"
            );
            _safeBatchTransferFrom(from, to, ids, amounts, data);
        }
        /**
         * @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
         *
         * Emits a {TransferSingle} event.
         *
         * Requirements:
         *
         * - `to` cannot be the zero address.
         * - `from` must have a balance of tokens of type `id` of at least `amount`.
         * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
         * acceptance magic value.
         */
        function _safeTransferFrom(
            address from,
            address to,
            uint256 id,
            uint256 amount,
            bytes memory data
        ) internal virtual {
            require(to != address(0), "ERC1155: transfer to the zero address");
            address operator = _msgSender();
            _beforeTokenTransfer(operator, from, to, _asSingletonArray(id), _asSingletonArray(amount), data);
            uint256 fromBalance = _balances[id][from];
            require(fromBalance >= amount, "ERC1155: insufficient balance for transfer");
            unchecked {
                _balances[id][from] = fromBalance - amount;
            }
            _balances[id][to] += amount;
            emit TransferSingle(operator, from, to, id, amount);
            _doSafeTransferAcceptanceCheck(operator, from, to, id, amount, data);
        }
        /**
         * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_safeTransferFrom}.
         *
         * Emits a {TransferBatch} event.
         *
         * Requirements:
         *
         * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
         * acceptance magic value.
         */
        function _safeBatchTransferFrom(
            address from,
            address to,
            uint256[] memory ids,
            uint256[] memory amounts,
            bytes memory data
        ) internal virtual {
            require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
            require(to != address(0), "ERC1155: transfer to the zero address");
            address operator = _msgSender();
            _beforeTokenTransfer(operator, from, to, ids, amounts, data);
            for (uint256 i = 0; i < ids.length; ++i) {
                uint256 id = ids[i];
                uint256 amount = amounts[i];
                uint256 fromBalance = _balances[id][from];
                require(fromBalance >= amount, "ERC1155: insufficient balance for transfer");
                unchecked {
                    _balances[id][from] = fromBalance - amount;
                }
                _balances[id][to] += amount;
            }
            emit TransferBatch(operator, from, to, ids, amounts);
            _doSafeBatchTransferAcceptanceCheck(operator, from, to, ids, amounts, data);
        }
        /**
         * @dev Sets a new URI for all token types, by relying on the token type ID
         * substitution mechanism
         * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP].
         *
         * By this mechanism, any occurrence of the `\\{id\\}` substring in either the
         * URI or any of the amounts in the JSON file at said URI will be replaced by
         * clients with the token type ID.
         *
         * For example, the `https://token-cdn-domain/\\{id\\}.json` URI would be
         * interpreted by clients as
         * `https://token-cdn-domain/000000000000000000000000000000000000000000000000000000000004cce0.json`
         * for token type ID 0x4cce0.
         *
         * See {uri}.
         *
         * Because these URIs cannot be meaningfully represented by the {URI} event,
         * this function emits no events.
         */
        function _setURI(string memory newuri) internal virtual {
            _uri = newuri;
        }
        /**
         * @dev Creates `amount` tokens of token type `id`, and assigns them to `to`.
         *
         * Emits a {TransferSingle} event.
         *
         * Requirements:
         *
         * - `to` cannot be the zero address.
         * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
         * acceptance magic value.
         */
        function _mint(
            address to,
            uint256 id,
            uint256 amount,
            bytes memory data
        ) internal virtual {
            require(to != address(0), "ERC1155: mint to the zero address");
            address operator = _msgSender();
            _beforeTokenTransfer(operator, address(0), to, _asSingletonArray(id), _asSingletonArray(amount), data);
            _balances[id][to] += amount;
            emit TransferSingle(operator, address(0), to, id, amount);
            _doSafeTransferAcceptanceCheck(operator, address(0), to, id, amount, data);
        }
        /**
         * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_mint}.
         *
         * Requirements:
         *
         * - `ids` and `amounts` must have the same length.
         * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
         * acceptance magic value.
         */
        function _mintBatch(
            address to,
            uint256[] memory ids,
            uint256[] memory amounts,
            bytes memory data
        ) internal virtual {
            require(to != address(0), "ERC1155: mint to the zero address");
            require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
            address operator = _msgSender();
            _beforeTokenTransfer(operator, address(0), to, ids, amounts, data);
            for (uint256 i = 0; i < ids.length; i++) {
                _balances[ids[i]][to] += amounts[i];
            }
            emit TransferBatch(operator, address(0), to, ids, amounts);
            _doSafeBatchTransferAcceptanceCheck(operator, address(0), to, ids, amounts, data);
        }
        /**
         * @dev Destroys `amount` tokens of token type `id` from `from`
         *
         * Requirements:
         *
         * - `from` cannot be the zero address.
         * - `from` must have at least `amount` tokens of token type `id`.
         */
        function _burn(
            address from,
            uint256 id,
            uint256 amount
        ) internal virtual {
            require(from != address(0), "ERC1155: burn from the zero address");
            address operator = _msgSender();
            _beforeTokenTransfer(operator, from, address(0), _asSingletonArray(id), _asSingletonArray(amount), "");
            uint256 fromBalance = _balances[id][from];
            require(fromBalance >= amount, "ERC1155: burn amount exceeds balance");
            unchecked {
                _balances[id][from] = fromBalance - amount;
            }
            emit TransferSingle(operator, from, address(0), id, amount);
        }
        /**
         * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_burn}.
         *
         * Requirements:
         *
         * - `ids` and `amounts` must have the same length.
         */
        function _burnBatch(
            address from,
            uint256[] memory ids,
            uint256[] memory amounts
        ) internal virtual {
            require(from != address(0), "ERC1155: burn from the zero address");
            require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
            address operator = _msgSender();
            _beforeTokenTransfer(operator, from, address(0), ids, amounts, "");
            for (uint256 i = 0; i < ids.length; i++) {
                uint256 id = ids[i];
                uint256 amount = amounts[i];
                uint256 fromBalance = _balances[id][from];
                require(fromBalance >= amount, "ERC1155: burn amount exceeds balance");
                unchecked {
                    _balances[id][from] = fromBalance - amount;
                }
            }
            emit TransferBatch(operator, from, address(0), ids, amounts);
        }
        /**
         * @dev Approve `operator` to operate on all of `owner` tokens
         *
         * Emits a {ApprovalForAll} event.
         */
        function _setApprovalForAll(
            address owner,
            address operator,
            bool approved
        ) internal virtual {
            require(owner != operator, "ERC1155: setting approval status for self");
            _operatorApprovals[owner][operator] = approved;
            emit ApprovalForAll(owner, operator, approved);
        }
        /**
         * @dev Hook that is called before any token transfer. This includes minting
         * and burning, as well as batched variants.
         *
         * The same hook is called on both single and batched variants. For single
         * transfers, the length of the `id` and `amount` arrays will be 1.
         *
         * Calling conditions (for each `id` and `amount` pair):
         *
         * - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens
         * of token type `id` will be  transferred to `to`.
         * - When `from` is zero, `amount` tokens of token type `id` will be minted
         * for `to`.
         * - when `to` is zero, `amount` of ``from``'s tokens of token type `id`
         * will be burned.
         * - `from` and `to` are never both zero.
         * - `ids` and `amounts` have the same, non-zero length.
         *
         * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
         */
        function _beforeTokenTransfer(
            address operator,
            address from,
            address to,
            uint256[] memory ids,
            uint256[] memory amounts,
            bytes memory data
        ) internal virtual {}
        function _doSafeTransferAcceptanceCheck(
            address operator,
            address from,
            address to,
            uint256 id,
            uint256 amount,
            bytes memory data
        ) private {
            if (to.isContract()) {
                try IERC1155Receiver(to).onERC1155Received(operator, from, id, amount, data) returns (bytes4 response) {
                    if (response != IERC1155Receiver.onERC1155Received.selector) {
                        revert("ERC1155: ERC1155Receiver rejected tokens");
                    }
                } catch Error(string memory reason) {
                    revert(reason);
                } catch {
                    revert("ERC1155: transfer to non ERC1155Receiver implementer");
                }
            }
        }
        function _doSafeBatchTransferAcceptanceCheck(
            address operator,
            address from,
            address to,
            uint256[] memory ids,
            uint256[] memory amounts,
            bytes memory data
        ) private {
            if (to.isContract()) {
                try IERC1155Receiver(to).onERC1155BatchReceived(operator, from, ids, amounts, data) returns (
                    bytes4 response
                ) {
                    if (response != IERC1155Receiver.onERC1155BatchReceived.selector) {
                        revert("ERC1155: ERC1155Receiver rejected tokens");
                    }
                } catch Error(string memory reason) {
                    revert(reason);
                } catch {
                    revert("ERC1155: transfer to non ERC1155Receiver implementer");
                }
            }
        }
        function _asSingletonArray(uint256 element) private pure returns (uint256[] memory) {
            uint256[] memory array = new uint256[](1);
            array[0] = element;
            return array;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (token/ERC1155/extensions/ERC1155Burnable.sol)
    pragma solidity ^0.8.0;
    import "../ERC1155.sol";
    /**
     * @dev Extension of {ERC1155} that allows token holders to destroy both their
     * own tokens and those that they have been approved to use.
     *
     * _Available since v3.1._
     */
    abstract contract ERC1155Burnable is ERC1155 {
        function burn(
            address account,
            uint256 id,
            uint256 value
        ) public virtual {
            require(
                account == _msgSender() || isApprovedForAll(account, _msgSender()),
                "ERC1155: caller is not owner nor approved"
            );
            _burn(account, id, value);
        }
        function burnBatch(
            address account,
            uint256[] memory ids,
            uint256[] memory values
        ) public virtual {
            require(
                account == _msgSender() || isApprovedForAll(account, _msgSender()),
                "ERC1155: caller is not owner nor approved"
            );
            _burnBatch(account, ids, values);
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (token/ERC1155/extensions/ERC1155Supply.sol)
    pragma solidity ^0.8.0;
    import "../ERC1155.sol";
    /**
     * @dev Extension of ERC1155 that adds tracking of total supply per id.
     *
     * Useful for scenarios where Fungible and Non-fungible tokens have to be
     * clearly identified. Note: While a totalSupply of 1 might mean the
     * corresponding is an NFT, there is no guarantees that no other token with the
     * same id are not going to be minted.
     */
    abstract contract ERC1155Supply is ERC1155 {
        mapping(uint256 => uint256) private _totalSupply;
        /**
         * @dev Total amount of tokens in with a given id.
         */
        function totalSupply(uint256 id) public view virtual returns (uint256) {
            return _totalSupply[id];
        }
        /**
         * @dev Indicates whether any token exist with a given id, or not.
         */
        function exists(uint256 id) public view virtual returns (bool) {
            return ERC1155Supply.totalSupply(id) > 0;
        }
        /**
         * @dev See {ERC1155-_beforeTokenTransfer}.
         */
        function _beforeTokenTransfer(
            address operator,
            address from,
            address to,
            uint256[] memory ids,
            uint256[] memory amounts,
            bytes memory data
        ) internal virtual override {
            super._beforeTokenTransfer(operator, from, to, ids, amounts, data);
            if (from == address(0)) {
                for (uint256 i = 0; i < ids.length; ++i) {
                    _totalSupply[ids[i]] += amounts[i];
                }
            }
            if (to == address(0)) {
                for (uint256 i = 0; i < ids.length; ++i) {
                    _totalSupply[ids[i]] -= amounts[i];
                }
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControl.sol)
    pragma solidity ^0.8.0;
    import "./IAccessControl.sol";
    import "../utils/Context.sol";
    import "../utils/Strings.sol";
    import "../utils/introspection/ERC165.sol";
    /**
     * @dev Contract module that allows children to implement role-based access
     * control mechanisms. This is a lightweight version that doesn't allow enumerating role
     * members except through off-chain means by accessing the contract event logs. Some
     * applications may benefit from on-chain enumerability, for those cases see
     * {AccessControlEnumerable}.
     *
     * Roles are referred to by their `bytes32` identifier. These should be exposed
     * in the external API and be unique. The best way to achieve this is by
     * using `public constant` hash digests:
     *
     * ```
     * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
     * ```
     *
     * Roles can be used to represent a set of permissions. To restrict access to a
     * function call, use {hasRole}:
     *
     * ```
     * function foo() public {
     *     require(hasRole(MY_ROLE, msg.sender));
     *     ...
     * }
     * ```
     *
     * Roles can be granted and revoked dynamically via the {grantRole} and
     * {revokeRole} functions. Each role has an associated admin role, and only
     * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
     *
     * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
     * that only accounts with this role will be able to grant or revoke other
     * roles. More complex role relationships can be created by using
     * {_setRoleAdmin}.
     *
     * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
     * grant and revoke this role. Extra precautions should be taken to secure
     * accounts that have been granted it.
     */
    abstract contract AccessControl is Context, IAccessControl, ERC165 {
        struct RoleData {
            mapping(address => bool) members;
            bytes32 adminRole;
        }
        mapping(bytes32 => RoleData) private _roles;
        bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
        /**
         * @dev Modifier that checks that an account has a specific role. Reverts
         * with a standardized message including the required role.
         *
         * The format of the revert reason is given by the following regular expression:
         *
         *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
         *
         * _Available since v4.1._
         */
        modifier onlyRole(bytes32 role) {
            _checkRole(role, _msgSender());
            _;
        }
        /**
         * @dev See {IERC165-supportsInterface}.
         */
        function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
            return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
        }
        /**
         * @dev Returns `true` if `account` has been granted `role`.
         */
        function hasRole(bytes32 role, address account) public view virtual override returns (bool) {
            return _roles[role].members[account];
        }
        /**
         * @dev Revert with a standard message if `account` is missing `role`.
         *
         * The format of the revert reason is given by the following regular expression:
         *
         *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
         */
        function _checkRole(bytes32 role, address account) internal view virtual {
            if (!hasRole(role, account)) {
                revert(
                    string(
                        abi.encodePacked(
                            "AccessControl: account ",
                            Strings.toHexString(uint160(account), 20),
                            " is missing role ",
                            Strings.toHexString(uint256(role), 32)
                        )
                    )
                );
            }
        }
        /**
         * @dev Returns the admin role that controls `role`. See {grantRole} and
         * {revokeRole}.
         *
         * To change a role's admin, use {_setRoleAdmin}.
         */
        function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {
            return _roles[role].adminRole;
        }
        /**
         * @dev Grants `role` to `account`.
         *
         * If `account` had not been already granted `role`, emits a {RoleGranted}
         * event.
         *
         * Requirements:
         *
         * - the caller must have ``role``'s admin role.
         */
        function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
            _grantRole(role, account);
        }
        /**
         * @dev Revokes `role` from `account`.
         *
         * If `account` had been granted `role`, emits a {RoleRevoked} event.
         *
         * Requirements:
         *
         * - the caller must have ``role``'s admin role.
         */
        function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
            _revokeRole(role, account);
        }
        /**
         * @dev Revokes `role` from the calling account.
         *
         * Roles are often managed via {grantRole} and {revokeRole}: this function's
         * purpose is to provide a mechanism for accounts to lose their privileges
         * if they are compromised (such as when a trusted device is misplaced).
         *
         * If the calling account had been revoked `role`, emits a {RoleRevoked}
         * event.
         *
         * Requirements:
         *
         * - the caller must be `account`.
         */
        function renounceRole(bytes32 role, address account) public virtual override {
            require(account == _msgSender(), "AccessControl: can only renounce roles for self");
            _revokeRole(role, account);
        }
        /**
         * @dev Grants `role` to `account`.
         *
         * If `account` had not been already granted `role`, emits a {RoleGranted}
         * event. Note that unlike {grantRole}, this function doesn't perform any
         * checks on the calling account.
         *
         * [WARNING]
         * ====
         * This function should only be called from the constructor when setting
         * up the initial roles for the system.
         *
         * Using this function in any other way is effectively circumventing the admin
         * system imposed by {AccessControl}.
         * ====
         *
         * NOTE: This function is deprecated in favor of {_grantRole}.
         */
        function _setupRole(bytes32 role, address account) internal virtual {
            _grantRole(role, account);
        }
        /**
         * @dev Sets `adminRole` as ``role``'s admin role.
         *
         * Emits a {RoleAdminChanged} event.
         */
        function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
            bytes32 previousAdminRole = getRoleAdmin(role);
            _roles[role].adminRole = adminRole;
            emit RoleAdminChanged(role, previousAdminRole, adminRole);
        }
        /**
         * @dev Grants `role` to `account`.
         *
         * Internal function without access restriction.
         */
        function _grantRole(bytes32 role, address account) internal virtual {
            if (!hasRole(role, account)) {
                _roles[role].members[account] = true;
                emit RoleGranted(role, account, _msgSender());
            }
        }
        /**
         * @dev Revokes `role` from `account`.
         *
         * Internal function without access restriction.
         */
        function _revokeRole(bytes32 role, address account) internal virtual {
            if (hasRole(role, account)) {
                _roles[role].members[account] = false;
                emit RoleRevoked(role, account, _msgSender());
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)
    pragma solidity ^0.8.0;
    import "../utils/Context.sol";
    /**
     * @dev Contract module which allows children to implement an emergency stop
     * mechanism that can be triggered by an authorized account.
     *
     * This module is used through inheritance. It will make available the
     * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
     * the functions of your contract. Note that they will not be pausable by
     * simply including this module, only once the modifiers are put in place.
     */
    abstract contract Pausable is Context {
        /**
         * @dev Emitted when the pause is triggered by `account`.
         */
        event Paused(address account);
        /**
         * @dev Emitted when the pause is lifted by `account`.
         */
        event Unpaused(address account);
        bool private _paused;
        /**
         * @dev Initializes the contract in unpaused state.
         */
        constructor() {
            _paused = false;
        }
        /**
         * @dev Returns true if the contract is paused, and false otherwise.
         */
        function paused() public view virtual returns (bool) {
            return _paused;
        }
        /**
         * @dev Modifier to make a function callable only when the contract is not paused.
         *
         * Requirements:
         *
         * - The contract must not be paused.
         */
        modifier whenNotPaused() {
            require(!paused(), "Pausable: paused");
            _;
        }
        /**
         * @dev Modifier to make a function callable only when the contract is paused.
         *
         * Requirements:
         *
         * - The contract must be paused.
         */
        modifier whenPaused() {
            require(paused(), "Pausable: not paused");
            _;
        }
        /**
         * @dev Triggers stopped state.
         *
         * Requirements:
         *
         * - The contract must not be paused.
         */
        function _pause() internal virtual whenNotPaused {
            _paused = true;
            emit Paused(_msgSender());
        }
        /**
         * @dev Returns to normal state.
         *
         * Requirements:
         *
         * - The contract must be paused.
         */
        function _unpause() internal virtual whenPaused {
            _paused = false;
            emit Unpaused(_msgSender());
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (utils/Strings.sol)
    pragma solidity ^0.8.0;
    /**
     * @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);
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.7;
    contract SignatureVerify
    {
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // *                  SIGNATURE VERIFYING
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        function getMessageHash(
            address to,
            uint256[] calldata tokenIndices,
            uint256[] calldata mintAmounts,
            uint256 networkName,
            address contractAddress,
            uint256 nonce) public pure returns (bytes32) {
            return keccak256(abi.encodePacked(
                to, 
                tokenIndices, 
                mintAmounts,
                networkName,
                contractAddress,
                nonce));
        }
        function getEthSignedMessageHash(bytes32 _messageHash) public pure returns (bytes32) {
            return keccak256(abi.encodePacked("\\x19Ethereum Signed Message:\
    32", _messageHash));
        }
        function verify(
        // Address
            address signer,
            address to,
        // Token Request
            uint256[] calldata tokenIndices,
            uint256[] calldata mintAmounts,
            uint256 networkName,
            address contractAddress,
            uint256 nonce,
        // // Epoch Time
        //     uint256 epochTime,
        // Signature to compare to
            bytes memory signature) public pure returns (bool) {
            bytes32 messageHash = getMessageHash(to, 
            tokenIndices, 
            mintAmounts, 
            networkName, 
            contractAddress, 
            nonce);//, epochTime);
            bytes32 ethSignedMessageHash = getEthSignedMessageHash(messageHash);
            return recoverSigner(ethSignedMessageHash, signature) == signer;
        }
        function recoverSigner(
            bytes32 ethSignedMessageHash, 
            bytes memory signature) public pure returns (address) {
            (bytes32 r, bytes32 s, uint8 v) = splitSignature(signature);
            return ecrecover(ethSignedMessageHash, v, r, s);
        }
        function splitSignature(bytes memory sig) public pure returns (
            bytes32 r,
            bytes32 s,
            uint8 v) {
            require(sig.length == 65, "invalid signature length");
            assembly {
                r := mload(add(sig, 32))
                s := mload(add(sig, 64))
                v := byte(0, mload(add(sig, 96)))
            }
        }
    }
    //SPDX-License-Identifier: Unlicense
    pragma solidity ^0.8.0;
    // Openzeppelin Contracts
    import "@openzeppelin/contracts/access/Ownable.sol";
    import "@openzeppelin/contracts/access/AccessControl.sol";
    import "@openzeppelin/contracts/utils/Strings.sol";
    // PlaySide Contracts
    import "./Roles.sol";
    import "./Items.sol";
    import "./Settings.sol";
    import "./ErrorCodes.sol";
    contract LootItems is AccessControl, Roles, Settings
    {
        using Strings for uint256;
        // Data Structure To Hold Each Tokens Information
        struct LootItem
        {
            // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            // *                    SETTINGS
            // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            // The cost of this token in wei
            // This can be updated later by calling updateLootItem
            uint256 WeiCost;
            // The total amount of this token possible
            uint256 TotalSupply;
            // The max mint per user
    \t\t// This could change per item if we store this data here instead of as a collection
            uint256 MaxMintPerUser;
            // The Uri of where the meta data is located
    \t\t// Each item is stored / can be stored 
            string Uri;
            // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            // *                       SALE
            // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    \t\t// Its like this for simplicity, its possible to have this off 
    \t\t// \tchain but for now its stored data here
            // The cost of this token in wei
            uint256 WeiSaleCost;
            // If this item is on sale or not
            bool IsOnSale;
            // Purely exists to track if an item has been added to the mapping or not. 
            //  solidity doesnt have exists functionality for mappings
            bool exists;
        }
        // Keeps track of the token id count, starts at 1 to start the collection at 1
        uint256 private currentTokenId;
        // Data Structure To Hold All Loot Items
        mapping(uint256 => LootItem) lootItemList;
        // Holds the indicies for 
        uint256[] internal dailyStore;
        constructor()
        {
            // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            // *                 ADD INITIAL ITEMS
            // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            // Adding Initial Items Into The Mapping To Keep Track Of Costs And Total Supply
            //            Cost |  Supply Max                     URI   
            addLootItem(0.0 ether, 3183, 1, getTokenURI(Items.FOUNDERS_BASEBALL_BAT)       ,0.0 ether, false);
            addLootItem(0.0 ether, 1025, 1, getTokenURI(Items.FOUNDERS_BASEBALL_BAT_GOLD)  ,0.0 ether, false);
            addLootItem(0.0 ether, 394, 1, getTokenURI(Items.FOUNDERS_BASEBALL_CAP_GOLD)  ,0.0 ether, false);
            addLootItem(0.0 ether, 1222, 1, getTokenURI(Items.DIAMOND_CROWN_GOLD)          ,0.0 ether, false);
            addLootItem(0.0 ether, 3814, 1, getTokenURI(Items.CYBORG_JACKET)               ,0.0 ether, false);
            addLootItem(0.0 ether, 1367, 1, getTokenURI(Items.CYBORG_LED_CAPE)             ,0.0 ether, false);
            addLootItem(0.0 ether, 486, 1, getTokenURI(Items.CYBORG_GLOWING_SWORD)        ,0.0 ether, false);
            addLootItem(0.0 ether, 53, 1, getTokenURI(Items.CYBORG_HELMET)               ,0.0 ether, false);
            addLootItem(0.0 ether, 4516, 1, getTokenURI(Items.TROPICAL_BACKPACK_BLING)     ,0.0 ether, false);
            addLootItem(0.0 ether, 1812, 1, getTokenURI(Items.PINEAPPLE_SUNGLASSES)        ,0.0 ether, false);
            addLootItem(0.0 ether, 664, 1, getTokenURI(Items.BLOW_UP_DONUT_RING)          ,0.0 ether, false);
            addLootItem(0.0 ether, 102, 1, getTokenURI(Items.SUPER_SOAKER_3000)           ,0.0 ether, false);
        }
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // *                 SERVER FUNCTIONALITY
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        /**
            @dev Add a loot item into the mapping to track data for later. 
            For example: Costs in ether and total supply of this item
            WARNING: This will increment the current token ID so only add valid items 
            @param cost: The cost in ether for this item when purchasing through a store 
                ( Cannot be lower than 0 ) !!!! COST IS IN WEI !!!!
            @param totalSupply: The max number of items that can ever be obtained ( Cannot be lower than 0 )
            @param maxMintPerUser: The max one wallet address can mint ( Cannot be less than 0 ) 
            @param newURI: The address that is associated with the meta data for this token
            @param saleCost: The cost of this item when it goes on sale
            @param isOnSale: If this item is currently on sale or not
        */
        function addLootItem(
            uint256 cost, 
            uint256 totalSupply, 
            uint256 maxMintPerUser, 
            string memory newURI,
            uint256 saleCost,
            bool isOnSale ) public onlyRole(Roles.ROLE_SAFE) {
            // Cannot be lower than 0
            if(cost < 0) revert ErrorCodes.LootItem_CostError();
            // Check that the total supply is larger than 0
            if(totalSupply <= 0) revert ErrorCodes.LootItem_SupplyError();
            // Check _MaxMintPerUser was set
            if(maxMintPerUser <= 0) revert ErrorCodes.LootItem_MaxMintError();
            
            // Increment the token ID to track the current token that is being added
            currentTokenId++;
            // Finally return the item at that unique identifier
            lootItemList[currentTokenId] = 
                LootItem(cost, totalSupply, maxMintPerUser, newURI, saleCost, isOnSale, true);
        }
        /**
            @dev Updates an item in the existing array. 
                Will fail if the item doesnt exist in the array already
            For example: Costs in ether and total supply of this item 
            @param newCostinWei: The cost in wei for this item when purchasing through a store 
                ( Cannot be lower than 0 ) !!!! COST IS IN WEI !!!!
            @param newTotalSupply: The max number of items that can ever be obtained ( Cannot be lower than 0 )
            @param newMaxMintPerUser: The max one wallet address can mint ( Cannot be less than 0 ) 
            @param newURI: The address that is associated with the meta data for this token
            @param newSaleCostinWei: The cost of this item when it goes on sale
            @param isOnSale: If this item is currently on sale or not
        */
        function updateLootItem(uint256 tokenIndex, 
        uint256 newCostinWei,
        uint256 newTotalSupply,
        uint256 newMaxMintPerUser,
        string memory newURI, 
        uint256 newSaleCostinWei,
        bool isOnSale) public onlyRole(Roles.ROLE_SAFE) {
            lootItemList[tokenIndex] = LootItem(
                newCostinWei, 
                newTotalSupply, 
                newMaxMintPerUser, 
                newURI, 
                newSaleCostinWei, 
                isOnSale, 
                true);
        }
        /**
            @dev Removes a loot item from the mapping. 
            @param deleteIndex: The unique ID for the loot item that you want to delete. ( has to be in the array )
         */
        function removeLootItem(uint256 deleteIndex) public onlyRole(Roles.ROLE_SAFE) {
            delete lootItemList[deleteIndex];
        }
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // *                    PUBLIC GETTERS
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        /**
            @dev Returns a loot item from the array
            @param id: A unique identifier of the item in this mapping, Should start at 1 -> 
            new items IDs should pick up off where the end of the array is. 
            ( Must be in the array to return a valid data set ) 
        */
        function getLootItem(uint256 id) public view returns (LootItem memory) {
            return lootItemList[id];
        }
        function getTokenURI(uint256 tokenIndex) internal view returns (string memory) {
            return string(abi.encodePacked(Settings.baseURI, tokenIndex.toString(), ".json"));
        }
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // *                    DAILY STORE
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        function setDailyStore(uint256[] calldata newDailyStore) public onlyRole(Roles.ROLE_SERVER) {
            dailyStore = newDailyStore;
        }
    \t/// @dev 
        function getDailyStore() public view returns (uint256[] memory) {
            return dailyStore;
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.7;
    // Openzeppelin Contracts
    import "@openzeppelin/contracts/access/AccessControl.sol";
    contract Roles is AccessControl {
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // *                                ROLES
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        /// Description: This role is for the Gnosis safe to ensure actions taken are approved by more than 1 person
        /// Permissions:
        /// - setURI
        /// - setPause
        /// - unpause
        /// - addLootItem
        /// - updateLootItem
        /// - removeLootItem
        /// - setSignerAddress
        /// - setDailyStoreActive
        bytes32 internal constant ROLE_SAFE = keccak256("ROLE_SAFE");
        /// Description: The role of the server to update daily store and setting the daily store active or not
        /// Permissions:
        /// - setDailyStore
        /// - setDailyStoreActive
        bytes32 internal constant ROLE_SERVER = keccak256("ROLE_SERVER");
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        constructor() {
            // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            // *                    SET ROLES
            // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            // Give every role to the owner of the contract
            _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
            _setRoleAdmin(ROLE_SAFE, DEFAULT_ADMIN_ROLE);
            _setRoleAdmin(ROLE_SERVER, DEFAULT_ADMIN_ROLE);
            _grantRole(Roles.ROLE_SAFE, msg.sender);
            _grantRole(Roles.ROLE_SERVER, msg.sender);
            // Aditional Roles also set in Settings.sol constructor
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.7;
    library Items 
    {
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // *                    INITIAL ITEMS
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        uint256 constant public FOUNDERS_BASEBALL_BAT       = 1;
        uint256 constant public FOUNDERS_BASEBALL_BAT_GOLD  = 2;
        uint256 constant public FOUNDERS_BASEBALL_CAP_GOLD  = 3;
        uint256 constant public DIAMOND_CROWN_GOLD          = 4;
        uint256 constant public CYBORG_JACKET               = 5;
        uint256 constant public CYBORG_LED_CAPE             = 6;
        uint256 constant public CYBORG_GLOWING_SWORD        = 7;
        uint256 constant public CYBORG_HELMET               = 8;
        uint256 constant public TROPICAL_BACKPACK_BLING     = 9;
        uint256 constant public PINEAPPLE_SUNGLASSES        = 10;
        uint256 constant public BLOW_UP_DONUT_RING          = 11;
        uint256 constant public SUPER_SOAKER_3000           = 12;
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.7;
    // Openzeppelin Contracts
    import "@openzeppelin/contracts/access/AccessControl.sol";
    // PlaySide Contracts
    import "./Roles.sol";
    contract Settings is AccessControl, Roles
    {
        // The base URI for each token
        string public baseURI       \t= "https://dwtd.playsidestudios-devel.com/loot/founders/metadata/";
        // The signing address on server side. This address signs the data on the server when users are trying to mintClaim
        address public signerAddress    = 0x6aE227412369c26Ab99cD457c393234f1b5a1a13;
        // If the daily store is active
        bool public dailyStoreActive    = false;
    \t// This is a hard coded reference to the current network that this contract is being deployed to
    \t// \t!!! ENSURE YOU CHANGE THIS WHEN DEPLOYING A NEW CONTRACT !!!
    \tuint256 public blockchain = 1;
    \t// If the claim function is active or not, this is a safeguard to pause this functionality
    \t// \tPrefer pausing the contract instead though
    \tbool public claimActive = true;
        constructor()
        {
            // Set Signer / Server Permissions
            _grantRole(Roles.ROLE_SERVER, signerAddress);
        }
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // *                        SERVER
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        /// @dev Sets the daily store active, this will allow the mintDailyStore function to be called.
        /// @param active: The next status of the dailyStoreActive bool. 
        /// True: The mintDailyStore will become active and users can mint whatever they want from the daily store array.
        /// False: This will lock the daily store function stopping users from using the mint function.
        function setDailyStoreActive(bool active) public onlyRole(Roles.ROLE_SERVER) {
            dailyStoreActive = active;
        }
    \t/// @dev Sets the claim mint function active or not
    \t/// @param active: The next status of the claim active mint function
    \t// \t\tUsed to pause or unpause the mint claim function
    \tfunction setClaimActive(bool active) public onlyRole(Roles.ROLE_SERVER) {
    \t\tclaimActive = active;
    \t}
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // *                        SAFE
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        /// @dev Sets the current signer address, this is the address that signs on the server side to validate signatures
        /// @param newSignerAddress: The address that signs the data that is passed from the dapp to the contract.
        function setSignerAddress(address newSignerAddress) public onlyRole(Roles.ROLE_SAFE) {
            signerAddress = newSignerAddress;
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.7;
    library ErrorCodes {
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // *                    ERROR MESSAGES
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // Mint Errors
        error SoldOut();
        error SignatureVerify();
        error ContractPaused();
        error WhitelistActive();
        error RequestedIncorrectAmount();
        error PublicSaleInactive();
        error NotInDailyStore();
        error InsufficientFunds(uint256 Expected, uint256 Actual);
        error MaxAmountMinted(uint256 Expected, uint256 Actual);
        error NetworkMissmatch();
        error ClaimMintDisabled();
        error IncorrectContractSignature();
        error DailyStoreInactive();
        // General Errors
        error ArrayMissmatch();
        // Adding Loot Item Errors
        error LootItem_CostError();
        error LootItem_SupplyError();
        error LootItem_MaxMintError();
        error LootItem_ItemDoesntExist();
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (token/ERC1155/IERC1155.sol)
    pragma solidity ^0.8.0;
    import "../../utils/introspection/IERC165.sol";
    /**
     * @dev Required interface of an ERC1155 compliant contract, as defined in the
     * https://eips.ethereum.org/EIPS/eip-1155[EIP].
     *
     * _Available since v3.1._
     */
    interface IERC1155 is IERC165 {
        /**
         * @dev Emitted when `value` tokens of token type `id` are transferred from `from` to `to` by `operator`.
         */
        event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);
        /**
         * @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all
         * transfers.
         */
        event TransferBatch(
            address indexed operator,
            address indexed from,
            address indexed to,
            uint256[] ids,
            uint256[] values
        );
        /**
         * @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to
         * `approved`.
         */
        event ApprovalForAll(address indexed account, address indexed operator, bool approved);
        /**
         * @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI.
         *
         * If an {URI} event was emitted for `id`, the standard
         * https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value
         * returned by {IERC1155MetadataURI-uri}.
         */
        event URI(string value, uint256 indexed id);
        /**
         * @dev Returns the amount of tokens of token type `id` owned by `account`.
         *
         * Requirements:
         *
         * - `account` cannot be the zero address.
         */
        function balanceOf(address account, uint256 id) external view returns (uint256);
        /**
         * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}.
         *
         * Requirements:
         *
         * - `accounts` and `ids` must have the same length.
         */
        function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids)
            external
            view
            returns (uint256[] memory);
        /**
         * @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`,
         *
         * Emits an {ApprovalForAll} event.
         *
         * Requirements:
         *
         * - `operator` cannot be the caller.
         */
        function setApprovalForAll(address operator, bool approved) external;
        /**
         * @dev Returns true if `operator` is approved to transfer ``account``'s tokens.
         *
         * See {setApprovalForAll}.
         */
        function isApprovedForAll(address account, address operator) external view returns (bool);
        /**
         * @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
         *
         * Emits a {TransferSingle} event.
         *
         * Requirements:
         *
         * - `to` cannot be the zero address.
         * - If the caller is not `from`, it must be have been approved to spend ``from``'s tokens via {setApprovalForAll}.
         * - `from` must have a balance of tokens of type `id` of at least `amount`.
         * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
         * acceptance magic value.
         */
        function safeTransferFrom(
            address from,
            address to,
            uint256 id,
            uint256 amount,
            bytes calldata data
        ) external;
        /**
         * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}.
         *
         * Emits a {TransferBatch} event.
         *
         * Requirements:
         *
         * - `ids` and `amounts` must have the same length.
         * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
         * acceptance magic value.
         */
        function safeBatchTransferFrom(
            address from,
            address to,
            uint256[] calldata ids,
            uint256[] calldata amounts,
            bytes calldata data
        ) external;
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC1155/IERC1155Receiver.sol)
    pragma solidity ^0.8.0;
    import "../../utils/introspection/IERC165.sol";
    /**
     * @dev _Available since v3.1._
     */
    interface IERC1155Receiver is IERC165 {
        /**
         * @dev Handles the receipt of a single ERC1155 token type. This function is
         * called at the end of a `safeTransferFrom` after the balance has been updated.
         *
         * NOTE: To accept the transfer, this must return
         * `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
         * (i.e. 0xf23a6e61, or its own function selector).
         *
         * @param operator The address which initiated the transfer (i.e. msg.sender)
         * @param from The address which previously owned the token
         * @param id The ID of the token being transferred
         * @param value The amount of tokens being transferred
         * @param data Additional data with no specified format
         * @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
         */
        function onERC1155Received(
            address operator,
            address from,
            uint256 id,
            uint256 value,
            bytes calldata data
        ) external returns (bytes4);
        /**
         * @dev Handles the receipt of a multiple ERC1155 token types. This function
         * is called at the end of a `safeBatchTransferFrom` after the balances have
         * been updated.
         *
         * NOTE: To accept the transfer(s), this must return
         * `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
         * (i.e. 0xbc197c81, or its own function selector).
         *
         * @param operator The address which initiated the batch transfer (i.e. msg.sender)
         * @param from The address which previously owned the token
         * @param ids An array containing ids of each token being transferred (order and length must match values array)
         * @param values An array containing amounts of each token being transferred (order and length must match ids array)
         * @param data Additional data with no specified format
         * @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
         */
        function onERC1155BatchReceived(
            address operator,
            address from,
            uint256[] calldata ids,
            uint256[] calldata values,
            bytes calldata data
        ) external returns (bytes4);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (token/ERC1155/extensions/IERC1155MetadataURI.sol)
    pragma solidity ^0.8.0;
    import "../IERC1155.sol";
    /**
     * @dev Interface of the optional ERC1155MetadataExtension interface, as defined
     * in the https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[EIP].
     *
     * _Available since v3.1._
     */
    interface IERC1155MetadataURI is IERC1155 {
        /**
         * @dev Returns the URI for token type `id`.
         *
         * If the `\\{id\\}` substring is present in the URI, it must be replaced by
         * clients with the actual token type ID.
         */
        function uri(uint256 id) external view returns (string memory);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)
    pragma solidity ^0.8.1;
    /**
     * @dev Collection of functions related to the address type
     */
    library Address {
        /**
         * @dev Returns true if `account` is a contract.
         *
         * [IMPORTANT]
         * ====
         * It is unsafe to assume that an address for which this function returns
         * false is an externally-owned account (EOA) and not a contract.
         *
         * Among others, `isContract` will return false for the following
         * types of addresses:
         *
         *  - an externally-owned account
         *  - a contract in construction
         *  - an address where a contract will be created
         *  - an address where a contract lived, but was destroyed
         * ====
         *
         * [IMPORTANT]
         * ====
         * You shouldn't rely on `isContract` to protect against flash loan attacks!
         *
         * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
         * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
         * constructor.
         * ====
         */
        function isContract(address account) internal view returns (bool) {
            // This method relies on extcodesize/address.code.length, which returns 0
            // for contracts in construction, since the code is only stored at the end
            // of the constructor execution.
            return account.code.length > 0;
        }
        /**
         * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
         * `recipient`, forwarding all available gas and reverting on errors.
         *
         * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
         * of certain opcodes, possibly making contracts go over the 2300 gas limit
         * imposed by `transfer`, making them unable to receive funds via
         * `transfer`. {sendValue} removes this limitation.
         *
         * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
         *
         * IMPORTANT: because control is transferred to `recipient`, care must be
         * taken to not create reentrancy vulnerabilities. Consider using
         * {ReentrancyGuard} or the
         * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
         */
        function sendValue(address payable recipient, uint256 amount) internal {
            require(address(this).balance >= amount, "Address: insufficient balance");
            (bool success, ) = recipient.call{value: amount}("");
            require(success, "Address: unable to send value, recipient may have reverted");
        }
        /**
         * @dev Performs a Solidity function call using a low level `call`. A
         * plain `call` is an unsafe replacement for a function call: use this
         * function instead.
         *
         * If `target` reverts with a revert reason, it is bubbled up by this
         * function (like regular Solidity function calls).
         *
         * Returns the raw returned data. To convert to the expected return value,
         * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
         *
         * Requirements:
         *
         * - `target` must be a contract.
         * - calling `target` with `data` must not revert.
         *
         * _Available since v3.1._
         */
        function functionCall(address target, bytes memory data) internal returns (bytes memory) {
            return functionCall(target, data, "Address: low-level call failed");
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
         * `errorMessage` as a fallback revert reason when `target` reverts.
         *
         * _Available since v3.1._
         */
        function functionCall(
            address target,
            bytes memory data,
            string memory errorMessage
        ) internal returns (bytes memory) {
            return functionCallWithValue(target, data, 0, errorMessage);
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but also transferring `value` wei to `target`.
         *
         * Requirements:
         *
         * - the calling contract must have an ETH balance of at least `value`.
         * - the called Solidity function must be `payable`.
         *
         * _Available since v3.1._
         */
        function functionCallWithValue(
            address target,
            bytes memory data,
            uint256 value
        ) internal returns (bytes memory) {
            return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
        }
        /**
         * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
         * with `errorMessage` as a fallback revert reason when `target` reverts.
         *
         * _Available since v3.1._
         */
        function functionCallWithValue(
            address target,
            bytes memory data,
            uint256 value,
            string memory errorMessage
        ) internal returns (bytes memory) {
            require(address(this).balance >= value, "Address: insufficient balance for call");
            require(isContract(target), "Address: call to non-contract");
            (bool success, bytes memory returndata) = target.call{value: value}(data);
            return verifyCallResult(success, returndata, errorMessage);
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but performing a static call.
         *
         * _Available since v3.3._
         */
        function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
            return functionStaticCall(target, data, "Address: low-level static call failed");
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
         * but performing a static call.
         *
         * _Available since v3.3._
         */
        function functionStaticCall(
            address target,
            bytes memory data,
            string memory errorMessage
        ) internal view returns (bytes memory) {
            require(isContract(target), "Address: static call to non-contract");
            (bool success, bytes memory returndata) = target.staticcall(data);
            return verifyCallResult(success, returndata, errorMessage);
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but performing a delegate call.
         *
         * _Available since v3.4._
         */
        function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
            return functionDelegateCall(target, data, "Address: low-level delegate call failed");
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
         * but performing a delegate call.
         *
         * _Available since v3.4._
         */
        function functionDelegateCall(
            address target,
            bytes memory data,
            string memory errorMessage
        ) internal returns (bytes memory) {
            require(isContract(target), "Address: delegate call to non-contract");
            (bool success, bytes memory returndata) = target.delegatecall(data);
            return verifyCallResult(success, returndata, errorMessage);
        }
        /**
         * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
         * revert reason 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 {
                // Look for revert reason and bubble it up if present
                if (returndata.length > 0) {
                    // The easiest way to bubble the revert reason is using memory via assembly
                    assembly {
                        let returndata_size := mload(returndata)
                        revert(add(32, returndata), returndata_size)
                    }
                } else {
                    revert(errorMessage);
                }
            }
        }
    }
    // 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/introspection/ERC165.sol)
    pragma solidity ^0.8.0;
    import "./IERC165.sol";
    /**
     * @dev Implementation of the {IERC165} interface.
     *
     * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
     * for the additional interface id that will be supported. For example:
     *
     * ```solidity
     * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
     *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
     * }
     * ```
     *
     * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
     */
    abstract contract ERC165 is IERC165 {
        /**
         * @dev See {IERC165-supportsInterface}.
         */
        function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
            return interfaceId == type(IERC165).interfaceId;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev Interface of the ERC165 standard, as defined in the
     * https://eips.ethereum.org/EIPS/eip-165[EIP].
     *
     * Implementers can declare support of contract interfaces, which can then be
     * queried by others ({ERC165Checker}).
     *
     * For an implementation, see {ERC165}.
     */
    interface IERC165 {
        /**
         * @dev Returns true if this contract implements the interface defined by
         * `interfaceId`. See the corresponding
         * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
         * to learn more about how these ids are created.
         *
         * This function call must use less than 30 000 gas.
         */
        function supportsInterface(bytes4 interfaceId) external view returns (bool);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev External interface of AccessControl declared to support ERC165 detection.
     */
    interface IAccessControl {
        /**
         * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
         *
         * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
         * {RoleAdminChanged} not being emitted signaling this.
         *
         * _Available since v3.1._
         */
        event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
        /**
         * @dev Emitted when `account` is granted `role`.
         *
         * `sender` is the account that originated the contract call, an admin role
         * bearer except when using {AccessControl-_setupRole}.
         */
        event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
        /**
         * @dev Emitted when `account` is revoked `role`.
         *
         * `sender` is the account that originated the contract call:
         *   - if using `revokeRole`, it is the admin role bearer
         *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
         */
        event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
        /**
         * @dev Returns `true` if `account` has been granted `role`.
         */
        function hasRole(bytes32 role, address account) external view returns (bool);
        /**
         * @dev Returns the admin role that controls `role`. See {grantRole} and
         * {revokeRole}.
         *
         * To change a role's admin, use {AccessControl-_setRoleAdmin}.
         */
        function getRoleAdmin(bytes32 role) external view returns (bytes32);
        /**
         * @dev Grants `role` to `account`.
         *
         * If `account` had not been already granted `role`, emits a {RoleGranted}
         * event.
         *
         * Requirements:
         *
         * - the caller must have ``role``'s admin role.
         */
        function grantRole(bytes32 role, address account) external;
        /**
         * @dev Revokes `role` from `account`.
         *
         * If `account` had been granted `role`, emits a {RoleRevoked} event.
         *
         * Requirements:
         *
         * - the caller must have ``role``'s admin role.
         */
        function revokeRole(bytes32 role, address account) external;
        /**
         * @dev Revokes `role` from the calling account.
         *
         * Roles are often managed via {grantRole} and {revokeRole}: this function's
         * purpose is to provide a mechanism for accounts to lose their privileges
         * if they are compromised (such as when a trusted device is misplaced).
         *
         * If the calling account had been granted `role`, emits a {RoleRevoked}
         * event.
         *
         * Requirements:
         *
         * - the caller must be `account`.
         */
        function renounceRole(bytes32 role, address account) external;
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)
    pragma solidity ^0.8.0;
    import "../utils/Context.sol";
    /**
     * @dev Contract module which provides a basic access control mechanism, where
     * there is an account (an owner) that can be granted exclusive access to
     * specific functions.
     *
     * By default, the owner account will be the one that deploys the contract. This
     * can later be changed with {transferOwnership}.
     *
     * This module is used through inheritance. It will make available the modifier
     * `onlyOwner`, which can be applied to your functions to restrict their use to
     * the owner.
     */
    abstract contract Ownable is Context {
        address private _owner;
        event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
        /**
         * @dev Initializes the contract setting the deployer as the initial owner.
         */
        constructor() {
            _transferOwnership(_msgSender());
        }
        /**
         * @dev Returns the address of the current owner.
         */
        function owner() public view virtual returns (address) {
            return _owner;
        }
        /**
         * @dev Throws if called by any account other than the owner.
         */
        modifier onlyOwner() {
            require(owner() == _msgSender(), "Ownable: caller is not the owner");
            _;
        }
        /**
         * @dev Leaves the contract without owner. It will not be possible to call
         * `onlyOwner` functions anymore. Can only be called by the current owner.
         *
         * NOTE: Renouncing ownership will leave the contract without an owner,
         * thereby removing any functionality that is only available to the owner.
         */
        function renounceOwnership() public virtual onlyOwner {
            _transferOwnership(address(0));
        }
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`).
         * Can only be called by the current owner.
         */
        function transferOwnership(address newOwner) public virtual onlyOwner {
            require(newOwner != address(0), "Ownable: new owner is the zero address");
            _transferOwnership(newOwner);
        }
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`).
         * Internal function without access restriction.
         */
        function _transferOwnership(address newOwner) internal virtual {
            address oldOwner = _owner;
            _owner = newOwner;
            emit OwnershipTransferred(oldOwner, newOwner);
        }
    }