ETH Price: $1,815.16 (-4.49%)

Transaction Decoder

Block:
14623134 at Apr-20-2022 05:03:45 PM +UTC
Transaction Fee:
0.070020586 ETH $127.10
Gas Used:
216,782 Gas / 323 Gwei

Account State Difference:

  Address   Before After State Difference Code
(Poolin 2)
3,251.075398890217486728 Eth3,251.080978462021022062 Eth0.005579571803535334
0x9b8b9c7C...03424c6Dc
0xDD5A649f...eb882e83c
(The Association NFT: Sales)
0xF6672624...bDeFd56e7
0.139437443345522208 Eth
Nonce: 7
0.069416857345522208 Eth
Nonce: 8
0.070020586

Execution Trace

The_Association_Sales.mint_approved( info=[{name:mint_free, type:bool, order:1, indexed:false, value:true, valueString:True}, {name:max_mint, type:uint256, order:2, indexed:false, value:1, valueString:1}, {name:from, type:address, order:3, indexed:false, value:0x4C5278613B28eF974C6C1762FD91b295dA6F569a, valueString:0x4C5278613B28eF974C6C1762FD91b295dA6F569a}, {name:start, type:uint256, order:4, indexed:false, value:0, valueString:0}, {name:end, type:uint256, order:5, indexed:false, value:5555555555, valueString:5555555555}, {name:eth_price, type:uint256, order:6, indexed:false, value:0, valueString:0}, {name:dust_price, type:uint256, order:7, indexed:false, value:0, valueString:0}, {name:signature, type:bytes, order:8, indexed:false, value:0xA700DAE0848F6BB193CA324D63487A97343A55074B1B7735F9AA5266CA995FD659D8C0682E81DAEFCEDCB58612B4021490207AF79BA7EC7BAA576FC73D9AA3231B, valueString:0xA700DAE0848F6BB193CA324D63487A97343A55074B1B7735F9AA5266CA995FD659D8C0682E81DAEFCEDCB58612B4021490207AF79BA7EC7BAA576FC73D9AA3231B}], number_of_items_requested=1, _batchNumber=0 )
  • Null: 0x000...001.512fd49f( )
  • The_Association_Token.mintCards( numberOfCards=1, recipient=0xF667262496AbD3A1F55BC17F330eF7AbDeFd56e7 )
    File 1 of 2: The_Association_Sales
    pragma solidity ^0.8.7;
    // SPDX-Licence-Identifier: GPL-3.0-or-later
    /*
    kkkkkkkOK0kkOXN0kkOXKkkkkkk0WMMM0kXMMMW0kkkkOXMMMNOdoodkXMWWKxoood0WMMMNOkkkk0NMMMW0doookXMWKkkkKMMN0kkkkONXkkkkkkkOK0kkOXMW0xoooxKWMXkkkOXMKkkONXkxkk
    ........l:...x0;..,Oo......:XMWX:,0MWMK;.....xMM0:.    .,ONx'.    .lXNd,......;kWXl.    .,kNl...oWM0,....'ko........c:...kXl.    .'xNd....dWo..'OKdl;.
    ;.   .':o,  .x0'  .kl   .,,oN0c'.'kNMMk.     cWNc   ',   :x'  .:.  .xd.  .,'.  '0d   ';.  ,0l   lWMd.     lx;.    ':o,  .xd.  .:.  'Oo    ;Xo  .kW0ocd
    Wx.  '0WK,  .x0'  .kl   cNWWK;   .,kMWo   .  ,KX;   dO;..cl.  ;Kd..'dc   lKk;  .xc   oK,  .kl   lWWc  ..  :XMx.  ,0WK,  .xl   lX;  .xo    .xo  .kk..lx
    Mk.  '0MK;   ok.  .kl   :KKXO'  .dllNX;  ',  .kX;   c00KXNx.  .x0KXNXc   oXO;  .dc   oNOxxkKc   lWK,  ,,  '0Mk.  ,KMK,  .xl   lX:  .dl     cc  .kO:;;;
    Mk.  '0MK,   ..   .kl    ..l0:   ,;.oO'  :l   dWo    ..,lK0,    .':kXc   oXO;  .d:   oMMMMMWc   lWO.  cc  .xMk.  ,KMK,  .xl   lX:  .dl   . .,  .kKo;,,
    Mk.  '0MK,   ..   .kl   .''lX0,  .;:xd.  ox.  cNNxc,'.   :X0o;,.   .xc   oXO;  .d:   oMMMMMNc   lWd   dd   lWk.  ,KMK,  .xl   lX:  .dl  .;.    .kMWdc:
    Mk.  '0MK,  .oO'  .kl   cXNWWMXo. lNWl   .'   ,0NXXXXd.  'ONXXXK;   c:   lXO;  .dc   oNkooxKc   lNc   ..   ;Xk.  ,KMK,  .xl   lX:  .dl  .o,    .kMWOxo
    Mk.  '0MK,  .x0'  .kl   cXNWMMMWKl;kX;        .xo..'kk.  ,o;..lKc   lc   oKk;  .xc   o0,  .kl   l0,        .Ok.  ,KMK,  .xl   lK;  .xo  .kl    .OWNNXc
    Mk.  '0MK;  .x0'  .kl   ..'lNMMMMWdxO.  .dk,   ol   .'   :k'  .,.  .xo   ':,.  'Od   .,.  ,0l   lk.  'xx'   dk.  ,KMK,  .xx.  .,.  'Oo  .kO.   .kKdol.
    MO,..:KMXc..'kK:..;Od......cXMMMMM0kx,..cXWo...o0o'.....:0Wk;.....'dNKc.      'xNXo'.....;ONo...od'..lNNl...dO,..cXMXc..,kNd'.....,kWd..,ONl...;0WWWNl
    MNK00KWMWX00KNWK00KWN000000XWMMMMMWWNKK0XWMN000XWWXOxkkKWMMMN0kxkOXWMMN0dllooxKWWMMXOkkk0NMWX000XX000XMMX000XNK00XWMWX00KNMMXOkkk0NMMNK0KNWX000KNMMMWX
    */
    /// @author: galaxis.xyz - The platform for decentralized communities.
    import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
    import "@openzeppelin/contracts/utils/Strings.sol";
    import "@openzeppelin/contracts/utils/math/SafeMath.sol";
    import "@openzeppelin/contracts/access/Ownable.sol";
    import "@openzeppelin/contracts/utils/introspection/IERC1820Registry.sol";
    import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
    import "@openzeppelin/contracts/token/ERC777/IERC777Recipient.sol";
    import "@openzeppelin/contracts/token/ERC777/IERC777.sol";
    import "./ssp/community_interface.sol";
    import "./ssp/sale_configuration.sol";
    import "./ssp/recovery.sol";
    import "./ssp/IRNG.sol";
    import "./ssp/dusty.sol";
    import "./ssp/card_with_card.sol";
    import "./ssp/token_interface.sol";
    struct sale_data {
        uint256 maxTokens;
        uint256 mintPosition;
        address[] wallets;
        uint16[] shares;
        uint256 fullPrice;
        uint256 discountPrice;
        uint256 presaleStart;
        uint256 presaleEnd;
        uint256 saleStart;
        uint256 saleEnd;
        uint256 dustPrice;
        bool areTokensLocked;
        uint256 maxFreeEC;
        uint256 totalFreeEC;
        uint256 maxDiscount;
        uint256 totalDiscount;
        uint256 freePerAddress;
        uint256 discountedPerAddress;
        string tokenPreRevealURI;
        address signer;
        bool presaleIsActive;
        bool saleIsActive;
        bool dustMintingActive;
        uint256 freeClaimedByThisUser;
        uint256 discountedClaimedByThisUser;
        address etherCards;
        address DUST;
        address ecVault;
        uint256 maxPerSaleMint;
        uint256 MaxUserMintable;
        uint256 userMinted;
        bool randomReceived;
        bool secondReceived;
        uint256 randomCL;
        uint256 randomCL2;
        uint256 ts1;
        uint256 ts;
    }
    struct sale_params {
        uint256 projectID;
        token_interface token;
        IERC721 ec;
        address dust;
        uint256 maxTokens;
        uint256 maxDiscount; //<--- max sold in presale across presale dust / eth
        uint256 maxPerSaleMint;
        uint256 clientMintLimit;
        uint256 ecMintLimit;
        uint256 discountedPerAddress; //<-- should apply to all presale
        uint256 freeForEC; //<-- for EC card holders
        uint256 discountPrice; //<-- for EC card holders - if zero not available should have *** dust ***
        uint256 discountDustPrice; //<-- for EC card holders - if zero not available should have *** dust ***
        uint256 fullPrice;
        address signer;
        uint256 saleStart;
        uint256 saleEnd;
        uint256 presaleStart;
        uint256 presaleEnd;
        uint256 fullDustPrice;
        address[] wallets;
        uint16[] shares;
    }
    // check approval limit - start / end of presale
    contract The_Association_Sales is
        sale_configuration,
        Ownable,
        recovery,
        ReentrancyGuard,
        dusty,
        card_with_card
    {
        using SafeMath for uint256;
        using Strings for uint256;
        uint256 public immutable projectID;
        token_interface public _token;
        uint256 immutable _MaxUserMintable;
        uint256 _userMinted;
        uint256 _ts1;
        uint256 _ts2;
        address private immutable _DUST;
        IERC721 private immutable _EC;
        uint16 public batchNumber;
        mapping(address => uint256) _freeClaimedPerWallet;
        mapping(address => uint256) _discountedClaimedPerWallet;
        event RandomProcessed(
            uint256 stage,
            uint256 randUsed_,
            uint256 _start,
            uint256 _stop,
            uint256 _supply
        );
        event batchWhitelistMint(uint16 indexed batchNumber, address receiver);
        event ETHPresale(address from, uint256 number_of_items, uint256 price);
        event ETHSale(address buyer, uint256 number_to_buy, uint256 ethAmount);
        event Allowed(address, bool);
        modifier onlyAllowed() {
            require(
                _token.permitted(msg.sender) || (msg.sender == owner()),
                "Unauthorised"
            );
            _;
        }
        // the constructor takes the bare minimum to conduct a presale and sale.
        constructor(sale_params memory sp)
            dusty(
                sp.dust,
                sp.signer,
                sp.fullDustPrice,
                sp.discountDustPrice,
                sp.maxPerSaleMint,
                sp.wallets,
                sp.shares
            )
            card_with_card(sp.signer)
        {
            projectID = sp.projectID;
            _EC = sp.ec;
            _token = sp.token;
            _DUST = sp.dust;
            _MaxUserMintable = sp.maxTokens - (sp.clientMintLimit + sp.ecMintLimit);
            _maxSupply = sp.maxTokens;
            _maxDiscount = sp.maxDiscount;
            _discountedPerAddress = sp.discountedPerAddress;
            _discountPrice = sp.discountPrice;
            _fullPrice = sp.fullPrice;
            _saleStart = sp.saleStart;
            _saleEnd = sp.saleEnd;
            _presaleStart = sp.presaleStart;
            _presaleEnd = sp.presaleEnd;
            _maxFreeEC = sp.freeForEC;
        }
        function _split(uint256 amount) internal {
            //  console.log("num wallets",wallets.length);
            bool sent;
            uint256 _total;
            for (uint256 j = 0; j < wallets.length; j++) {
                uint256 _amount = (amount * shares[j]) / 1000;
                if (j == wallets.length - 1) {
                    _amount = amount - _total;
                } else {
                    _total += _amount;
                }
                (sent, ) = wallets[j].call{value: _amount}(""); // don't use send or xfer (gas)
                require(sent, "Failed to send Ether");
            }
        }
        function setup(uint16 _batchNumber) external onlyAllowed {
            batchNumber = _batchNumber;
        }
        function checkDiscountAvailable(address buyer)
            public
            view
            returns (
                bool[3] memory,
                bool,
                uint256,
                uint256,
                uint256
            )
        {
            bool _final = false;
            return (
                [false, false, true],
                _final, // _final,
                _discountedClaimedPerWallet[buyer], // EC
                presold[buyer], // Not in use.
                _token.availableToMint()
            );
        }
        function mint_approved(
            vData memory info,
            uint256 number_of_items_requested,
            uint16 _batchNumber
        ) external {
            require(batchNumber == _batchNumber, "!batch");
            address from = msg.sender;
            require(verify(info), "Unauthorised access secret");
            _discountedClaimedPerWallet[msg.sender] += 1;
            require(
                _discountedClaimedPerWallet[msg.sender] <= 1,
                "Number exceeds max discounted per address"
            );
            presold[from] = 1;
            _mintCards(number_of_items_requested, from);
            emit batchWhitelistMint(_batchNumber, msg.sender);
        }
        // make sure this respects ec_limit and client_limit
        function mint(uint256 numberOfCards) external {
            _discountedClaimedPerWallet[msg.sender] += numberOfCards;
            require(
                _discountedClaimedPerWallet[msg.sender] <= maxPerSaleMint,
                "Number exceeds max discounted per address"
            );
            require(checkSaleIsActive(), "sale is not open");
            require(
                numberOfCards <= maxPerSaleMint,
                "Exceeds max per Transaction Mint"
            );
            _mintPayable(numberOfCards, msg.sender, _fullPrice);
        }
        function _mintPayable(
            uint256 numberOfCards,
            address recipient,
            uint256 price
        ) internal override {
            _mintCards(numberOfCards, recipient);
        }
        function _mintCards(uint256 numberOfCards, address recipient)
            internal
            override(dusty, card_with_card)
        {
            _userMinted += numberOfCards;
            require(
                _userMinted <= _MaxUserMintable,
                "This exceeds maximum number of user mintable cards"
            );
            _token.mintCards(numberOfCards, recipient);
        }
        function _mintDiscountCards(uint256 numberOfCards, address recipient)
            internal
            override(dusty, card_with_card)
        {
            _totalDiscount += numberOfCards;
            require(
                _maxDiscount >= _totalDiscount,
                "Too many discount tokens claimed"
            );
            _mintCards(numberOfCards, recipient);
        }
        function _mintDiscountPayable(
            uint256 numberOfCards,
            address recipient,
            uint256 price
        ) internal override(card_with_card) {
            require(msg.value == numberOfCards.mul(price), "wrong amount sent");
            _mintDiscountCards(numberOfCards, recipient);
            // _split(msg.value);
        }
        function setSaleDates(uint256 _start, uint256 _end) external onlyAllowed {
            _saleStart = _start;
            _saleEnd = _end;
        }
        function setPresaleDates(uint256 _start, uint256 _end)
            external
            onlyAllowed
        {
            _presaleStart = _start;
            _presaleEnd = _end;
        }
        function setMaxPerSaleMint(uint256 _maxPerSaleMint) external onlyOwner {
            maxPerSaleMint = _maxPerSaleMint;
        }
        function checkSaleIsActive() public view override returns (bool) {
            if ((_saleStart <= block.timestamp) && (_saleEnd >= block.timestamp))
                return true;
            return false;
        }
        function checkPresaleIsActive() public view override returns (bool) {
            if (
                (_presaleStart <= block.timestamp) &&
                (_presaleEnd >= block.timestamp)
            ) return true;
            return false;
        }
        function setWallets(
            address payable[] memory _wallets,
            uint16[] memory _shares
        ) public onlyOwner {
            require(_wallets.length == _shares.length, "!l");
            wallets = _wallets;
            shares = _shares;
        }
        receive() external payable {
            _split(msg.value);
        }
        function tellEverything(address addr)
            external
            view
            returns (sale_data memory)
        {
            // if community module active - get the community.taken[msg.sender]
            token_interface.TKS memory tokenData = _token.tellEverything();
            return
                sale_data(
                    _maxSupply,
                    tokenData._mintPosition,
                    wallets,
                    shares,
                    _fullPrice,
                    _discountPrice,
                    _presaleStart,
                    _presaleEnd,
                    _saleStart,
                    _saleEnd,
                    _dustPrice,
                    tokenData._lockTillSaleEnd,
                    _maxFreeEC,
                    _totalFreeEC,
                    _maxDiscount,
                    _totalDiscount,
                    _freePerAddress,
                    _discountedPerAddress,
                    _token.tokenPreRevealURI(),
                    _signer,
                    checkPresaleIsActive(),
                    checkSaleIsActive(),
                    checkSaleIsActive() &&
                        (fullDustPrice > 0 || discountDustPrice > 0),
                    _freeClaimedPerWallet[addr],
                    _discountedClaimedPerWallet[addr],
                    address(_EC),
                    _DUST,
                    _ecVault,
                    maxPerSaleMint,
                    _MaxUserMintable,
                    _userMinted,
                    tokenData._randomReceived,
                    tokenData._secondReceived,
                    tokenData._randomCL,
                    tokenData._randomCL2,
                    tokenData._ts1,
                    tokenData._ts2
                );
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/ERC721Enumerable.sol)
    pragma solidity ^0.8.0;
    import "../ERC721.sol";
    import "./IERC721Enumerable.sol";
    /**
     * @dev This implements an optional extension of {ERC721} defined in the EIP that adds
     * enumerability of all the token ids in the contract as well as all token ids owned by each
     * account.
     */
    abstract contract ERC721Enumerable is ERC721, IERC721Enumerable {
        // Mapping from owner to list of owned token IDs
        mapping(address => mapping(uint256 => uint256)) private _ownedTokens;
        // Mapping from token ID to index of the owner tokens list
        mapping(uint256 => uint256) private _ownedTokensIndex;
        // Array with all token ids, used for enumeration
        uint256[] private _allTokens;
        // Mapping from token id to position in the allTokens array
        mapping(uint256 => uint256) private _allTokensIndex;
        /**
         * @dev See {IERC165-supportsInterface}.
         */
        function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC721) returns (bool) {
            return interfaceId == type(IERC721Enumerable).interfaceId || super.supportsInterface(interfaceId);
        }
        /**
         * @dev See {IERC721Enumerable-tokenOfOwnerByIndex}.
         */
        function tokenOfOwnerByIndex(address owner, uint256 index) public view virtual override returns (uint256) {
            require(index < ERC721.balanceOf(owner), "ERC721Enumerable: owner index out of bounds");
            return _ownedTokens[owner][index];
        }
        /**
         * @dev See {IERC721Enumerable-totalSupply}.
         */
        function totalSupply() public view virtual override returns (uint256) {
            return _allTokens.length;
        }
        /**
         * @dev See {IERC721Enumerable-tokenByIndex}.
         */
        function tokenByIndex(uint256 index) public view virtual override returns (uint256) {
            require(index < ERC721Enumerable.totalSupply(), "ERC721Enumerable: global index out of bounds");
            return _allTokens[index];
        }
        /**
         * @dev Hook that is called before any token transfer. This includes minting
         * and burning.
         *
         * Calling conditions:
         *
         * - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be
         * transferred to `to`.
         * - When `from` is zero, `tokenId` will be minted for `to`.
         * - When `to` is zero, ``from``'s `tokenId` will be burned.
         * - `from` cannot be the zero address.
         * - `to` cannot be the zero address.
         *
         * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
         */
        function _beforeTokenTransfer(
            address from,
            address to,
            uint256 tokenId
        ) internal virtual override {
            super._beforeTokenTransfer(from, to, tokenId);
            if (from == address(0)) {
                _addTokenToAllTokensEnumeration(tokenId);
            } else if (from != to) {
                _removeTokenFromOwnerEnumeration(from, tokenId);
            }
            if (to == address(0)) {
                _removeTokenFromAllTokensEnumeration(tokenId);
            } else if (to != from) {
                _addTokenToOwnerEnumeration(to, tokenId);
            }
        }
        /**
         * @dev Private function to add a token to this extension's ownership-tracking data structures.
         * @param to address representing the new owner of the given token ID
         * @param tokenId uint256 ID of the token to be added to the tokens list of the given address
         */
        function _addTokenToOwnerEnumeration(address to, uint256 tokenId) private {
            uint256 length = ERC721.balanceOf(to);
            _ownedTokens[to][length] = tokenId;
            _ownedTokensIndex[tokenId] = length;
        }
        /**
         * @dev Private function to add a token to this extension's token tracking data structures.
         * @param tokenId uint256 ID of the token to be added to the tokens list
         */
        function _addTokenToAllTokensEnumeration(uint256 tokenId) private {
            _allTokensIndex[tokenId] = _allTokens.length;
            _allTokens.push(tokenId);
        }
        /**
         * @dev Private function to remove a token from this extension's ownership-tracking data structures. Note that
         * while the token is not assigned a new owner, the `_ownedTokensIndex` mapping is _not_ updated: this allows for
         * gas optimizations e.g. when performing a transfer operation (avoiding double writes).
         * This has O(1) time complexity, but alters the order of the _ownedTokens array.
         * @param from address representing the previous owner of the given token ID
         * @param tokenId uint256 ID of the token to be removed from the tokens list of the given address
         */
        function _removeTokenFromOwnerEnumeration(address from, uint256 tokenId) private {
            // To prevent a gap in from's tokens array, we store the last token in the index of the token to delete, and
            // then delete the last slot (swap and pop).
            uint256 lastTokenIndex = ERC721.balanceOf(from) - 1;
            uint256 tokenIndex = _ownedTokensIndex[tokenId];
            // When the token to delete is the last token, the swap operation is unnecessary
            if (tokenIndex != lastTokenIndex) {
                uint256 lastTokenId = _ownedTokens[from][lastTokenIndex];
                _ownedTokens[from][tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
                _ownedTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index
            }
            // This also deletes the contents at the last position of the array
            delete _ownedTokensIndex[tokenId];
            delete _ownedTokens[from][lastTokenIndex];
        }
        /**
         * @dev Private function to remove a token from this extension's token tracking data structures.
         * This has O(1) time complexity, but alters the order of the _allTokens array.
         * @param tokenId uint256 ID of the token to be removed from the tokens list
         */
        function _removeTokenFromAllTokensEnumeration(uint256 tokenId) private {
            // To prevent a gap in the tokens array, we store the last token in the index of the token to delete, and
            // then delete the last slot (swap and pop).
            uint256 lastTokenIndex = _allTokens.length - 1;
            uint256 tokenIndex = _allTokensIndex[tokenId];
            // When the token to delete is the last token, the swap operation is unnecessary. However, since this occurs so
            // rarely (when the last minted token is burnt) that we still do the swap here to avoid the gas cost of adding
            // an 'if' statement (like in _removeTokenFromOwnerEnumeration)
            uint256 lastTokenId = _allTokens[lastTokenIndex];
            _allTokens[tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
            _allTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index
            // This also deletes the contents at the last position of the array
            delete _allTokensIndex[tokenId];
            _allTokens.pop();
        }
    }
    // 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
    // OpenZeppelin Contracts v4.4.1 (utils/math/SafeMath.sol)
    pragma solidity ^0.8.0;
    // CAUTION
    // This version of SafeMath should only be used with Solidity 0.8 or later,
    // because it relies on the compiler's built in overflow checks.
    /**
     * @dev Wrappers over Solidity's arithmetic operations.
     *
     * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler
     * now has built in overflow checking.
     */
    library SafeMath {
        /**
         * @dev Returns the addition of two unsigned integers, with an overflow flag.
         *
         * _Available since v3.4._
         */
        function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            unchecked {
                uint256 c = a + b;
                if (c < a) return (false, 0);
                return (true, c);
            }
        }
        /**
         * @dev Returns the substraction of two unsigned integers, with an overflow flag.
         *
         * _Available since v3.4._
         */
        function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            unchecked {
                if (b > a) return (false, 0);
                return (true, a - b);
            }
        }
        /**
         * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
         *
         * _Available since v3.4._
         */
        function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            unchecked {
                // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                // benefit is lost if 'b' is also tested.
                // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
                if (a == 0) return (true, 0);
                uint256 c = a * b;
                if (c / a != b) return (false, 0);
                return (true, c);
            }
        }
        /**
         * @dev Returns the division of two unsigned integers, with a division by zero flag.
         *
         * _Available since v3.4._
         */
        function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            unchecked {
                if (b == 0) return (false, 0);
                return (true, a / b);
            }
        }
        /**
         * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
         *
         * _Available since v3.4._
         */
        function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            unchecked {
                if (b == 0) return (false, 0);
                return (true, a % b);
            }
        }
        /**
         * @dev Returns the addition of two unsigned integers, reverting on
         * overflow.
         *
         * Counterpart to Solidity's `+` operator.
         *
         * Requirements:
         *
         * - Addition cannot overflow.
         */
        function add(uint256 a, uint256 b) internal pure returns (uint256) {
            return a + b;
        }
        /**
         * @dev Returns the subtraction of two unsigned integers, reverting on
         * overflow (when the result is negative).
         *
         * Counterpart to Solidity's `-` operator.
         *
         * Requirements:
         *
         * - Subtraction cannot overflow.
         */
        function sub(uint256 a, uint256 b) internal pure returns (uint256) {
            return a - b;
        }
        /**
         * @dev Returns the multiplication of two unsigned integers, reverting on
         * overflow.
         *
         * Counterpart to Solidity's `*` operator.
         *
         * Requirements:
         *
         * - Multiplication cannot overflow.
         */
        function mul(uint256 a, uint256 b) internal pure returns (uint256) {
            return a * b;
        }
        /**
         * @dev Returns the integer division of two unsigned integers, reverting on
         * division by zero. The result is rounded towards zero.
         *
         * Counterpart to Solidity's `/` operator.
         *
         * Requirements:
         *
         * - The divisor cannot be zero.
         */
        function div(uint256 a, uint256 b) internal pure returns (uint256) {
            return a / b;
        }
        /**
         * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
         * reverting when dividing by zero.
         *
         * Counterpart to Solidity's `%` operator. This function uses a `revert`
         * opcode (which leaves remaining gas untouched) while Solidity uses an
         * invalid opcode to revert (consuming all remaining gas).
         *
         * Requirements:
         *
         * - The divisor cannot be zero.
         */
        function mod(uint256 a, uint256 b) internal pure returns (uint256) {
            return a % b;
        }
        /**
         * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
         * overflow (when the result is negative).
         *
         * CAUTION: This function is deprecated because it requires allocating memory for the error
         * message unnecessarily. For custom revert reasons use {trySub}.
         *
         * Counterpart to Solidity's `-` operator.
         *
         * Requirements:
         *
         * - Subtraction cannot overflow.
         */
        function sub(
            uint256 a,
            uint256 b,
            string memory errorMessage
        ) internal pure returns (uint256) {
            unchecked {
                require(b <= a, errorMessage);
                return a - b;
            }
        }
        /**
         * @dev Returns the integer division of two unsigned integers, reverting with custom message on
         * division by zero. The result is rounded towards zero.
         *
         * Counterpart to Solidity's `/` operator. Note: this function uses a
         * `revert` opcode (which leaves remaining gas untouched) while Solidity
         * uses an invalid opcode to revert (consuming all remaining gas).
         *
         * Requirements:
         *
         * - The divisor cannot be zero.
         */
        function div(
            uint256 a,
            uint256 b,
            string memory errorMessage
        ) internal pure returns (uint256) {
            unchecked {
                require(b > 0, errorMessage);
                return a / b;
            }
        }
        /**
         * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
         * reverting with custom message when dividing by zero.
         *
         * CAUTION: This function is deprecated because it requires allocating memory for the error
         * message unnecessarily. For custom revert reasons use {tryMod}.
         *
         * Counterpart to Solidity's `%` operator. This function uses a `revert`
         * opcode (which leaves remaining gas untouched) while Solidity uses an
         * invalid opcode to revert (consuming all remaining gas).
         *
         * Requirements:
         *
         * - The divisor cannot be zero.
         */
        function mod(
            uint256 a,
            uint256 b,
            string memory errorMessage
        ) internal pure returns (uint256) {
            unchecked {
                require(b > 0, errorMessage);
                return a % b;
            }
        }
    }
    // 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);
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC1820Registry.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev Interface of the global ERC1820 Registry, as defined in the
     * https://eips.ethereum.org/EIPS/eip-1820[EIP]. Accounts may register
     * implementers for interfaces in this registry, as well as query support.
     *
     * Implementers may be shared by multiple accounts, and can also implement more
     * than a single interface for each account. Contracts can implement interfaces
     * for themselves, but externally-owned accounts (EOA) must delegate this to a
     * contract.
     *
     * {IERC165} interfaces can also be queried via the registry.
     *
     * For an in-depth explanation and source code analysis, see the EIP text.
     */
    interface IERC1820Registry {
        /**
         * @dev Sets `newManager` as the manager for `account`. A manager of an
         * account is able to set interface implementers for it.
         *
         * By default, each account is its own manager. Passing a value of `0x0` in
         * `newManager` will reset the manager to this initial state.
         *
         * Emits a {ManagerChanged} event.
         *
         * Requirements:
         *
         * - the caller must be the current manager for `account`.
         */
        function setManager(address account, address newManager) external;
        /**
         * @dev Returns the manager for `account`.
         *
         * See {setManager}.
         */
        function getManager(address account) external view returns (address);
        /**
         * @dev Sets the `implementer` contract as ``account``'s implementer for
         * `interfaceHash`.
         *
         * `account` being the zero address is an alias for the caller's address.
         * The zero address can also be used in `implementer` to remove an old one.
         *
         * See {interfaceHash} to learn how these are created.
         *
         * Emits an {InterfaceImplementerSet} event.
         *
         * Requirements:
         *
         * - the caller must be the current manager for `account`.
         * - `interfaceHash` must not be an {IERC165} interface id (i.e. it must not
         * end in 28 zeroes).
         * - `implementer` must implement {IERC1820Implementer} and return true when
         * queried for support, unless `implementer` is the caller. See
         * {IERC1820Implementer-canImplementInterfaceForAddress}.
         */
        function setInterfaceImplementer(
            address account,
            bytes32 _interfaceHash,
            address implementer
        ) external;
        /**
         * @dev Returns the implementer of `interfaceHash` for `account`. If no such
         * implementer is registered, returns the zero address.
         *
         * If `interfaceHash` is an {IERC165} interface id (i.e. it ends with 28
         * zeroes), `account` will be queried for support of it.
         *
         * `account` being the zero address is an alias for the caller's address.
         */
        function getInterfaceImplementer(address account, bytes32 _interfaceHash) external view returns (address);
        /**
         * @dev Returns the interface hash for an `interfaceName`, as defined in the
         * corresponding
         * https://eips.ethereum.org/EIPS/eip-1820#interface-name[section of the EIP].
         */
        function interfaceHash(string calldata interfaceName) external pure returns (bytes32);
        /**
         * @notice Updates the cache with whether the contract implements an ERC165 interface or not.
         * @param account Address of the contract for which to update the cache.
         * @param interfaceId ERC165 interface for which to update the cache.
         */
        function updateERC165Cache(address account, bytes4 interfaceId) external;
        /**
         * @notice Checks whether a contract implements an ERC165 interface or not.
         * If the result is not cached a direct lookup on the contract address is performed.
         * If the result is not cached or the cached value is out-of-date, the cache MUST be updated manually by calling
         * {updateERC165Cache} with the contract address.
         * @param account Address of the contract to check.
         * @param interfaceId ERC165 interface to check.
         * @return True if `account` implements `interfaceId`, false otherwise.
         */
        function implementsERC165Interface(address account, bytes4 interfaceId) external view returns (bool);
        /**
         * @notice Checks whether a contract implements an ERC165 interface or not without using nor updating the cache.
         * @param account Address of the contract to check.
         * @param interfaceId ERC165 interface to check.
         * @return True if `account` implements `interfaceId`, false otherwise.
         */
        function implementsERC165InterfaceNoCache(address account, bytes4 interfaceId) external view returns (bool);
        event InterfaceImplementerSet(address indexed account, bytes32 indexed interfaceHash, address indexed implementer);
        event ManagerChanged(address indexed account, address indexed newManager);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev Contract module that helps prevent reentrant calls to a function.
     *
     * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
     * available, which can be applied to functions to make sure there are no nested
     * (reentrant) calls to them.
     *
     * Note that because there is a single `nonReentrant` guard, functions marked as
     * `nonReentrant` may not call one another. This can be worked around by making
     * those functions `private`, and then adding `external` `nonReentrant` entry
     * points to them.
     *
     * TIP: If you would like to learn more about reentrancy and alternative ways
     * to protect against it, check out our blog post
     * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
     */
    abstract contract ReentrancyGuard {
        // Booleans are more expensive than uint256 or any type that takes up a full
        // word because each write operation emits an extra SLOAD to first read the
        // slot's contents, replace the bits taken up by the boolean, and then write
        // back. This is the compiler's defense against contract upgrades and
        // pointer aliasing, and it cannot be disabled.
        // The values being non-zero value makes deployment a bit more expensive,
        // but in exchange the refund on every call to nonReentrant will be lower in
        // amount. Since refunds are capped to a percentage of the total
        // transaction's gas, it is best to keep them low in cases like this one, to
        // increase the likelihood of the full refund coming into effect.
        uint256 private constant _NOT_ENTERED = 1;
        uint256 private constant _ENTERED = 2;
        uint256 private _status;
        constructor() {
            _status = _NOT_ENTERED;
        }
        /**
         * @dev Prevents a contract from calling itself, directly or indirectly.
         * Calling a `nonReentrant` function from another `nonReentrant`
         * function is not supported. It is possible to prevent this from happening
         * by making the `nonReentrant` function external, and making it call a
         * `private` function that does the actual work.
         */
        modifier nonReentrant() {
            // On the first call to nonReentrant, _notEntered will be true
            require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
            // Any calls to nonReentrant after this point will fail
            _status = _ENTERED;
            _;
            // By storing the original value once again, a refund is triggered (see
            // https://eips.ethereum.org/EIPS/eip-2200)
            _status = _NOT_ENTERED;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (token/ERC777/IERC777Recipient.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev Interface of the ERC777TokensRecipient standard as defined in the EIP.
     *
     * Accounts can be notified of {IERC777} tokens being sent to them by having a
     * contract implement this interface (contract holders can be their own
     * implementer) and registering it on the
     * https://eips.ethereum.org/EIPS/eip-1820[ERC1820 global registry].
     *
     * See {IERC1820Registry} and {ERC1820Implementer}.
     */
    interface IERC777Recipient {
        /**
         * @dev Called by an {IERC777} token contract whenever tokens are being
         * moved or created into a registered account (`to`). The type of operation
         * is conveyed by `from` being the zero address or not.
         *
         * This call occurs _after_ the token contract's state is updated, so
         * {IERC777-balanceOf}, etc., can be used to query the post-operation state.
         *
         * This function may revert to prevent the operation from being executed.
         */
        function tokensReceived(
            address operator,
            address from,
            address to,
            uint256 amount,
            bytes calldata userData,
            bytes calldata operatorData
        ) external;
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (token/ERC777/IERC777.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev Interface of the ERC777Token standard as defined in the EIP.
     *
     * This contract uses the
     * https://eips.ethereum.org/EIPS/eip-1820[ERC1820 registry standard] to let
     * token holders and recipients react to token movements by using setting implementers
     * for the associated interfaces in said registry. See {IERC1820Registry} and
     * {ERC1820Implementer}.
     */
    interface IERC777 {
        /**
         * @dev Returns the name of the token.
         */
        function name() external view returns (string memory);
        /**
         * @dev Returns the symbol of the token, usually a shorter version of the
         * name.
         */
        function symbol() external view returns (string memory);
        /**
         * @dev Returns the smallest part of the token that is not divisible. This
         * means all token operations (creation, movement and destruction) must have
         * amounts that are a multiple of this number.
         *
         * For most token contracts, this value will equal 1.
         */
        function granularity() external view returns (uint256);
        /**
         * @dev Returns the amount of tokens in existence.
         */
        function totalSupply() external view returns (uint256);
        /**
         * @dev Returns the amount of tokens owned by an account (`owner`).
         */
        function balanceOf(address owner) external view returns (uint256);
        /**
         * @dev Moves `amount` tokens from the caller's account to `recipient`.
         *
         * If send or receive hooks are registered for the caller and `recipient`,
         * the corresponding functions will be called with `data` and empty
         * `operatorData`. See {IERC777Sender} and {IERC777Recipient}.
         *
         * Emits a {Sent} event.
         *
         * Requirements
         *
         * - the caller must have at least `amount` tokens.
         * - `recipient` cannot be the zero address.
         * - if `recipient` is a contract, it must implement the {IERC777Recipient}
         * interface.
         */
        function send(
            address recipient,
            uint256 amount,
            bytes calldata data
        ) external;
        /**
         * @dev Destroys `amount` tokens from the caller's account, reducing the
         * total supply.
         *
         * If a send hook is registered for the caller, the corresponding function
         * will be called with `data` and empty `operatorData`. See {IERC777Sender}.
         *
         * Emits a {Burned} event.
         *
         * Requirements
         *
         * - the caller must have at least `amount` tokens.
         */
        function burn(uint256 amount, bytes calldata data) external;
        /**
         * @dev Returns true if an account is an operator of `tokenHolder`.
         * Operators can send and burn tokens on behalf of their owners. All
         * accounts are their own operator.
         *
         * See {operatorSend} and {operatorBurn}.
         */
        function isOperatorFor(address operator, address tokenHolder) external view returns (bool);
        /**
         * @dev Make an account an operator of the caller.
         *
         * See {isOperatorFor}.
         *
         * Emits an {AuthorizedOperator} event.
         *
         * Requirements
         *
         * - `operator` cannot be calling address.
         */
        function authorizeOperator(address operator) external;
        /**
         * @dev Revoke an account's operator status for the caller.
         *
         * See {isOperatorFor} and {defaultOperators}.
         *
         * Emits a {RevokedOperator} event.
         *
         * Requirements
         *
         * - `operator` cannot be calling address.
         */
        function revokeOperator(address operator) external;
        /**
         * @dev Returns the list of default operators. These accounts are operators
         * for all token holders, even if {authorizeOperator} was never called on
         * them.
         *
         * This list is immutable, but individual holders may revoke these via
         * {revokeOperator}, in which case {isOperatorFor} will return false.
         */
        function defaultOperators() external view returns (address[] memory);
        /**
         * @dev Moves `amount` tokens from `sender` to `recipient`. The caller must
         * be an operator of `sender`.
         *
         * If send or receive hooks are registered for `sender` and `recipient`,
         * the corresponding functions will be called with `data` and
         * `operatorData`. See {IERC777Sender} and {IERC777Recipient}.
         *
         * Emits a {Sent} event.
         *
         * Requirements
         *
         * - `sender` cannot be the zero address.
         * - `sender` must have at least `amount` tokens.
         * - the caller must be an operator for `sender`.
         * - `recipient` cannot be the zero address.
         * - if `recipient` is a contract, it must implement the {IERC777Recipient}
         * interface.
         */
        function operatorSend(
            address sender,
            address recipient,
            uint256 amount,
            bytes calldata data,
            bytes calldata operatorData
        ) external;
        /**
         * @dev Destroys `amount` tokens from `account`, reducing the total supply.
         * The caller must be an operator of `account`.
         *
         * If a send hook is registered for `account`, the corresponding function
         * will be called with `data` and `operatorData`. See {IERC777Sender}.
         *
         * Emits a {Burned} event.
         *
         * Requirements
         *
         * - `account` cannot be the zero address.
         * - `account` must have at least `amount` tokens.
         * - the caller must be an operator for `account`.
         */
        function operatorBurn(
            address account,
            uint256 amount,
            bytes calldata data,
            bytes calldata operatorData
        ) external;
        event Sent(
            address indexed operator,
            address indexed from,
            address indexed to,
            uint256 amount,
            bytes data,
            bytes operatorData
        );
        event Minted(address indexed operator, address indexed to, uint256 amount, bytes data, bytes operatorData);
        event Burned(address indexed operator, address indexed from, uint256 amount, bytes data, bytes operatorData);
        event AuthorizedOperator(address indexed operator, address indexed tokenHolder);
        event RevokedOperator(address indexed operator, address indexed tokenHolder);
    }
    pragma solidity ^0.8.7;
    interface community_interface {
        function community_claimed(address) external view returns (uint256);
        function communityPurchase(
            address recipient,
            uint256 tokenCount,
            bytes memory signature,
            uint256 role
        ) external payable;
    }
    pragma solidity ^0.8.7;
    contract sale_configuration {
        uint256 _maxSupply;
        uint256 _clientMintLimit;
        uint256 _ecMintLimit;
        uint256 _fullPrice;
        uint256 _discountPrice; // obsolete
        uint256 _communityPrice; // obsolete
        uint256 _presaleStart; // obsolete
        uint256 _presaleEnd; // obsolete
        uint256 _saleStart;
        uint256 _saleEnd;
        uint256 _dustPrice; // obsolete
        uint256 _discountedPerAddress; // obsolete
        uint256 _totalDiscount; // obsolete
        uint256 _maxDiscount; // obsolete
        uint256 _maxPerSaleMint;
        uint256 _freePerAddress;
        address _signer;
        uint256 _maxFreeEC; // obsolete
        uint256 _totalFreeEC; // obsolete
        uint256 _ecMintPosition;
        uint256 _clientMintPosition;
        address _ecVault;
    }
    pragma solidity ^0.8.7;
    // SPDX-Licence-Identifier: RIGHT-CLICK-SAVE-ONLY
    import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
    import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    import "@openzeppelin/contracts/access/Ownable.sol";
    contract recovery is Ownable {
        // blackhole prevention methods
        function retrieveETH() external onlyOwner {
            (bool sent, ) =
                payable(msg.sender).call{value: address(this).balance}(""); // don't use send or xfer (gas)
            require(sent, "Failed to send Ether");
        }
        function retrieveERC20(address _tracker, uint256 amount)
            external
            onlyOwner
        {
            IERC20(_tracker).transfer(msg.sender, amount);
        }
        function retrieve721(address _tracker, uint256 id) external onlyOwner {
            IERC721(_tracker).transferFrom(address(this), msg.sender, id);
        }
    }
    pragma solidity ^0.8.7;
    interface IRNG {
        function requestRandomNumber() external returns (bytes32);
        function requestRandomNumberWithCallback() external returns (bytes32);
        function isRequestComplete(bytes32 requestId)
            external
            view
            returns (bool isCompleted);
        function randomNumber(bytes32 requestId)
            external
            view
            returns (uint256 randomNum);
        function setAuth(address user, bool grant) external;
    }
    pragma solidity ^0.8.7;
    // SPDX-Licence-Identifier: RIGHT-CLICK-SAVE-ONLY
    import "@openzeppelin/contracts/utils/introspection/IERC1820Registry.sol";
    import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
    import "@openzeppelin/contracts/token/ERC777/IERC777Recipient.sol";
    import "@openzeppelin/contracts/token/ERC777/IERC777.sol";
    //import "hardhat/console.sol";
    abstract contract dusty is IERC777Recipient, ReentrancyGuard {
        // struct community_data {
        //     uint256 start;
        //     uint256 end;
        //     uint256 price;
        //     bool    isDust;
        //     uint256 max_purchase;
        //     address purchaser;
        // }
        address DUST_TOKEN;
        address signer;
        address[] wallets;
        uint16[] shares;
        uint256 fullDustPrice;
        uint256 discountDustPrice;
        uint256 maxPerSaleMint;
        mapping(address => uint256) presold;
        bytes32 private constant TOKENS_RECIPIENT_INTERFACE_HASH =
            keccak256("ERC777TokensRecipient");
        IERC1820Registry internal constant _ERC1820_REGISTRY =
            IERC1820Registry(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24);
        event DustPresale(address from, uint256 number_of_items, uint256 price);
        event DustSale(address buyer, uint256 number_to_buy, uint256 dustAmount);
        constructor(
            address dust,
            address _signer,
            uint256 _fullDustPrice,
            uint256 _discountDustPrice,
            uint256 _maxPerSaleMint,
            address[] memory _wallets,
            uint16[] memory _shares
        ) {
            DUST_TOKEN = dust;
            _ERC1820_REGISTRY.setInterfaceImplementer(
                address(this),
                TOKENS_RECIPIENT_INTERFACE_HASH,
                address(this)
            );
            wallets = _wallets;
            shares = _shares;
            signer = _signer;
            fullDustPrice = _fullDustPrice;
            discountDustPrice = _discountDustPrice;
            maxPerSaleMint = _maxPerSaleMint;
        }
        // https://twitter.com/nicksdjohnson/status/1485769228481605639?s=20&t=22PZlf-8awaea2bubNw72A
        // ```
        // bytes4 selector = bytes4(data[:4]);
        // uint256 id = uint256(bytes32(data[4:36]));
        // ```
        struct vData {
            bool mint_free;
            uint256 max_mint;
            address from;
            uint256 start;
            uint256 end;
            uint256 eth_price;
            uint256 dust_price;
            bytes signature;
        }
        function checkSaleIsActive() public view virtual returns (bool);
        function checkPresaleIsActive() public view virtual returns (bool);
        function tokensReceived(
            address,
            address from,
            address,
            uint256 amount,
            bytes calldata userData,
            bytes calldata
        ) external override nonReentrant {}
        function _mintCards(uint256 numberOfCards, address recipient)
            internal
            virtual;
        function _mintDiscountCards(uint256 numberOfCards, address recipient)
            internal
            virtual;
        function verify(vData memory info) public view returns (bool) {
            require(info.from != address(0), "INVALID_SIGNER");
            bytes memory cat =
                abi.encode(
                    info.from,
                    info.start,
                    info.end,
                    info.eth_price,
                    info.dust_price,
                    info.max_mint,
                    info.mint_free
                );
            // console.log("data-->");
            // console.logBytes(cat);
            bytes32 hash = keccak256(cat);
            // console.log("hash ->");
            //    console.logBytes32(hash);
            require(info.signature.length == 65, "Invalid signature length");
            bytes32 sigR;
            bytes32 sigS;
            uint8 sigV;
            bytes memory signature = info.signature;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            assembly {
                sigR := mload(add(signature, 0x20))
                sigS := mload(add(signature, 0x40))
                sigV := byte(0, mload(add(signature, 0x60)))
            }
            bytes32 data =
                keccak256(
                    abi.encodePacked("\\x19Ethereum Signed Message:\
    32", hash)
                );
            address recovered = ecrecover(data, sigV, sigR, sigS);
            return signer == recovered;
        }
    }
    pragma solidity ^0.8.7;
    // SPDX-Licence-Identifier: RIGHT-CLICK-SAVE-ONLY
    import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
    // import "hardhat/console.sol";
    abstract contract card_with_card {
        struct saleInfo {
            address token_address;
            uint256 start;
            uint256 end;
            uint256 price;
            uint256 max_per_user;
            uint256 total;
            bool oneForOne;
            bytes signature;
        }
        address cwc_signer;
        constructor(address _signer) {
            cwc_signer = _signer;
        }
        function _mintCards(uint256 numberOfCards, address recipient)
            internal
            virtual;
        function _mintDiscountCards(uint256 numberOfCards, address recipient)
            internal
            virtual;
        function _mintDiscountPayable(
            uint256 numberOfCards,
            address recipient,
            uint256 price
        ) internal virtual;
        function _mintPayable(
            uint256 numberOfCards,
            address recipient,
            uint256 price
        ) internal virtual;
        function verify(saleInfo calldata sp) internal view returns (bool) {
            require(sp.token_address != address(0), "INVALID_TOKEN_ADDRESS");
            bytes memory cat =
                abi.encode(
                    sp.token_address,
                    sp.start,
                    sp.end,
                    sp.price,
                    sp.max_per_user,
                    sp.total,
                    sp.oneForOne
                );
            bytes32 hash = keccak256(cat);
            require(sp.signature.length == 65, "Invalid signature length");
            bytes32 sigR;
            bytes32 sigS;
            uint8 sigV;
            bytes memory signature = sp.signature;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            assembly {
                sigR := mload(add(signature, 0x20))
                sigS := mload(add(signature, 0x40))
                sigV := byte(0, mload(add(signature, 0x60)))
            }
            bytes32 data =
                keccak256(
                    abi.encodePacked("\\x19Ethereum Signed Message:\
    32", hash)
                );
            address recovered = ecrecover(data, sigV, sigR, sigS);
            return cwc_signer == recovered;
        }
    }
    pragma solidity ^0.8.7;
    interface token_interface {
        struct TKS {
            // Token Kitchen Sink
            uint256 _mintPosition;
            uint256 _ts1;
            uint256 _ts2;
            bool _randomReceived;
            bool _secondReceived;
            uint256 _randomCL;
            uint256 _randomCL2;
            bool _lockTillSaleEnd;
        }
        function setAllowed(address _addr, bool _state) external;
        function permitted(address) external view returns (bool);
        function mintCards(uint256 numberOfCards, address recipient) external;
        function tellEverything() external view returns (TKS memory);
        function tokenPreRevealURI() external view returns (string memory);
        function availableToMint() external view returns (uint256);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (token/ERC721/ERC721.sol)
    pragma solidity ^0.8.0;
    import "./IERC721.sol";
    import "./IERC721Receiver.sol";
    import "./extensions/IERC721Metadata.sol";
    import "../../utils/Address.sol";
    import "../../utils/Context.sol";
    import "../../utils/Strings.sol";
    import "../../utils/introspection/ERC165.sol";
    /**
     * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
     * the Metadata extension, but not including the Enumerable extension, which is available separately as
     * {ERC721Enumerable}.
     */
    contract ERC721 is Context, ERC165, IERC721, IERC721Metadata {
        using Address for address;
        using Strings for uint256;
        // Token name
        string private _name;
        // Token symbol
        string private _symbol;
        // Mapping from token ID to owner address
        mapping(uint256 => address) private _owners;
        // Mapping owner address to token count
        mapping(address => uint256) private _balances;
        // Mapping from token ID to approved address
        mapping(uint256 => address) private _tokenApprovals;
        // Mapping from owner to operator approvals
        mapping(address => mapping(address => bool)) private _operatorApprovals;
        /**
         * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
         */
        constructor(string memory name_, string memory symbol_) {
            _name = name_;
            _symbol = symbol_;
        }
        /**
         * @dev See {IERC165-supportsInterface}.
         */
        function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
            return
                interfaceId == type(IERC721).interfaceId ||
                interfaceId == type(IERC721Metadata).interfaceId ||
                super.supportsInterface(interfaceId);
        }
        /**
         * @dev See {IERC721-balanceOf}.
         */
        function balanceOf(address owner) public view virtual override returns (uint256) {
            require(owner != address(0), "ERC721: balance query for the zero address");
            return _balances[owner];
        }
        /**
         * @dev See {IERC721-ownerOf}.
         */
        function ownerOf(uint256 tokenId) public view virtual override returns (address) {
            address owner = _owners[tokenId];
            require(owner != address(0), "ERC721: owner query for nonexistent token");
            return owner;
        }
        /**
         * @dev See {IERC721Metadata-name}.
         */
        function name() public view virtual override returns (string memory) {
            return _name;
        }
        /**
         * @dev See {IERC721Metadata-symbol}.
         */
        function symbol() public view virtual override returns (string memory) {
            return _symbol;
        }
        /**
         * @dev See {IERC721Metadata-tokenURI}.
         */
        function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
            require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
            string memory baseURI = _baseURI();
            return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : "";
        }
        /**
         * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
         * token will be the concatenation of the `baseURI` and the `tokenId`. Empty
         * by default, can be overriden in child contracts.
         */
        function _baseURI() internal view virtual returns (string memory) {
            return "";
        }
        /**
         * @dev See {IERC721-approve}.
         */
        function approve(address to, uint256 tokenId) public virtual override {
            address owner = ERC721.ownerOf(tokenId);
            require(to != owner, "ERC721: approval to current owner");
            require(
                _msgSender() == owner || isApprovedForAll(owner, _msgSender()),
                "ERC721: approve caller is not owner nor approved for all"
            );
            _approve(to, tokenId);
        }
        /**
         * @dev See {IERC721-getApproved}.
         */
        function getApproved(uint256 tokenId) public view virtual override returns (address) {
            require(_exists(tokenId), "ERC721: approved query for nonexistent token");
            return _tokenApprovals[tokenId];
        }
        /**
         * @dev See {IERC721-setApprovalForAll}.
         */
        function setApprovalForAll(address operator, bool approved) public virtual override {
            _setApprovalForAll(_msgSender(), operator, approved);
        }
        /**
         * @dev See {IERC721-isApprovedForAll}.
         */
        function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
            return _operatorApprovals[owner][operator];
        }
        /**
         * @dev See {IERC721-transferFrom}.
         */
        function transferFrom(
            address from,
            address to,
            uint256 tokenId
        ) public virtual override {
            //solhint-disable-next-line max-line-length
            require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");
            _transfer(from, to, tokenId);
        }
        /**
         * @dev See {IERC721-safeTransferFrom}.
         */
        function safeTransferFrom(
            address from,
            address to,
            uint256 tokenId
        ) public virtual override {
            safeTransferFrom(from, to, tokenId, "");
        }
        /**
         * @dev See {IERC721-safeTransferFrom}.
         */
        function safeTransferFrom(
            address from,
            address to,
            uint256 tokenId,
            bytes memory _data
        ) public virtual override {
            require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");
            _safeTransfer(from, to, tokenId, _data);
        }
        /**
         * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
         * are aware of the ERC721 protocol to prevent tokens from being forever locked.
         *
         * `_data` is additional data, it has no specified format and it is sent in call to `to`.
         *
         * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g.
         * implement alternative mechanisms to perform token transfer, such as signature-based.
         *
         * Requirements:
         *
         * - `from` cannot be the zero address.
         * - `to` cannot be the zero address.
         * - `tokenId` token must exist and be owned by `from`.
         * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
         *
         * Emits a {Transfer} event.
         */
        function _safeTransfer(
            address from,
            address to,
            uint256 tokenId,
            bytes memory _data
        ) internal virtual {
            _transfer(from, to, tokenId);
            require(_checkOnERC721Received(from, to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer");
        }
        /**
         * @dev Returns whether `tokenId` exists.
         *
         * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
         *
         * Tokens start existing when they are minted (`_mint`),
         * and stop existing when they are burned (`_burn`).
         */
        function _exists(uint256 tokenId) internal view virtual returns (bool) {
            return _owners[tokenId] != address(0);
        }
        /**
         * @dev Returns whether `spender` is allowed to manage `tokenId`.
         *
         * Requirements:
         *
         * - `tokenId` must exist.
         */
        function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {
            require(_exists(tokenId), "ERC721: operator query for nonexistent token");
            address owner = ERC721.ownerOf(tokenId);
            return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender));
        }
        /**
         * @dev Safely mints `tokenId` and transfers it to `to`.
         *
         * Requirements:
         *
         * - `tokenId` must not exist.
         * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
         *
         * Emits a {Transfer} event.
         */
        function _safeMint(address to, uint256 tokenId) internal virtual {
            _safeMint(to, tokenId, "");
        }
        /**
         * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
         * forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
         */
        function _safeMint(
            address to,
            uint256 tokenId,
            bytes memory _data
        ) internal virtual {
            _mint(to, tokenId);
            require(
                _checkOnERC721Received(address(0), to, tokenId, _data),
                "ERC721: transfer to non ERC721Receiver implementer"
            );
        }
        /**
         * @dev Mints `tokenId` and transfers it to `to`.
         *
         * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
         *
         * Requirements:
         *
         * - `tokenId` must not exist.
         * - `to` cannot be the zero address.
         *
         * Emits a {Transfer} event.
         */
        function _mint(address to, uint256 tokenId) internal virtual {
            require(to != address(0), "ERC721: mint to the zero address");
            require(!_exists(tokenId), "ERC721: token already minted");
            _beforeTokenTransfer(address(0), to, tokenId);
            _balances[to] += 1;
            _owners[tokenId] = to;
            emit Transfer(address(0), to, tokenId);
        }
        /**
         * @dev Destroys `tokenId`.
         * The approval is cleared when the token is burned.
         *
         * Requirements:
         *
         * - `tokenId` must exist.
         *
         * Emits a {Transfer} event.
         */
        function _burn(uint256 tokenId) internal virtual {
            address owner = ERC721.ownerOf(tokenId);
            _beforeTokenTransfer(owner, address(0), tokenId);
            // Clear approvals
            _approve(address(0), tokenId);
            _balances[owner] -= 1;
            delete _owners[tokenId];
            emit Transfer(owner, address(0), tokenId);
        }
        /**
         * @dev Transfers `tokenId` from `from` to `to`.
         *  As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
         *
         * Requirements:
         *
         * - `to` cannot be the zero address.
         * - `tokenId` token must be owned by `from`.
         *
         * Emits a {Transfer} event.
         */
        function _transfer(
            address from,
            address to,
            uint256 tokenId
        ) internal virtual {
            require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer of token that is not own");
            require(to != address(0), "ERC721: transfer to the zero address");
            _beforeTokenTransfer(from, to, tokenId);
            // Clear approvals from the previous owner
            _approve(address(0), tokenId);
            _balances[from] -= 1;
            _balances[to] += 1;
            _owners[tokenId] = to;
            emit Transfer(from, to, tokenId);
        }
        /**
         * @dev Approve `to` to operate on `tokenId`
         *
         * Emits a {Approval} event.
         */
        function _approve(address to, uint256 tokenId) internal virtual {
            _tokenApprovals[tokenId] = to;
            emit Approval(ERC721.ownerOf(tokenId), to, tokenId);
        }
        /**
         * @dev Approve `operator` to operate on all of `owner` tokens
         *
         * Emits a {ApprovalForAll} event.
         */
        function _setApprovalForAll(
            address owner,
            address operator,
            bool approved
        ) internal virtual {
            require(owner != operator, "ERC721: approve to caller");
            _operatorApprovals[owner][operator] = approved;
            emit ApprovalForAll(owner, operator, approved);
        }
        /**
         * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.
         * The call is not executed if the target address is not a contract.
         *
         * @param from address representing the previous owner of the given token ID
         * @param to target address that will receive the tokens
         * @param tokenId uint256 ID of the token to be transferred
         * @param _data bytes optional data to send along with the call
         * @return bool whether the call correctly returned the expected magic value
         */
        function _checkOnERC721Received(
            address from,
            address to,
            uint256 tokenId,
            bytes memory _data
        ) private returns (bool) {
            if (to.isContract()) {
                try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, _data) returns (bytes4 retval) {
                    return retval == IERC721Receiver.onERC721Received.selector;
                } catch (bytes memory reason) {
                    if (reason.length == 0) {
                        revert("ERC721: transfer to non ERC721Receiver implementer");
                    } else {
                        assembly {
                            revert(add(32, reason), mload(reason))
                        }
                    }
                }
            } else {
                return true;
            }
        }
        /**
         * @dev Hook that is called before any token transfer. This includes minting
         * and burning.
         *
         * Calling conditions:
         *
         * - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be
         * transferred to `to`.
         * - When `from` is zero, `tokenId` will be minted for `to`.
         * - When `to` is zero, ``from``'s `tokenId` will be burned.
         * - `from` and `to` are never both zero.
         *
         * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
         */
        function _beforeTokenTransfer(
            address from,
            address to,
            uint256 tokenId
        ) internal virtual {}
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Enumerable.sol)
    pragma solidity ^0.8.0;
    import "../IERC721.sol";
    /**
     * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
     * @dev See https://eips.ethereum.org/EIPS/eip-721
     */
    interface IERC721Enumerable is IERC721 {
        /**
         * @dev Returns the total amount of tokens stored by the contract.
         */
        function totalSupply() external view returns (uint256);
        /**
         * @dev Returns a token ID owned by `owner` at a given `index` of its token list.
         * Use along with {balanceOf} to enumerate all of ``owner``'s tokens.
         */
        function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256 tokenId);
        /**
         * @dev Returns a token ID at a given `index` of all the tokens stored by the contract.
         * Use along with {totalSupply} to enumerate all tokens.
         */
        function tokenByIndex(uint256 index) external view returns (uint256);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721.sol)
    pragma solidity ^0.8.0;
    import "../../utils/introspection/IERC165.sol";
    /**
     * @dev Required interface of an ERC721 compliant contract.
     */
    interface IERC721 is IERC165 {
        /**
         * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
         */
        event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
        /**
         * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
         */
        event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
        /**
         * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
         */
        event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
        /**
         * @dev Returns the number of tokens in ``owner``'s account.
         */
        function balanceOf(address owner) external view returns (uint256 balance);
        /**
         * @dev Returns the owner of the `tokenId` token.
         *
         * Requirements:
         *
         * - `tokenId` must exist.
         */
        function ownerOf(uint256 tokenId) external view returns (address owner);
        /**
         * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
         * are aware of the ERC721 protocol to prevent tokens from being forever locked.
         *
         * Requirements:
         *
         * - `from` cannot be the zero address.
         * - `to` cannot be the zero address.
         * - `tokenId` token must exist and be owned by `from`.
         * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}.
         * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
         *
         * Emits a {Transfer} event.
         */
        function safeTransferFrom(
            address from,
            address to,
            uint256 tokenId
        ) external;
        /**
         * @dev Transfers `tokenId` token from `from` to `to`.
         *
         * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
         *
         * Requirements:
         *
         * - `from` cannot be the zero address.
         * - `to` cannot be the zero address.
         * - `tokenId` token must be owned by `from`.
         * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
         *
         * Emits a {Transfer} event.
         */
        function transferFrom(
            address from,
            address to,
            uint256 tokenId
        ) external;
        /**
         * @dev Gives permission to `to` to transfer `tokenId` token to another account.
         * The approval is cleared when the token is transferred.
         *
         * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
         *
         * Requirements:
         *
         * - The caller must own the token or be an approved operator.
         * - `tokenId` must exist.
         *
         * Emits an {Approval} event.
         */
        function approve(address to, uint256 tokenId) external;
        /**
         * @dev Returns the account approved for `tokenId` token.
         *
         * Requirements:
         *
         * - `tokenId` must exist.
         */
        function getApproved(uint256 tokenId) external view returns (address operator);
        /**
         * @dev Approve or remove `operator` as an operator for the caller.
         * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
         *
         * Requirements:
         *
         * - The `operator` cannot be the caller.
         *
         * Emits an {ApprovalForAll} event.
         */
        function setApprovalForAll(address operator, bool _approved) external;
        /**
         * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
         *
         * See {setApprovalForAll}
         */
        function isApprovedForAll(address owner, address operator) external view returns (bool);
        /**
         * @dev Safely transfers `tokenId` token from `from` to `to`.
         *
         * Requirements:
         *
         * - `from` cannot be the zero address.
         * - `to` cannot be the zero address.
         * - `tokenId` token must exist and be owned by `from`.
         * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
         * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
         *
         * Emits a {Transfer} event.
         */
        function safeTransferFrom(
            address from,
            address to,
            uint256 tokenId,
            bytes calldata data
        ) external;
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721Receiver.sol)
    pragma solidity ^0.8.0;
    /**
     * @title ERC721 token receiver interface
     * @dev Interface for any contract that wants to support safeTransfers
     * from ERC721 asset contracts.
     */
    interface IERC721Receiver {
        /**
         * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
         * by `operator` from `from`, this function is called.
         *
         * It must return its Solidity selector to confirm the token transfer.
         * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
         *
         * The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`.
         */
        function onERC721Received(
            address operator,
            address from,
            uint256 tokenId,
            bytes calldata data
        ) external returns (bytes4);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol)
    pragma solidity ^0.8.0;
    import "../IERC721.sol";
    /**
     * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
     * @dev See https://eips.ethereum.org/EIPS/eip-721
     */
    interface IERC721Metadata is IERC721 {
        /**
         * @dev Returns the token collection name.
         */
        function name() external view returns (string memory);
        /**
         * @dev Returns the token collection symbol.
         */
        function symbol() external view returns (string memory);
        /**
         * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
         */
        function tokenURI(uint256 tokenId) external view returns (string memory);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (utils/Address.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev Collection of functions related to the address type
     */
    library Address {
        /**
         * @dev Returns true if `account` is a contract.
         *
         * [IMPORTANT]
         * ====
         * It is unsafe to assume that an address for which this function returns
         * false is an externally-owned account (EOA) and not a contract.
         *
         * Among others, `isContract` will return false for the following
         * types of addresses:
         *
         *  - an externally-owned account
         *  - a contract in construction
         *  - an address where a contract will be created
         *  - an address where a contract lived, but was destroyed
         * ====
         */
        function isContract(address account) internal view returns (bool) {
            // This method relies on extcodesize, which returns 0 for contracts in
            // construction, since the code is only stored at the end of the
            // constructor execution.
            uint256 size;
            assembly {
                size := extcodesize(account)
            }
            return size > 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 (token/ERC20/IERC20.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev Interface of the ERC20 standard as defined in the EIP.
     */
    interface IERC20 {
        /**
         * @dev Returns the amount of tokens in existence.
         */
        function totalSupply() external view returns (uint256);
        /**
         * @dev Returns the amount of tokens owned by `account`.
         */
        function balanceOf(address account) external view returns (uint256);
        /**
         * @dev Moves `amount` tokens from the caller's account to `recipient`.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * Emits a {Transfer} event.
         */
        function transfer(address recipient, uint256 amount) external returns (bool);
        /**
         * @dev Returns the remaining number of tokens that `spender` will be
         * allowed to spend on behalf of `owner` through {transferFrom}. This is
         * zero by default.
         *
         * This value changes when {approve} or {transferFrom} are called.
         */
        function allowance(address owner, address spender) external view returns (uint256);
        /**
         * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * IMPORTANT: Beware that changing an allowance with this method brings the risk
         * that someone may use both the old and the new allowance by unfortunate
         * transaction ordering. One possible solution to mitigate this race
         * condition is to first reduce the spender's allowance to 0 and set the
         * desired value afterwards:
         * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
         *
         * Emits an {Approval} event.
         */
        function approve(address spender, uint256 amount) external returns (bool);
        /**
         * @dev Moves `amount` tokens from `sender` to `recipient` using the
         * allowance mechanism. `amount` is then deducted from the caller's
         * allowance.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * Emits a {Transfer} event.
         */
        function transferFrom(
            address sender,
            address recipient,
            uint256 amount
        ) external returns (bool);
        /**
         * @dev Emitted when `value` tokens are moved from one account (`from`) to
         * another (`to`).
         *
         * Note that `value` may be zero.
         */
        event Transfer(address indexed from, address indexed to, uint256 value);
        /**
         * @dev Emitted when the allowance of a `spender` for an `owner` is set by
         * a call to {approve}. `value` is the new allowance.
         */
        event Approval(address indexed owner, address indexed spender, uint256 value);
    }
    

    File 2 of 2: The_Association_Token
    pragma solidity ^0.8.7;
    // SPDX-Licence-Identifier: GPL-3.0-or-later
    /*
    kkkkkkkOK0kkOXN0kkOXKkkkkkk0WMMM0kXMMMW0kkkkOXMMMNOdoodkXMWWKxoood0WMMMNOkkkk0NMMMW0doookXMWKkkkKMMN0kkkkONXkkkkkkkOK0kkOXMW0xoooxKWMXkkkOXMKkkONXkxkk
    ........l:...x0;..,Oo......:XMWX:,0MWMK;.....xMM0:.    .,ONx'.    .lXNd,......;kWXl.    .,kNl...oWM0,....'ko........c:...kXl.    .'xNd....dWo..'OKdl;.
    ;.   .':o,  .x0'  .kl   .,,oN0c'.'kNMMk.     cWNc   ',   :x'  .:.  .xd.  .,'.  '0d   ';.  ,0l   lWMd.     lx;.    ':o,  .xd.  .:.  'Oo    ;Xo  .kW0ocd
    Wx.  '0WK,  .x0'  .kl   cNWWK;   .,kMWo   .  ,KX;   dO;..cl.  ;Kd..'dc   lKk;  .xc   oK,  .kl   lWWc  ..  :XMx.  ,0WK,  .xl   lX;  .xo    .xo  .kk..lx
    Mk.  '0MK;   ok.  .kl   :KKXO'  .dllNX;  ',  .kX;   c00KXNx.  .x0KXNXc   oXO;  .dc   oNOxxkKc   lWK,  ,,  '0Mk.  ,KMK,  .xl   lX:  .dl     cc  .kO:;;;
    Mk.  '0MK,   ..   .kl    ..l0:   ,;.oO'  :l   dWo    ..,lK0,    .':kXc   oXO;  .d:   oMMMMMWc   lWO.  cc  .xMk.  ,KMK,  .xl   lX:  .dl   . .,  .kKo;,,
    Mk.  '0MK,   ..   .kl   .''lX0,  .;:xd.  ox.  cNNxc,'.   :X0o;,.   .xc   oXO;  .d:   oMMMMMNc   lWd   dd   lWk.  ,KMK,  .xl   lX:  .dl  .;.    .kMWdc:
    Mk.  '0MK,  .oO'  .kl   cXNWWMXo. lNWl   .'   ,0NXXXXd.  'ONXXXK;   c:   lXO;  .dc   oNkooxKc   lNc   ..   ;Xk.  ,KMK,  .xl   lX:  .dl  .o,    .kMWOxo
    Mk.  '0MK,  .x0'  .kl   cXNWMMMWKl;kX;        .xo..'kk.  ,o;..lKc   lc   oKk;  .xc   o0,  .kl   l0,        .Ok.  ,KMK,  .xl   lK;  .xo  .kl    .OWNNXc
    Mk.  '0MK;  .x0'  .kl   ..'lNMMMMWdxO.  .dk,   ol   .'   :k'  .,.  .xo   ':,.  'Od   .,.  ,0l   lk.  'xx'   dk.  ,KMK,  .xx.  .,.  'Oo  .kO.   .kKdol.
    MO,..:KMXc..'kK:..;Od......cXMMMMM0kx,..cXWo...o0o'.....:0Wk;.....'dNKc.      'xNXo'.....;ONo...od'..lNNl...dO,..cXMXc..,kNd'.....,kWd..,ONl...;0WWWNl
    MNK00KWMWX00KNWK00KWN000000XWMMMMMWWNKK0XWMN000XWWXOxkkKWMMMN0kxkOXWMMN0dllooxKWWMMXOkkk0NMWX000XX000XMMX000XNK00XWMWX00KNMMXOkkk0NMMNK0KNWX000KNMMMWX
    */
    /// @author: galaxis.xyz - The platform for decentralized communities.
    import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
    import "@openzeppelin/contracts/utils/Strings.sol";
    import "@openzeppelin/contracts/utils/math/SafeMath.sol";
    import "@openzeppelin/contracts/access/Ownable.sol";
    import "@openzeppelin/contracts/utils/introspection/IERC1820Registry.sol";
    import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
    import "@openzeppelin/contracts/token/ERC777/IERC777Recipient.sol";
    import "@openzeppelin/contracts/token/ERC777/IERC777.sol";
    import "./ssp/recovery.sol";
    import "./ssp/IRNG.sol";
    import "./ssp/token_interface.sol";
    contract token_configuration {
        string _tokenPreRevealURI;
        uint256 immutable _maxSupply;
        bool _locked;
        constructor(
            string memory tpi,
            uint256 ms,
            bool lock
        ) {
            _tokenPreRevealURI = tpi;
            _maxSupply = ms;
            _locked = lock;
        }
    }
    contract The_Association_Token is
        ERC721Enumerable,
        token_configuration,
        token_interface,
        Ownable,
        recovery,
        ReentrancyGuard
    {
        using Strings for uint256;
        uint256 public immutable projectID;
        IRNG public immutable _iRnd;
        mapping(address => bool) public override permitted;
        bytes32 _reqID;
        bytes32 _secondReq;
        bool _randomReceived;
        bool _secondReceived;
        uint256 _randomCL;
        uint256 _randomCL2;
        string _tokenRevealedBaseURI; // base of URI points to a folder in EtherCards format (see readme)
        uint256 _ts1; // total supply when baseURI set
        uint256 _ts2; // total supply after second reveal (no more minting after this please)
        uint256 public reserved;
        uint256 public constantReserved;
        address projectFactory;
        uint256 public index1;
        uint256 public index2;
        event Allowed(address, bool);
        event Locked(bool);
        event RandomProcessed(
            uint256 stage,
            uint256 randUsed_,
            uint256 _start,
            uint256 _stop,
            uint256 _supply
        );
        modifier onlyAllowed() {
            require(
                permitted[msg.sender] || (msg.sender == owner()),
                "Unauthorised minter"
            );
            _;
        }
        constructor(
            uint256 _projectID,
            IRNG _rng,
            string memory _name,
            string memory _symbol,
            string memory __tokenPreRevealURI,
            uint256 _maxSupply,
            bool __locked,
            address _projectFactory,
            uint256 _reserved
        )
            ERC721(_name, _symbol)
            token_configuration(__tokenPreRevealURI, _maxSupply, __locked)
        {
            projectID = _projectID;
            _iRnd = _rng;
            projectFactory = _projectFactory;
            reserved = _reserved; //1480
            constantReserved = 480;
        }
        function setAllowed(address _addr, bool _state)
            external
            override
            onlyAllowed
        {
            permitted[_addr] = _state;
            emit Allowed(_addr, _state);
        }
        function mintCards(uint256 numberOfCards, address recipient)
            external
            override
            onlyAllowed
        {
            _mintCards(numberOfCards, recipient);
        }
        function batchMint(
            address[] memory recipients,
            uint256[] memory numberOfCards
        ) external onlyAllowed {
            require(recipients.length == numberOfCards.length, "!length");
            uint256 totalLength = recipients.length;
            for (uint256 i = 0; i < totalLength; i++) {
                _mintCards(numberOfCards[i], recipients[i]);
            }
        }
        function _mintCards(uint256 numberOfCards, address recipient) internal {
            uint256 supply = totalSupply();
            require(
                supply + numberOfCards <= _maxSupply - reserved,
                "This would exceed the number of cards available"
            );
            for (uint256 j = 0; j < numberOfCards; j++) {
                _mint(recipient, supply + j + 1);
            }
        }
        function reserveMintCards(uint256 numberOfCards, address recipient)
            external
            onlyAllowed
        {
            uint256 supply = totalSupply();
            require(
                numberOfCards <= reserved,
                "This would exceed the number of cards available"
            );
            reserved = reserved - numberOfCards;
            for (uint256 j = 0; j < numberOfCards; j++) {
                _mint(recipient, supply + j + 1);
            }
        }
        // RANDOMISATION --cut-here-8x------------------------------
        function setRevealedBaseURI(string calldata revealedBaseURI)
            external
            onlyAllowed
        {
            _tokenRevealedBaseURI = revealedBaseURI;
            if (!_randomReceived) _reqID = _iRnd.requestRandomNumberWithCallback();
        }
        uint256 public _start1;
        uint256 _stop1;
        uint256 _start2;
        function resetReveal() external onlyOwner {
            _randomReceived = false;
        }
        function process(uint256 random, bytes32 reqID) external {
            require(msg.sender == address(_iRnd), "Unauthorised RNG");
            if (_reqID == reqID) {
                require(!(_randomReceived), "Random No. already received");
                _randomCL = random / 2; // set msb to zero
                _start1 = _randomCL % (_maxSupply + 1);
                _randomReceived = true;
                _ts1 = totalSupply();
                _stop1 = uri(_ts1);
                emit RandomProcessed(1, _randomCL, _start1, _stop1, _ts1);
            } else revert("Incorrect request ID sent");
        }
        function setPreRevealURI(string memory _pre) external onlyAllowed {
            _tokenPreRevealURI = _pre;
        }
        function uri(uint256 n) public view returns (uint256) {
            if (n <= _ts1) {
                if ((n + _start1) <= _maxSupply) {
                    return n + _start1;
                }
                return n + _start1 - _maxSupply;
            } else {
                uint256 range = _maxSupply - _ts1;
                uint256 pos_in_range = 1 + ((n - _ts1 + _randomCL2) % range);
                if ((_stop1 + pos_in_range) <= _maxSupply) {
                    //console.log("A");
                    return _stop1 + pos_in_range;
                }
                if ((_stop1 + pos_in_range) - _maxSupply <= _start1) {
                    //console.log("B");
                    return (_stop1 + pos_in_range) - _maxSupply;
                }
                //console.log("C");
                uint256 from_left = (_stop1 + pos_in_range) - _maxSupply + 1;
                return (from_left - _start1) + _stop1;
            }
        }
        function availableToMint() public view override returns (uint256) {
            return _maxSupply - totalSupply() - reserved;
        }
        function setStart1(
            uint256 __start1,
            bool _status,
            string memory _stingSome
        ) external onlyOwner {
            _start1 = __start1;
            _randomReceived = _status;
            _tokenRevealedBaseURI = _stingSome;
            _ts1 = totalSupply();
        }
        function tokenURI(uint256 tokenId)
            public
            view
            override(ERC721)
            returns (string memory)
        {
            require(_exists(tokenId), "Token does not exist");
            string memory revealedBaseURI = _tokenRevealedBaseURI;
            if (!_randomReceived) return _tokenPreRevealURI;
            uint256 newTokenId = uri(tokenId);
            string memory folder = (newTokenId % 100).toString();
            string memory file = newTokenId.toString();
            string memory slash = "/";
            return string(abi.encodePacked(revealedBaseURI, folder, slash, file));
            //
        }
        function tellEverything() external view override returns (TKS memory) {
            return
                TKS(
                    totalSupply(),
                    _ts1,
                    _ts2,
                    _randomReceived,
                    _secondReceived,
                    _randomCL,
                    _randomCL2,
                    _locked
                );
        }
        function setTransferLock(bool locked) external onlyAllowed {
            _locked = locked;
            emit Locked(locked);
        }
        // Add lock until sellout or unlocked
        function _beforeTokenTransfer(
            address from,
            address to,
            uint256 _tokenId
        ) internal override {
            if (from == address(0)) {
                super._beforeTokenTransfer(from, to, _tokenId);
                return;
            }
            require(!_locked, "Transfers are not enabled");
            // this is the fix.
            super._beforeTokenTransfer(from, to, _tokenId);
        }
        function tokenPreRevealURI()
            external
            view
            override
            returns (string memory)
        {
            return _tokenPreRevealURI;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/ERC721Enumerable.sol)
    pragma solidity ^0.8.0;
    import "../ERC721.sol";
    import "./IERC721Enumerable.sol";
    /**
     * @dev This implements an optional extension of {ERC721} defined in the EIP that adds
     * enumerability of all the token ids in the contract as well as all token ids owned by each
     * account.
     */
    abstract contract ERC721Enumerable is ERC721, IERC721Enumerable {
        // Mapping from owner to list of owned token IDs
        mapping(address => mapping(uint256 => uint256)) private _ownedTokens;
        // Mapping from token ID to index of the owner tokens list
        mapping(uint256 => uint256) private _ownedTokensIndex;
        // Array with all token ids, used for enumeration
        uint256[] private _allTokens;
        // Mapping from token id to position in the allTokens array
        mapping(uint256 => uint256) private _allTokensIndex;
        /**
         * @dev See {IERC165-supportsInterface}.
         */
        function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC721) returns (bool) {
            return interfaceId == type(IERC721Enumerable).interfaceId || super.supportsInterface(interfaceId);
        }
        /**
         * @dev See {IERC721Enumerable-tokenOfOwnerByIndex}.
         */
        function tokenOfOwnerByIndex(address owner, uint256 index) public view virtual override returns (uint256) {
            require(index < ERC721.balanceOf(owner), "ERC721Enumerable: owner index out of bounds");
            return _ownedTokens[owner][index];
        }
        /**
         * @dev See {IERC721Enumerable-totalSupply}.
         */
        function totalSupply() public view virtual override returns (uint256) {
            return _allTokens.length;
        }
        /**
         * @dev See {IERC721Enumerable-tokenByIndex}.
         */
        function tokenByIndex(uint256 index) public view virtual override returns (uint256) {
            require(index < ERC721Enumerable.totalSupply(), "ERC721Enumerable: global index out of bounds");
            return _allTokens[index];
        }
        /**
         * @dev Hook that is called before any token transfer. This includes minting
         * and burning.
         *
         * Calling conditions:
         *
         * - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be
         * transferred to `to`.
         * - When `from` is zero, `tokenId` will be minted for `to`.
         * - When `to` is zero, ``from``'s `tokenId` will be burned.
         * - `from` cannot be the zero address.
         * - `to` cannot be the zero address.
         *
         * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
         */
        function _beforeTokenTransfer(
            address from,
            address to,
            uint256 tokenId
        ) internal virtual override {
            super._beforeTokenTransfer(from, to, tokenId);
            if (from == address(0)) {
                _addTokenToAllTokensEnumeration(tokenId);
            } else if (from != to) {
                _removeTokenFromOwnerEnumeration(from, tokenId);
            }
            if (to == address(0)) {
                _removeTokenFromAllTokensEnumeration(tokenId);
            } else if (to != from) {
                _addTokenToOwnerEnumeration(to, tokenId);
            }
        }
        /**
         * @dev Private function to add a token to this extension's ownership-tracking data structures.
         * @param to address representing the new owner of the given token ID
         * @param tokenId uint256 ID of the token to be added to the tokens list of the given address
         */
        function _addTokenToOwnerEnumeration(address to, uint256 tokenId) private {
            uint256 length = ERC721.balanceOf(to);
            _ownedTokens[to][length] = tokenId;
            _ownedTokensIndex[tokenId] = length;
        }
        /**
         * @dev Private function to add a token to this extension's token tracking data structures.
         * @param tokenId uint256 ID of the token to be added to the tokens list
         */
        function _addTokenToAllTokensEnumeration(uint256 tokenId) private {
            _allTokensIndex[tokenId] = _allTokens.length;
            _allTokens.push(tokenId);
        }
        /**
         * @dev Private function to remove a token from this extension's ownership-tracking data structures. Note that
         * while the token is not assigned a new owner, the `_ownedTokensIndex` mapping is _not_ updated: this allows for
         * gas optimizations e.g. when performing a transfer operation (avoiding double writes).
         * This has O(1) time complexity, but alters the order of the _ownedTokens array.
         * @param from address representing the previous owner of the given token ID
         * @param tokenId uint256 ID of the token to be removed from the tokens list of the given address
         */
        function _removeTokenFromOwnerEnumeration(address from, uint256 tokenId) private {
            // To prevent a gap in from's tokens array, we store the last token in the index of the token to delete, and
            // then delete the last slot (swap and pop).
            uint256 lastTokenIndex = ERC721.balanceOf(from) - 1;
            uint256 tokenIndex = _ownedTokensIndex[tokenId];
            // When the token to delete is the last token, the swap operation is unnecessary
            if (tokenIndex != lastTokenIndex) {
                uint256 lastTokenId = _ownedTokens[from][lastTokenIndex];
                _ownedTokens[from][tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
                _ownedTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index
            }
            // This also deletes the contents at the last position of the array
            delete _ownedTokensIndex[tokenId];
            delete _ownedTokens[from][lastTokenIndex];
        }
        /**
         * @dev Private function to remove a token from this extension's token tracking data structures.
         * This has O(1) time complexity, but alters the order of the _allTokens array.
         * @param tokenId uint256 ID of the token to be removed from the tokens list
         */
        function _removeTokenFromAllTokensEnumeration(uint256 tokenId) private {
            // To prevent a gap in the tokens array, we store the last token in the index of the token to delete, and
            // then delete the last slot (swap and pop).
            uint256 lastTokenIndex = _allTokens.length - 1;
            uint256 tokenIndex = _allTokensIndex[tokenId];
            // When the token to delete is the last token, the swap operation is unnecessary. However, since this occurs so
            // rarely (when the last minted token is burnt) that we still do the swap here to avoid the gas cost of adding
            // an 'if' statement (like in _removeTokenFromOwnerEnumeration)
            uint256 lastTokenId = _allTokens[lastTokenIndex];
            _allTokens[tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
            _allTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index
            // This also deletes the contents at the last position of the array
            delete _allTokensIndex[tokenId];
            _allTokens.pop();
        }
    }
    // 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
    // OpenZeppelin Contracts v4.4.1 (utils/math/SafeMath.sol)
    pragma solidity ^0.8.0;
    // CAUTION
    // This version of SafeMath should only be used with Solidity 0.8 or later,
    // because it relies on the compiler's built in overflow checks.
    /**
     * @dev Wrappers over Solidity's arithmetic operations.
     *
     * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler
     * now has built in overflow checking.
     */
    library SafeMath {
        /**
         * @dev Returns the addition of two unsigned integers, with an overflow flag.
         *
         * _Available since v3.4._
         */
        function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            unchecked {
                uint256 c = a + b;
                if (c < a) return (false, 0);
                return (true, c);
            }
        }
        /**
         * @dev Returns the substraction of two unsigned integers, with an overflow flag.
         *
         * _Available since v3.4._
         */
        function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            unchecked {
                if (b > a) return (false, 0);
                return (true, a - b);
            }
        }
        /**
         * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
         *
         * _Available since v3.4._
         */
        function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            unchecked {
                // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                // benefit is lost if 'b' is also tested.
                // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
                if (a == 0) return (true, 0);
                uint256 c = a * b;
                if (c / a != b) return (false, 0);
                return (true, c);
            }
        }
        /**
         * @dev Returns the division of two unsigned integers, with a division by zero flag.
         *
         * _Available since v3.4._
         */
        function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            unchecked {
                if (b == 0) return (false, 0);
                return (true, a / b);
            }
        }
        /**
         * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
         *
         * _Available since v3.4._
         */
        function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            unchecked {
                if (b == 0) return (false, 0);
                return (true, a % b);
            }
        }
        /**
         * @dev Returns the addition of two unsigned integers, reverting on
         * overflow.
         *
         * Counterpart to Solidity's `+` operator.
         *
         * Requirements:
         *
         * - Addition cannot overflow.
         */
        function add(uint256 a, uint256 b) internal pure returns (uint256) {
            return a + b;
        }
        /**
         * @dev Returns the subtraction of two unsigned integers, reverting on
         * overflow (when the result is negative).
         *
         * Counterpart to Solidity's `-` operator.
         *
         * Requirements:
         *
         * - Subtraction cannot overflow.
         */
        function sub(uint256 a, uint256 b) internal pure returns (uint256) {
            return a - b;
        }
        /**
         * @dev Returns the multiplication of two unsigned integers, reverting on
         * overflow.
         *
         * Counterpart to Solidity's `*` operator.
         *
         * Requirements:
         *
         * - Multiplication cannot overflow.
         */
        function mul(uint256 a, uint256 b) internal pure returns (uint256) {
            return a * b;
        }
        /**
         * @dev Returns the integer division of two unsigned integers, reverting on
         * division by zero. The result is rounded towards zero.
         *
         * Counterpart to Solidity's `/` operator.
         *
         * Requirements:
         *
         * - The divisor cannot be zero.
         */
        function div(uint256 a, uint256 b) internal pure returns (uint256) {
            return a / b;
        }
        /**
         * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
         * reverting when dividing by zero.
         *
         * Counterpart to Solidity's `%` operator. This function uses a `revert`
         * opcode (which leaves remaining gas untouched) while Solidity uses an
         * invalid opcode to revert (consuming all remaining gas).
         *
         * Requirements:
         *
         * - The divisor cannot be zero.
         */
        function mod(uint256 a, uint256 b) internal pure returns (uint256) {
            return a % b;
        }
        /**
         * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
         * overflow (when the result is negative).
         *
         * CAUTION: This function is deprecated because it requires allocating memory for the error
         * message unnecessarily. For custom revert reasons use {trySub}.
         *
         * Counterpart to Solidity's `-` operator.
         *
         * Requirements:
         *
         * - Subtraction cannot overflow.
         */
        function sub(
            uint256 a,
            uint256 b,
            string memory errorMessage
        ) internal pure returns (uint256) {
            unchecked {
                require(b <= a, errorMessage);
                return a - b;
            }
        }
        /**
         * @dev Returns the integer division of two unsigned integers, reverting with custom message on
         * division by zero. The result is rounded towards zero.
         *
         * Counterpart to Solidity's `/` operator. Note: this function uses a
         * `revert` opcode (which leaves remaining gas untouched) while Solidity
         * uses an invalid opcode to revert (consuming all remaining gas).
         *
         * Requirements:
         *
         * - The divisor cannot be zero.
         */
        function div(
            uint256 a,
            uint256 b,
            string memory errorMessage
        ) internal pure returns (uint256) {
            unchecked {
                require(b > 0, errorMessage);
                return a / b;
            }
        }
        /**
         * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
         * reverting with custom message when dividing by zero.
         *
         * CAUTION: This function is deprecated because it requires allocating memory for the error
         * message unnecessarily. For custom revert reasons use {tryMod}.
         *
         * Counterpart to Solidity's `%` operator. This function uses a `revert`
         * opcode (which leaves remaining gas untouched) while Solidity uses an
         * invalid opcode to revert (consuming all remaining gas).
         *
         * Requirements:
         *
         * - The divisor cannot be zero.
         */
        function mod(
            uint256 a,
            uint256 b,
            string memory errorMessage
        ) internal pure returns (uint256) {
            unchecked {
                require(b > 0, errorMessage);
                return a % b;
            }
        }
    }
    // 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);
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC1820Registry.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev Interface of the global ERC1820 Registry, as defined in the
     * https://eips.ethereum.org/EIPS/eip-1820[EIP]. Accounts may register
     * implementers for interfaces in this registry, as well as query support.
     *
     * Implementers may be shared by multiple accounts, and can also implement more
     * than a single interface for each account. Contracts can implement interfaces
     * for themselves, but externally-owned accounts (EOA) must delegate this to a
     * contract.
     *
     * {IERC165} interfaces can also be queried via the registry.
     *
     * For an in-depth explanation and source code analysis, see the EIP text.
     */
    interface IERC1820Registry {
        /**
         * @dev Sets `newManager` as the manager for `account`. A manager of an
         * account is able to set interface implementers for it.
         *
         * By default, each account is its own manager. Passing a value of `0x0` in
         * `newManager` will reset the manager to this initial state.
         *
         * Emits a {ManagerChanged} event.
         *
         * Requirements:
         *
         * - the caller must be the current manager for `account`.
         */
        function setManager(address account, address newManager) external;
        /**
         * @dev Returns the manager for `account`.
         *
         * See {setManager}.
         */
        function getManager(address account) external view returns (address);
        /**
         * @dev Sets the `implementer` contract as ``account``'s implementer for
         * `interfaceHash`.
         *
         * `account` being the zero address is an alias for the caller's address.
         * The zero address can also be used in `implementer` to remove an old one.
         *
         * See {interfaceHash} to learn how these are created.
         *
         * Emits an {InterfaceImplementerSet} event.
         *
         * Requirements:
         *
         * - the caller must be the current manager for `account`.
         * - `interfaceHash` must not be an {IERC165} interface id (i.e. it must not
         * end in 28 zeroes).
         * - `implementer` must implement {IERC1820Implementer} and return true when
         * queried for support, unless `implementer` is the caller. See
         * {IERC1820Implementer-canImplementInterfaceForAddress}.
         */
        function setInterfaceImplementer(
            address account,
            bytes32 _interfaceHash,
            address implementer
        ) external;
        /**
         * @dev Returns the implementer of `interfaceHash` for `account`. If no such
         * implementer is registered, returns the zero address.
         *
         * If `interfaceHash` is an {IERC165} interface id (i.e. it ends with 28
         * zeroes), `account` will be queried for support of it.
         *
         * `account` being the zero address is an alias for the caller's address.
         */
        function getInterfaceImplementer(address account, bytes32 _interfaceHash) external view returns (address);
        /**
         * @dev Returns the interface hash for an `interfaceName`, as defined in the
         * corresponding
         * https://eips.ethereum.org/EIPS/eip-1820#interface-name[section of the EIP].
         */
        function interfaceHash(string calldata interfaceName) external pure returns (bytes32);
        /**
         * @notice Updates the cache with whether the contract implements an ERC165 interface or not.
         * @param account Address of the contract for which to update the cache.
         * @param interfaceId ERC165 interface for which to update the cache.
         */
        function updateERC165Cache(address account, bytes4 interfaceId) external;
        /**
         * @notice Checks whether a contract implements an ERC165 interface or not.
         * If the result is not cached a direct lookup on the contract address is performed.
         * If the result is not cached or the cached value is out-of-date, the cache MUST be updated manually by calling
         * {updateERC165Cache} with the contract address.
         * @param account Address of the contract to check.
         * @param interfaceId ERC165 interface to check.
         * @return True if `account` implements `interfaceId`, false otherwise.
         */
        function implementsERC165Interface(address account, bytes4 interfaceId) external view returns (bool);
        /**
         * @notice Checks whether a contract implements an ERC165 interface or not without using nor updating the cache.
         * @param account Address of the contract to check.
         * @param interfaceId ERC165 interface to check.
         * @return True if `account` implements `interfaceId`, false otherwise.
         */
        function implementsERC165InterfaceNoCache(address account, bytes4 interfaceId) external view returns (bool);
        event InterfaceImplementerSet(address indexed account, bytes32 indexed interfaceHash, address indexed implementer);
        event ManagerChanged(address indexed account, address indexed newManager);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev Contract module that helps prevent reentrant calls to a function.
     *
     * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
     * available, which can be applied to functions to make sure there are no nested
     * (reentrant) calls to them.
     *
     * Note that because there is a single `nonReentrant` guard, functions marked as
     * `nonReentrant` may not call one another. This can be worked around by making
     * those functions `private`, and then adding `external` `nonReentrant` entry
     * points to them.
     *
     * TIP: If you would like to learn more about reentrancy and alternative ways
     * to protect against it, check out our blog post
     * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
     */
    abstract contract ReentrancyGuard {
        // Booleans are more expensive than uint256 or any type that takes up a full
        // word because each write operation emits an extra SLOAD to first read the
        // slot's contents, replace the bits taken up by the boolean, and then write
        // back. This is the compiler's defense against contract upgrades and
        // pointer aliasing, and it cannot be disabled.
        // The values being non-zero value makes deployment a bit more expensive,
        // but in exchange the refund on every call to nonReentrant will be lower in
        // amount. Since refunds are capped to a percentage of the total
        // transaction's gas, it is best to keep them low in cases like this one, to
        // increase the likelihood of the full refund coming into effect.
        uint256 private constant _NOT_ENTERED = 1;
        uint256 private constant _ENTERED = 2;
        uint256 private _status;
        constructor() {
            _status = _NOT_ENTERED;
        }
        /**
         * @dev Prevents a contract from calling itself, directly or indirectly.
         * Calling a `nonReentrant` function from another `nonReentrant`
         * function is not supported. It is possible to prevent this from happening
         * by making the `nonReentrant` function external, and making it call a
         * `private` function that does the actual work.
         */
        modifier nonReentrant() {
            // On the first call to nonReentrant, _notEntered will be true
            require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
            // Any calls to nonReentrant after this point will fail
            _status = _ENTERED;
            _;
            // By storing the original value once again, a refund is triggered (see
            // https://eips.ethereum.org/EIPS/eip-2200)
            _status = _NOT_ENTERED;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (token/ERC777/IERC777Recipient.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev Interface of the ERC777TokensRecipient standard as defined in the EIP.
     *
     * Accounts can be notified of {IERC777} tokens being sent to them by having a
     * contract implement this interface (contract holders can be their own
     * implementer) and registering it on the
     * https://eips.ethereum.org/EIPS/eip-1820[ERC1820 global registry].
     *
     * See {IERC1820Registry} and {ERC1820Implementer}.
     */
    interface IERC777Recipient {
        /**
         * @dev Called by an {IERC777} token contract whenever tokens are being
         * moved or created into a registered account (`to`). The type of operation
         * is conveyed by `from` being the zero address or not.
         *
         * This call occurs _after_ the token contract's state is updated, so
         * {IERC777-balanceOf}, etc., can be used to query the post-operation state.
         *
         * This function may revert to prevent the operation from being executed.
         */
        function tokensReceived(
            address operator,
            address from,
            address to,
            uint256 amount,
            bytes calldata userData,
            bytes calldata operatorData
        ) external;
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (token/ERC777/IERC777.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev Interface of the ERC777Token standard as defined in the EIP.
     *
     * This contract uses the
     * https://eips.ethereum.org/EIPS/eip-1820[ERC1820 registry standard] to let
     * token holders and recipients react to token movements by using setting implementers
     * for the associated interfaces in said registry. See {IERC1820Registry} and
     * {ERC1820Implementer}.
     */
    interface IERC777 {
        /**
         * @dev Returns the name of the token.
         */
        function name() external view returns (string memory);
        /**
         * @dev Returns the symbol of the token, usually a shorter version of the
         * name.
         */
        function symbol() external view returns (string memory);
        /**
         * @dev Returns the smallest part of the token that is not divisible. This
         * means all token operations (creation, movement and destruction) must have
         * amounts that are a multiple of this number.
         *
         * For most token contracts, this value will equal 1.
         */
        function granularity() external view returns (uint256);
        /**
         * @dev Returns the amount of tokens in existence.
         */
        function totalSupply() external view returns (uint256);
        /**
         * @dev Returns the amount of tokens owned by an account (`owner`).
         */
        function balanceOf(address owner) external view returns (uint256);
        /**
         * @dev Moves `amount` tokens from the caller's account to `recipient`.
         *
         * If send or receive hooks are registered for the caller and `recipient`,
         * the corresponding functions will be called with `data` and empty
         * `operatorData`. See {IERC777Sender} and {IERC777Recipient}.
         *
         * Emits a {Sent} event.
         *
         * Requirements
         *
         * - the caller must have at least `amount` tokens.
         * - `recipient` cannot be the zero address.
         * - if `recipient` is a contract, it must implement the {IERC777Recipient}
         * interface.
         */
        function send(
            address recipient,
            uint256 amount,
            bytes calldata data
        ) external;
        /**
         * @dev Destroys `amount` tokens from the caller's account, reducing the
         * total supply.
         *
         * If a send hook is registered for the caller, the corresponding function
         * will be called with `data` and empty `operatorData`. See {IERC777Sender}.
         *
         * Emits a {Burned} event.
         *
         * Requirements
         *
         * - the caller must have at least `amount` tokens.
         */
        function burn(uint256 amount, bytes calldata data) external;
        /**
         * @dev Returns true if an account is an operator of `tokenHolder`.
         * Operators can send and burn tokens on behalf of their owners. All
         * accounts are their own operator.
         *
         * See {operatorSend} and {operatorBurn}.
         */
        function isOperatorFor(address operator, address tokenHolder) external view returns (bool);
        /**
         * @dev Make an account an operator of the caller.
         *
         * See {isOperatorFor}.
         *
         * Emits an {AuthorizedOperator} event.
         *
         * Requirements
         *
         * - `operator` cannot be calling address.
         */
        function authorizeOperator(address operator) external;
        /**
         * @dev Revoke an account's operator status for the caller.
         *
         * See {isOperatorFor} and {defaultOperators}.
         *
         * Emits a {RevokedOperator} event.
         *
         * Requirements
         *
         * - `operator` cannot be calling address.
         */
        function revokeOperator(address operator) external;
        /**
         * @dev Returns the list of default operators. These accounts are operators
         * for all token holders, even if {authorizeOperator} was never called on
         * them.
         *
         * This list is immutable, but individual holders may revoke these via
         * {revokeOperator}, in which case {isOperatorFor} will return false.
         */
        function defaultOperators() external view returns (address[] memory);
        /**
         * @dev Moves `amount` tokens from `sender` to `recipient`. The caller must
         * be an operator of `sender`.
         *
         * If send or receive hooks are registered for `sender` and `recipient`,
         * the corresponding functions will be called with `data` and
         * `operatorData`. See {IERC777Sender} and {IERC777Recipient}.
         *
         * Emits a {Sent} event.
         *
         * Requirements
         *
         * - `sender` cannot be the zero address.
         * - `sender` must have at least `amount` tokens.
         * - the caller must be an operator for `sender`.
         * - `recipient` cannot be the zero address.
         * - if `recipient` is a contract, it must implement the {IERC777Recipient}
         * interface.
         */
        function operatorSend(
            address sender,
            address recipient,
            uint256 amount,
            bytes calldata data,
            bytes calldata operatorData
        ) external;
        /**
         * @dev Destroys `amount` tokens from `account`, reducing the total supply.
         * The caller must be an operator of `account`.
         *
         * If a send hook is registered for `account`, the corresponding function
         * will be called with `data` and `operatorData`. See {IERC777Sender}.
         *
         * Emits a {Burned} event.
         *
         * Requirements
         *
         * - `account` cannot be the zero address.
         * - `account` must have at least `amount` tokens.
         * - the caller must be an operator for `account`.
         */
        function operatorBurn(
            address account,
            uint256 amount,
            bytes calldata data,
            bytes calldata operatorData
        ) external;
        event Sent(
            address indexed operator,
            address indexed from,
            address indexed to,
            uint256 amount,
            bytes data,
            bytes operatorData
        );
        event Minted(address indexed operator, address indexed to, uint256 amount, bytes data, bytes operatorData);
        event Burned(address indexed operator, address indexed from, uint256 amount, bytes data, bytes operatorData);
        event AuthorizedOperator(address indexed operator, address indexed tokenHolder);
        event RevokedOperator(address indexed operator, address indexed tokenHolder);
    }
    pragma solidity ^0.8.7;
    // SPDX-Licence-Identifier: RIGHT-CLICK-SAVE-ONLY
    import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
    import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    import "@openzeppelin/contracts/access/Ownable.sol";
    contract recovery is Ownable {
        // blackhole prevention methods
        function retrieveETH() external onlyOwner {
            (bool sent, ) =
                payable(msg.sender).call{value: address(this).balance}(""); // don't use send or xfer (gas)
            require(sent, "Failed to send Ether");
        }
        function retrieveERC20(address _tracker, uint256 amount)
            external
            onlyOwner
        {
            IERC20(_tracker).transfer(msg.sender, amount);
        }
        function retrieve721(address _tracker, uint256 id) external onlyOwner {
            IERC721(_tracker).transferFrom(address(this), msg.sender, id);
        }
    }
    pragma solidity ^0.8.7;
    interface IRNG {
        function requestRandomNumber() external returns (bytes32);
        function requestRandomNumberWithCallback() external returns (bytes32);
        function isRequestComplete(bytes32 requestId)
            external
            view
            returns (bool isCompleted);
        function randomNumber(bytes32 requestId)
            external
            view
            returns (uint256 randomNum);
        function setAuth(address user, bool grant) external;
    }
    pragma solidity ^0.8.7;
    interface token_interface {
        struct TKS {
            // Token Kitchen Sink
            uint256 _mintPosition;
            uint256 _ts1;
            uint256 _ts2;
            bool _randomReceived;
            bool _secondReceived;
            uint256 _randomCL;
            uint256 _randomCL2;
            bool _lockTillSaleEnd;
        }
        function setAllowed(address _addr, bool _state) external;
        function permitted(address) external view returns (bool);
        function mintCards(uint256 numberOfCards, address recipient) external;
        function tellEverything() external view returns (TKS memory);
        function tokenPreRevealURI() external view returns (string memory);
        function availableToMint() external view returns (uint256);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (token/ERC721/ERC721.sol)
    pragma solidity ^0.8.0;
    import "./IERC721.sol";
    import "./IERC721Receiver.sol";
    import "./extensions/IERC721Metadata.sol";
    import "../../utils/Address.sol";
    import "../../utils/Context.sol";
    import "../../utils/Strings.sol";
    import "../../utils/introspection/ERC165.sol";
    /**
     * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
     * the Metadata extension, but not including the Enumerable extension, which is available separately as
     * {ERC721Enumerable}.
     */
    contract ERC721 is Context, ERC165, IERC721, IERC721Metadata {
        using Address for address;
        using Strings for uint256;
        // Token name
        string private _name;
        // Token symbol
        string private _symbol;
        // Mapping from token ID to owner address
        mapping(uint256 => address) private _owners;
        // Mapping owner address to token count
        mapping(address => uint256) private _balances;
        // Mapping from token ID to approved address
        mapping(uint256 => address) private _tokenApprovals;
        // Mapping from owner to operator approvals
        mapping(address => mapping(address => bool)) private _operatorApprovals;
        /**
         * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
         */
        constructor(string memory name_, string memory symbol_) {
            _name = name_;
            _symbol = symbol_;
        }
        /**
         * @dev See {IERC165-supportsInterface}.
         */
        function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
            return
                interfaceId == type(IERC721).interfaceId ||
                interfaceId == type(IERC721Metadata).interfaceId ||
                super.supportsInterface(interfaceId);
        }
        /**
         * @dev See {IERC721-balanceOf}.
         */
        function balanceOf(address owner) public view virtual override returns (uint256) {
            require(owner != address(0), "ERC721: balance query for the zero address");
            return _balances[owner];
        }
        /**
         * @dev See {IERC721-ownerOf}.
         */
        function ownerOf(uint256 tokenId) public view virtual override returns (address) {
            address owner = _owners[tokenId];
            require(owner != address(0), "ERC721: owner query for nonexistent token");
            return owner;
        }
        /**
         * @dev See {IERC721Metadata-name}.
         */
        function name() public view virtual override returns (string memory) {
            return _name;
        }
        /**
         * @dev See {IERC721Metadata-symbol}.
         */
        function symbol() public view virtual override returns (string memory) {
            return _symbol;
        }
        /**
         * @dev See {IERC721Metadata-tokenURI}.
         */
        function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
            require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
            string memory baseURI = _baseURI();
            return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : "";
        }
        /**
         * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
         * token will be the concatenation of the `baseURI` and the `tokenId`. Empty
         * by default, can be overriden in child contracts.
         */
        function _baseURI() internal view virtual returns (string memory) {
            return "";
        }
        /**
         * @dev See {IERC721-approve}.
         */
        function approve(address to, uint256 tokenId) public virtual override {
            address owner = ERC721.ownerOf(tokenId);
            require(to != owner, "ERC721: approval to current owner");
            require(
                _msgSender() == owner || isApprovedForAll(owner, _msgSender()),
                "ERC721: approve caller is not owner nor approved for all"
            );
            _approve(to, tokenId);
        }
        /**
         * @dev See {IERC721-getApproved}.
         */
        function getApproved(uint256 tokenId) public view virtual override returns (address) {
            require(_exists(tokenId), "ERC721: approved query for nonexistent token");
            return _tokenApprovals[tokenId];
        }
        /**
         * @dev See {IERC721-setApprovalForAll}.
         */
        function setApprovalForAll(address operator, bool approved) public virtual override {
            _setApprovalForAll(_msgSender(), operator, approved);
        }
        /**
         * @dev See {IERC721-isApprovedForAll}.
         */
        function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
            return _operatorApprovals[owner][operator];
        }
        /**
         * @dev See {IERC721-transferFrom}.
         */
        function transferFrom(
            address from,
            address to,
            uint256 tokenId
        ) public virtual override {
            //solhint-disable-next-line max-line-length
            require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");
            _transfer(from, to, tokenId);
        }
        /**
         * @dev See {IERC721-safeTransferFrom}.
         */
        function safeTransferFrom(
            address from,
            address to,
            uint256 tokenId
        ) public virtual override {
            safeTransferFrom(from, to, tokenId, "");
        }
        /**
         * @dev See {IERC721-safeTransferFrom}.
         */
        function safeTransferFrom(
            address from,
            address to,
            uint256 tokenId,
            bytes memory _data
        ) public virtual override {
            require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");
            _safeTransfer(from, to, tokenId, _data);
        }
        /**
         * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
         * are aware of the ERC721 protocol to prevent tokens from being forever locked.
         *
         * `_data` is additional data, it has no specified format and it is sent in call to `to`.
         *
         * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g.
         * implement alternative mechanisms to perform token transfer, such as signature-based.
         *
         * Requirements:
         *
         * - `from` cannot be the zero address.
         * - `to` cannot be the zero address.
         * - `tokenId` token must exist and be owned by `from`.
         * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
         *
         * Emits a {Transfer} event.
         */
        function _safeTransfer(
            address from,
            address to,
            uint256 tokenId,
            bytes memory _data
        ) internal virtual {
            _transfer(from, to, tokenId);
            require(_checkOnERC721Received(from, to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer");
        }
        /**
         * @dev Returns whether `tokenId` exists.
         *
         * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
         *
         * Tokens start existing when they are minted (`_mint`),
         * and stop existing when they are burned (`_burn`).
         */
        function _exists(uint256 tokenId) internal view virtual returns (bool) {
            return _owners[tokenId] != address(0);
        }
        /**
         * @dev Returns whether `spender` is allowed to manage `tokenId`.
         *
         * Requirements:
         *
         * - `tokenId` must exist.
         */
        function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {
            require(_exists(tokenId), "ERC721: operator query for nonexistent token");
            address owner = ERC721.ownerOf(tokenId);
            return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender));
        }
        /**
         * @dev Safely mints `tokenId` and transfers it to `to`.
         *
         * Requirements:
         *
         * - `tokenId` must not exist.
         * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
         *
         * Emits a {Transfer} event.
         */
        function _safeMint(address to, uint256 tokenId) internal virtual {
            _safeMint(to, tokenId, "");
        }
        /**
         * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
         * forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
         */
        function _safeMint(
            address to,
            uint256 tokenId,
            bytes memory _data
        ) internal virtual {
            _mint(to, tokenId);
            require(
                _checkOnERC721Received(address(0), to, tokenId, _data),
                "ERC721: transfer to non ERC721Receiver implementer"
            );
        }
        /**
         * @dev Mints `tokenId` and transfers it to `to`.
         *
         * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
         *
         * Requirements:
         *
         * - `tokenId` must not exist.
         * - `to` cannot be the zero address.
         *
         * Emits a {Transfer} event.
         */
        function _mint(address to, uint256 tokenId) internal virtual {
            require(to != address(0), "ERC721: mint to the zero address");
            require(!_exists(tokenId), "ERC721: token already minted");
            _beforeTokenTransfer(address(0), to, tokenId);
            _balances[to] += 1;
            _owners[tokenId] = to;
            emit Transfer(address(0), to, tokenId);
        }
        /**
         * @dev Destroys `tokenId`.
         * The approval is cleared when the token is burned.
         *
         * Requirements:
         *
         * - `tokenId` must exist.
         *
         * Emits a {Transfer} event.
         */
        function _burn(uint256 tokenId) internal virtual {
            address owner = ERC721.ownerOf(tokenId);
            _beforeTokenTransfer(owner, address(0), tokenId);
            // Clear approvals
            _approve(address(0), tokenId);
            _balances[owner] -= 1;
            delete _owners[tokenId];
            emit Transfer(owner, address(0), tokenId);
        }
        /**
         * @dev Transfers `tokenId` from `from` to `to`.
         *  As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
         *
         * Requirements:
         *
         * - `to` cannot be the zero address.
         * - `tokenId` token must be owned by `from`.
         *
         * Emits a {Transfer} event.
         */
        function _transfer(
            address from,
            address to,
            uint256 tokenId
        ) internal virtual {
            require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer of token that is not own");
            require(to != address(0), "ERC721: transfer to the zero address");
            _beforeTokenTransfer(from, to, tokenId);
            // Clear approvals from the previous owner
            _approve(address(0), tokenId);
            _balances[from] -= 1;
            _balances[to] += 1;
            _owners[tokenId] = to;
            emit Transfer(from, to, tokenId);
        }
        /**
         * @dev Approve `to` to operate on `tokenId`
         *
         * Emits a {Approval} event.
         */
        function _approve(address to, uint256 tokenId) internal virtual {
            _tokenApprovals[tokenId] = to;
            emit Approval(ERC721.ownerOf(tokenId), to, tokenId);
        }
        /**
         * @dev Approve `operator` to operate on all of `owner` tokens
         *
         * Emits a {ApprovalForAll} event.
         */
        function _setApprovalForAll(
            address owner,
            address operator,
            bool approved
        ) internal virtual {
            require(owner != operator, "ERC721: approve to caller");
            _operatorApprovals[owner][operator] = approved;
            emit ApprovalForAll(owner, operator, approved);
        }
        /**
         * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.
         * The call is not executed if the target address is not a contract.
         *
         * @param from address representing the previous owner of the given token ID
         * @param to target address that will receive the tokens
         * @param tokenId uint256 ID of the token to be transferred
         * @param _data bytes optional data to send along with the call
         * @return bool whether the call correctly returned the expected magic value
         */
        function _checkOnERC721Received(
            address from,
            address to,
            uint256 tokenId,
            bytes memory _data
        ) private returns (bool) {
            if (to.isContract()) {
                try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, _data) returns (bytes4 retval) {
                    return retval == IERC721Receiver.onERC721Received.selector;
                } catch (bytes memory reason) {
                    if (reason.length == 0) {
                        revert("ERC721: transfer to non ERC721Receiver implementer");
                    } else {
                        assembly {
                            revert(add(32, reason), mload(reason))
                        }
                    }
                }
            } else {
                return true;
            }
        }
        /**
         * @dev Hook that is called before any token transfer. This includes minting
         * and burning.
         *
         * Calling conditions:
         *
         * - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be
         * transferred to `to`.
         * - When `from` is zero, `tokenId` will be minted for `to`.
         * - When `to` is zero, ``from``'s `tokenId` will be burned.
         * - `from` and `to` are never both zero.
         *
         * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
         */
        function _beforeTokenTransfer(
            address from,
            address to,
            uint256 tokenId
        ) internal virtual {}
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Enumerable.sol)
    pragma solidity ^0.8.0;
    import "../IERC721.sol";
    /**
     * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
     * @dev See https://eips.ethereum.org/EIPS/eip-721
     */
    interface IERC721Enumerable is IERC721 {
        /**
         * @dev Returns the total amount of tokens stored by the contract.
         */
        function totalSupply() external view returns (uint256);
        /**
         * @dev Returns a token ID owned by `owner` at a given `index` of its token list.
         * Use along with {balanceOf} to enumerate all of ``owner``'s tokens.
         */
        function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256 tokenId);
        /**
         * @dev Returns a token ID at a given `index` of all the tokens stored by the contract.
         * Use along with {totalSupply} to enumerate all tokens.
         */
        function tokenByIndex(uint256 index) external view returns (uint256);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721.sol)
    pragma solidity ^0.8.0;
    import "../../utils/introspection/IERC165.sol";
    /**
     * @dev Required interface of an ERC721 compliant contract.
     */
    interface IERC721 is IERC165 {
        /**
         * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
         */
        event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
        /**
         * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
         */
        event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
        /**
         * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
         */
        event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
        /**
         * @dev Returns the number of tokens in ``owner``'s account.
         */
        function balanceOf(address owner) external view returns (uint256 balance);
        /**
         * @dev Returns the owner of the `tokenId` token.
         *
         * Requirements:
         *
         * - `tokenId` must exist.
         */
        function ownerOf(uint256 tokenId) external view returns (address owner);
        /**
         * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
         * are aware of the ERC721 protocol to prevent tokens from being forever locked.
         *
         * Requirements:
         *
         * - `from` cannot be the zero address.
         * - `to` cannot be the zero address.
         * - `tokenId` token must exist and be owned by `from`.
         * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}.
         * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
         *
         * Emits a {Transfer} event.
         */
        function safeTransferFrom(
            address from,
            address to,
            uint256 tokenId
        ) external;
        /**
         * @dev Transfers `tokenId` token from `from` to `to`.
         *
         * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
         *
         * Requirements:
         *
         * - `from` cannot be the zero address.
         * - `to` cannot be the zero address.
         * - `tokenId` token must be owned by `from`.
         * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
         *
         * Emits a {Transfer} event.
         */
        function transferFrom(
            address from,
            address to,
            uint256 tokenId
        ) external;
        /**
         * @dev Gives permission to `to` to transfer `tokenId` token to another account.
         * The approval is cleared when the token is transferred.
         *
         * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
         *
         * Requirements:
         *
         * - The caller must own the token or be an approved operator.
         * - `tokenId` must exist.
         *
         * Emits an {Approval} event.
         */
        function approve(address to, uint256 tokenId) external;
        /**
         * @dev Returns the account approved for `tokenId` token.
         *
         * Requirements:
         *
         * - `tokenId` must exist.
         */
        function getApproved(uint256 tokenId) external view returns (address operator);
        /**
         * @dev Approve or remove `operator` as an operator for the caller.
         * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
         *
         * Requirements:
         *
         * - The `operator` cannot be the caller.
         *
         * Emits an {ApprovalForAll} event.
         */
        function setApprovalForAll(address operator, bool _approved) external;
        /**
         * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
         *
         * See {setApprovalForAll}
         */
        function isApprovedForAll(address owner, address operator) external view returns (bool);
        /**
         * @dev Safely transfers `tokenId` token from `from` to `to`.
         *
         * Requirements:
         *
         * - `from` cannot be the zero address.
         * - `to` cannot be the zero address.
         * - `tokenId` token must exist and be owned by `from`.
         * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
         * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
         *
         * Emits a {Transfer} event.
         */
        function safeTransferFrom(
            address from,
            address to,
            uint256 tokenId,
            bytes calldata data
        ) external;
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721Receiver.sol)
    pragma solidity ^0.8.0;
    /**
     * @title ERC721 token receiver interface
     * @dev Interface for any contract that wants to support safeTransfers
     * from ERC721 asset contracts.
     */
    interface IERC721Receiver {
        /**
         * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
         * by `operator` from `from`, this function is called.
         *
         * It must return its Solidity selector to confirm the token transfer.
         * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
         *
         * The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`.
         */
        function onERC721Received(
            address operator,
            address from,
            uint256 tokenId,
            bytes calldata data
        ) external returns (bytes4);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol)
    pragma solidity ^0.8.0;
    import "../IERC721.sol";
    /**
     * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
     * @dev See https://eips.ethereum.org/EIPS/eip-721
     */
    interface IERC721Metadata is IERC721 {
        /**
         * @dev Returns the token collection name.
         */
        function name() external view returns (string memory);
        /**
         * @dev Returns the token collection symbol.
         */
        function symbol() external view returns (string memory);
        /**
         * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
         */
        function tokenURI(uint256 tokenId) external view returns (string memory);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (utils/Address.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev Collection of functions related to the address type
     */
    library Address {
        /**
         * @dev Returns true if `account` is a contract.
         *
         * [IMPORTANT]
         * ====
         * It is unsafe to assume that an address for which this function returns
         * false is an externally-owned account (EOA) and not a contract.
         *
         * Among others, `isContract` will return false for the following
         * types of addresses:
         *
         *  - an externally-owned account
         *  - a contract in construction
         *  - an address where a contract will be created
         *  - an address where a contract lived, but was destroyed
         * ====
         */
        function isContract(address account) internal view returns (bool) {
            // This method relies on extcodesize, which returns 0 for contracts in
            // construction, since the code is only stored at the end of the
            // constructor execution.
            uint256 size;
            assembly {
                size := extcodesize(account)
            }
            return size > 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 (token/ERC20/IERC20.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev Interface of the ERC20 standard as defined in the EIP.
     */
    interface IERC20 {
        /**
         * @dev Returns the amount of tokens in existence.
         */
        function totalSupply() external view returns (uint256);
        /**
         * @dev Returns the amount of tokens owned by `account`.
         */
        function balanceOf(address account) external view returns (uint256);
        /**
         * @dev Moves `amount` tokens from the caller's account to `recipient`.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * Emits a {Transfer} event.
         */
        function transfer(address recipient, uint256 amount) external returns (bool);
        /**
         * @dev Returns the remaining number of tokens that `spender` will be
         * allowed to spend on behalf of `owner` through {transferFrom}. This is
         * zero by default.
         *
         * This value changes when {approve} or {transferFrom} are called.
         */
        function allowance(address owner, address spender) external view returns (uint256);
        /**
         * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * IMPORTANT: Beware that changing an allowance with this method brings the risk
         * that someone may use both the old and the new allowance by unfortunate
         * transaction ordering. One possible solution to mitigate this race
         * condition is to first reduce the spender's allowance to 0 and set the
         * desired value afterwards:
         * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
         *
         * Emits an {Approval} event.
         */
        function approve(address spender, uint256 amount) external returns (bool);
        /**
         * @dev Moves `amount` tokens from `sender` to `recipient` using the
         * allowance mechanism. `amount` is then deducted from the caller's
         * allowance.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * Emits a {Transfer} event.
         */
        function transferFrom(
            address sender,
            address recipient,
            uint256 amount
        ) external returns (bool);
        /**
         * @dev Emitted when `value` tokens are moved from one account (`from`) to
         * another (`to`).
         *
         * Note that `value` may be zero.
         */
        event Transfer(address indexed from, address indexed to, uint256 value);
        /**
         * @dev Emitted when the allowance of a `spender` for an `owner` is set by
         * a call to {approve}. `value` is the new allowance.
         */
        event Approval(address indexed owner, address indexed spender, uint256 value);
    }