ETH Price: $2,696.46 (+2.56%)

Transaction Decoder

Block:
16285920 at Dec-28-2022 09:24:23 PM +UTC
Transaction Fee:
0.006953437208468844 ETH $18.75
Gas Used:
402,627 Gas / 17.270171172 Gwei

Emitted Events:

190 TheWhitelist.Approval( owner=[Sender] 0x35d587d4fd525e37d38c23a6bccb1c4d2531400e, approved=0x00000000...000000000, tokenId=275 )
191 TheWhitelist.Transfer( from=[Sender] 0x35d587d4fd525e37d38c23a6bccb1c4d2531400e, to=[Receiver] AceStaking, tokenId=275 )
192 AceStaking.StakedNFT( stakedToken=[{name:contractAddress, type:address, order:1, indexed:false, value:0xB3B814cCd178De120687a9Ad01C6886C56399198, valueString:0xB3B814cCd178De120687a9Ad01C6886C56399198}, {name:tokenIdentifier, type:uint256, order:2, indexed:false, value:275, valueString:275}, {name:lastRewardedAt, type:uint256, order:3, indexed:false, value:1672262663, valueString:1672262663}, {name:stakedAt, type:uint256, order:4, indexed:false, value:1672262663, valueString:1672262663}], staker=[Sender] 0x35d587d4fd525e37d38c23a6bccb1c4d2531400e )
193 TheWhitelist.Approval( owner=[Sender] 0x35d587d4fd525e37d38c23a6bccb1c4d2531400e, approved=0x00000000...000000000, tokenId=5046 )
194 TheWhitelist.Transfer( from=[Sender] 0x35d587d4fd525e37d38c23a6bccb1c4d2531400e, to=[Receiver] AceStaking, tokenId=5046 )
195 AceStaking.StakedNFT( stakedToken=[{name:contractAddress, type:address, order:1, indexed:false, value:0xB3B814cCd178De120687a9Ad01C6886C56399198, valueString:0xB3B814cCd178De120687a9Ad01C6886C56399198}, {name:tokenIdentifier, type:uint256, order:2, indexed:false, value:5046, valueString:5046}, {name:lastRewardedAt, type:uint256, order:3, indexed:false, value:1672262663, valueString:1672262663}, {name:stakedAt, type:uint256, order:4, indexed:false, value:1672262663, valueString:1672262663}], staker=[Sender] 0x35d587d4fd525e37d38c23a6bccb1c4d2531400e )

Account State Difference:

  Address   Before After State Difference Code
0x35D587d4...D2531400E
0.186972214104110307 Eth
Nonce: 31
0.180018776895641463 Eth
Nonce: 32
0.006953437208468844
0x430D5DDE...1a9a08531
(beaverbuild)
128.481232170428637389 Eth128.481836110928637389 Eth0.0006039405
0xB3B814cC...C56399198

Execution Trace

AceStaking.stake( _tokens= )
  • TheWhitelist.transferFrom( from=0x35D587d4fd525e37d38C23a6BcCB1c4D2531400E, to=0x430D5DDE0Cd48989737a95B4C46e1801a9a08531, tokenId=275 )
  • TheWhitelist.transferFrom( from=0x35D587d4fd525e37d38C23a6BcCB1c4D2531400E, to=0x430D5DDE0Cd48989737a95B4C46e1801a9a08531, tokenId=5046 )
    File 1 of 2: AceStaking
    // SPDX-License-Identifier: MIT
    // Creator: andreitoma8
    pragma solidity ^0.8.4;
    import "@openzeppelin/contracts/token/ERC777/IERC777Recipient.sol";
    import "@openzeppelin/contracts/token/ERC777/IERC777Sender.sol";
    import "@openzeppelin/contracts/token/ERC777/IERC777.sol";
    import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
    import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
    import "@openzeppelin/contracts/access/Ownable.sol";
    import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
    import "@openzeppelin/contracts/utils/introspection/IERC1820Registry.sol";
    import "@openzeppelin/contracts/utils/introspection/ERC1820Implementer.sol";
    contract AceStaking is
        Ownable,
        ReentrancyGuard,
        IERC721Receiver,
        IERC777Sender,
        IERC777Recipient,
        ERC1820Implementer
    {
        // Interfaces for ERC777 and ERC721
        IERC777 public immutable rewardsToken;
        mapping(address => IERC721) public nftContracts;
        address[] public nftContractAddresses;
        // Reward Settings
        mapping(address => uint256) public rewardsForContract;
        mapping(address => mapping(uint256 => uint256))
            public additionalRewardsForToken;
        uint256 public rewardPeriodInSeconds;
        uint256 public totalTokensStakedCount;
        address internal REWARD_UPDATER;
        // Definition of Staker
        struct Staker {
            uint256 unclaimedRewards;
            uint256 lifetimeRewards;
            uint256 lastRewardedAt;
            uint256 lastClaimedAt;
        }
        // This is how we expect Tokens to be sent
        struct TokenData {
            address contractAddress;
            uint256 tokenIdentifier;
        }
        // How we keep track of a Staked token
        struct StakedToken {
            address contractAddress;
            uint256 tokenIdentifier;
            uint256 lastRewardedAt;
            uint256 stakedAt;
        }
        event UnstakedNFT(StakedToken stakedToken, address indexed staker);
        event StakedNFT(StakedToken stakedToken, address indexed staker);
        event RewardClaimed(address indexed staker, uint256 indexed amount);
        address[] public currentStakers;
        mapping(address => Staker) public stakers;
        mapping(address => StakedToken[]) public stakedTokensForAddress;
        mapping(address => mapping(uint256 => address)) public stakedTokenOwner;
        mapping(uint256 => mapping(address => uint256))
            internal sharesForWalletInRound;
        mapping(uint256 => address[]) internal walletsToRewardInRound;
        uint256 internal currentRoundId;
        uint256 public slashingPeriod = 24;
        // ERC777 Definitions
        event TokensToSendCalled(
            address operator,
            address from,
            address to,
            uint256 amount,
            bytes data,
            bytes operatorData,
            address token,
            uint256 fromBalance,
            uint256 toBalance
        );
        event TokensReceivedCalled(
            address operator,
            address from,
            address to,
            uint256 amount,
            bytes data,
            bytes operatorData,
            address token,
            uint256 fromBalance,
            uint256 toBalance
        );
        bool private _shouldRevertSend;
        bool private _shouldRevertReceive;
        IERC1820Registry internal constant _ERC1820_REGISTRY =
            IERC1820Registry(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24);
        bytes32 private constant _TOKENS_SENDER_INTERFACE_HASH =
            keccak256("ERC777TokensSender");
        bytes32 private constant _TOKENS_RECIPIENT_INTERFACE_HASH =
            keccak256("ERC777TokensRecipient");
        /**
         * @dev Setsup the new Contract.
         *
         * @param _rewardsToken A ERC777 Token. Can't be changed!
         * @param _rewardPeriodInSeconds Every x Seconds the Reward gets emitted. Can't be changed! Needs to be at least 60 Seconds but at least a day is recommneded.
         */
        constructor(IERC777 _rewardsToken, uint256 _rewardPeriodInSeconds) {
            require(
                _rewardPeriodInSeconds > 60,
                "AceStaking: Rewards need to be paid at least once per minute"
            );
            rewardsToken = _rewardsToken;
            rewardPeriodInSeconds = _rewardPeriodInSeconds;
            REWARD_UPDATER = msg.sender;
            _ERC1820_REGISTRY.setInterfaceImplementer(
                address(this),
                _TOKENS_RECIPIENT_INTERFACE_HASH,
                address(this)
            );
            _ERC1820_REGISTRY.setInterfaceImplementer(
                address(this),
                _TOKENS_SENDER_INTERFACE_HASH,
                address(this)
            );
        }
        /**
         * @dev Call this function to add a new NFT Contract to be accepted for staking.
         * Each contract can have differen rewards.
         *
         * Requirements: onlyOwner can add new Contracts; Contract needs to be ERC721 compliant.
         * @param _nftContract A ERC721 Contract that can be used for staking.
         * @param _reward ERC777 Token Value that is rewarded for each token every rewardPeriodInSeconds.
         */
        function addNFTContract(IERC721 _nftContract, uint256 _reward)
            public
            onlyOwner
        {
            nftContracts[address(_nftContract)] = _nftContract;
            nftContractAddresses.push(address(_nftContract));
            rewardsForContract[address(_nftContract)] = _reward;
        }
        /**
         * @dev Call this function to remove a NFT contract from beeing accepted for staking.
         * All tokens remain in the contract but wont receive any further rewards. They can be withdrawn by
         * the token owner.
         * Warning: Additional Rewards might stay in place, so call setAdditionalRewardsForTokens first and set their reward to 0.
         *
         * Requirements: onlyOwner can remove Contracts; Contract needs to be ERC721 compliant and already added through addNFTContract.
         * @param _nftContract A ERC721 Contract that should be removed from staking.
         */
        function removeNFTContract(address _nftContract) public onlyOwner {
            require(
                nftContracts[_nftContract] == IERC721(_nftContract),
                "AceStaking: Unkown Contract"
            );
            nftContracts[address(_nftContract)] = IERC721(address(0));
            rewardsForContract[address(_nftContract)] = 0;
            for (uint256 i; i < nftContractAddresses.length; i = unsafe_inc(i)) {
                if (nftContractAddresses[i] == _nftContract) {
                    nftContractAddresses[i] = nftContractAddresses[
                        nftContractAddresses.length - 1
                    ];
                    nftContractAddresses.pop();
                }
            }
        }
        /**
         * @dev This function allows the contract owner to set an additional bonus that is
         * added for each token. The reward is added on top to the default reward for the contract.
         *
         * Requirements: onlyOwner or rewardUpdate (external contract) can remove Contracts; Contract needs to be ERC721 compliant
         * and already added through addNFTContract.
         * @param _nftContract A ERC721 Contract that is accepted by the contract.
         * @param _tokenIdentifiers Array of Identifiers that should receive the additional reward
         * @param _additionalReward ERC777 Token Value that is rewarded for each token every rewardPeriodInSeconds additionally to the default reward.
         */
        function setAdditionalRewardsForTokens(
            IERC721 _nftContract,
            uint256[] memory _tokenIdentifiers,
            uint256 _additionalReward
        ) external onlyRewardUpdater {
            require(
                nftContracts[address(_nftContract)] == IERC721(_nftContract),
                "AceStaking: Unkown Contract"
            );
            uint256 tokenCounter = _tokenIdentifiers.length;
            for (uint256 i; i < tokenCounter; i = unsafe_inc(i)) {
                additionalRewardsForToken[address(_nftContract)][
                    _tokenIdentifiers[i]
                ] = _additionalReward;
            }
        }
        /**
         * @dev You need to claim your rewards at least once within this period.
         * If not you won't get any new rewards until you claim again.
         *
         * Requirements: onlyOwner can change that value
         *
         * @param _slashingPeriod Amount of Periods after that rewards get slashed
         */
        function setSlashingPeriod(uint256 _slashingPeriod) external onlyOwner {
            slashingPeriod = _slashingPeriod;
        }
        /**
         * @dev We reward a Bonus depending on different traits in some periods.
         * The choosen traits and to be rewareded tokens are calculated off-chain.
         * Tokens need to be staked when the reward is paid and already staked in the Snapshot of Tokens that is sent.
         * If you want to learn more about how our trait based bonus works take a look at our website.
         *
         * @param _tokens Array of TokenData(contractAddress, tokenId)
         * @param _totalBonus Amount of Tokens that should be distributed among sent tokens
         */
        function rewardBonus(TokenData[] calldata _tokens, uint256 _totalBonus)
            external
            onlyRewardUpdater
        {
            uint256 stakedTokensLength = _tokens.length;
            require(stakedTokensLength > 0, "AceStaking: No Tokens");
            require(
                _totalBonus > 0,
                "AceStaking: No Bonus to be distributed"
            );
            uint256 totalShares;
            currentRoundId += 1;
            for (uint256 i; i < stakedTokensLength; i = unsafe_inc(i)) {
                address _staker = stakedTokenOwner[_tokens[i].contractAddress][
                    _tokens[i].tokenIdentifier
                ];
                if (_staker != address(0)) {
                    sharesForWalletInRound[currentRoundId][_staker] += 100;
                    walletsToRewardInRound[currentRoundId].push(_staker);
                    totalShares += 1;
                }
            }
            require(totalShares > 0, "AceStaking: No shares to distribute");
            uint256 walletsToRewardLength = walletsToRewardInRound[currentRoundId]
                .length;
            for (uint256 i; i < walletsToRewardLength; i = unsafe_inc(i)) {
                address walletToCheck = walletsToRewardInRound[currentRoundId][i];
                if (sharesForWalletInRound[currentRoundId][walletToCheck] > 0) {
                    uint256 rewardsForWallet = (sharesForWalletInRound[
                        currentRoundId
                    ][walletToCheck] / totalShares) * (_totalBonus / 100);
                    stakers[walletToCheck].unclaimedRewards += rewardsForWallet;
                    stakers[walletToCheck].lifetimeRewards += rewardsForWallet;
                    sharesForWalletInRound[currentRoundId][walletToCheck] = 0;
                }
            }
        }
        /**
         * @dev Function to estimate rewards for specific token on a contract in one period.
         * Token ID could be out of range we don't care since this is just for
         * simulating unstaked token rewards for UI.
         *
         * Requirements: nftContractAddress needs to be registereed on the staking contract.
         * @param nftContractAddress A ERC721 Contract of the token
         * @param tokenIdentifier Token Identifier that you want an estimation for
         */
        function estimateRewardsForToken(
            address nftContractAddress,
            uint256 tokenIdentifier
        ) public view returns (uint256) {
            require(
                nftContracts[nftContractAddress] == IERC721(nftContractAddress),
                "AceStaking: Unkown Contract"
            );
            return rewardsForToken(nftContractAddress, tokenIdentifier);
        }
        /**
         * @dev Returns multiple stats for the address. Returns those values:
         * - totalTokensStaked: Count of Tokens for this Wallet on the Staking Contract
         * - unclaimedRewards: Rewards that can be claimed but are unclaimed by the user
         * - unaccountedRewards: Rewards that are not ready to be claimed
         * because the current period did not finish yet. If tokens were staked on different
         * start times this number might never be 0.
         * - lifetimeRewards: Just counting up what a user earned over life
         *
         * @param _stakerAddress Wallet Address that has staked tokens on the contract
         */
        function stakerStats(address _stakerAddress)
            public
            view
            returns (
                uint256 totalTokensStaked,
                uint256 unclaimedRewards,
                uint256 unaccountedRewards,
                uint256 lifetimeRewards
            )
        {
            Staker memory staker = stakers[_stakerAddress];
            uint256 claimableRewards = calculateUnaccountedRewards(
                _stakerAddress,
                false
            );
            return (
                stakedTokensForAddress[_stakerAddress].length,
                staker.unclaimedRewards + claimableRewards,
                calculateUnaccountedRewards(_stakerAddress, true),
                staker.lifetimeRewards + claimableRewards
            );
        }
        /**
         * @dev Function to unstake all tokens for the msg.sender.
         * Also rewards msg.sender for all of his staked tokens his staked tokens and ejects all tokens after this.
         * If you have many tokens staked (50+) we recommend unstaking them in badges to not hit the gas limit of a block.
         */
        function unstakeAllTokens() external nonReentrant {
            StakedToken[] memory stakedTokens = stakedTokensForAddress[msg.sender];
            uint256 stakedTokensLength = stakedTokens.length;
            require(stakedTokensLength > 0, "AceStaking: No Tokens found");
            rewardStaker(msg.sender);
            for (uint256 i; i < stakedTokensLength; i = unsafe_inc(i)) {
                ejectToken(
                    stakedTokens[i].contractAddress,
                    stakedTokens[i].tokenIdentifier
                );
            }
        }
        /**
         * @dev Unstake a Set of Tokens for msg.sender.
         * Also rewards msg.sender for all of his staked tokens his staked tokens and ejects all sent tokens after this.
         *
         * @param _tokens Array of TokenData(contractAddress, tokenId)
         */
        function unstake(TokenData[] calldata _tokens) external nonReentrant {
            uint256 stakedTokensLength = _tokens.length;
            require(stakedTokensLength > 0, "AceStaking: No Tokens found");
            rewardStaker(msg.sender);
            for (uint256 i; i < stakedTokensLength; i = unsafe_inc(i)) {
                ejectToken(_tokens[i].contractAddress, _tokens[i].tokenIdentifier);
            }
        }
        /**
         * @dev Emergency Unstake Function: Unstake without any reward calculation.
         * !!! NOT RECOMMENDED, YOU MIGHT LOSE UNACCOUNTED REWARDS !!!
         * When to use? This function consumes less gas then the normal unstake since we do not reward all tokens before unstaking.
         * In case you hit the block limit for gas (very unlikely) we have a way to withdrawal your tokens somehow.
         * @param _tokens Array of TokenData(contractAddress, tokenId)
         */
        function emergencyUnstake(TokenData[] calldata _tokens)
            external
            nonReentrant
        {
            uint256 stakedTokensLength = _tokens.length;
            require(stakedTokensLength > 0, "AceStaking: No Tokens in calldata");
            for (uint256 i; i < stakedTokensLength; i = unsafe_inc(i)) {
                ejectToken(_tokens[i].contractAddress, _tokens[i].tokenIdentifier);
            }
        }
        /**
         * @dev This function transfers tokens to the contract with transferFrom.
         * thusfor we need to call addToken manually but we save a little on gas.
         *
         * Requirements: All token contracts need to be added to this contract and be approved by the user.
         *
         * @param _tokens Array of TokenData(contractAddress, tokenId)
         */
        function stake(TokenData[] calldata _tokens) external {
            beforeTokensAdded(msg.sender);
            uint256 tokensLength = _tokens.length;
            for (uint256 i; i < tokensLength; i = unsafe_inc(i)) {
                IERC721(_tokens[i].contractAddress).transferFrom(
                    msg.sender,
                    address(this),
                    _tokens[i].tokenIdentifier
                );
                addToken(
                    _tokens[i].contractAddress,
                    _tokens[i].tokenIdentifier,
                    msg.sender
                );
            }
        }
        /**
         * @dev Call this function to get your ERC777 Token rewards transfered to your wallet.
         * This is an expensive call since we calculate your current earnings and send them in one
         * Transaction to your wallet.
         *
         */
        function claimRewards() external nonReentrant {
            rewardStaker(msg.sender);
            require(
                stakers[msg.sender].unclaimedRewards > 0,
                "AceStaking: Nothing to claim"
            );
            IERC777(rewardsToken).send(
                msg.sender,
                stakers[msg.sender].unclaimedRewards,
                ""
            );
            emit RewardClaimed(msg.sender, stakers[msg.sender].unclaimedRewards);
            stakers[msg.sender].unclaimedRewards = 0;
            stakers[msg.sender].lastClaimedAt = block.timestamp;
        }
        /**
         * @dev This function determains how many periods should be calculated for rewarding.
         *
         * @param _lastRewardedAt timestamp when the token was rewarded last
         * @param _lastClaimedAt timestamp when ist was last claimed
         *
         */
        function rewardPeriods(uint256 _lastRewardedAt, uint256 _lastClaimedAt)
            internal
            view
            returns (uint256 _rewardPeriodCounter)
        {
            uint256 referenceTimestamp = block.timestamp;
            if (
                referenceTimestamp >
                (_lastClaimedAt + slashingPeriod * rewardPeriodInSeconds)
            ) {
                referenceTimestamp =
                    _lastClaimedAt +
                    slashingPeriod *
                    rewardPeriodInSeconds;
            }
            return (referenceTimestamp - _lastRewardedAt) / rewardPeriodInSeconds;
        }
        /**
         * @dev Calculates Rewards for a User and accounts them to his entry.
         *
         * @param _stakerAddress staker that should be rewarded
         *
         */
        function rewardUnaccountedRewards(address _stakerAddress)
            internal
            returns (uint256 _rewards)
        {
            StakedToken[] memory stakedTokens = stakedTokensForAddress[
                _stakerAddress
            ];
            uint256 totalRewards;
            uint256 stakedTokensCount = stakedTokens.length;
            for (uint256 i; i < stakedTokensCount; i = unsafe_inc(i)) {
                uint256 periodsToReward = rewardPeriods(
                    stakedTokens[i].lastRewardedAt,
                    stakers[_stakerAddress].lastClaimedAt
                );
                if (periodsToReward > 0) {
                    totalRewards +=
                        periodsToReward *
                        rewardsForToken(
                            stakedTokens[i].contractAddress,
                            stakedTokens[i].tokenIdentifier
                        );
                    if (periodsToReward == slashingPeriod) {
                        stakedTokensForAddress[_stakerAddress][i].lastRewardedAt =
                            stakedTokensForAddress[_stakerAddress][i].stakedAt +
                            (
                                uint256(
                                    (block.timestamp -
                                        stakedTokensForAddress[_stakerAddress][i]
                                            .stakedAt) / rewardPeriodInSeconds
                                )
                            ) *
                            rewardPeriodInSeconds;
                    } else {
                        stakedTokensForAddress[_stakerAddress][i].lastRewardedAt =
                            stakedTokensForAddress[_stakerAddress][i].stakedAt +
                            periodsToReward *
                            rewardPeriodInSeconds;
                    }
                }
            }
            return totalRewards;
        }
        /**
         * @dev Calculates Rewards for a User but does not account them.
         *
         * @param _stakerAddress staker that should be rewarded
         * @param _simulateUnaccounted include unaccounted rewards that can't be claimed yet
         *
         */
        function calculateUnaccountedRewards(
            address _stakerAddress,
            bool _simulateUnaccounted
        ) internal view returns (uint256 _rewards) {
            StakedToken[] memory stakedTokens = stakedTokensForAddress[
                _stakerAddress
            ];
            uint256 totalRewards;
            uint256 stakedTokensCount = stakedTokens.length;
            for (uint256 i; i < stakedTokensCount; i = unsafe_inc(i)) {
                uint256 periodsToReward = rewardPeriods(
                    stakedTokens[i].lastRewardedAt,
                    stakers[_stakerAddress].lastClaimedAt
                );
                uint256 tokenReward = rewardsForToken(
                    stakedTokens[i].contractAddress,
                    stakedTokens[i].tokenIdentifier
                );
                if (_simulateUnaccounted) {
                    totalRewards +=
                        ((((block.timestamp - stakedTokens[i].lastRewardedAt) *
                            100) / rewardPeriodInSeconds) * tokenReward) /
                        100 -
                        periodsToReward *
                        tokenReward;
                } else {
                    totalRewards += tokenReward * periodsToReward;
                }
            }
            return totalRewards;
        }
        /**
         * @dev Summarize rewards for a specific token on a contract.
         * Sums up default rewards for contract and bonus for token identifier.
         *
         * @param nftContractAddress Contract to check
         * @param tokenIdentifier Token Identifier to check
         *
         */
        function rewardsForToken(
            address nftContractAddress,
            uint256 tokenIdentifier
        ) internal view returns (uint256) {
            return
                rewardsForContract[nftContractAddress] +
                additionalRewardsForToken[nftContractAddress][tokenIdentifier];
        }
        /**
         * @dev Function that moves all unaccounted rewards to unclaimed.
         * This call is required to keep our internal balance sheets up to date.
         * Depending on Token Amount this is very expensive to call since we loop through all tokens!
         *
         * @param _address Wallet Address that should be rewarded
         *
         */
        function rewardStaker(address _address) internal {
            uint256 unaccountedRewards = rewardUnaccountedRewards(_address);
            stakers[_address].lastRewardedAt = block.timestamp;
            stakers[_address].unclaimedRewards += unaccountedRewards;
            stakers[_address].lifetimeRewards += unaccountedRewards;
        }
        /**
         * @dev Internal function to send a token back to a user. Also
         * removes / updates all contract internal trackings.
         *
         * Requirements: msg.sender needs to be the wallet that sent the token to the contract.
         *
         * @param nftContractAddress Contract for the token to be ejected
         * @param tokenIdentifier Token Identifier for the token to be ejected
         *
         */
        function ejectToken(address nftContractAddress, uint256 tokenIdentifier)
            internal
        {
            require(
                stakedTokenOwner[nftContractAddress][tokenIdentifier] == msg.sender,
                "AceStaking: Not your token..."
            );
            IERC721(nftContractAddress).transferFrom(
                address(this),
                msg.sender,
                tokenIdentifier
            );
            for (
                uint256 i;
                i < stakedTokensForAddress[msg.sender].length;
                i = unsafe_inc(i)
            ) {
                if (
                    stakedTokensForAddress[msg.sender][i].tokenIdentifier ==
                    tokenIdentifier &&
                    stakedTokensForAddress[msg.sender][i].contractAddress ==
                    nftContractAddress
                ) {
                    emit UnstakedNFT(
                        stakedTokensForAddress[msg.sender][i],
                        msg.sender
                    );
                    stakedTokensForAddress[msg.sender][i] = stakedTokensForAddress[
                        msg.sender
                    ][stakedTokensForAddress[msg.sender].length - 1];
                    stakedTokensForAddress[msg.sender].pop();
                }
            }
            if (stakedTokensForAddress[msg.sender].length == 0) {
                for (uint256 i; i < currentStakers.length; i = unsafe_inc(i)) {
                    if (currentStakers[i] == msg.sender) {
                        currentStakers[i] = currentStakers[
                            currentStakers.length - 1
                        ];
                        currentStakers.pop();
                    }
                }
            }
            stakedTokenOwner[msg.sender][tokenIdentifier] = address(0);
            totalTokensStakedCount -= 1;
        }
        /**
         * @dev Helper that should be called before any token is added. Needs to be called
         * only once per batch. It basically setup the staker object.
         *
         * @param _staker Wallet Address for Staker
         */
        function beforeTokensAdded(address _staker) internal {
            if (stakedTokensForAddress[_staker].length == 0) {
                if (stakers[_staker].lastRewardedAt > 0) {
                    // This wallet already staked before and was at least rewarded once.
                    stakers[_staker].lastRewardedAt = block.timestamp;
                    stakers[_staker].lastClaimedAt = block.timestamp;
                } else {
                    // This wallet is new to us
                    stakers[_staker] = Staker(
                        stakers[_staker].unclaimedRewards,
                        stakers[_staker].lifetimeRewards,
                        block.timestamp,
                        block.timestamp
                    );
                }
                currentStakers.push(_staker);
            }
        }
        /**
         * @dev Function to add a token and regiter it in all mappings that we need to
         * return and reward a token.
         *
         * @param nftContractAddress Contract of the token
         * @param tokenIdentifier The Identifier of a token
         * @param tokenOwnerAddress The address of the current owner
         */
        function addToken(
            address nftContractAddress,
            uint256 tokenIdentifier,
            address tokenOwnerAddress
        ) internal {
            require(
                nftContracts[nftContractAddress] == IERC721(nftContractAddress),
                "AceStaking: Unkown Contract"
            );
            StakedToken memory newToken = StakedToken(
                nftContractAddress,
                tokenIdentifier,
                block.timestamp,
                block.timestamp
            );
            stakedTokenOwner[nftContractAddress][
                tokenIdentifier
            ] = tokenOwnerAddress;
            stakedTokensForAddress[tokenOwnerAddress].push(newToken);
            totalTokensStakedCount += 1;
            emit StakedNFT(newToken, tokenOwnerAddress);
        }
        function unsafe_inc(uint256 x) private pure returns (uint256) {
            unchecked {
                return x + 1;
            }
        }
        /**
         * @dev See {IERC721Receiver-onERC721Received}. Also registers token in our TokenRegistry.
         *
         * Always returns `IERC721Receiver.onERC721Received.selector`.
         */
        function onERC721Received(
            address, // operator not required
            address tokenOwnerAddress,
            uint256 tokenIdentifier,
            bytes memory
        ) public virtual override returns (bytes4) {
            beforeTokensAdded(tokenOwnerAddress);
            addToken(msg.sender, tokenIdentifier, tokenOwnerAddress);
            return this.onERC721Received.selector;
        }
        function tokensToSend(
            address operator,
            address from,
            address to,
            uint256 amount,
            bytes calldata userData,
            bytes calldata operatorData
        ) external override {
            if (_shouldRevertSend) {
                revert();
            }
            IERC777 token = IERC777(_msgSender());
            uint256 fromBalance = token.balanceOf(from);
            // when called due to burn, to will be the zero address, which will have a balance of 0
            uint256 toBalance = token.balanceOf(to);
            emit TokensToSendCalled(
                operator,
                from,
                to,
                amount,
                userData,
                operatorData,
                address(token),
                fromBalance,
                toBalance
            );
        }
        function tokensReceived(
            address operator,
            address from,
            address to,
            uint256 amount,
            bytes calldata userData,
            bytes calldata operatorData
        ) external override {
            if (_shouldRevertReceive) {
                revert();
            }
            IERC777 token = IERC777(_msgSender());
            uint256 fromBalance = token.balanceOf(from);
            // when called due to burn, to will be the zero address, which will have a balance of 0
            uint256 toBalance = token.balanceOf(to);
            emit TokensReceivedCalled(
                operator,
                from,
                to,
                amount,
                userData,
                operatorData,
                address(token),
                fromBalance,
                toBalance
            );
        }
        /**
         * @dev This address is allowed to change the rewards for a specific token.
         * Since opening a chest door results in a different reward, this is implemented in the chest door opener contract later.
         *
         * @param _REWARD_UPDATER Address that is allowed to modify rewards
         */
        function setRewardUpdater(address _REWARD_UPDATER) external onlyOwner {
            REWARD_UPDATER = _REWARD_UPDATER;
        }
        modifier onlyRewardUpdater() {
            require(
                msg.sender == REWARD_UPDATER || msg.sender == owner(),
                "AceStaking: Only REWARD_UPDATE or OWNER."
            );
            _;
        }
    }
    /** created with bowline.app **/// SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.0 (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.0 (utils/introspection/IERC1820Implementer.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev Interface for an ERC1820 implementer, as defined in the
     * https://eips.ethereum.org/EIPS/eip-1820#interface-implementation-erc1820implementerinterface[EIP].
     * Used by contracts that will be registered as implementers in the
     * {IERC1820Registry}.
     */
    interface IERC1820Implementer {
        /**
         * @dev Returns a special value (`ERC1820_ACCEPT_MAGIC`) if this contract
         * implements `interfaceHash` for `account`.
         *
         * See {IERC1820Registry-setInterfaceImplementer}.
         */
        function canImplementInterfaceForAddress(bytes32 interfaceHash, address account) external view returns (bytes32);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.0 (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.0 (utils/introspection/ERC1820Implementer.sol)
    pragma solidity ^0.8.0;
    import "./IERC1820Implementer.sol";
    /**
     * @dev Implementation of the {IERC1820Implementer} interface.
     *
     * Contracts may inherit from this and call {_registerInterfaceForAddress} to
     * declare their willingness to be implementers.
     * {IERC1820Registry-setInterfaceImplementer} should then be called for the
     * registration to be complete.
     */
    contract ERC1820Implementer is IERC1820Implementer {
        bytes32 private constant _ERC1820_ACCEPT_MAGIC = keccak256("ERC1820_ACCEPT_MAGIC");
        mapping(bytes32 => mapping(address => bool)) private _supportedInterfaces;
        /**
         * @dev See {IERC1820Implementer-canImplementInterfaceForAddress}.
         */
        function canImplementInterfaceForAddress(bytes32 interfaceHash, address account)
            public
            view
            virtual
            override
            returns (bytes32)
        {
            return _supportedInterfaces[interfaceHash][account] ? _ERC1820_ACCEPT_MAGIC : bytes32(0x00);
        }
        /**
         * @dev Declares the contract as willing to be an implementer of
         * `interfaceHash` for `account`.
         *
         * See {IERC1820Registry-setInterfaceImplementer} and
         * {IERC1820Registry-interfaceHash}.
         */
        function _registerInterfaceForAddress(bytes32 interfaceHash, address account) internal virtual {
            _supportedInterfaces[interfaceHash][account] = true;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.0 (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.0 (token/ERC777/IERC777Sender.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev Interface of the ERC777TokensSender standard as defined in the EIP.
     *
     * {IERC777} Token holders can be notified of operations performed on their
     * tokens 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 IERC777Sender {
        /**
         * @dev Called by an {IERC777} token contract whenever a registered holder's
         * (`from`) tokens are about to be moved or destroyed. The type of operation
         * is conveyed by `to` being the zero address or not.
         *
         * This call occurs _before_ the token contract's state is updated, so
         * {IERC777-balanceOf}, etc., can be used to query the pre-operation state.
         *
         * This function may revert to prevent the operation from being executed.
         */
        function tokensToSend(
            address operator,
            address from,
            address to,
            uint256 amount,
            bytes calldata userData,
            bytes calldata operatorData
        ) external;
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.0 (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.0 (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);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.0 (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.0 (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.0 (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.0 (access/Ownable.sol)
    pragma solidity ^0.8.0;
    import "../utils/Context.sol";
    /**
     * @dev Contract module which provides a basic access control mechanism, where
     * there is an account (an owner) that can be granted exclusive access to
     * specific functions.
     *
     * By default, the owner account will be the one that deploys the contract. This
     * can later be changed with {transferOwnership}.
     *
     * This module is used through inheritance. It will make available the modifier
     * `onlyOwner`, which can be applied to your functions to restrict their use to
     * the owner.
     */
    abstract contract Ownable is Context {
        address private _owner;
        event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
        /**
         * @dev Initializes the contract setting the deployer as the initial owner.
         */
        constructor() {
            _transferOwnership(_msgSender());
        }
        /**
         * @dev 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);
        }
    }
    

    File 2 of 2: TheWhitelist
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.10;
    /**
    Learn more about our project on thewhitelist.io
    MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
    MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMXxclxddOxcdxllxKMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
    MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWk':O:.:' ';.'lKMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
    MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMXdkXkd0xcdxloxXMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
    MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
    MMXKKKXWMMMNKKKXWMMMWKKKKNXKKKXWMMWXKKKXWWXKKKNNKKKKKKKKKKKKXNNKKKKKKKKKKXWNKKKKNMMMMMMWKkdkKWMMWNKOOOO0XWNXKKKKKKKKKKKNM
    MWx...'xWMNo....dWMWd...:Ol...:KMMK:...cX0;...ld'...........,dd..........lXk'..'kMMMMMWx.   .xN0c'.    ..ld,...........xM
    MMK,   ,KWk.    '0MO'  .dXc   '0MMK,   ;KO.   cx:,,.    .',;cxl    .,,,,;dNd.  .xWMMMMNl     cx'   .::;''xOc,,.    .',;OM
    MMWx.   o0;      l0c   :XNc   .;cl;.   ;KO.   cXWWNd.   oNWWWNo   .;llllxXWd.  .xWMMMMMK;   ;0x.   .codxONWWNNk.   lXWWWM
    MMMNc   .,.  ..  .,.  .kWXc            ;XO.   cNMMMx.   oWMMMWo         ;KWd.  .xWMMMMMNl   cNNx,.      .c0WMMO.   oWMMMM
    MMMMO'      'ko       lNMNc   .cddl.   ;XO.   cNMMMx.   oWMMMWo   .:ddddkXWd.  .oXNNNNWK;   ;KMXkxxdl;.   cXMMO.   oWMMMM
    MMMMWd.    .oNK,     ,KMMNc   '0MMK,   ;XO.   cNMMMx.   oWMMMWo    .'''''lKd.   .'''',xO'   .OK:..';;'   .dWMMO.   oWMMMM
    MMMMMXc....cXMWk,...,kWMMNo...:KMMXc...lX0:...oNMMMO,..'xWMMMWx'.........:Kk,........'dk,...,k0c'......':kNMMM0;..'xWMMMM
    MMMMMMXK000XWMMWK000KWMMMWX000XWMMWX000XWWK00KXMMMMNK00KNMMMMMNK000000000XWNK00000000KNNK000KNMWXKOOOO0XWMMMMMWK00KNMMMMM
    */
    import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Burnable.sol";
    import "@openzeppelin/contracts/utils/Counters.sol";
    import "@openzeppelin/contracts/access/Ownable.sol";
    import "./ERC2981.sol";
    import "./MintpassValidator.sol";
    import "./LibMintpass.sol";
    /**
     * @dev Learn more about this project on thewhitelist.io.
     *
     * TheWhitelist is a ERC721 Contract that supports Burnable.
     * The minting process is processed in a public and a whitelist
     * sale.
     */
    contract TheWhitelist is MintpassValidator, ERC721Burnable, ERC2981, Ownable {
        using Counters for Counters.Counter;
        using Strings for uint256;
        // Token Limit and Mint Limits
        uint256 public TOKEN_LIMIT = 10000;
        uint256 public whitelistMintLimitPerWallet = 2;
        uint256 public publicMintLimitPerWallet = 5;
        // Price per Token depending on Category
        uint256 public whitelistMintPrice = 0.17 ether;
        uint256 public publicMintPrice = 0.19 ether;
        // Sale Stages Enabled / Disabled
        bool public whitelistMintEnabled = false;
        bool public publicMintEnabled = false;
        // Mapping from minter to minted amounts
        mapping(address => uint256) public boughtAmounts;
        mapping(address => uint256) public boughtWhitelistAmounts;
        // Mapping from mintpass signature to minted amounts (Free Mints)
        mapping(bytes => uint256) public mintpassRedemptions;
        // Optional mapping to overwrite specific token URIs
        mapping(uint256 => string) private _tokenURIs;
        // counter for tracking current token id
        Counters.Counter private _tokenIdTracker;
        // _abseTokenURI serving nft metadata per token
        string private _baseTokenURI = "https://api.thewhitelist.io/tokens/";
        event TokenUriChanged(
            address indexed _address,
            uint256 indexed _tokenId,
            string _tokenURI
        );
        /**
         * @dev ERC721 Constructor
         */
        constructor(string memory name, string memory symbol) ERC721(name, symbol) {
            _setDefaultRoyalty(msg.sender, 700);
            ACE_WALLET = 0x8F564ad0FBdf89b4925c91Be37b3891244544Abf;
        }
        /**
         * @dev Withrawal all Funds sent to the contract to Owner
         *
         * Requirements:
         * - `msg.sender` needs to be Owner and payable
         */
        function withdrawalAll() external onlyOwner {
            require(payable(msg.sender).send(address(this).balance));
        }
        /**
         * @dev Function to mint Tokens for only Gas. This function is used
         * for Community Wallet Mints, Raffle Winners and Cooperation Partners.
         * Redemptions are tracked and can be done in chunks.
         *
         * @param quantity amount of tokens to be minted
         * @param mintpass issued by thewhitelist.io
         * @param mintpassSignature issued by thewhitelist.io and signed by ACE_WALLET
         *
         * Requirements:
         * - `quantity` can't be higher than mintpass.amount
         * - `mintpass` needs to match the signature contents
         * - `mintpassSignature` needs to be obtained from thewhitelist.io and
         *    signed by ACE_WALLET
         */
        function freeMint(
            uint256 quantity,
            LibMintpass.Mintpass memory mintpass,
            bytes memory mintpassSignature
        ) public {
            require(
                whitelistMintEnabled == true || publicMintEnabled == true,
                "TheWhitelist: Minting is not Enabled"
            );
            require(
                mintpass.minterAddress == msg.sender,
                "TheWhitelist: Mintpass Address and Sender do not match"
            );
            require(
                mintpassRedemptions[mintpassSignature] + quantity <=
                    mintpass.amount,
                "TheWhitelist: Mintpass already redeemed"
            );
            require(
                mintpass.minterCategory == 99,
                "TheWhitelist: Mintpass not a Free Mint"
            );
            validateMintpass(mintpass, mintpassSignature);
            mintQuantityToWallet(quantity, mintpass.minterAddress);
            mintpassRedemptions[mintpassSignature] =
                mintpassRedemptions[mintpassSignature] +
                quantity;
        }
        /**
         * @dev Function to mint Tokens during Whitelist Sale. This function is
         * should only be called on thewhitelist.io minting page to ensure
         * signature validity.
         *
         * @param quantity amount of tokens to be minted
         * @param mintpass issued by thewhitelist.io
         * @param mintpassSignature issued by thewhitelist.io and signed by ACE_WALLET
         *
         * Requirements:
         * - `quantity` can't be higher than {whitelistMintLimitPerWallet}
         * - `mintpass` needs to match the signature contents
         * - `mintpassSignature` needs to be obtained from thewhitelist.io and
         *    signed by ACE_WALLET
         */
        function mintWhitelist(
            uint256 quantity,
            LibMintpass.Mintpass memory mintpass,
            bytes memory mintpassSignature
        ) public payable {
            require(
                whitelistMintEnabled == true,
                "TheWhitelist: Whitelist Minting is not Enabled"
            );
            require(
                mintpass.minterAddress == msg.sender,
                "TheWhitelist: Mintpass Address and Sender do not match"
            );
            require(
                msg.value >= whitelistMintPrice * quantity,
                "TheWhitelist: Insufficient Amount"
            );
            require(
                boughtWhitelistAmounts[mintpass.minterAddress] + quantity <=
                    whitelistMintLimitPerWallet,
                "TheWhitelist: Maximum Whitelist per Wallet reached"
            );
            validateMintpass(mintpass, mintpassSignature);
            mintQuantityToWallet(quantity, mintpass.minterAddress);
            boughtWhitelistAmounts[mintpass.minterAddress] =
                boughtWhitelistAmounts[mintpass.minterAddress] +
                quantity;
        }
        /**
         * @dev Public Mint Function.
         *
         * @param quantity amount of tokens to be minted
         *
         * Requirements:
         * - `quantity` can't be higher than {publicMintLimitPerWallet}
         */
        function mint(uint256 quantity) public payable {
            require(
                publicMintEnabled == true,
                "TheWhitelist: Public Minting is not Enabled"
            );
            require(
                msg.value >= publicMintPrice * quantity,
                "TheWhitelist: Insufficient Amount"
            );
            require(
                boughtAmounts[msg.sender] + quantity <= publicMintLimitPerWallet,
                "TheWhitelist: Maximum per Wallet reached"
            );
            mintQuantityToWallet(quantity, msg.sender);
            boughtAmounts[msg.sender] = boughtAmounts[msg.sender] + quantity;
        }
        /**
         * @dev internal mintQuantityToWallet function used to mint tokens
         * to a wallet (cpt. obivous out). We start with tokenId 1.
         *
         * @param quantity amount of tokens to be minted
         * @param minterAddress address that receives the tokens
         *
         * Requirements:
         * - `TOKEN_LIMIT` should not be reahed
         */
        function mintQuantityToWallet(uint256 quantity, address minterAddress)
            internal
            virtual
        {
            require(
                TOKEN_LIMIT >= quantity + _tokenIdTracker.current(),
                "TheWhitelist: Token Limit reached"
            );
            for (uint256 i; i < quantity; i++) {
                _mint(minterAddress, _tokenIdTracker.current() + 1);
                _tokenIdTracker.increment();
            }
        }
        /**
         * @dev Function to change the ACE_WALLET by contract owner.
         * Learn more about the ACE_WALLET on our Roadmap.
         * This wallet is used to verify mintpass signatures and is allowed to
         * change tokenURIs for specific tokens.
         *
         * @param _ace_wallet The new ACE_WALLET address
         */
        function setAceWallet(address _ace_wallet) public virtual onlyOwner {
            ACE_WALLET = _ace_wallet;
        }
        /**
         */
        function setMintingLimits(
            uint256 _whitelistMintLimitPerWallet,
            uint256 _publicMintLimitPerWallet
        ) public virtual onlyOwner {
            whitelistMintLimitPerWallet = _whitelistMintLimitPerWallet;
            publicMintLimitPerWallet = _publicMintLimitPerWallet;
        }
        /**
         * @dev Function to be called by contract owner to enable / disable
         * different mint stages
         *
         * @param _whitelistMintEnabled true/false
         * @param _publicMintEnabled true/false
         */
        function setMintingEnabled(
            bool _whitelistMintEnabled,
            bool _publicMintEnabled
        ) public virtual onlyOwner {
            whitelistMintEnabled = _whitelistMintEnabled;
            publicMintEnabled = _publicMintEnabled;
        }
        /**
         * @dev Helper to replace _baseURI
         */
        function _baseURI() internal view virtual override returns (string memory) {
            return _baseTokenURI;
        }
        /**
         * @dev Can be called by owner to change base URI. This is recommend to be used
         * after tokens are revealed to freeze metadata on IPFS or similar.
         *
         * @param permanentBaseURI URI to be prefixed before tokenId
         */
        function setBaseURI(string memory permanentBaseURI)
            public
            virtual
            onlyOwner
        {
            _baseTokenURI = permanentBaseURI;
        }
        /**
         * @dev _tokenURIs setter for a tokenId. This can only be done by owner or our
         * ACE_WALLET. Learn more about this on our Roadmap.
         *
         * Emits TokenUriChanged Event
         *
         * @param tokenId tokenId that should be updated
         * @param permanentTokenURI URI to OVERWRITE the entire tokenURI
         *
         * Requirements:
         * - `msg.sender` needs to be owner or {ACE_WALLET}
         */
        function setTokenURI(uint256 tokenId, string memory permanentTokenURI)
            public
            virtual
        {
            require(
                (msg.sender == ACE_WALLET || msg.sender == owner()),
                "TheWhitelist: Can only be modified by ACE"
            );
            require(_exists(tokenId), "TheWhitelist: URI set of nonexistent token");
            _tokenURIs[tokenId] = permanentTokenURI;
            emit TokenUriChanged(msg.sender, tokenId, permanentTokenURI);
        }
        function mintedTokenCount() public view returns (uint256) {
            return _tokenIdTracker.current();
        }
        /**
         * @dev _tokenURIs getter for a tokenId. If tokenURIs has an entry for
         * this tokenId we return this URL. Otherwise we fallback to baseURI with
         * tokenID.
         *
         * @param tokenId URI requested for this tokenId
         *
         * Requirements:
         * - `tokenID` needs to exist
         */
        function tokenURI(uint256 tokenId)
            public
            view
            virtual
            override
            returns (string memory)
        {
            require(
                _exists(tokenId),
                "TheWhitelist: URI query for nonexistent token"
            );
            string memory _tokenURI = _tokenURIs[tokenId];
            if (bytes(_tokenURI).length > 0) {
                return _tokenURI;
            }
            return super.tokenURI(tokenId);
        }
        /**
         * @dev Extends default burn behaviour with deletion of overwritten tokenURI
         * if it exists. Calls super._burn before deletion of tokenURI; Reset Token Royality if set
         *
         * @param tokenId tokenID that should be burned
         *
         * Requirements:
         * - `tokenID` needs to exist
         * - `msg.sender` needs to be current token Owner
         */
        function _burn(uint256 tokenId) internal virtual override {
            super._burn(tokenId);
            _resetTokenRoyalty(tokenId);
            if (bytes(_tokenURIs[tokenId]).length != 0) {
                delete _tokenURIs[tokenId];
            }
        }
        /**
         * @dev Sets the royalty information that all ids in this contract will default to.
         *
         * Requirements:
         *
         * - `receiver` cannot be the zero address.
         * - `feeNumerator` cannot be greater than the fee denominator.
         */
        function setDefaultRoyalty(address receiver, uint96 feeNumerator)
            public
            virtual
            onlyOwner
        {
            _setDefaultRoyalty(receiver, feeNumerator);
        }
        function supportsInterface(bytes4 interfaceId)
            public
            view
            virtual
            override(ERC721, ERC2981)
            returns (bool)
        {
            return super.supportsInterface(interfaceId);
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.10;
    import "./LibMintpass.sol";
    import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
    import "@openzeppelin/contracts/utils/cryptography/draft-EIP712.sol";
    /**
     * @dev EIP712 based contract module which validates a Mintpass Issued by
     * The Whitelist. The signer is {ACE_WALLET} and checks for integrity of
     * minterCategory, amount and Address. {mintpass} is struct defined in
     * LibMintpass.
     *
     */
    abstract contract MintpassValidator is EIP712 {
        constructor() EIP712("TheWhitelist", "1") {}
        // Wallet that signs our mintpasses
        address public ACE_WALLET;
        /**
         * @dev Validates if {mintpass} was signed by {ACE_WALLET} and created {signature}.
         *
         * @param mintpass Struct with mintpass properties
         * @param signature Signature to decode and compare
         */
        function validateMintpass(LibMintpass.Mintpass memory mintpass, bytes memory signature)
            internal
            view
        {
            bytes32 mintpassHash = LibMintpass.mintpassHash(mintpass);
            bytes32 digest = _hashTypedDataV4(mintpassHash);
            address signer = ECDSA.recover(digest, signature);
            require(
                signer == ACE_WALLET,
                "MintpassValidator: Mintpass signature verification error"
            );
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.10;
    /**
     * @dev Mintpass Struct definition used to validate EIP712.
     *
     * {minterAddress} is the mintpass owner (It's reommenced to
     * check if it matches msg.sender in your call function)
     * {amount} is the maximum mintable amount, only used for Free Mints.
     * {minterCategory} determines what type of minter is calling:
     * (1, default) Whitelist, (99) Freemint
     */
    library LibMintpass {
        bytes32 private constant MINTPASS_TYPE =
            keccak256(
                "Mintpass(address minterAddress,uint256 amount,uint256 minterCategory)"
            );
        struct Mintpass {
            address minterAddress;
            uint256 amount;
            uint256 minterCategory;
        }
        function mintpassHash(Mintpass memory mintpass) internal pure returns (bytes32) {
            return
                keccak256(
                    abi.encode(
                        MINTPASS_TYPE,
                        mintpass.minterAddress,
                        mintpass.amount,
                        mintpass.minterCategory
                    )
                );
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.0 (token/common/ERC2981.sol)
    pragma solidity ^0.8.0;
    import "@openzeppelin/contracts/interfaces/IERC2981.sol";
    import "@openzeppelin/contracts/utils/introspection/ERC165.sol";
    /**
     * @dev Implementation of the NFT Royalty Standard, a standardized way to retrieve royalty payment information.
     *
     * Royalty information can be specified globally for all token ids via {_setDefaultRoyalty}, and/or individually for
     * specific token ids via {_setTokenRoyalty}. The latter takes precedence over the first.
     *
     * Royalty is specified as a fraction of sale price. {_feeDenominator} is overridable but defaults to 10000, meaning the
     * fee is specified in basis points by default.
     *
     * _Available since v4.5._
     */
    abstract contract ERC2981 is IERC2981, ERC165 {
        struct RoyaltyInfo {
            address receiver;
            uint96 royaltyFraction;
        }
        RoyaltyInfo private _defaultRoyaltyInfo;
        mapping(uint256 => RoyaltyInfo) private _tokenRoyaltyInfo;
        /**
         * @dev See {IERC165-supportsInterface}.
         */
        function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC165) returns (bool) {
            return interfaceId == type(IERC2981).interfaceId || super.supportsInterface(interfaceId);
        }
        /**
         * @inheritdoc IERC2981
         */
        function royaltyInfo(uint256 _tokenId, uint256 _salePrice)
            external
            view
            virtual
            override
            returns (address, uint256)
        {
            RoyaltyInfo memory royalty = _tokenRoyaltyInfo[_tokenId];
            if (royalty.receiver == address(0)) {
                royalty = _defaultRoyaltyInfo;
            }
            uint256 royaltyAmount = (_salePrice * royalty.royaltyFraction) / _feeDenominator();
            return (royalty.receiver, royaltyAmount);
        }
        /**
         * @dev The denominator with which to interpret the fee set in {_setTokenRoyalty} and {_setDefaultRoyalty} as a
         * fraction of the sale price. Defaults to 10000 so fees are expressed in basis points, but may be customized by an
         * override.
         */
        function _feeDenominator() internal pure virtual returns (uint96) {
            return 10000;
        }
        /**
         * @dev Sets the royalty information that all ids in this contract will default to.
         *
         * Requirements:
         *
         * - `receiver` cannot be the zero address.
         * - `feeNumerator` cannot be greater than the fee denominator.
         */
        function _setDefaultRoyalty(address receiver, uint96 feeNumerator) internal virtual {
            require(feeNumerator <= _feeDenominator(), "ERC2981: royalty fee will exceed salePrice");
            require(receiver != address(0), "ERC2981: invalid receiver");
            _defaultRoyaltyInfo = RoyaltyInfo(receiver, feeNumerator);
        }
        /**
         * @dev Sets the royalty information for a specific token id, overriding the global default.
         *
         * Requirements:
         *
         * - `tokenId` must be already minted.
         * - `receiver` cannot be the zero address.
         * - `feeNumerator` cannot be greater than the fee denominator.
         */
        function _setTokenRoyalty(
            uint256 tokenId,
            address receiver,
            uint96 feeNumerator
        ) internal virtual {
            require(feeNumerator <= _feeDenominator(), "ERC2981: royalty fee will exceed salePrice");
            require(receiver != address(0), "ERC2981: Invalid parameters");
            _tokenRoyaltyInfo[tokenId] = RoyaltyInfo(receiver, feeNumerator);
        }
        /**
         * @dev Resets royalty information for the token id back to the global default.
         */
        function _resetTokenRoyalty(uint256 tokenId) internal virtual {
            delete _tokenRoyaltyInfo[tokenId];
        }
    }// SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.0 (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.0 (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.0 (utils/cryptography/draft-EIP712.sol)
    pragma solidity ^0.8.0;
    import "./ECDSA.sol";
    /**
     * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
     *
     * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
     * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
     * they need in their contracts using a combination of `abi.encode` and `keccak256`.
     *
     * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
     * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
     * ({_hashTypedDataV4}).
     *
     * The implementation of the domain separator was designed to be as efficient as possible while still properly updating
     * the chain id to protect against replay attacks on an eventual fork of the chain.
     *
     * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
     * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
     *
     * _Available since v3.4._
     */
    abstract contract EIP712 {
        /* solhint-disable var-name-mixedcase */
        // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to
        // invalidate the cached domain separator if the chain id changes.
        bytes32 private immutable _CACHED_DOMAIN_SEPARATOR;
        uint256 private immutable _CACHED_CHAIN_ID;
        address private immutable _CACHED_THIS;
        bytes32 private immutable _HASHED_NAME;
        bytes32 private immutable _HASHED_VERSION;
        bytes32 private immutable _TYPE_HASH;
        /* solhint-enable var-name-mixedcase */
        /**
         * @dev Initializes the domain separator and parameter caches.
         *
         * The meaning of `name` and `version` is specified in
         * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
         *
         * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
         * - `version`: the current major version of the signing domain.
         *
         * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
         * contract upgrade].
         */
        constructor(string memory name, string memory version) {
            bytes32 hashedName = keccak256(bytes(name));
            bytes32 hashedVersion = keccak256(bytes(version));
            bytes32 typeHash = keccak256(
                "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
            );
            _HASHED_NAME = hashedName;
            _HASHED_VERSION = hashedVersion;
            _CACHED_CHAIN_ID = block.chainid;
            _CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(typeHash, hashedName, hashedVersion);
            _CACHED_THIS = address(this);
            _TYPE_HASH = typeHash;
        }
        /**
         * @dev Returns the domain separator for the current chain.
         */
        function _domainSeparatorV4() internal view returns (bytes32) {
            if (address(this) == _CACHED_THIS && block.chainid == _CACHED_CHAIN_ID) {
                return _CACHED_DOMAIN_SEPARATOR;
            } else {
                return _buildDomainSeparator(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION);
            }
        }
        function _buildDomainSeparator(
            bytes32 typeHash,
            bytes32 nameHash,
            bytes32 versionHash
        ) private view returns (bytes32) {
            return keccak256(abi.encode(typeHash, nameHash, versionHash, block.chainid, address(this)));
        }
        /**
         * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
         * function returns the hash of the fully encoded EIP712 message for this domain.
         *
         * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
         *
         * ```solidity
         * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
         *     keccak256("Mail(address to,string contents)"),
         *     mailTo,
         *     keccak256(bytes(mailContents))
         * )));
         * address signer = ECDSA.recover(digest, signature);
         * ```
         */
        function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
            return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash);
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.0 (utils/cryptography/ECDSA.sol)
    pragma solidity ^0.8.0;
    import "../Strings.sol";
    /**
     * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
     *
     * These functions can be used to verify that a message was signed by the holder
     * of the private keys of a given address.
     */
    library ECDSA {
        enum RecoverError {
            NoError,
            InvalidSignature,
            InvalidSignatureLength,
            InvalidSignatureS,
            InvalidSignatureV
        }
        function _throwError(RecoverError error) private pure {
            if (error == RecoverError.NoError) {
                return; // no error: do nothing
            } else if (error == RecoverError.InvalidSignature) {
                revert("ECDSA: invalid signature");
            } else if (error == RecoverError.InvalidSignatureLength) {
                revert("ECDSA: invalid signature length");
            } else if (error == RecoverError.InvalidSignatureS) {
                revert("ECDSA: invalid signature 's' value");
            } else if (error == RecoverError.InvalidSignatureV) {
                revert("ECDSA: invalid signature 'v' value");
            }
        }
        /**
         * @dev Returns the address that signed a hashed message (`hash`) with
         * `signature` or error string. This address can then be used for verification purposes.
         *
         * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
         * this function rejects them by requiring the `s` value to be in the lower
         * half order, and the `v` value to be either 27 or 28.
         *
         * IMPORTANT: `hash` _must_ be the result of a hash operation for the
         * verification to be secure: it is possible to craft signatures that
         * recover to arbitrary addresses for non-hashed data. A safe way to ensure
         * this is by receiving a hash of the original message (which may otherwise
         * be too long), and then calling {toEthSignedMessageHash} on it.
         *
         * Documentation for signature generation:
         * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
         * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
         *
         * _Available since v4.3._
         */
        function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
            // Check the signature length
            // - case 65: r,s,v signature (standard)
            // - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._
            if (signature.length == 65) {
                bytes32 r;
                bytes32 s;
                uint8 v;
                // ecrecover takes the signature parameters, and the only way to get them
                // currently is to use assembly.
                assembly {
                    r := mload(add(signature, 0x20))
                    s := mload(add(signature, 0x40))
                    v := byte(0, mload(add(signature, 0x60)))
                }
                return tryRecover(hash, v, r, s);
            } else if (signature.length == 64) {
                bytes32 r;
                bytes32 vs;
                // ecrecover takes the signature parameters, and the only way to get them
                // currently is to use assembly.
                assembly {
                    r := mload(add(signature, 0x20))
                    vs := mload(add(signature, 0x40))
                }
                return tryRecover(hash, r, vs);
            } else {
                return (address(0), RecoverError.InvalidSignatureLength);
            }
        }
        /**
         * @dev Returns the address that signed a hashed message (`hash`) with
         * `signature`. This address can then be used for verification purposes.
         *
         * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
         * this function rejects them by requiring the `s` value to be in the lower
         * half order, and the `v` value to be either 27 or 28.
         *
         * IMPORTANT: `hash` _must_ be the result of a hash operation for the
         * verification to be secure: it is possible to craft signatures that
         * recover to arbitrary addresses for non-hashed data. A safe way to ensure
         * this is by receiving a hash of the original message (which may otherwise
         * be too long), and then calling {toEthSignedMessageHash} on it.
         */
        function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
            (address recovered, RecoverError error) = tryRecover(hash, signature);
            _throwError(error);
            return recovered;
        }
        /**
         * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
         *
         * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
         *
         * _Available since v4.3._
         */
        function tryRecover(
            bytes32 hash,
            bytes32 r,
            bytes32 vs
        ) internal pure returns (address, RecoverError) {
            bytes32 s;
            uint8 v;
            assembly {
                s := and(vs, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)
                v := add(shr(255, vs), 27)
            }
            return tryRecover(hash, v, r, s);
        }
        /**
         * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
         *
         * _Available since v4.2._
         */
        function recover(
            bytes32 hash,
            bytes32 r,
            bytes32 vs
        ) internal pure returns (address) {
            (address recovered, RecoverError error) = tryRecover(hash, r, vs);
            _throwError(error);
            return recovered;
        }
        /**
         * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
         * `r` and `s` signature fields separately.
         *
         * _Available since v4.3._
         */
        function tryRecover(
            bytes32 hash,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) internal pure returns (address, RecoverError) {
            // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
            // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
            // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
            // signatures from current libraries generate a unique signature with an s-value in the lower half order.
            //
            // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
            // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
            // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
            // these malleable signatures as well.
            if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
                return (address(0), RecoverError.InvalidSignatureS);
            }
            if (v != 27 && v != 28) {
                return (address(0), RecoverError.InvalidSignatureV);
            }
            // If the signature is valid (and not malleable), return the signer address
            address signer = ecrecover(hash, v, r, s);
            if (signer == address(0)) {
                return (address(0), RecoverError.InvalidSignature);
            }
            return (signer, RecoverError.NoError);
        }
        /**
         * @dev Overload of {ECDSA-recover} that receives the `v`,
         * `r` and `s` signature fields separately.
         */
        function recover(
            bytes32 hash,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) internal pure returns (address) {
            (address recovered, RecoverError error) = tryRecover(hash, v, r, s);
            _throwError(error);
            return recovered;
        }
        /**
         * @dev Returns an Ethereum Signed Message, created from a `hash`. This
         * produces hash corresponding to the one signed with the
         * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
         * JSON-RPC method as part of EIP-191.
         *
         * See {recover}.
         */
        function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
            // 32 is the length in bytes of hash,
            // enforced by the type signature above
            return keccak256(abi.encodePacked("\\x19Ethereum Signed Message:\
    32", hash));
        }
        /**
         * @dev Returns an Ethereum Signed Message, created from `s`. This
         * produces hash corresponding to the one signed with the
         * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
         * JSON-RPC method as part of EIP-191.
         *
         * See {recover}.
         */
        function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
            return keccak256(abi.encodePacked("\\x19Ethereum Signed Message:\
    ", Strings.toString(s.length), s));
        }
        /**
         * @dev Returns an Ethereum Signed Typed Data, created from a
         * `domainSeparator` and a `structHash`. This produces hash corresponding
         * to the one signed with the
         * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
         * JSON-RPC method as part of EIP-712.
         *
         * See {recover}.
         */
        function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
            return keccak256(abi.encodePacked("\\x19\\x01", domainSeparator, structHash));
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.0 (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.0 (utils/Counters.sol)
    pragma solidity ^0.8.0;
    /**
     * @title Counters
     * @author Matt Condon (@shrugs)
     * @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number
     * of elements in a mapping, issuing ERC721 ids, or counting request ids.
     *
     * Include with `using Counters for Counters.Counter;`
     */
    library Counters {
        struct Counter {
            // This variable should never be directly accessed by users of the library: interactions must be restricted to
            // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
            // this feature: see https://github.com/ethereum/solidity/issues/4637
            uint256 _value; // default: 0
        }
        function current(Counter storage counter) internal view returns (uint256) {
            return counter._value;
        }
        function increment(Counter storage counter) internal {
            unchecked {
                counter._value += 1;
            }
        }
        function decrement(Counter storage counter) internal {
            uint256 value = counter._value;
            require(value > 0, "Counter: decrement overflow");
            unchecked {
                counter._value = value - 1;
            }
        }
        function reset(Counter storage counter) internal {
            counter._value = 0;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.0 (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.0 (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.0 (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.0 (token/ERC721/extensions/ERC721Burnable.sol)
    pragma solidity ^0.8.0;
    import "../ERC721.sol";
    import "../../../utils/Context.sol";
    /**
     * @title ERC721 Burnable Token
     * @dev ERC721 Token that can be irreversibly burned (destroyed).
     */
    abstract contract ERC721Burnable is Context, ERC721 {
        /**
         * @dev Burns `tokenId`. See {ERC721-_burn}.
         *
         * Requirements:
         *
         * - The caller must own `tokenId` or be an approved operator.
         */
        function burn(uint256 tokenId) public virtual {
            //solhint-disable-next-line max-line-length
            require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721Burnable: caller is not owner nor approved");
            _burn(tokenId);
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.0 (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.0 (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.0 (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.0 (interfaces/IERC2981.sol)
    pragma solidity ^0.8.0;
    import "./IERC165.sol";
    /**
     * @dev Interface for the NFT Royalty Standard
     */
    interface IERC2981 is IERC165 {
        /**
         * @dev Called with the sale price to determine how much royalty is owed and to whom.
         * @param tokenId - the NFT asset queried for royalty information
         * @param salePrice - the sale price of the NFT asset specified by `tokenId`
         * @return receiver - address of who should be sent the royalty payment
         * @return royaltyAmount - the royalty payment amount for `salePrice`
         */
        function royaltyInfo(uint256 tokenId, uint256 salePrice)
            external
            view
            returns (address receiver, uint256 royaltyAmount);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.0 (interfaces/IERC165.sol)
    pragma solidity ^0.8.0;
    import "../utils/introspection/IERC165.sol";
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.0 (access/Ownable.sol)
    pragma solidity ^0.8.0;
    import "../utils/Context.sol";
    /**
     * @dev Contract module which provides a basic access control mechanism, where
     * there is an account (an owner) that can be granted exclusive access to
     * specific functions.
     *
     * By default, the owner account will be the one that deploys the contract. This
     * can later be changed with {transferOwnership}.
     *
     * This module is used through inheritance. It will make available the modifier
     * `onlyOwner`, which can be applied to your functions to restrict their use to
     * the owner.
     */
    abstract contract Ownable is Context {
        address private _owner;
        event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
        /**
         * @dev Initializes the contract setting the deployer as the initial owner.
         */
        constructor() {
            _transferOwnership(_msgSender());
        }
        /**
         * @dev 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);
        }
    }