ETH Price: $2,611.78 (-0.65%)

Transaction Decoder

Block:
14318003 at Mar-04-2022 03:20:17 AM +UTC
Transaction Fee:
0.003674664432726568 ETH $9.60
Gas Used:
87,784 Gas / 41.860298377 Gwei

Account State Difference:

  Address   Before After State Difference Code
(Hiveon Pool)
11,019.188309857424017857 Eth11,019.188529317424017857 Eth0.00021946
0x6D9B6821...5066f0999
0x73543b6e...Cf1a0bd93
0xd7Cc08b5...95e9B3156
1.348296336410602194 Eth
Nonce: 36
1.344621671977875626 Eth
Nonce: 37
0.003674664432726568
0xD87bA8ea...134ed905C

Execution Trace

BloodShedBearsStaking.unStakeTokens( selections_= )
  • BloodShedBearsGen1.safeTransferFrom( from=0x73543b6e2545077D7f4FA04652eB787Cf1a0bd93, to=0xd7Cc08b5a4B663780baFe66e86fEE8d95e9B3156, tokenId=256 )
  • 0x3414930b2474211f7c9015e6c636be59ad48a09a.18a5da77( )
  • BloodToken.add( recipient_=0xd7Cc08b5a4B663780baFe66e86fEE8d95e9B3156, amount_=2195826388888888888888 )
    File 1 of 3: BloodShedBearsStaking
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
    import "@openzeppelin/contracts/access/Ownable.sol";
    import "@openzeppelin/contracts/security/Pausable.sol";
    interface ICollection721 {
        function isOwnerOfBatch(uint256[] calldata tokenIds_, address address_) external view returns (bool);
        function ownerOf(uint256 tokenId) external view returns (address);
        function safeTransferFrom(address from, address to, uint256 tokenId) external;
    }
    interface IMintPass {
        function balanceOf(address account, uint256 id) external view returns (uint256);
        function safeTransferFrom(
            address from,
            address to,
            uint256 id,
            uint256 amount,
            bytes memory data
        ) external;
    }
    interface IToken {
        function add(address wallet, uint256 amount) external;
        function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
        function mintTokens(address to, uint256 amount) external;
    }
    interface IGameStats {
        function getLevelBoosts(address collection, uint256[] calldata tokenIds) external view returns (uint256[] memory);
        function getLevelBoost(address collection, uint256 tokenId) external view returns (uint256);
        function getLevel(address collection, uint256 tokenId) external returns (uint256);
        function setTokensData(
            address collection,
            uint256[] calldata tokenIds,
            uint256[] calldata levels,
            bool[] calldata factions,
            bool[] calldata elites
        ) external;
        function isUnStakeClaimSuccessful(
            address collection,
            uint256 tokenId,
            uint256 amount,
            uint256 stakeType
        ) external returns (bool);
        function setStakedTokenData(
            address collection,
            address owner,
            uint256 stakeType,
            uint256[] calldata tokenIds
        ) external;
        function unsetStakedTokenData(
            address collection,
            uint256[] calldata tokenIds
        ) external;
    }
    contract BloodShedBearsStaking is Ownable, Pausable {
        struct CollectionSettings {
            bool hasLevel;
            uint256 baseYieldRate;
        }
        struct StakeData {
            uint256 stakeType;
            uint256 claimDate;
            uint256 houseTokenId;
        }
        struct HouseEnrol {
            uint256 enrolDate;
            uint256 tokenId;
        }
        struct UnStakeSelection {
            address collectionAddress;
            uint256[] tokens;
        }
        struct StakeSelection {
            uint256 targetHouse;
            uint256 stakeType;
            address collectionAddress;
            uint256[] tokens;
            uint256[] levels;
            bool[] factions;
            bool[] elites;
            bytes[] signatures;
        }
        struct StakedTokens {
            uint256 tokenId;
            uint256 stakeType;
            uint256 claimDate;
            uint256 houseTokenId;
        }
        address private signerAddress;
        mapping(string => address) public contractsAddressesMap;
        mapping(string => uint256) public stakeTypesVariables;
        mapping(address => mapping(address => uint256[])) public stakedTokensByWallet;
        mapping(address => mapping(uint256 => StakeData)) public stakedTokensData;
        uint256 public treeHouseBoost = 30;
        mapping(uint256 => mapping(address => HouseEnrol[])) public treeHousesEnrolments;
        mapping(address => CollectionSettings) public collectionsSettings;
        address[] public collections;
        // PARTNER COLLECTIONS
        mapping(address => uint256) public partnerCollectionYield;
        mapping(address => mapping(uint256 => bool)) public claimedPartnerTokens;
        // MINT PASSES
        mapping(address => uint256) public walletsStakedPasses;
        mapping(address => uint256) public passesStakeDate;
        // ADMIN
        function addCollectionSettings(
            CollectionSettings[] calldata settings_,
            address[] calldata collections_
        ) external onlyOwner {
            for (uint256 i = 0; i < collections_.length; ++i) {
                CollectionSettings storage cs = collectionsSettings[collections_[i]];
                cs.baseYieldRate = settings_[i].baseYieldRate;
                cs.hasLevel = settings_[i].hasLevel;
            }
            collections = collections_;
        }
        function setTreeHouseBoost(uint256 boost_) external onlyOwner {
            treeHouseBoost = boost_;
        }
        function setSignerAddress(address signerAddress_) external onlyOwner {
            signerAddress = signerAddress_;
        }
        function setContractAddressesKeys(
            string[] calldata keys_,
            address[] calldata collections_
        ) external onlyOwner {
            for (uint i = 0; i < keys_.length; ++i) {
                contractsAddressesMap[keys_[i]] = collections_[i];
            }
        }
        function setStakeTypesKeys(
            string[] calldata keys_,
            uint256[] calldata stakeTypesIndexes_
        ) external onlyOwner {
            for (uint256 i = 0; i < keys_.length; ++i) {
                stakeTypesVariables[keys_[i]] = stakeTypesIndexes_[i];
            }
        }
        function setPartnerProjectsYielding(
            address[] calldata addresses_,
            uint256[] calldata yieldingAmounts_
        ) external onlyOwner {
            for (uint256 i = 0; i <  addresses_.length; ++i) {
                partnerCollectionYield[addresses_[i]] = yieldingAmounts_[i];
            }
        }
        // UTILS
        function _moveTokenInTheListStake(
            uint256[] storage list,
            uint256 tokenId
        ) internal {
            uint256 foundIndex;
            uint256 i;
            for (; i < list.length; ++i) {
                if (list[i] == tokenId) {
                    foundIndex = i;
                    break;
                }
            }
            require(i != list.length, "NOT OWNED");
            if (foundIndex != list.length - 1) {
                uint256 t = list[foundIndex];
                list[foundIndex] = list[list.length - 1];
                list[list.length - 1] = t;
            }
        }
        function _moveTokenInTheListHouse(
            HouseEnrol[] storage list,
            uint256 tokenId
        ) internal  {
            uint256 foundIndex;
            uint256 i;
            for (; i < list.length; ++i) {
                if (list[i].tokenId == tokenId) {
                    foundIndex = i;
                    break;
                }
            }
            require(i != list.length, "NOT OWNED");
            if (foundIndex != list.length - 1) {
                HouseEnrol memory he = list[foundIndex];
                list[foundIndex] = list[list.length - 1];
                list[list.length - 1] = he;
            }
        }
        function onERC721Received(address, address, uint256, bytes calldata) external pure returns(bytes4) {
            return bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"));
        }
        function onERC1155Received(address, address, uint256, uint256, bytes calldata) external pure returns (bytes4) {
            return bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"));
        }
        function _validateMultipleSignature(
            StakeSelection[] calldata selections_
        ) internal {
            for (uint256 i = 0; i < selections_.length; i++) {
                if (selections_[i].signatures.length == 0) {
                    continue;
                }
                bool isValid = true;
                for (uint256 j = 0; j < selections_[i].tokens.length; j++) {
                    bytes32 dataHash = keccak256(abi.encodePacked(
                            selections_[i].collectionAddress,
                            selections_[i].tokens[j],
                            selections_[i].levels[j],
                            selections_[i].elites[j],
                            selections_[i].factions[j]
                        ));
                    bytes32 message = ECDSA.toEthSignedMessageHash(dataHash);
                    address receivedAddress = ECDSA.recover(message, selections_[i].signatures[j]);
                    isValid = isValid && (receivedAddress != address(0) && receivedAddress == signerAddress);
                }
                require(isValid);
                IGameStats(contractsAddressesMap["gameStats"]).setTokensData(
                    selections_[i].collectionAddress,
                    selections_[i].tokens,
                    selections_[i].levels,
                    selections_[i].factions,
                    selections_[i].elites
                );
            }
        }
        // MINT PASS
        function stakePasses(
            uint256 amount_,
            bool withdrawCurrentYield
        ) external whenNotPaused {
            require(IMintPass(contractsAddressesMap["mintPass"]).balanceOf(msg.sender, 0) >= amount_);
            if (walletsStakedPasses[msg.sender] != 0) {
                if (withdrawCurrentYield) {
                    withdrawPassesYield();
                } else {
                    transferPassesYieldIntoInternalWallet(msg.sender);
                }
            } else {
                passesStakeDate[msg.sender] = block.timestamp;
            }
            walletsStakedPasses[msg.sender] += amount_;
            IMintPass(contractsAddressesMap["mintPass"]).safeTransferFrom(msg.sender, address(this), 0, amount_, "");
        }
        function unStakePasses(bool withdraw) external whenNotPaused  {
            if (withdraw) {
                withdrawPassesYield();
            } else {
                transferPassesYieldIntoInternalWallet(msg.sender);
            }
            IMintPass(contractsAddressesMap["mintPass"]).safeTransferFrom(address(this), msg.sender, 0, walletsStakedPasses[msg.sender], "");
            walletsStakedPasses[msg.sender] = 0;
        }
        function withdrawPassesYield() public whenNotPaused {
            IToken(contractsAddressesMap["token"]).mintTokens(msg.sender, _claimMetaPassEarning(msg.sender));
        }
        function transferPassesYieldIntoInternalWallet(address wallet_) public whenNotPaused {
            IToken(contractsAddressesMap["token"]).add(wallet_, _claimMetaPassEarning(msg.sender));
        }
        function _claimMetaPassEarning(address wallet_) internal returns (uint256) {
            uint256 amount = calculateMetaPassesYield(wallet_);
            passesStakeDate[wallet_] = block.timestamp;
            return amount;
        }
        function calculateMetaPassesYield(address wallet_) public view returns (uint256) {
            return (block.timestamp - passesStakeDate[wallet_]) *
                walletsStakedPasses[wallet_] *
                collectionsSettings[contractsAddressesMap["mintPass"]].baseYieldRate / 1 days;
        }
        // PARTNER PROJECTS
        function claimPartnerTokens(
            address collection_,
            uint256[] calldata tokenIds_,
            bool withdrawYield
        ) external whenNotPaused {
            require(partnerCollectionYield[collection_] != 0);
            for (uint256 i = 0; i < tokenIds_.length; ++i) {
                require(ICollection721(collection_).ownerOf(tokenIds_[i]) == msg.sender);
                claimedPartnerTokens[collection_][tokenIds_[i]] = true;
            }
            if (withdrawYield) {
                IToken(contractsAddressesMap["token"]).mintTokens(msg.sender, partnerCollectionYield[collection_] * tokenIds_.length);
            } else {
                IToken(contractsAddressesMap["token"]).add(msg.sender, partnerCollectionYield[collection_] * tokenIds_.length);
            }
        }
        // STAKE
        function _getHouseOccupancy(uint256 houseId) internal view returns (uint256) {
            uint256 occupancy = 0;
            for (uint256 i = 0; i < collections.length; ++i) {
                occupancy += treeHousesEnrolments[houseId][collections[i]].length;
            }
            return occupancy;
        }
        function isOwner(address wallet_, address collection_, uint256 tokenId_) public view returns (bool) {
            for (uint256 i = 0; i <  stakedTokensByWallet[wallet_][collection_].length; ++i) {
                if (stakedTokensByWallet[wallet_][collection_][i] == tokenId_) {
                    return true;
                }
            }
            return false;
        }
        function stakeTokens(StakeSelection[] calldata selections_) external whenNotPaused {
            _validateMultipleSignature(selections_);
            for (uint256 i = 0; i < selections_.length; ++i) {
                require(ICollection721(selections_[i].collectionAddress).isOwnerOfBatch(selections_[i].tokens, msg.sender));
                if (selections_[i].collectionAddress == contractsAddressesMap["tokenGenerator"]) {
                    require(selections_[i].stakeType == stakeTypesVariables["treeHouse"]);
                }
                if (selections_[i].collectionAddress == contractsAddressesMap["treeHouse"]) {
                    require(selections_[i].stakeType == stakeTypesVariables["home"]);
                }
                bool stack = selections_[i].stakeType == stakeTypesVariables["treeHouse"];
                if (stack) {
                    require(isOwner(msg.sender, contractsAddressesMap["treeHouse"], selections_[i].targetHouse));
                    require(
                        selections_[i].tokens.length + _getHouseOccupancy(selections_[i].targetHouse)
                        <= IGameStats(contractsAddressesMap["gameStats"]).getLevel(
                                contractsAddressesMap["treeHouse"],
                                selections_[i].targetHouse
                    ));
                }
                for (uint256 j = 0; j < selections_[i].tokens.length; ++j) {
                    _enterStakeStance(
                        selections_[i].collectionAddress,
                        selections_[i].tokens[j],
                        selections_[i].stakeType,
                        false
                    );
                    if (stack) {
                        _enterStackStance(
                            selections_[i].collectionAddress,
                            selections_[i].tokens[j],
                            selections_[i].targetHouse
                        );
                    }
                    ICollection721(selections_[i].collectionAddress)
                        .safeTransferFrom(msg.sender, address(this), selections_[i].tokens[j]);
                }
                IGameStats(contractsAddressesMap["gameStats"]).setStakedTokenData(
                    selections_[i].collectionAddress,
                    msg.sender,
                    selections_[i].stakeType,
                    selections_[i].tokens
                );
                delete stack;
            }
        }
        function _enterStakeStance(
            address collection_,
            uint256 tokenId_,
            uint256 stakeType_,
            bool skipPushing
        ) internal {
            if (!skipPushing) {
                stakedTokensByWallet[msg.sender][collection_].push(tokenId_);
            }
            StakeData storage stake = stakedTokensData[collection_][tokenId_];
            stake.stakeType = stakeType_;
            stake.claimDate = block.timestamp;
        }
        function _enterStackStance(
            address collection_,
            uint256 tokenId_,
            uint256 treeHouseId_
        ) internal {
            treeHousesEnrolments[treeHouseId_][collection_].push(HouseEnrol({
                enrolDate : block.timestamp,
                tokenId: tokenId_
            }));
            stakedTokensData[collection_][tokenId_].houseTokenId = treeHouseId_;
        }
        function unStakeTokens(UnStakeSelection[] calldata selections_) external whenNotPaused {
            uint256 totalAccumulatedYield;
            for (uint256 i = 0; i < selections_.length; ++i) {
                totalAccumulatedYield += _exitStakeStanceMany(
                    selections_[i].collectionAddress,
                    selections_[i].tokens,
                    true,
                    true,
                    false
                );
                IGameStats(contractsAddressesMap["gameStats"]).unsetStakedTokenData(
                    selections_[i].collectionAddress,
                    selections_[i].tokens
                );
            }
            IToken(contractsAddressesMap["token"]).add(msg.sender, totalAccumulatedYield);
        }
        function _exitStakeStanceMany(
            address collection_,
            uint256[] calldata tokenIds_,
            bool transferTokens,
            bool updateClaimDate,
            bool skipPopping
        ) internal returns(uint256) {
            uint256 yieldedAmount;
            uint256[] memory levelBoosts;
            if(collectionsSettings[collection_].hasLevel) {
                levelBoosts = IGameStats(contractsAddressesMap["gameStats"]).getLevelBoosts(collection_, tokenIds_);
            } else {
                levelBoosts = new uint256[](tokenIds_.length);
            }
            for (uint256 i = 0; i < tokenIds_.length; ++i) {
                require(isOwner(msg.sender, collection_, tokenIds_[i]));
                yieldedAmount += _getYield(collection_, tokenIds_[i], levelBoosts[i], updateClaimDate);
                _exitStakeStance(collection_, tokenIds_[i], skipPopping);
                if (transferTokens) {
                    ICollection721(collection_).safeTransferFrom(address(this), msg.sender, tokenIds_[i]);
                }
            }
            delete levelBoosts;
            return yieldedAmount;
        }
        function _exitStakeStance(
            address collection_,
            uint256 tokenId_,
            bool skipPopping
        ) internal {
            if (collection_ == contractsAddressesMap["treeHouse"]) {
                require(_getHouseOccupancy(tokenId_) == 0);
            }
            if (!skipPopping) {
                _moveTokenInTheListStake(stakedTokensByWallet[msg.sender][collection_], tokenId_);
                stakedTokensByWallet[msg.sender][collection_].pop();
            }
            if (stakedTokensData[collection_][tokenId_].stakeType == stakeTypesVariables["treeHouse"]) {
                _exitStackStance(collection_, tokenId_);
            }
            delete stakedTokensData[collection_][tokenId_];
        }
        function _exitStackStance(
            address collection_,
            uint256 tokenId_
        ) internal {
            _moveTokenInTheListHouse(
                treeHousesEnrolments[stakedTokensData[collection_][tokenId_].houseTokenId][collection_],
                tokenId_
            );
            treeHousesEnrolments[stakedTokensData[collection_][tokenId_].houseTokenId][collection_].pop();
        }
        function moveIntoDifferentStake(StakeSelection[] calldata selections_) external whenNotPaused {
            uint256 totalAccumulatedYield;
            for (uint256 i = 0; i < selections_.length; ++i) {
                totalAccumulatedYield += _exitStakeStanceMany(
                    selections_[i].collectionAddress,
                    selections_[i].tokens,
                    false,
                    false,
                    true
                );
                IGameStats(contractsAddressesMap["gameStats"]).unsetStakedTokenData(
                    selections_[i].collectionAddress,
                    selections_[i].tokens
                );
                require(selections_[i].collectionAddress != contractsAddressesMap["tokenGenerator"]);
                require(selections_[i].collectionAddress != contractsAddressesMap["treeHouse"]);
                bool stack = selections_[i].stakeType == stakeTypesVariables["treeHouse"];
                if (stack) {
                    require(isOwner(msg.sender, contractsAddressesMap["treeHouse"], selections_[i].targetHouse));
                    require(
                        selections_[i].tokens.length + _getHouseOccupancy(selections_[i].targetHouse)
                        <= IGameStats(contractsAddressesMap["gameStats"])
                            .getLevel(contractsAddressesMap["treeHouse"], selections_[i].targetHouse)
                    );
                }
                for (uint256 j = 0; j < selections_[i].tokens.length; ++j) {
                    _enterStakeStance(
                        selections_[i].collectionAddress,
                        selections_[i].tokens[j],
                        selections_[i].stakeType,
                        true
                    );
                    if (stack) {
                        _enterStackStance(
                            selections_[i].collectionAddress,
                            selections_[i].tokens[j],
                            selections_[i].targetHouse
                        );
                    }
                }
                IGameStats(contractsAddressesMap["gameStats"]).setStakedTokenData(
                    selections_[i].collectionAddress,
                    msg.sender,
                    selections_[i].stakeType,
                    selections_[i].tokens
                );
            }
            IToken(contractsAddressesMap["token"]).add(msg.sender, totalAccumulatedYield);
            delete totalAccumulatedYield;
        }
        // TOKENS
        function _getYield(
            address collection_,
            uint256 tokenId_,
            uint256 tokenLevelBoost,
            bool updateClaimField
        ) internal returns (uint256) {
            uint256 yield = collectionsSettings[collection_].baseYieldRate *
            (block.timestamp - stakedTokensData[collection_][tokenId_].claimDate) / 1 days;
            if (stakedTokensData[collection_][tokenId_].stakeType == stakeTypesVariables["training"]) {
                yield = yield / 2;
            }
            if (collectionsSettings[collection_].hasLevel) {
                yield += yield * tokenLevelBoost / 100;
            }
            if (stakedTokensData[collection_][tokenId_].stakeType == stakeTypesVariables["treeHouse"]) {
                HouseEnrol[] storage houseEnrols = treeHousesEnrolments[stakedTokensData[collection_][tokenId_].houseTokenId][collection_];
                uint256 foundIndex;
                uint256 i;
                for (; i < houseEnrols.length; ++i) {
                    if (houseEnrols[i].tokenId == tokenId_) {
                        foundIndex = i;
                        break;
                    }
                }
                require(i != houseEnrols.length, "NOT OWNED");
                yield += (block.timestamp - houseEnrols[foundIndex].enrolDate) *
                    collectionsSettings[collection_].baseYieldRate *
                    treeHouseBoost /
                    100 days;
                if (updateClaimField) {
                    houseEnrols[foundIndex].enrolDate = block.timestamp;
                }
            }
            if (
                stakedTokensData[collection_][tokenId_].stakeType != stakeTypesVariables["training"] &&
                (
                    collection_ == contractsAddressesMap["gen0"] ||
                    collection_ == contractsAddressesMap["gen1"]
                )
            ) {
                if (
                    !IGameStats(contractsAddressesMap["gameStats"])
                        .isUnStakeClaimSuccessful(
                            collection_,
                            tokenId_,
                            yield,
                            stakedTokensData[collection_][tokenId_].stakeType)
                ) {
                    yield = 0;
                }
            }
            if (updateClaimField) {
                stakedTokensData[collection_][tokenId_].claimDate = block.timestamp;
            }
            return yield;
        }
        function claimYield(
            address collection_,
            uint256[] memory tokenIds_
        ) external whenNotPaused returns (uint256) {
            uint256 amount;
            uint256[] memory levelBoosts;
            if(collectionsSettings[collection_].hasLevel) {
                levelBoosts = IGameStats(contractsAddressesMap["gameStats"]).getLevelBoosts(collection_, tokenIds_);
            } else {
                levelBoosts = new uint256[](tokenIds_.length);
            }
            for (uint256 i = 0; i <  tokenIds_.length; ++i) {
                require(isOwner(msg.sender, collection_, tokenIds_[i]));
                amount += _getYield(collection_, tokenIds_[i], levelBoosts[i], true);
            }
            IToken(contractsAddressesMap["token"]).add(msg.sender, amount);
            delete levelBoosts;
            return amount;
        }
        function claimYieldForAll() external whenNotPaused {
            uint256 totalYield;
            for (uint256 i = 0; i < collections.length; ++i) {
                if (stakedTokensByWallet[msg.sender][collections[i]].length > 0) {
                    uint256[] memory levelBoosts;
                    if(collectionsSettings[collections[i]].hasLevel) {
                        levelBoosts = IGameStats(contractsAddressesMap["gameStats"]).getLevelBoosts(collections[i], stakedTokensByWallet[msg.sender][collections[i]]);
                    } else {
                        levelBoosts = new uint256[](stakedTokensByWallet[msg.sender][collections[i]].length);
                    }
                    for (uint256 j = 0; j < stakedTokensByWallet[msg.sender][collections[i]].length; ++j) {
                        totalYield += _getYield(
                            collections[i],
                            stakedTokensByWallet[msg.sender][collections[i]][j],
                            levelBoosts[j],
                            true
                        );
                    }
                }
            }
            IToken(contractsAddressesMap["token"]).add(msg.sender, totalYield);
            delete totalYield;
        }
        function calculateYieldSafe(address collection_, uint256[] memory tokenIds_) public view returns(uint256) {
            uint256 totalYield;
            for (uint256 i = 0; i < tokenIds_.length; ++i) {
                totalYield += _getYieldSafe(collection_, tokenIds_[i]);
            }
            return totalYield;
        }
        function calculateYieldForAll(address wallet_) external view returns(uint256) {
            uint256 totalYield;
            for (uint256 i = 0; i < collections.length; ++i) {
                if (stakedTokensByWallet[wallet_][collections[i]].length > 0) {
                   totalYield += calculateYieldSafe(collections[i], stakedTokensByWallet[wallet_][collections[i]]);
                }
            }
            return totalYield;
        }
        function _getYieldSafe(
            address collection_,
            uint256 tokenId_
        ) internal view returns (uint256) {
            uint256 yield = collectionsSettings[collection_].baseYieldRate *
            (block.timestamp - stakedTokensData[collection_][tokenId_].claimDate) / 1 days;
            if (stakedTokensData[collection_][tokenId_].stakeType == stakeTypesVariables["training"]) {
                yield = yield / 2;
            }
            if (collectionsSettings[collection_].hasLevel) {
                yield += yield * IGameStats(contractsAddressesMap["gameStats"]).getLevelBoost(collection_, tokenId_) / 100;
            }
            if (stakedTokensData[collection_][tokenId_].stakeType == stakeTypesVariables["treeHouse"]) {
                HouseEnrol[] storage houseEnrols = treeHousesEnrolments[stakedTokensData[collection_][tokenId_].houseTokenId][collection_];
                uint256 foundIndex;
                uint256 i;
                for (; i < houseEnrols.length; ++i) {
                    if (houseEnrols[i].tokenId == tokenId_) {
                        foundIndex = i;
                        break;
                    }
                }
                require(i != houseEnrols.length, "NOT OWNED");
                yield +=
                (block.timestamp - houseEnrols[foundIndex].enrolDate) *
                collectionsSettings[collection_].baseYieldRate *
                treeHouseBoost /
                100 days;
            }
            return yield;
        }
        function getStakedTokens(address wallet_, address collection_) external view returns (StakedTokens[] memory) {
            StakedTokens[] memory stakedTokens = new StakedTokens[](stakedTokensByWallet[wallet_][collection_].length);
            for (uint256 i = 0; i < stakedTokensByWallet[wallet_][collection_].length; ++i) {
                stakedTokens[i].tokenId = stakedTokensByWallet[wallet_][collection_][i];
                stakedTokens[i].stakeType = stakedTokensData[collection_][stakedTokensByWallet[wallet_][collection_][i]].stakeType;
                stakedTokens[i].claimDate = stakedTokensData[collection_][stakedTokensByWallet[wallet_][collection_][i]].claimDate;
                stakedTokens[i].houseTokenId = stakedTokensData[collection_][stakedTokensByWallet[wallet_][collection_][i]].houseTokenId;
            }
            return stakedTokens;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.5.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 = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
            uint8 v = uint8((uint256(vs) >> 255) + 27);
            return tryRecover(hash, v, r, s);
        }
        /**
         * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
         *
         * _Available since v4.2._
         */
        function recover(
            bytes32 hash,
            bytes32 r,
            bytes32 vs
        ) internal pure returns (address) {
            (address recovered, RecoverError error) = tryRecover(hash, r, vs);
            _throwError(error);
            return recovered;
        }
        /**
         * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
         * `r` and `s` signature fields separately.
         *
         * _Available since v4.3._
         */
        function tryRecover(
            bytes32 hash,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) internal pure returns (address, RecoverError) {
            // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
            // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
            // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
            // signatures from current libraries generate a unique signature with an s-value in the lower half order.
            //
            // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
            // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
            // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
            // these malleable signatures as well.
            if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
                return (address(0), RecoverError.InvalidSignatureS);
            }
            if (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.1 (access/Ownable.sol)
    pragma solidity ^0.8.0;
    import "../utils/Context.sol";
    /**
     * @dev Contract module which provides a basic access control mechanism, where
     * there is an account (an owner) that can be granted exclusive access to
     * specific functions.
     *
     * By default, the owner account will be the one that deploys the contract. This
     * can later be changed with {transferOwnership}.
     *
     * This module is used through inheritance. It will make available the modifier
     * `onlyOwner`, which can be applied to your functions to restrict their use to
     * the owner.
     */
    abstract contract Ownable is Context {
        address private _owner;
        event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
        /**
         * @dev Initializes the contract setting the deployer as the initial owner.
         */
        constructor() {
            _transferOwnership(_msgSender());
        }
        /**
         * @dev Returns the address of the current owner.
         */
        function owner() public view virtual returns (address) {
            return _owner;
        }
        /**
         * @dev Throws if called by any account other than the owner.
         */
        modifier onlyOwner() {
            require(owner() == _msgSender(), "Ownable: caller is not the owner");
            _;
        }
        /**
         * @dev Leaves the contract without owner. It will not be possible to call
         * `onlyOwner` functions anymore. Can only be called by the current owner.
         *
         * NOTE: Renouncing ownership will leave the contract without an owner,
         * thereby removing any functionality that is only available to the owner.
         */
        function renounceOwnership() public virtual onlyOwner {
            _transferOwnership(address(0));
        }
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`).
         * Can only be called by the current owner.
         */
        function transferOwnership(address newOwner) public virtual onlyOwner {
            require(newOwner != address(0), "Ownable: new owner is the zero address");
            _transferOwnership(newOwner);
        }
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`).
         * Internal function without access restriction.
         */
        function _transferOwnership(address newOwner) internal virtual {
            address oldOwner = _owner;
            _owner = newOwner;
            emit OwnershipTransferred(oldOwner, newOwner);
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)
    pragma solidity ^0.8.0;
    import "../utils/Context.sol";
    /**
     * @dev Contract module which allows children to implement an emergency stop
     * mechanism that can be triggered by an authorized account.
     *
     * This module is used through inheritance. It will make available the
     * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
     * the functions of your contract. Note that they will not be pausable by
     * simply including this module, only once the modifiers are put in place.
     */
    abstract contract Pausable is Context {
        /**
         * @dev Emitted when the pause is triggered by `account`.
         */
        event Paused(address account);
        /**
         * @dev Emitted when the pause is lifted by `account`.
         */
        event Unpaused(address account);
        bool private _paused;
        /**
         * @dev Initializes the contract in unpaused state.
         */
        constructor() {
            _paused = false;
        }
        /**
         * @dev Returns true if the contract is paused, and false otherwise.
         */
        function paused() public view virtual returns (bool) {
            return _paused;
        }
        /**
         * @dev Modifier to make a function callable only when the contract is not paused.
         *
         * Requirements:
         *
         * - The contract must not be paused.
         */
        modifier whenNotPaused() {
            require(!paused(), "Pausable: paused");
            _;
        }
        /**
         * @dev Modifier to make a function callable only when the contract is paused.
         *
         * Requirements:
         *
         * - The contract must be paused.
         */
        modifier whenPaused() {
            require(paused(), "Pausable: not paused");
            _;
        }
        /**
         * @dev Triggers stopped state.
         *
         * Requirements:
         *
         * - The contract must not be paused.
         */
        function _pause() internal virtual whenNotPaused {
            _paused = true;
            emit Paused(_msgSender());
        }
        /**
         * @dev Returns to normal state.
         *
         * Requirements:
         *
         * - The contract must be paused.
         */
        function _unpause() internal virtual whenPaused {
            _paused = false;
            emit Unpaused(_msgSender());
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (utils/Strings.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev String operations.
     */
    library Strings {
        bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
        /**
         * @dev Converts a `uint256` to its ASCII `string` decimal representation.
         */
        function toString(uint256 value) internal pure returns (string memory) {
            // Inspired by OraclizeAPI's implementation - MIT licence
            // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
            if (value == 0) {
                return "0";
            }
            uint256 temp = value;
            uint256 digits;
            while (temp != 0) {
                digits++;
                temp /= 10;
            }
            bytes memory buffer = new bytes(digits);
            while (value != 0) {
                digits -= 1;
                buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
                value /= 10;
            }
            return string(buffer);
        }
        /**
         * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
         */
        function toHexString(uint256 value) internal pure returns (string memory) {
            if (value == 0) {
                return "0x00";
            }
            uint256 temp = value;
            uint256 length = 0;
            while (temp != 0) {
                length++;
                temp >>= 8;
            }
            return toHexString(value, length);
        }
        /**
         * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
         */
        function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
            bytes memory buffer = new bytes(2 * length + 2);
            buffer[0] = "0";
            buffer[1] = "x";
            for (uint256 i = 2 * length + 1; i > 1; --i) {
                buffer[i] = _HEX_SYMBOLS[value & 0xf];
                value >>= 4;
            }
            require(value == 0, "Strings: hex length insufficient");
            return string(buffer);
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev Provides information about the current execution context, including the
     * sender of the transaction and its data. While these are generally available
     * via msg.sender and msg.data, they should not be accessed in such a direct
     * manner, since when dealing with meta-transactions the account sending and
     * paying for execution may not be the actual sender (as far as an application
     * is concerned).
     *
     * This contract is only required for intermediate, library-like contracts.
     */
    abstract contract Context {
        function _msgSender() internal view virtual returns (address) {
            return msg.sender;
        }
        function _msgData() internal view virtual returns (bytes calldata) {
            return msg.data;
        }
    }
    

    File 2 of 3: BloodShedBearsGen1
    // SPDX-License-Identifier: GPL-3.0
    
    /*
     ______     __                            __           __                      __
    |_   _ \   [  |                          |  ]         [  |                    |  ]
      | |_) |   | |    .--.     .--.     .--.| |   .--.    | |--.    .---.    .--.| |
      |  __'.   | |  / .'`\ \ / .'`\ \ / /'`\' |  ( (`\]   | .-. |  / /__\\ / /'`\' |
     _| |__) |  | |  | \__. | | \__. | | \__/  |   `'.'.   | | | |  | \__., | \__/  |
    |_______/  [___]  '.__.'   '.__.'   '.__.;__] [\__) ) [___]|__]  '.__.'  '.__.;__]
                          ________
                          ___  __ )_____ ______ _________________
                          __  __  |_  _ \_  __ `/__  ___/__  ___/
                          _  /_/ / /  __// /_/ / _  /    _(__  )
                          /_____/  \___/ \__,_/  /_/     /____/
    */
    
    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;
        }
    }
    
    pragma solidity ^0.8.0;
    
    /**
     * @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() {
            _setOwner(_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 {
            _setOwner(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");
            _setOwner(newOwner);
        }
    
        function _setOwner(address newOwner) private {
            address oldOwner = _owner;
            _owner = newOwner;
            emit OwnershipTransferred(oldOwner, newOwner);
        }
    }
    
    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);
        }
    }
    
    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 make 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;
        }
    }
    
    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);
    }
    
    pragma solidity ^0.8.0;
    
    /**
     * @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;
    }
    
    pragma solidity ^0.8.0;
    
    /**
     * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
     * @dev See https://eips.ethereum.org/EIPS/eip-721
     */
    interface IERC721Enumerable is IERC721 {
        /**
         * @dev Returns the total amount of tokens stored by the contract.
         */
        function totalSupply() external view returns (uint256);
    
        /**
         * @dev Returns a token ID owned by `owner` at a given `index` of its token list.
         * Use along with {balanceOf} to enumerate all of ``owner``'s tokens.
         */
        function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256 tokenId);
    
        /**
         * @dev Returns a token ID at a given `index` of all the tokens stored by the contract.
         * Use along with {totalSupply} to enumerate all tokens.
         */
        function tokenByIndex(uint256 index) external view returns (uint256);
    }
    
    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);
    }
    
    pragma solidity ^0.8.0;
    
    /**
     * @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);
    }
    
    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);
                }
            }
        }
    }
    
    pragma solidity ^0.8.0;
    
    /**
     * @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;
        }
    }
    pragma solidity ^0.8.10;
    
    abstract contract ERC721P is Context, ERC165, IERC721, IERC721Metadata {
        using Address for address;
        string private _name;
        string private _symbol;
        address[] internal _owners;
        mapping(uint256 => address) private _tokenApprovals;
        mapping(address => mapping(address => bool)) private _operatorApprovals;
        constructor(string memory name_, string memory symbol_) {
            _name = name_;
            _symbol = symbol_;
        }
        function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
            return
            interfaceId == type(IERC721).interfaceId ||
            interfaceId == type(IERC721Metadata).interfaceId ||
            super.supportsInterface(interfaceId);
        }
        function balanceOf(address owner) public view virtual override returns (uint256) {
            require(owner != address(0), "ERC721: balance query for the zero address");
            uint count = 0;
            uint length = _owners.length;
            for( uint i = 0; i < length; ++i ){
                if( owner == _owners[i] ){
                    ++count;
                }
            }
            delete length;
            return count;
        }
        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;
        }
        function name() public view virtual override returns (string memory) {
            return _name;
        }
        function symbol() public view virtual override returns (string memory) {
            return _symbol;
        }
        function approve(address to, uint256 tokenId) public virtual override {
            address owner = ERC721P.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);
        }
        function getApproved(uint256 tokenId) public view virtual override returns (address) {
            require(_exists(tokenId), "ERC721: approved query for nonexistent token");
    
            return _tokenApprovals[tokenId];
        }
        function setApprovalForAll(address operator, bool approved) public virtual override {
            require(operator != _msgSender(), "ERC721: approve to caller");
    
            _operatorApprovals[_msgSender()][operator] = approved;
            emit ApprovalForAll(_msgSender(), operator, approved);
        }
        function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
            return _operatorApprovals[owner][operator];
        }
        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);
        }
        function safeTransferFrom(
            address from,
            address to,
            uint256 tokenId
        ) public virtual override {
            safeTransferFrom(from, to, tokenId, "");
        }
        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);
        }
        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");
        }
        function _exists(uint256 tokenId) internal view virtual returns (bool) {
            return tokenId < _owners.length && _owners[tokenId] != address(0);
        }
        function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {
            require(_exists(tokenId), "ERC721: operator query for nonexistent token");
            address owner = ERC721P.ownerOf(tokenId);
            return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender));
        }
        function _safeMint(address to, uint256 tokenId) internal virtual {
            _safeMint(to, tokenId, "");
        }
        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"
            );
        }
        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);
            _owners.push(to);
    
            emit Transfer(address(0), to, tokenId);
        }
        function _burn(uint256 tokenId) internal virtual {
            address owner = ERC721P.ownerOf(tokenId);
    
            _beforeTokenTransfer(owner, address(0), tokenId);
    
            // Clear approvals
            _approve(address(0), tokenId);
            _owners[tokenId] = address(0);
    
            emit Transfer(owner, address(0), tokenId);
        }
        function _transfer(
            address from,
            address to,
            uint256 tokenId
        ) internal virtual {
            require(ERC721P.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);
            _owners[tokenId] = to;
    
            emit Transfer(from, to, tokenId);
        }
        function _approve(address to, uint256 tokenId) internal virtual {
            _tokenApprovals[tokenId] = to;
            emit Approval(ERC721P.ownerOf(tokenId), to, tokenId);
        }
        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;
            }
        }
        function _beforeTokenTransfer(
            address from,
            address to,
            uint256 tokenId
        ) internal virtual {}
    }
    
    pragma solidity ^0.8.0;
    
    /**
     * @title ERC721 Burnable Token
     * @dev ERC721 Token that can be irreversibly burned (destroyed).
     */
    abstract contract ERC721Burnable is Context, ERC721P {
        /**
         * @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);
        }
    }
    
    pragma solidity ^0.8.10;
    
    abstract contract ERC721Enum is ERC721Burnable, IERC721Enumerable {
        function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC721P) returns (bool) {
            return interfaceId == type(IERC721Enumerable).interfaceId || super.supportsInterface(interfaceId);
        }
        function tokenOfOwnerByIndex(address owner, uint256 index) public view override returns (uint256 tokenId) {
            require(index < ERC721P.balanceOf(owner), "ERC721Enum: owner ioob");
            uint count;
            for( uint i; i < _owners.length; ++i ){
                if( owner == _owners[i] ){
                    if( count == index )
                        return i;
                    else
                        ++count;
                }
            }
            require(false, "ERC721Enum: owner ioob");
        }
        function tokensOfOwner(address owner) public view returns (uint256[] memory) {
            require(0 < ERC721P.balanceOf(owner), "ERC721Enum: owner ioob");
            uint256 tokenCount = balanceOf(owner);
            uint256[] memory tokenIds = new uint256[](tokenCount);
            for (uint256 i = 0; i < tokenCount; i++) {
                tokenIds[i] = tokenOfOwnerByIndex(owner, i);
            }
            return tokenIds;
        }
        function totalSupply() public view virtual override returns (uint256) {
            return _owners.length;
        }
        function tokenByIndex(uint256 index) public view virtual override returns (uint256) {
            require(index < ERC721Enum.totalSupply(), "ERC721Enum: global ioob");
            return index;
        }
    }
    
    pragma solidity ^0.8.0;
    
    interface LinkTokenInterface {
        function allowance(address owner, address spender) external view returns (uint256 remaining);
    
        function approve(address spender, uint256 value) external returns (bool success);
    
        function balanceOf(address owner) external view returns (uint256 balance);
    
        function decimals() external view returns (uint8 decimalPlaces);
    
        function decreaseApproval(address spender, uint256 addedValue) external returns (bool success);
    
        function increaseApproval(address spender, uint256 subtractedValue) external;
    
        function name() external view returns (string memory tokenName);
    
        function symbol() external view returns (string memory tokenSymbol);
    
        function totalSupply() external view returns (uint256 totalTokensIssued);
    
        function transfer(address to, uint256 value) external returns (bool success);
    
        function transferAndCall(
            address to,
            uint256 value,
            bytes calldata data
        ) external returns (bool success);
    
        function transferFrom(
            address from,
            address to,
            uint256 value
        ) external returns (bool success);
    }
    
    pragma solidity ^0.8.0;
    
    contract VRFRequestIDBase {
        /**
         * @notice returns the seed which is actually input to the VRF coordinator
       *
       * @dev To prevent repetition of VRF output due to repetition of the
       * @dev user-supplied seed, that seed is combined in a hash with the
       * @dev user-specific nonce, and the address of the consuming contract. The
       * @dev risk of repetition is mostly mitigated by inclusion of a blockhash in
       * @dev the final seed, but the nonce does protect against repetition in
       * @dev requests which are included in a single block.
       *
       * @param _userSeed VRF seed input provided by user
       * @param _requester Address of the requesting contract
       * @param _nonce User-specific nonce at the time of the request
       */
        function makeVRFInputSeed(
            bytes32 _keyHash,
            uint256 _userSeed,
            address _requester,
            uint256 _nonce
        ) internal pure returns (uint256) {
            return uint256(keccak256(abi.encode(_keyHash, _userSeed, _requester, _nonce)));
        }
    
        /**
         * @notice Returns the id for this request
       * @param _keyHash The serviceAgreement ID to be used for this request
       * @param _vRFInputSeed The seed to be passed directly to the VRF
       * @return The id for this request
       *
       * @dev Note that _vRFInputSeed is not the seed passed by the consuming
       * @dev contract, but the one generated by makeVRFInputSeed
       */
        function makeRequestId(bytes32 _keyHash, uint256 _vRFInputSeed) internal pure returns (bytes32) {
            return keccak256(abi.encodePacked(_keyHash, _vRFInputSeed));
        }
    }
    
    pragma solidity ^0.8.0;
    
    /** ****************************************************************************
     * @notice Interface for contracts using VRF randomness
     * *****************************************************************************
     * @dev PURPOSE
     *
     * @dev Reggie the Random Oracle (not his real job) wants to provide randomness
     * @dev to Vera the verifier in such a way that Vera can be sure he's not
     * @dev making his output up to suit himself. Reggie provides Vera a public key
     * @dev to which he knows the secret key. Each time Vera provides a seed to
     * @dev Reggie, he gives back a value which is computed completely
     * @dev deterministically from the seed and the secret key.
     *
     * @dev Reggie provides a proof by which Vera can verify that the output was
     * @dev correctly computed once Reggie tells it to her, but without that proof,
     * @dev the output is indistinguishable to her from a uniform random sample
     * @dev from the output space.
     *
     * @dev The purpose of this contract is to make it easy for unrelated contracts
     * @dev to talk to Vera the verifier about the work Reggie is doing, to provide
     * @dev simple access to a verifiable source of randomness.
     * *****************************************************************************
     * @dev USAGE
     *
     * @dev Calling contracts must inherit from VRFConsumerBase, and can
     * @dev initialize VRFConsumerBase's attributes in their constructor as
     * @dev shown:
     *
     * @dev   contract VRFConsumer {
     * @dev     constructor(<other arguments>, address _vrfCoordinator, address _link)
     * @dev       VRFConsumerBase(_vrfCoordinator, _link) public {
     * @dev         <initialization with other arguments goes here>
     * @dev       }
     * @dev   }
     *
     * @dev The oracle will have given you an ID for the VRF keypair they have
     * @dev committed to (let's call it keyHash), and have told you the minimum LINK
     * @dev price for VRF service. Make sure your contract has sufficient LINK, and
     * @dev call requestRandomness(keyHash, fee, seed), where seed is the input you
     * @dev want to generate randomness from.
     *
     * @dev Once the VRFCoordinator has received and validated the oracle's response
     * @dev to your request, it will call your contract's fulfillRandomness method.
     *
     * @dev The randomness argument to fulfillRandomness is the actual random value
     * @dev generated from your seed.
     *
     * @dev The requestId argument is generated from the keyHash and the seed by
     * @dev makeRequestId(keyHash, seed). If your contract could have concurrent
     * @dev requests open, you can use the requestId to track which seed is
     * @dev associated with which randomness. See VRFRequestIDBase.sol for more
     * @dev details. (See "SECURITY CONSIDERATIONS" for principles to keep in mind,
     * @dev if your contract could have multiple requests in flight simultaneously.)
     *
     * @dev Colliding `requestId`s are cryptographically impossible as long as seeds
     * @dev differ. (Which is critical to making unpredictable randomness! See the
     * @dev next section.)
     *
     * *****************************************************************************
     * @dev SECURITY CONSIDERATIONS
     *
     * @dev A method with the ability to call your fulfillRandomness method directly
     * @dev could spoof a VRF response with any random value, so it's critical that
     * @dev it cannot be directly called by anything other than this base contract
     * @dev (specifically, by the VRFConsumerBase.rawFulfillRandomness method).
     *
     * @dev For your users to trust that your contract's random behavior is free
     * @dev from malicious interference, it's best if you can write it so that all
     * @dev behaviors implied by a VRF response are executed *during* your
     * @dev fulfillRandomness method. If your contract must store the response (or
     * @dev anything derived from it) and use it later, you must ensure that any
     * @dev user-significant behavior which depends on that stored value cannot be
     * @dev manipulated by a subsequent VRF request.
     *
     * @dev Similarly, both miners and the VRF oracle itself have some influence
     * @dev over the order in which VRF responses appear on the blockchain, so if
     * @dev your contract could have multiple VRF requests in flight simultaneously,
     * @dev you must ensure that the order in which the VRF responses arrive cannot
     * @dev be used to manipulate your contract's user-significant behavior.
     *
     * @dev Since the ultimate input to the VRF is mixed with the block hash of the
     * @dev block in which the request is made, user-provided seeds have no impact
     * @dev on its economic security properties. They are only included for API
     * @dev compatability with previous versions of this contract.
     *
     * @dev Since the block hash of the block which contains the requestRandomness
     * @dev call is mixed into the input to the VRF *last*, a sufficiently powerful
     * @dev miner could, in principle, fork the blockchain to evict the block
     * @dev containing the request, forcing the request to be included in a
     * @dev different block with a different hash, and therefore a different input
     * @dev to the VRF. However, such an attack would incur a substantial economic
     * @dev cost. This cost scales with the number of blocks the VRF oracle waits
     * @dev until it calls responds to a request.
     */
    abstract contract VRFConsumerBase is VRFRequestIDBase {
        /**
         * @notice fulfillRandomness handles the VRF response. Your contract must
       * @notice implement it. See "SECURITY CONSIDERATIONS" above for important
       * @notice principles to keep in mind when implementing your fulfillRandomness
       * @notice method.
       *
       * @dev VRFConsumerBase expects its subcontracts to have a method with this
       * @dev signature, and will call it once it has verified the proof
       * @dev associated with the randomness. (It is triggered via a call to
       * @dev rawFulfillRandomness, below.)
       *
       * @param requestId The Id initially returned by requestRandomness
       * @param randomness the VRF output
       */
        function fulfillRandomness(bytes32 requestId, uint256 randomness) internal virtual;
    
        /**
         * @dev In order to keep backwards compatibility we have kept the user
       * seed field around. We remove the use of it because given that the blockhash
       * enters later, it overrides whatever randomness the used seed provides.
       * Given that it adds no security, and can easily lead to misunderstandings,
       * we have removed it from usage and can now provide a simpler API.
       */
        uint256 private constant USER_SEED_PLACEHOLDER = 0;
    
        /**
         * @notice requestRandomness initiates a request for VRF output given _seed
       *
       * @dev The fulfillRandomness method receives the output, once it's provided
       * @dev by the Oracle, and verified by the vrfCoordinator.
       *
       * @dev The _keyHash must already be registered with the VRFCoordinator, and
       * @dev the _fee must exceed the fee specified during registration of the
       * @dev _keyHash.
       *
       * @dev The _seed parameter is vestigial, and is kept only for API
       * @dev compatibility with older versions. It can't *hurt* to mix in some of
       * @dev your own randomness, here, but it's not necessary because the VRF
       * @dev oracle will mix the hash of the block containing your request into the
       * @dev VRF seed it ultimately uses.
       *
       * @param _keyHash ID of public key against which randomness is generated
       * @param _fee The amount of LINK to send with the request
       *
       * @return requestId unique ID for this request
       *
       * @dev The returned requestId can be used to distinguish responses to
       * @dev concurrent requests. It is passed as the first argument to
       * @dev fulfillRandomness.
       */
        function requestRandomness(bytes32 _keyHash, uint256 _fee) internal returns (bytes32 requestId) {
            LINK.transferAndCall(vrfCoordinator, _fee, abi.encode(_keyHash, USER_SEED_PLACEHOLDER));
            // This is the seed passed to VRFCoordinator. The oracle will mix this with
            // the hash of the block containing this request to obtain the seed/input
            // which is finally passed to the VRF cryptographic machinery.
            uint256 vRFSeed = makeVRFInputSeed(_keyHash, USER_SEED_PLACEHOLDER, address(this), nonces[_keyHash]);
            // nonces[_keyHash] must stay in sync with
            // VRFCoordinator.nonces[_keyHash][this], which was incremented by the above
            // successful LINK.transferAndCall (in VRFCoordinator.randomnessRequest).
            // This provides protection against the user repeating their input seed,
            // which would result in a predictable/duplicate output, if multiple such
            // requests appeared in the same block.
            nonces[_keyHash] = nonces[_keyHash] + 1;
            return makeRequestId(_keyHash, vRFSeed);
        }
    
        LinkTokenInterface internal immutable LINK;
        address private immutable vrfCoordinator;
    
        // Nonces for each VRF key from which randomness has been requested.
        //
        // Must stay in sync with VRFCoordinator[_keyHash][this]
        mapping(bytes32 => uint256) /* keyHash */ /* nonce */
        private nonces;
    
        /**
         * @param _vrfCoordinator address of VRFCoordinator contract
       * @param _link address of LINK token contract
       *
       * @dev https://docs.chain.link/docs/link-token-contracts
       */
        constructor(address _vrfCoordinator, address _link) {
            vrfCoordinator = _vrfCoordinator;
            LINK = LinkTokenInterface(_link);
        }
    
        // rawFulfillRandomness is called by VRFCoordinator when it receives a valid VRF
        // proof. rawFulfillRandomness then calls fulfillRandomness, after validating
        // the origin of the call
        function rawFulfillRandomness(bytes32 requestId, uint256 randomness) external {
            require(msg.sender == vrfCoordinator, "Only VRFCoordinator can fulfill");
            fulfillRandomness(requestId, randomness);
        }
    }
    
    pragma solidity ^0.8.0;
    
    /**
     * @dev Contract module which allows children to implement an emergency stop
     * mechanism that can be triggered by an authorized account.
     *
     * This module is used through inheritance. It will make available the
     * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
     * the functions of your contract. Note that they will not be pausable by
     * simply including this module, only once the modifiers are put in place.
     */
    abstract contract Pausable is Context {
        /**
         * @dev Emitted when the pause is triggered by `account`.
         */
        event Paused(address account);
    
        /**
         * @dev Emitted when the pause is lifted by `account`.
         */
        event Unpaused(address account);
    
        bool private _paused;
    
        /**
         * @dev Initializes the contract in unpaused state.
         */
        constructor() {
            _paused = false;
        }
    
        /**
         * @dev Returns true if the contract is paused, and false otherwise.
         */
        function paused() public view virtual returns (bool) {
            return _paused;
        }
    
        /**
         * @dev Modifier to make a function callable only when the contract is not paused.
         *
         * Requirements:
         *
         * - The contract must not be paused.
         */
        modifier whenNotPaused() {
            require(!paused(), "Pausable: paused");
            _;
        }
    
        /**
         * @dev Modifier to make a function callable only when the contract is paused.
         *
         * Requirements:
         *
         * - The contract must be paused.
         */
        modifier whenPaused() {
            require(paused(), "Pausable: not paused");
            _;
        }
    
        /**
         * @dev Triggers stopped state.
         *
         * Requirements:
         *
         * - The contract must not be paused.
         */
        function _pause() internal virtual whenNotPaused {
            _paused = true;
            emit Paused(_msgSender());
        }
    
        /**
         * @dev Returns to normal state.
         *
         * Requirements:
         *
         * - The contract must be paused.
         */
        function _unpause() internal virtual whenPaused {
            _paused = false;
            emit Unpaused(_msgSender());
        }
    }
    
    pragma solidity ^0.8.10;
    
    interface IBloodTokenContract {
        function walletsBalances(address wallet) external view returns (uint256);
        function spend(address owner, uint256 amount) external;
    }
    
    interface IGameStats {
        function pickWinnerFromElites(bool faction, uint256 tokenId) external returns (address);
    }
    
    contract BloodShedBearsGen1 is ERC721Enum, Ownable, VRFConsumerBase, Pausable {
        using Strings for uint256;
    
        //sale settings
        uint256 constant private maxSupply = 20000;
        uint256 private teamReserveAvailableMints = 200;
        uint256 private cost = 20000 ether;
        address public bloodTokenContract = 0x0000000000000000000000000000000000000000;
        address public gameStatsContract = 0x0000000000000000000000000000000000000000;
    
        mapping(uint256 => bool) public tokenFactions;
    
        string private baseURI;
    
        uint256 private vrfFee;
        bytes32 private vrfKeyHash;
    
        uint256 private riskSeed;
        uint256 private factionSeed;
    
        bytes32 private riskSeedReqId;
        bytes32 private factionSeedReqId;
    
        event SeedFulfilled(string seedType);
    
        constructor(
            string memory initBaseURI_,
            address vrfCoordinatorAddr_,
            address linkTokenAddr_,
            bytes32 vrfKeyHash_,
            uint256 fee_
        )
        VRFConsumerBase(vrfCoordinatorAddr_, linkTokenAddr_)
        ERC721P("BloodShedBearsGen1", "BSBG1") {
            setBaseURI(initBaseURI_);
            vrfKeyHash = vrfKeyHash_;
            vrfFee = fee_;
            //        pause();
        }
    
        function _baseURI() internal view virtual returns (string memory) {
            return baseURI;
        }
    
        function setBloodTokenContract(address contractAddress_) external onlyOwner {
            bloodTokenContract = contractAddress_;
        }
    
        function setGameStatsContract(address contractAddress_) external onlyOwner {
            gameStatsContract = contractAddress_;
        }
    
        function setCost(uint256 _newCost) external onlyOwner {
            cost = _newCost;
        }
    
        function mint(uint256 mintAmount_) external payable whenNotPaused returns (uint256) {
    
            require(riskSeed != 0, "Seed not set");
            require(factionSeed != 0, "Seed not set");
    
            require(IBloodTokenContract(bloodTokenContract).walletsBalances(msg.sender) >= mintAmount_ * cost, "NOT ENOUGH");
            IBloodTokenContract(bloodTokenContract).spend(msg.sender, mintAmount_ * cost);
    
            uint256 totalSupply = totalSupply();
            uint256 lostBears;
            for (uint256 i = 0; i < mintAmount_; i++) {
    
                tokenFactions[totalSupply + i] = _getFaction(totalSupply + i);
    
                address recipient = msg.sender;
                if(_isLost(totalSupply + i)) {
                    address winner = IGameStats(gameStatsContract).pickWinnerFromElites(tokenFactions[totalSupply + i], totalSupply + i);
                    if(winner != address(0)) {
                        ++lostBears;
                        recipient = winner;
                    }
                }
    
                _safeMint(recipient, totalSupply + i);
    
                delete recipient;
            }
    
            delete totalSupply;
    
            return lostBears;
        }
    
        function tokenURI(uint256 _tokenId) external view virtual override returns (string memory) {
            require(_exists(_tokenId), "ERC721Metadata: Nonexistent token");
            string memory currentBaseURI = _baseURI();
            return bytes(currentBaseURI).length > 0	? string(abi.encodePacked(currentBaseURI, _tokenId.toString(), ".json")) : "";
        }
    
        function reserve(address _to, uint256 _reserveAmount) external onlyOwner {
            uint256 supply = totalSupply();
            require(
                _reserveAmount > 0 && _reserveAmount <= teamReserveAvailableMints,
                "Not enough reserve left for team"
            );
            for (uint256 i = 0; i < _reserveAmount; i++) {
    
                tokenFactions[supply + i] = _getFaction(supply + i);
                _safeMint(_to, supply + i);
            }
            teamReserveAvailableMints -= _reserveAmount;
        }
    
        function setBaseURI(string memory _newBaseURI) public onlyOwner {
            baseURI = _newBaseURI;
        }
    
        function isOwnerOfBatch(uint256[] calldata tokenIds_, address address_) external view returns (bool) {
            bool ownership = true;
    
            for (uint256 i = 0; i < tokenIds_.length; ++i) {
                ownership = ownership && (ownerOf(tokenIds_[i]) == address_);
            }
    
            return ownership;
        }
    
        function _isLost(uint256 tokenId) internal view returns (bool) {
            return uint256(
                keccak256(
                    abi.encodePacked(
                        riskSeed,
                        tokenId,
                        tx.origin,
                        blockhash(block.number - 1),
                        block.timestamp
                    )
                )
            ) % 5 == 0;
        }
    
        function _getFaction(uint256 tokenId) internal view returns (bool) {
            return uint256(
                keccak256(
                    abi.encodePacked(
                        factionSeed,
                        tokenId,
                        tx.origin,
                        blockhash(block.number - 1),
                        block.timestamp
                    )
                )
            ) & 1 == 1;
        }
    
        function initSeedGeneration(bool seedType) public onlyOwner returns (bytes32) {
            require(LINK.balanceOf(address(this)) >= vrfFee, "Not enough LINK");
            bytes32 requestId = requestRandomness(vrfKeyHash, vrfFee);
            if (seedType) {
                riskSeedReqId = requestId;
            } else {
                factionSeedReqId = requestId;
            }
            return requestId;
        }
    
        function fulfillRandomness(bytes32 requestId, uint256 randomness) internal override {
            if (requestId == riskSeedReqId) {
                riskSeed = randomness;
                emit SeedFulfilled("Risk");
            } else if (requestId == factionSeedReqId) {
                factionSeed = randomness;
                emit SeedFulfilled("Faction");
            }
        }
    
        function pause() public onlyOwner {
            _pause();
        }
    
        function unpause() public onlyOwner {
            _unpause();
        }
    
        function getFactions(uint256[] calldata tokenIds_) external view returns (bool[] memory) {
            bool[] memory factions = new bool[](tokenIds_.length);
            for (uint256 i = 0; i < tokenIds_.length; ++i) {
                require(_exists(tokenIds_[i]), "Token doesn't exist");
                factions[i] = tokenFactions[tokenIds_[i]];
            }
    
            return factions;
        }
    
    }

    File 3 of 3: BloodToken
    // SPDX-License-Identifier: MIT
    /*
     ______     __                            __           __                      __
    |_   _ \\   [  |                          |  ]         [  |                    |  ]
      | |_) |   | |    .--.     .--.     .--.| |   .--.    | |--.    .---.    .--.| |
      |  __'.   | |  / .'`\\ \\ / .'`\\ \\ / /'`\\' |  ( (`\\]   | .-. |  / /__\\\\ / /'`\\' |
     _| |__) |  | |  | \\__. | | \\__. | | \\__/  |   `'.'.   | | | |  | \\__., | \\__/  |
    |_______/  [___]  '.__.'   '.__.'   '.__.;__] [\\__) ) [___]|__]  '.__.'  '.__.;__]
                          ________
                          ___  __ )_____ ______ _________________
                          __  __  |_  _ \\_  __ `/__  ___/__  ___/
                          _  /_/ / /  __// /_/ / _  /    _(__  )
                          /_____/  \\___/ \\__,_/  /_/     /____/
    */
    pragma solidity ^0.8.0;
    import "@openzeppelin/contracts/access/Ownable.sol";
    import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
    import "@openzeppelin/contracts/security/Pausable.sol";
    contract BloodToken is Ownable, ERC20("BloodToken", "BLD"), Pausable {
        uint256 public taxFunds;
        uint256 public tax = 25;
        uint256 public spendingTax = 30;
        uint256 public spending;
        mapping(address => bool) public authorisedAddresses;
        mapping(address => uint256) public walletsBalances;
        modifier authorised() {
            require(authorisedAddresses[msg.sender], "The token contract is not authorised");
            _;
        }
        constructor() {}
        function setAuthorised(address[] calldata addresses_, bool[] calldata authorisations_) external onlyOwner {
            for (uint256 i = 0; i < addresses_.length; ++i) {
                authorisedAddresses[addresses_[i]] = authorisations_[i];
            }
        }
        function setTax(uint256 tax_) external onlyOwner {
            tax = tax_;
        }
        function setSpendingHolding(uint256 spendingHolding_) external onlyOwner {
            spendingTax = spendingHolding_;
        }
        function depositTokens(address wallet_, uint256 amount_) external whenNotPaused {
            _burn(msg.sender, amount_);
            walletsBalances[wallet_] += amount_;
        }
        function add(address recipient_, uint256 amount_) external authorised {
            walletsBalances[recipient_] += amount_;
        }
        function spend(address wallet_, uint256 amount_) external authorised {
            require(walletsBalances[wallet_] >= amount_);
            spending += amount_ * spendingTax / 100;
            walletsBalances[wallet_] -= amount_;
        }
        function withdrawTax(uint256 amount_) external onlyOwner {
            require(taxFunds >= amount_);
            _mint(msg.sender, taxFunds);
            taxFunds -= amount_;
        }
        function withdrawSpendings(uint256 amount_) external onlyOwner {
            require(spending >= amount_);
            _mint(msg.sender, spending);
            spending -= amount_;
        }
        function withdraw(uint256 amount_) external whenNotPaused {
            require(amount_ <= walletsBalances[msg.sender]);
            uint256 taxed = amount_ * tax / 100;
            taxFunds += taxed;
            _mint(msg.sender, amount_ - taxed);
            walletsBalances[msg.sender] -= amount_;
        }
        function mintTokens(address recipient_, uint256 amount_) external authorised {
            _mint(recipient_, amount_);
        }
        function transferTokensBetweenWallets(address to_, uint256 amount_) external whenNotPaused {
            require(walletsBalances[msg.sender] >= amount_);
            walletsBalances[msg.sender] -= amount_;
            walletsBalances[to_] += amount_;
        }
        function pause() external onlyOwner {
            _pause();
        }
        function unpause() external onlyOwner {
            _unpause();
        }
        function addToInternalWallets(address[] calldata addresses_, uint256[] calldata amounts_) external onlyOwner {
            for (uint256 i = 0 ; i < addresses_.length; ++i) {
                walletsBalances[addresses_[i]] += amounts_[i];
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)
    pragma solidity ^0.8.0;
    import "../utils/Context.sol";
    /**
     * @dev Contract module which provides a basic access control mechanism, where
     * there is an account (an owner) that can be granted exclusive access to
     * specific functions.
     *
     * By default, the owner account will be the one that deploys the contract. This
     * can later be changed with {transferOwnership}.
     *
     * This module is used through inheritance. It will make available the modifier
     * `onlyOwner`, which can be applied to your functions to restrict their use to
     * the owner.
     */
    abstract contract Ownable is Context {
        address private _owner;
        event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
        /**
         * @dev Initializes the contract setting the deployer as the initial owner.
         */
        constructor() {
            _transferOwnership(_msgSender());
        }
        /**
         * @dev Returns the address of the current owner.
         */
        function owner() public view virtual returns (address) {
            return _owner;
        }
        /**
         * @dev Throws if called by any account other than the owner.
         */
        modifier onlyOwner() {
            require(owner() == _msgSender(), "Ownable: caller is not the owner");
            _;
        }
        /**
         * @dev Leaves the contract without owner. It will not be possible to call
         * `onlyOwner` functions anymore. Can only be called by the current owner.
         *
         * NOTE: Renouncing ownership will leave the contract without an owner,
         * thereby removing any functionality that is only available to the owner.
         */
        function renounceOwnership() public virtual onlyOwner {
            _transferOwnership(address(0));
        }
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`).
         * Can only be called by the current owner.
         */
        function transferOwnership(address newOwner) public virtual onlyOwner {
            require(newOwner != address(0), "Ownable: new owner is the zero address");
            _transferOwnership(newOwner);
        }
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`).
         * Internal function without access restriction.
         */
        function _transferOwnership(address newOwner) internal virtual {
            address oldOwner = _owner;
            _owner = newOwner;
            emit OwnershipTransferred(oldOwner, newOwner);
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/ERC20.sol)
    pragma solidity ^0.8.0;
    import "./IERC20.sol";
    import "./extensions/IERC20Metadata.sol";
    import "../../utils/Context.sol";
    /**
     * @dev Implementation of the {IERC20} interface.
     *
     * This implementation is agnostic to the way tokens are created. This means
     * that a supply mechanism has to be added in a derived contract using {_mint}.
     * For a generic mechanism see {ERC20PresetMinterPauser}.
     *
     * TIP: For a detailed writeup see our guide
     * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
     * to implement supply mechanisms].
     *
     * We have followed general OpenZeppelin Contracts guidelines: functions revert
     * instead returning `false` on failure. This behavior is nonetheless
     * conventional and does not conflict with the expectations of ERC20
     * applications.
     *
     * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
     * This allows applications to reconstruct the allowance for all accounts just
     * by listening to said events. Other implementations of the EIP may not emit
     * these events, as it isn't required by the specification.
     *
     * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
     * functions have been added to mitigate the well-known issues around setting
     * allowances. See {IERC20-approve}.
     */
    contract ERC20 is Context, IERC20, IERC20Metadata {
        mapping(address => uint256) private _balances;
        mapping(address => mapping(address => uint256)) private _allowances;
        uint256 private _totalSupply;
        string private _name;
        string private _symbol;
        /**
         * @dev Sets the values for {name} and {symbol}.
         *
         * The default value of {decimals} is 18. To select a different value for
         * {decimals} you should overload it.
         *
         * All two of these values are immutable: they can only be set once during
         * construction.
         */
        constructor(string memory name_, string memory symbol_) {
            _name = name_;
            _symbol = symbol_;
        }
        /**
         * @dev Returns the name of the token.
         */
        function name() public view virtual override returns (string memory) {
            return _name;
        }
        /**
         * @dev Returns the symbol of the token, usually a shorter version of the
         * name.
         */
        function symbol() public view virtual override returns (string memory) {
            return _symbol;
        }
        /**
         * @dev Returns the number of decimals used to get its user representation.
         * For example, if `decimals` equals `2`, a balance of `505` tokens should
         * be displayed to a user as `5.05` (`505 / 10 ** 2`).
         *
         * Tokens usually opt for a value of 18, imitating the relationship between
         * Ether and Wei. This is the value {ERC20} uses, unless this function is
         * overridden;
         *
         * NOTE: This information is only used for _display_ purposes: it in
         * no way affects any of the arithmetic of the contract, including
         * {IERC20-balanceOf} and {IERC20-transfer}.
         */
        function decimals() public view virtual override returns (uint8) {
            return 18;
        }
        /**
         * @dev See {IERC20-totalSupply}.
         */
        function totalSupply() public view virtual override returns (uint256) {
            return _totalSupply;
        }
        /**
         * @dev See {IERC20-balanceOf}.
         */
        function balanceOf(address account) public view virtual override returns (uint256) {
            return _balances[account];
        }
        /**
         * @dev See {IERC20-transfer}.
         *
         * Requirements:
         *
         * - `to` cannot be the zero address.
         * - the caller must have a balance of at least `amount`.
         */
        function transfer(address to, uint256 amount) public virtual override returns (bool) {
            address owner = _msgSender();
            _transfer(owner, to, amount);
            return true;
        }
        /**
         * @dev See {IERC20-allowance}.
         */
        function allowance(address owner, address spender) public view virtual override returns (uint256) {
            return _allowances[owner][spender];
        }
        /**
         * @dev See {IERC20-approve}.
         *
         * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
         * `transferFrom`. This is semantically equivalent to an infinite approval.
         *
         * Requirements:
         *
         * - `spender` cannot be the zero address.
         */
        function approve(address spender, uint256 amount) public virtual override returns (bool) {
            address owner = _msgSender();
            _approve(owner, spender, amount);
            return true;
        }
        /**
         * @dev See {IERC20-transferFrom}.
         *
         * Emits an {Approval} event indicating the updated allowance. This is not
         * required by the EIP. See the note at the beginning of {ERC20}.
         *
         * NOTE: Does not update the allowance if the current allowance
         * is the maximum `uint256`.
         *
         * Requirements:
         *
         * - `from` and `to` cannot be the zero address.
         * - `from` must have a balance of at least `amount`.
         * - the caller must have allowance for ``from``'s tokens of at least
         * `amount`.
         */
        function transferFrom(
            address from,
            address to,
            uint256 amount
        ) public virtual override returns (bool) {
            address spender = _msgSender();
            _spendAllowance(from, spender, amount);
            _transfer(from, to, amount);
            return true;
        }
        /**
         * @dev Atomically increases the allowance granted to `spender` by the caller.
         *
         * This is an alternative to {approve} that can be used as a mitigation for
         * problems described in {IERC20-approve}.
         *
         * Emits an {Approval} event indicating the updated allowance.
         *
         * Requirements:
         *
         * - `spender` cannot be the zero address.
         */
        function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
            address owner = _msgSender();
            _approve(owner, spender, _allowances[owner][spender] + addedValue);
            return true;
        }
        /**
         * @dev Atomically decreases the allowance granted to `spender` by the caller.
         *
         * This is an alternative to {approve} that can be used as a mitigation for
         * problems described in {IERC20-approve}.
         *
         * Emits an {Approval} event indicating the updated allowance.
         *
         * Requirements:
         *
         * - `spender` cannot be the zero address.
         * - `spender` must have allowance for the caller of at least
         * `subtractedValue`.
         */
        function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
            address owner = _msgSender();
            uint256 currentAllowance = _allowances[owner][spender];
            require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
            unchecked {
                _approve(owner, spender, currentAllowance - subtractedValue);
            }
            return true;
        }
        /**
         * @dev Moves `amount` of tokens from `sender` to `recipient`.
         *
         * This internal function is equivalent to {transfer}, and can be used to
         * e.g. implement automatic token fees, slashing mechanisms, etc.
         *
         * Emits a {Transfer} event.
         *
         * Requirements:
         *
         * - `from` cannot be the zero address.
         * - `to` cannot be the zero address.
         * - `from` must have a balance of at least `amount`.
         */
        function _transfer(
            address from,
            address to,
            uint256 amount
        ) internal virtual {
            require(from != address(0), "ERC20: transfer from the zero address");
            require(to != address(0), "ERC20: transfer to the zero address");
            _beforeTokenTransfer(from, to, amount);
            uint256 fromBalance = _balances[from];
            require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
            unchecked {
                _balances[from] = fromBalance - amount;
            }
            _balances[to] += amount;
            emit Transfer(from, to, amount);
            _afterTokenTransfer(from, to, amount);
        }
        /** @dev Creates `amount` tokens and assigns them to `account`, increasing
         * the total supply.
         *
         * Emits a {Transfer} event with `from` set to the zero address.
         *
         * Requirements:
         *
         * - `account` cannot be the zero address.
         */
        function _mint(address account, uint256 amount) internal virtual {
            require(account != address(0), "ERC20: mint to the zero address");
            _beforeTokenTransfer(address(0), account, amount);
            _totalSupply += amount;
            _balances[account] += amount;
            emit Transfer(address(0), account, amount);
            _afterTokenTransfer(address(0), account, amount);
        }
        /**
         * @dev Destroys `amount` tokens from `account`, reducing the
         * total supply.
         *
         * Emits a {Transfer} event with `to` set to the zero address.
         *
         * Requirements:
         *
         * - `account` cannot be the zero address.
         * - `account` must have at least `amount` tokens.
         */
        function _burn(address account, uint256 amount) internal virtual {
            require(account != address(0), "ERC20: burn from the zero address");
            _beforeTokenTransfer(account, address(0), amount);
            uint256 accountBalance = _balances[account];
            require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
            unchecked {
                _balances[account] = accountBalance - amount;
            }
            _totalSupply -= amount;
            emit Transfer(account, address(0), amount);
            _afterTokenTransfer(account, address(0), amount);
        }
        /**
         * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
         *
         * This internal function is equivalent to `approve`, and can be used to
         * e.g. set automatic allowances for certain subsystems, etc.
         *
         * Emits an {Approval} event.
         *
         * Requirements:
         *
         * - `owner` cannot be the zero address.
         * - `spender` cannot be the zero address.
         */
        function _approve(
            address owner,
            address spender,
            uint256 amount
        ) internal virtual {
            require(owner != address(0), "ERC20: approve from the zero address");
            require(spender != address(0), "ERC20: approve to the zero address");
            _allowances[owner][spender] = amount;
            emit Approval(owner, spender, amount);
        }
        /**
         * @dev Spend `amount` form the allowance of `owner` toward `spender`.
         *
         * Does not update the allowance amount in case of infinite allowance.
         * Revert if not enough allowance is available.
         *
         * Might emit an {Approval} event.
         */
        function _spendAllowance(
            address owner,
            address spender,
            uint256 amount
        ) internal virtual {
            uint256 currentAllowance = allowance(owner, spender);
            if (currentAllowance != type(uint256).max) {
                require(currentAllowance >= amount, "ERC20: insufficient allowance");
                unchecked {
                    _approve(owner, spender, currentAllowance - amount);
                }
            }
        }
        /**
         * @dev Hook that is called before any transfer of tokens. This includes
         * minting and burning.
         *
         * Calling conditions:
         *
         * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
         * will be transferred to `to`.
         * - when `from` is zero, `amount` tokens will be minted for `to`.
         * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
         * - `from` and `to` are never both zero.
         *
         * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
         */
        function _beforeTokenTransfer(
            address from,
            address to,
            uint256 amount
        ) internal virtual {}
        /**
         * @dev Hook that is called after any transfer of tokens. This includes
         * minting and burning.
         *
         * Calling conditions:
         *
         * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
         * has been transferred to `to`.
         * - when `from` is zero, `amount` tokens have been minted for `to`.
         * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
         * - `from` and `to` are never both zero.
         *
         * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
         */
        function _afterTokenTransfer(
            address from,
            address to,
            uint256 amount
        ) internal virtual {}
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)
    pragma solidity ^0.8.0;
    import "../utils/Context.sol";
    /**
     * @dev Contract module which allows children to implement an emergency stop
     * mechanism that can be triggered by an authorized account.
     *
     * This module is used through inheritance. It will make available the
     * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
     * the functions of your contract. Note that they will not be pausable by
     * simply including this module, only once the modifiers are put in place.
     */
    abstract contract Pausable is Context {
        /**
         * @dev Emitted when the pause is triggered by `account`.
         */
        event Paused(address account);
        /**
         * @dev Emitted when the pause is lifted by `account`.
         */
        event Unpaused(address account);
        bool private _paused;
        /**
         * @dev Initializes the contract in unpaused state.
         */
        constructor() {
            _paused = false;
        }
        /**
         * @dev Returns true if the contract is paused, and false otherwise.
         */
        function paused() public view virtual returns (bool) {
            return _paused;
        }
        /**
         * @dev Modifier to make a function callable only when the contract is not paused.
         *
         * Requirements:
         *
         * - The contract must not be paused.
         */
        modifier whenNotPaused() {
            require(!paused(), "Pausable: paused");
            _;
        }
        /**
         * @dev Modifier to make a function callable only when the contract is paused.
         *
         * Requirements:
         *
         * - The contract must be paused.
         */
        modifier whenPaused() {
            require(paused(), "Pausable: not paused");
            _;
        }
        /**
         * @dev Triggers stopped state.
         *
         * Requirements:
         *
         * - The contract must not be paused.
         */
        function _pause() internal virtual whenNotPaused {
            _paused = true;
            emit Paused(_msgSender());
        }
        /**
         * @dev Returns to normal state.
         *
         * Requirements:
         *
         * - The contract must be paused.
         */
        function _unpause() internal virtual whenPaused {
            _paused = false;
            emit Unpaused(_msgSender());
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (utils/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 (last updated v4.5.0) (token/ERC20/IERC20.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev Interface of the ERC20 standard as defined in the EIP.
     */
    interface IERC20 {
        /**
         * @dev Returns the amount of tokens in existence.
         */
        function totalSupply() external view returns (uint256);
        /**
         * @dev Returns the amount of tokens owned by `account`.
         */
        function balanceOf(address account) external view returns (uint256);
        /**
         * @dev Moves `amount` tokens from the caller's account to `to`.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * Emits a {Transfer} event.
         */
        function transfer(address to, uint256 amount) external returns (bool);
        /**
         * @dev Returns the remaining number of tokens that `spender` will be
         * allowed to spend on behalf of `owner` through {transferFrom}. This is
         * zero by default.
         *
         * This value changes when {approve} or {transferFrom} are called.
         */
        function allowance(address owner, address spender) external view returns (uint256);
        /**
         * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * IMPORTANT: Beware that changing an allowance with this method brings the risk
         * that someone may use both the old and the new allowance by unfortunate
         * transaction ordering. One possible solution to mitigate this race
         * condition is to first reduce the spender's allowance to 0 and set the
         * desired value afterwards:
         * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
         *
         * Emits an {Approval} event.
         */
        function approve(address spender, uint256 amount) external returns (bool);
        /**
         * @dev Moves `amount` tokens from `from` to `to` using the
         * allowance mechanism. `amount` is then deducted from the caller's
         * allowance.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * Emits a {Transfer} event.
         */
        function transferFrom(
            address from,
            address to,
            uint256 amount
        ) external returns (bool);
        /**
         * @dev Emitted when `value` tokens are moved from one account (`from`) to
         * another (`to`).
         *
         * Note that `value` may be zero.
         */
        event Transfer(address indexed from, address indexed to, uint256 value);
        /**
         * @dev Emitted when the allowance of a `spender` for an `owner` is set by
         * a call to {approve}. `value` is the new allowance.
         */
        event Approval(address indexed owner, address indexed spender, uint256 value);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
    pragma solidity ^0.8.0;
    import "../IERC20.sol";
    /**
     * @dev Interface for the optional metadata functions from the ERC20 standard.
     *
     * _Available since v4.1._
     */
    interface IERC20Metadata is IERC20 {
        /**
         * @dev Returns the name of the token.
         */
        function name() external view returns (string memory);
        /**
         * @dev Returns the symbol of the token.
         */
        function symbol() external view returns (string memory);
        /**
         * @dev Returns the decimals places of the token.
         */
        function decimals() external view returns (uint8);
    }