Contract 0xD2247182D7BfdFc1e1B5d26FB7dcEb64eDABD5cb

 
Txn Hash Method
Block
From
To
Value [Txn Fee]
0x55ce4a236c66495d298074f610a3bdeb0716f7e8a7c5a8b2fbf0ab41b03b04d20x60806040185207362022-07-25 10:56:1570 days 22 hrs ago0x60e788ee9a094c402b6cec03e3fce0ef90944b87 IN  Create: BattleflyFounderVaultV080 ETH0.00338742167 ETH
[ Download CSV Export 
Latest 25 internal transaction
Parent Txn Hash Block From To Value
0xf749047f86b3add47b16e9082f1016ada1c2ff00a798ae489beb9fc7083f75d6189701362022-07-30 19:03:0465 days 14 hrs ago 0x6c9cc5a6d5484cb8eab1438632bbf667a5e25ed9 0xd2247182d7bfdfc1e1b5d26fb7dceb64edabd5cb0 ETH
0xa2282683d5522e3f1316bc1ab869295f269fa61586e134b89c42d2d2cf096eb7189664092022-07-30 17:49:4765 days 16 hrs ago 0x6c9cc5a6d5484cb8eab1438632bbf667a5e25ed9 0xd2247182d7bfdfc1e1b5d26fb7dceb64edabd5cb0 ETH
0x42132547c6ef644cb68a34aef8bc1fd5d57400706e09fc8539e498dc9fb40f86189660852022-07-30 17:44:3465 days 16 hrs ago 0x6c9cc5a6d5484cb8eab1438632bbf667a5e25ed9 0xd2247182d7bfdfc1e1b5d26fb7dceb64edabd5cb0 ETH
0x42132547c6ef644cb68a34aef8bc1fd5d57400706e09fc8539e498dc9fb40f86189660852022-07-30 17:44:3465 days 16 hrs ago 0x6c9cc5a6d5484cb8eab1438632bbf667a5e25ed9 0xd2247182d7bfdfc1e1b5d26fb7dceb64edabd5cb0 ETH
0x9b943c202d089a8849fb8e2a1f33a596459f5673a57652ddbd87b3c2c1419a77189658962022-07-30 17:40:1465 days 16 hrs ago 0x6c9cc5a6d5484cb8eab1438632bbf667a5e25ed9 0xd2247182d7bfdfc1e1b5d26fb7dceb64edabd5cb0 ETH
0xa526e5761146d6ac127e3bbab56a75fd6580f12d818ea3de6100282f1868f4bb189656652022-07-30 17:35:5865 days 16 hrs ago 0x6c9cc5a6d5484cb8eab1438632bbf667a5e25ed9 0xd2247182d7bfdfc1e1b5d26fb7dceb64edabd5cb0 ETH
0xa526e5761146d6ac127e3bbab56a75fd6580f12d818ea3de6100282f1868f4bb189656652022-07-30 17:35:5865 days 16 hrs ago 0x6c9cc5a6d5484cb8eab1438632bbf667a5e25ed9 0xd2247182d7bfdfc1e1b5d26fb7dceb64edabd5cb0 ETH
0xd166e6f438a449c6fc91675e44d643d85c07e998473d5ea8bd2a716080e7671e189633432022-07-30 16:58:2165 days 16 hrs ago 0x6c9cc5a6d5484cb8eab1438632bbf667a5e25ed9 0xd2247182d7bfdfc1e1b5d26fb7dceb64edabd5cb0 ETH
0xd166e6f438a449c6fc91675e44d643d85c07e998473d5ea8bd2a716080e7671e189633432022-07-30 16:58:2165 days 16 hrs ago 0x6c9cc5a6d5484cb8eab1438632bbf667a5e25ed9 0xd2247182d7bfdfc1e1b5d26fb7dceb64edabd5cb0 ETH
0xab43b7f3ff4f043375a7c5771429e731d5db6c5512e0a49e7fac7958d5ecbe7c189624622022-07-30 16:43:5365 days 17 hrs ago 0x6c9cc5a6d5484cb8eab1438632bbf667a5e25ed9 0xd2247182d7bfdfc1e1b5d26fb7dceb64edabd5cb0 ETH
0x35db6a71723b2de059b2b1e5d2237f6acd6a0e2732c079c0fb0f135bb012efd0189612332022-07-30 16:22:0465 days 17 hrs ago 0x6c9cc5a6d5484cb8eab1438632bbf667a5e25ed9 0xd2247182d7bfdfc1e1b5d26fb7dceb64edabd5cb0 ETH
0x35db6a71723b2de059b2b1e5d2237f6acd6a0e2732c079c0fb0f135bb012efd0189612332022-07-30 16:22:0465 days 17 hrs ago 0x6c9cc5a6d5484cb8eab1438632bbf667a5e25ed9 0xd2247182d7bfdfc1e1b5d26fb7dceb64edabd5cb0 ETH
0xfa2ad918830bfa48d47abed75c39918e242cddb5e6e1efd5a55d4716c01f45b6189609472022-07-30 16:18:3265 days 17 hrs ago 0x6c9cc5a6d5484cb8eab1438632bbf667a5e25ed9 0xd2247182d7bfdfc1e1b5d26fb7dceb64edabd5cb0 ETH
0xfa2ad918830bfa48d47abed75c39918e242cddb5e6e1efd5a55d4716c01f45b6189609472022-07-30 16:18:3265 days 17 hrs ago 0x6c9cc5a6d5484cb8eab1438632bbf667a5e25ed9 0xd2247182d7bfdfc1e1b5d26fb7dceb64edabd5cb0 ETH
0xfa6aedcf04d62fe6254379127dee2505cae5824effa0c9f761c707fa31b51f90189602042022-07-30 16:06:4965 days 17 hrs ago 0x6c9cc5a6d5484cb8eab1438632bbf667a5e25ed9 0xd2247182d7bfdfc1e1b5d26fb7dceb64edabd5cb0 ETH
0xfa6aedcf04d62fe6254379127dee2505cae5824effa0c9f761c707fa31b51f90189602042022-07-30 16:06:4965 days 17 hrs ago 0x6c9cc5a6d5484cb8eab1438632bbf667a5e25ed9 0xd2247182d7bfdfc1e1b5d26fb7dceb64edabd5cb0 ETH
0xb620f1ba1baaf949bb2a9410b9679a5de6320c2d7b645f7a8e0094949e9efb65189583052022-07-30 15:40:0365 days 18 hrs ago 0x6c9cc5a6d5484cb8eab1438632bbf667a5e25ed9 0xd2247182d7bfdfc1e1b5d26fb7dceb64edabd5cb0 ETH
0xb620f1ba1baaf949bb2a9410b9679a5de6320c2d7b645f7a8e0094949e9efb65189583052022-07-30 15:40:0365 days 18 hrs ago 0x6c9cc5a6d5484cb8eab1438632bbf667a5e25ed9 0xd2247182d7bfdfc1e1b5d26fb7dceb64edabd5cb0 ETH
0x1db9b0e8c4eaa8827f3320576651a10119ffc766c1380716ec044049af67595d189582122022-07-30 15:38:4165 days 18 hrs ago 0x6c9cc5a6d5484cb8eab1438632bbf667a5e25ed9 0xd2247182d7bfdfc1e1b5d26fb7dceb64edabd5cb0 ETH
0x780c409b7a252396b4c7a4ec36ef1366daee6a78f0dbeac15761c41944312a04189511072022-07-30 13:53:0465 days 20 hrs ago 0x6c9cc5a6d5484cb8eab1438632bbf667a5e25ed9 0xd2247182d7bfdfc1e1b5d26fb7dceb64edabd5cb0 ETH
0x7b40b058b571b78ed28054ac32647325a180880a18dce1e0d4b4b8583ac41e1b189338702022-07-30 9:15:4466 days 38 mins ago 0x6c9cc5a6d5484cb8eab1438632bbf667a5e25ed9 0xd2247182d7bfdfc1e1b5d26fb7dceb64edabd5cb0 ETH
0x9c44e11d7b24595a154926bcf2147787b855b239e60b3b50e5221425e48246ba189132522022-07-30 2:02:5466 days 7 hrs ago 0x6c9cc5a6d5484cb8eab1438632bbf667a5e25ed9 0xd2247182d7bfdfc1e1b5d26fb7dceb64edabd5cb0 ETH
0xabe977411f0f93d872bdb132946fbafef7854275e5bb9deafe3b5123477906ab189085712022-07-30 0:29:0566 days 9 hrs ago 0x6c9cc5a6d5484cb8eab1438632bbf667a5e25ed9 0xd2247182d7bfdfc1e1b5d26fb7dceb64edabd5cb0 ETH
0xabe977411f0f93d872bdb132946fbafef7854275e5bb9deafe3b5123477906ab189085712022-07-30 0:29:0566 days 9 hrs ago 0x6c9cc5a6d5484cb8eab1438632bbf667a5e25ed9 0xd2247182d7bfdfc1e1b5d26fb7dceb64edabd5cb0 ETH
0x8719a107e34dd4a5cc66daccf10d319979321a18857cfe595beb2804a2889a72188921222022-07-29 18:49:1666 days 15 hrs ago 0x6c9cc5a6d5484cb8eab1438632bbf667a5e25ed9 0xd2247182d7bfdfc1e1b5d26fb7dceb64edabd5cb0 ETH
[ Download CSV Export 
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
BattleflyFounderVaultV08

Compiler Version
v0.8.4+commit.c7e474f2

Optimization Enabled:
Yes with 100 runs

Other Settings:
default evmVersion, MIT license

Contract Source Code (Solidity Standard Json-Input format)

File 1 of 122 : AtlasMine.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;

import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC1155/IERC1155Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/structs/EnumerableSetUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC1155/utils/ERC1155HolderUpgradeable.sol";

import "./interfaces/IMasterOfCoin.sol";
import "./interfaces/ILegionMetadataStore.sol";

contract AtlasMine is Initializable, AccessControlEnumerableUpgradeable, ERC1155HolderUpgradeable {
    using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet;
    using EnumerableSetUpgradeable for EnumerableSetUpgradeable.UintSet;

    using SafeERC20Upgradeable for IERC20Upgradeable;
    using SafeCastUpgradeable for uint256;
    using SafeCastUpgradeable for int256;

    enum Lock {
        twoWeeks,
        oneMonth,
        threeMonths,
        sixMonths,
        twelveMonths
    }

    struct UserInfo {
        uint256 originalDepositAmount;
        uint256 depositAmount;
        uint256 lpAmount;
        uint256 lockedUntil;
        uint256 vestingLastUpdate;
        int256 rewardDebt;
        Lock lock;
    }

    bytes32 public constant ATLAS_MINE_ADMIN_ROLE = keccak256("ATLAS_MINE_ADMIN_ROLE");

    uint256 public constant DAY = 1 days;
    uint256 public constant ONE_WEEK = 7 days;
    uint256 public constant TWO_WEEKS = ONE_WEEK * 2;
    uint256 public constant ONE_MONTH = 30 days;
    uint256 public constant THREE_MONTHS = ONE_MONTH * 3;
    uint256 public constant SIX_MONTHS = ONE_MONTH * 6;
    uint256 public constant TWELVE_MONTHS = 365 days;
    uint256 public constant ONE = 1e18;

    // Magic token addr
    IERC20Upgradeable public magic;
    IMasterOfCoin public masterOfCoin;

    bool public unlockAll;

    uint256 public totalRewardsEarned;
    uint256 public totalUndistributedRewards;
    uint256 public accMagicPerShare;
    uint256 public totalLpToken;
    uint256 public magicTotalDeposits;

    uint256 public utilizationOverride;
    EnumerableSetUpgradeable.AddressSet private excludedAddresses;

    address public legionMetadataStore;
    address public treasure;
    address public legion;

    // user => staked 1/1
    mapping(address => bool) public isLegion1_1Staked;
    uint256[][] public legionBoostMatrix;

    /// @notice user => depositId => UserInfo
    mapping(address => mapping(uint256 => UserInfo)) public userInfo;
    /// @notice user => depositId[]
    mapping(address => EnumerableSetUpgradeable.UintSet) private allUserDepositIds;
    /// @notice user => deposit index
    mapping(address => uint256) public currentId;

    // user => tokenIds
    mapping(address => EnumerableSetUpgradeable.UintSet) private legionStaked;
    // user => tokenId => amount
    mapping(address => mapping(uint256 => uint256)) public treasureStaked;
    // user => total amount staked
    mapping(address => uint256) public treasureStakedAmount;
    // user => boost
    mapping(address => uint256) public boosts;

    event Staked(address nft, uint256 tokenId, uint256 amount, uint256 currentBoost);
    event Unstaked(address nft, uint256 tokenId, uint256 amount, uint256 currentBoost);

    event Deposit(address indexed user, uint256 indexed index, uint256 amount, Lock lock);
    event Withdraw(address indexed user, uint256 indexed index, uint256 amount);
    event UndistributedRewardsWithdraw(address indexed to, uint256 amount);
    event Harvest(address indexed user, uint256 indexed index, uint256 amount);
    event LogUpdateRewards(
        uint256 distributedRewards,
        uint256 undistributedRewards,
        uint256 lpSupply,
        uint256 accMagicPerShare
    );
    event UtilizationRate(uint256 util);

    modifier updateRewards() {
        uint256 lpSupply = totalLpToken;
        if (lpSupply > 0) {
            (uint256 distributedRewards, uint256 undistributedRewards) = getRealMagicReward(
                masterOfCoin.requestRewards()
            );
            totalRewardsEarned += distributedRewards;
            totalUndistributedRewards += undistributedRewards;
            accMagicPerShare += (distributedRewards * ONE) / lpSupply;
            emit LogUpdateRewards(distributedRewards, undistributedRewards, lpSupply, accMagicPerShare);
        }

        uint256 util = utilization();
        emit UtilizationRate(util);
        _;
    }

    function init(address _magic, address _masterOfCoin) external initializer {
        magic = IERC20Upgradeable(_magic);
        masterOfCoin = IMasterOfCoin(_masterOfCoin);

        _setRoleAdmin(ATLAS_MINE_ADMIN_ROLE, ATLAS_MINE_ADMIN_ROLE);
        _grantRole(ATLAS_MINE_ADMIN_ROLE, msg.sender);

        // array follows values from ILegionMetadataStore.LegionGeneration and ILegionMetadataStore.LegionRarity
        legionBoostMatrix = [
            // GENESIS
            // LEGENDARY,RARE,SPECIAL,UNCOMMON,COMMON,RECRUIT
            [uint256(600e16), uint256(200e16), uint256(75e16), uint256(100e16), uint256(50e16), uint256(0)],
            // AUXILIARY
            // LEGENDARY,RARE,SPECIAL,UNCOMMON,COMMON,RECRUIT
            [uint256(0), uint256(25e16), uint256(0), uint256(10e16), uint256(5e16), uint256(0)],
            // RECRUIT
            // LEGENDARY,RARE,SPECIAL,UNCOMMON,COMMON,RECRUIT
            [uint256(0), uint256(0), uint256(0), uint256(0), uint256(0), uint256(0)]
        ];

        __AccessControlEnumerable_init();
        __ERC1155Holder_init();
    }

    function supportsInterface(bytes4 interfaceId)
        public
        view
        virtual
        override(ERC1155ReceiverUpgradeable, AccessControlEnumerableUpgradeable)
        returns (bool)
    {
        return super.supportsInterface(interfaceId);
    }

    function getStakedLegions(address _user) external view virtual returns (uint256[] memory) {
        return legionStaked[_user].values();
    }

    function getUserBoost(address _user) external view virtual returns (uint256) {
        return boosts[_user];
    }

    function getLegionBoostMatrix() external view virtual returns (uint256[][] memory) {
        return legionBoostMatrix;
    }

    function getLegionBoost(uint256 _legionGeneration, uint256 _legionRarity) public view virtual returns (uint256) {
        if (
            _legionGeneration < legionBoostMatrix.length && _legionRarity < legionBoostMatrix[_legionGeneration].length
        ) {
            return legionBoostMatrix[_legionGeneration][_legionRarity];
        }
        return 0;
    }

    function utilization() public view virtual returns (uint256 util) {
        if (utilizationOverride > 0) return utilizationOverride;

        uint256 circulatingSupply = magic.totalSupply();
        uint256 len = excludedAddresses.length();
        for (uint256 i = 0; i < len; i++) {
            circulatingSupply -= magic.balanceOf(excludedAddresses.at(i));
        }
        uint256 rewardsAmount = magic.balanceOf(address(this)) - magicTotalDeposits;
        circulatingSupply -= rewardsAmount;
        if (circulatingSupply != 0) {
            util = (magicTotalDeposits * ONE) / circulatingSupply;
        }
    }

    function getRealMagicReward(uint256 _magicReward)
        public
        view
        virtual
        returns (uint256 distributedRewards, uint256 undistributedRewards)
    {
        //Disabled for testing
        /*   uint256 util = utilization();

        if (util < 3e17) {
            distributedRewards = 0;
        } else if (util < 4e17) { // >30%
            // 50%
            distributedRewards = _magicReward * 5 / 10;
        } else if (util < 5e17) { // >40%
            // 60%
            distributedRewards = _magicReward * 6 / 10;
        } else if (util < 6e17) { // >50%
            // 80%
            distributedRewards = _magicReward * 8 / 10;
        } else { // >60%
            // 100%
            distributedRewards = _magicReward;
        }

        undistributedRewards = _magicReward - distributedRewards;   */
        distributedRewards = _magicReward;
        undistributedRewards = 0;
    }

    function getAllUserDepositIds(address _user) public view virtual returns (uint256[] memory) {
        return allUserDepositIds[_user].values();
    }

    function getExcludedAddresses() public view virtual returns (address[] memory) {
        return excludedAddresses.values();
    }

    function getLockBoost(Lock _lock) public pure virtual returns (uint256 boost, uint256 timelock) {
        if (_lock == Lock.twoWeeks) {
            // 10%
            return (10e16, TWO_WEEKS);
        } else if (_lock == Lock.oneMonth) {
            // 25%
            return (25e16, ONE_MONTH);
        } else if (_lock == Lock.threeMonths) {
            // 80%
            return (80e16, THREE_MONTHS);
        } else if (_lock == Lock.sixMonths) {
            // 180%
            return (180e16, SIX_MONTHS);
        } else if (_lock == Lock.twelveMonths) {
            // 400%
            return (400e16, TWELVE_MONTHS);
        } else {
            revert("Invalid lock value");
        }
    }

    function getVestingTime(Lock _lock) public pure virtual returns (uint256 vestingTime) {
        if (_lock == Lock.twoWeeks) {
            vestingTime = 0;
        } else if (_lock == Lock.oneMonth) {
            vestingTime = 7 days;
        } else if (_lock == Lock.threeMonths) {
            vestingTime = 14 days;
        } else if (_lock == Lock.sixMonths) {
            vestingTime = 30 days;
        } else if (_lock == Lock.twelveMonths) {
            vestingTime = 45 days;
        }
    }

    function calcualteVestedPrincipal(address _user, uint256 _depositId) public view virtual returns (uint256 amount) {
        UserInfo storage user = userInfo[_user][_depositId];
        Lock _lock = user.lock;

        uint256 vestingEnd = user.lockedUntil + getVestingTime(_lock);
        uint256 vestingBegin = user.lockedUntil;

        if (block.timestamp >= vestingEnd || unlockAll) {
            amount = user.originalDepositAmount;
        } else if (block.timestamp > user.vestingLastUpdate) {
            amount =
                (user.originalDepositAmount * (block.timestamp - user.vestingLastUpdate)) /
                (vestingEnd - vestingBegin);
        }
    }

    function pendingRewardsPosition(address _user, uint256 _depositId) public view virtual returns (uint256 pending) {
        UserInfo storage user = userInfo[_user][_depositId];
        uint256 _accMagicPerShare = accMagicPerShare;
        uint256 lpSupply = totalLpToken;

        (uint256 distributedRewards, ) = getRealMagicReward(masterOfCoin.getPendingRewards(address(this)));
        _accMagicPerShare += (distributedRewards * ONE) / lpSupply;

        pending = (((user.lpAmount * _accMagicPerShare) / ONE).toInt256() - user.rewardDebt).toUint256();
    }

    function pendingRewardsAll(address _user) external view virtual returns (uint256 pending) {
        uint256 len = allUserDepositIds[_user].length();
        for (uint256 i = 0; i < len; i++) {
            uint256 depositId = allUserDepositIds[_user].at(i);
            pending += pendingRewardsPosition(_user, depositId);
        }
    }

    function deposit(uint256 _amount, Lock _lock) public virtual updateRewards {
        (UserInfo storage user, uint256 depositId) = _addDeposit(msg.sender);
        (uint256 lockBoost, uint256 timelock) = getLockBoost(_lock);
        uint256 nftBoost = boosts[msg.sender];
        uint256 lpAmount = _amount + (_amount * (lockBoost + nftBoost)) / ONE;
        magicTotalDeposits += _amount;
        totalLpToken += lpAmount;

        user.originalDepositAmount = _amount;
        user.depositAmount = _amount;
        user.lpAmount = lpAmount;
        user.lockedUntil = block.timestamp + timelock;
        user.vestingLastUpdate = user.lockedUntil;
        user.rewardDebt = ((lpAmount * accMagicPerShare) / ONE).toInt256();
        user.lock = _lock;

        magic.safeTransferFrom(msg.sender, address(this), _amount);

        emit Deposit(msg.sender, depositId, _amount, _lock);
    }

    function withdrawPosition(uint256 _depositId, uint256 _amount) public virtual updateRewards returns (bool) {
        UserInfo storage user = userInfo[msg.sender][_depositId];
        uint256 depositAmount = user.depositAmount;
        if (depositAmount == 0) return false;

        if (_amount > depositAmount) {
            _amount = depositAmount;
        }
        // anyone can withdraw if kill swith was used
        if (!unlockAll) {
            require(block.timestamp >= user.lockedUntil, "Position is still locked");
            uint256 vestedAmount = _vestedPrincipal(msg.sender, _depositId);
            if (_amount > vestedAmount) {
                _amount = vestedAmount;
            }
        }

        // Effects
        uint256 ratio = (_amount * ONE) / depositAmount;
        uint256 lpAmount = (user.lpAmount * ratio) / ONE;

        totalLpToken -= lpAmount;
        magicTotalDeposits -= _amount;

        user.depositAmount -= _amount;
        user.lpAmount -= lpAmount;
        user.rewardDebt -= ((lpAmount * accMagicPerShare) / ONE).toInt256();

        // Interactions
        magic.safeTransfer(msg.sender, _amount);

        emit Withdraw(msg.sender, _depositId, _amount);

        return true;
    }

    function withdrawAll() public virtual {
        uint256[] memory depositIds = allUserDepositIds[msg.sender].values();
        for (uint256 i = 0; i < depositIds.length; i++) {
            withdrawPosition(depositIds[i], type(uint256).max);
        }
    }

    function harvestPosition(uint256 _depositId) public virtual updateRewards {
        UserInfo storage user = userInfo[msg.sender][_depositId];

        int256 accumulatedMagic = ((user.lpAmount * accMagicPerShare) / ONE).toInt256();
        uint256 _pendingMagic = (accumulatedMagic - user.rewardDebt).toUint256();

        // Effects
        user.rewardDebt = accumulatedMagic;

        if (user.depositAmount == 0 && user.lpAmount == 0) {
            _removeDeposit(msg.sender, _depositId);
        }

        // Interactions
        if (_pendingMagic != 0) {
            magic.safeTransfer(msg.sender, _pendingMagic);
        }

        emit Harvest(msg.sender, _depositId, _pendingMagic);

        require(magic.balanceOf(address(this)) >= magicTotalDeposits, "Run on banks");
    }

    function harvestAll() public virtual {
        uint256[] memory depositIds = allUserDepositIds[msg.sender].values();
        for (uint256 i = 0; i < depositIds.length; i++) {
            harvestPosition(depositIds[i]);
        }
    }

    function withdrawAndHarvestPosition(uint256 _depositId, uint256 _amount) public virtual {
        withdrawPosition(_depositId, _amount);
        harvestPosition(_depositId);
    }

    function withdrawAndHarvestAll() public virtual {
        uint256[] memory depositIds = allUserDepositIds[msg.sender].values();
        for (uint256 i = 0; i < depositIds.length; i++) {
            withdrawAndHarvestPosition(depositIds[i], type(uint256).max);
        }
    }

    function stakeTreasure(uint256 _tokenId, uint256 _amount) external virtual updateRewards {
        require(treasure != address(0), "Cannot stake Treasure");
        require(_amount > 0, "Amount is 0");

        treasureStaked[msg.sender][_tokenId] += _amount;
        treasureStakedAmount[msg.sender] += _amount;

        require(treasureStakedAmount[msg.sender] <= 20, "Max 20 treasures per wallet");

        uint256 boost = getNftBoost(treasure, _tokenId, _amount);
        boosts[msg.sender] += boost;

        _recalculateLpAmount(msg.sender);

        IERC1155Upgradeable(treasure).safeTransferFrom(msg.sender, address(this), _tokenId, _amount, bytes(""));

        emit Staked(treasure, _tokenId, _amount, boosts[msg.sender]);
    }

    function unstakeTreasure(uint256 _tokenId, uint256 _amount) external virtual updateRewards {
        require(treasure != address(0), "Cannot stake Treasure");
        require(_amount > 0, "Amount is 0");
        require(treasureStaked[msg.sender][_tokenId] >= _amount, "Withdraw amount too big");

        treasureStaked[msg.sender][_tokenId] -= _amount;
        treasureStakedAmount[msg.sender] -= _amount;

        uint256 boost = getNftBoost(treasure, _tokenId, _amount);
        boosts[msg.sender] -= boost;

        _recalculateLpAmount(msg.sender);

        IERC1155Upgradeable(treasure).safeTransferFrom(address(this), msg.sender, _tokenId, _amount, bytes(""));

        emit Unstaked(treasure, _tokenId, _amount, boosts[msg.sender]);
    }

    function stakeLegion(uint256 _tokenId) external virtual updateRewards {
        require(legion != address(0), "Cannot stake Legion");
        require(legionStaked[msg.sender].add(_tokenId), "NFT already staked");
        require(legionStaked[msg.sender].length() <= 3, "Max 3 legions per wallet");

        if (isLegion1_1(_tokenId)) {
            require(!isLegion1_1Staked[msg.sender], "Max 1 1/1 legion per wallet");
            isLegion1_1Staked[msg.sender] = true;
        }

        uint256 boost = getNftBoost(legion, _tokenId, 1);
        boosts[msg.sender] += boost;

        _recalculateLpAmount(msg.sender);

        IERC721Upgradeable(legion).transferFrom(msg.sender, address(this), _tokenId);

        emit Staked(legion, _tokenId, 1, boosts[msg.sender]);
    }

    function unstakeLegion(uint256 _tokenId) external virtual updateRewards {
        require(legionStaked[msg.sender].remove(_tokenId), "NFT is not staked");

        if (isLegion1_1(_tokenId)) {
            isLegion1_1Staked[msg.sender] = false;
        }

        uint256 boost = getNftBoost(legion, _tokenId, 1);
        boosts[msg.sender] -= boost;

        _recalculateLpAmount(msg.sender);

        IERC721Upgradeable(legion).transferFrom(address(this), msg.sender, _tokenId);

        emit Unstaked(legion, _tokenId, 1, boosts[msg.sender]);
    }

    function isLegion1_1(uint256 _tokenId) public view virtual returns (bool) {
        try ILegionMetadataStore(legionMetadataStore).metadataForLegion(_tokenId) returns (
            ILegionMetadataStore.LegionMetadata memory metadata
        ) {
            return
                metadata.legionGeneration == ILegionMetadataStore.LegionGeneration.GENESIS &&
                metadata.legionRarity == ILegionMetadataStore.LegionRarity.LEGENDARY;
        } catch Error(
            string memory /*reason*/
        ) {
            return false;
        } catch Panic(uint256) {
            return false;
        } catch (
            bytes memory /*lowLevelData*/
        ) {
            return false;
        }
    }

    function getNftBoost(
        address _nft,
        uint256 _tokenId,
        uint256 _amount
    ) public view virtual returns (uint256) {
        if (_nft == treasure) {
            return getTreasureBoost(_tokenId, _amount);
        } else if (_nft == legion) {
            try ILegionMetadataStore(legionMetadataStore).metadataForLegion(_tokenId) returns (
                ILegionMetadataStore.LegionMetadata memory metadata
            ) {
                return getLegionBoost(uint256(metadata.legionGeneration), uint256(metadata.legionRarity));
            } catch Error(
                string memory /*reason*/
            ) {
                return 0;
            } catch Panic(uint256) {
                return 0;
            } catch (
                bytes memory /*lowLevelData*/
            ) {
                return 0;
            }
        }

        return 0;
    }

    function _recalculateLpAmount(address _user) internal virtual {
        uint256 nftBoost = boosts[_user];

        uint256[] memory depositIds = allUserDepositIds[_user].values();
        for (uint256 i = 0; i < depositIds.length; i++) {
            uint256 depositId = depositIds[i];
            UserInfo storage user = userInfo[_user][depositId];

            (uint256 lockBoost, ) = getLockBoost(user.lock);
            uint256 _amount = user.depositAmount;
            uint256 newlLpAmount = _amount + (_amount * (lockBoost + nftBoost)) / ONE;
            uint256 oldLpAmount = user.lpAmount;

            if (newlLpAmount > oldLpAmount) {
                uint256 lpDiff = newlLpAmount - oldLpAmount;
                user.rewardDebt += ((lpDiff * accMagicPerShare) / ONE).toInt256();
                totalLpToken += lpDiff;
                user.lpAmount += lpDiff;
            } else if (newlLpAmount < oldLpAmount) {
                uint256 lpDiff = oldLpAmount - newlLpAmount;
                user.rewardDebt -= ((lpDiff * accMagicPerShare) / ONE).toInt256();
                totalLpToken -= lpDiff;
                user.lpAmount -= lpDiff;
            }
        }
    }

    function addExcludedAddress(address _exclude) external virtual onlyRole(ATLAS_MINE_ADMIN_ROLE) updateRewards {
        require(excludedAddresses.add(_exclude), "Address already excluded");
    }

    function removeExcludedAddress(address _excluded) external virtual onlyRole(ATLAS_MINE_ADMIN_ROLE) updateRewards {
        require(excludedAddresses.remove(_excluded), "Address is not excluded");
    }

    function setUtilizationOverride(uint256 _utilizationOverride)
        external
        virtual
        onlyRole(ATLAS_MINE_ADMIN_ROLE)
        updateRewards
    {
        utilizationOverride = _utilizationOverride;
    }

    function setMagicToken(address _magic) external virtual onlyRole(ATLAS_MINE_ADMIN_ROLE) {
        magic = IERC20Upgradeable(_magic);
    }

    function setTreasure(address _treasure) external virtual onlyRole(ATLAS_MINE_ADMIN_ROLE) {
        treasure = _treasure;
    }

    function setLegion(address _legion) external virtual onlyRole(ATLAS_MINE_ADMIN_ROLE) {
        legion = _legion;
    }

    function setLegionMetadataStore(address _legionMetadataStore) external virtual onlyRole(ATLAS_MINE_ADMIN_ROLE) {
        legionMetadataStore = _legionMetadataStore;
    }

    function setLegionBoostMatrix(uint256[][] memory _legionBoostMatrix)
        external
        virtual
        onlyRole(ATLAS_MINE_ADMIN_ROLE)
    {
        legionBoostMatrix = _legionBoostMatrix;
    }

    /// @notice EMERGENCY ONLY
    function toggleUnlockAll() external virtual onlyRole(ATLAS_MINE_ADMIN_ROLE) updateRewards {
        unlockAll = unlockAll ? false : true;
    }

    function withdrawUndistributedRewards(address _to) external virtual onlyRole(ATLAS_MINE_ADMIN_ROLE) updateRewards {
        uint256 _totalUndistributedRewards = totalUndistributedRewards;
        totalUndistributedRewards = 0;

        magic.safeTransfer(_to, _totalUndistributedRewards);
        emit UndistributedRewardsWithdraw(_to, _totalUndistributedRewards);
    }

    function getTreasureBoost(uint256 _tokenId, uint256 _amount) public pure virtual returns (uint256 boost) {
        if (_tokenId == 39) {
            // Ancient Relic 8%
            boost = 75e15;
        } else if (_tokenId == 46) {
            // Bag of Rare Mushrooms 6.2%
            boost = 62e15;
        } else if (_tokenId == 47) {
            // Bait for Monsters 7.3%
            boost = 73e15;
        } else if (_tokenId == 48) {
            // Beetle-wing 0.8%
            boost = 8e15;
        } else if (_tokenId == 49) {
            // Blue Rupee 1.5%
            boost = 15e15;
        } else if (_tokenId == 51) {
            // Bottomless Elixir 7.6%
            boost = 76e15;
        } else if (_tokenId == 52) {
            // Cap of Invisibility 7.6%
            boost = 76e15;
        } else if (_tokenId == 53) {
            // Carriage 6.1%
            boost = 61e15;
        } else if (_tokenId == 54) {
            // Castle 7.3%
            boost = 71e15;
        } else if (_tokenId == 68) {
            // Common Bead 5.6%
            boost = 56e15;
        } else if (_tokenId == 69) {
            // Common Feather 3.4%
            boost = 34e15;
        } else if (_tokenId == 71) {
            // Common Relic 2.2%
            boost = 22e15;
        } else if (_tokenId == 72) {
            // Cow 5.8%
            boost = 58e15;
        } else if (_tokenId == 73) {
            // Diamond 0.8%
            boost = 8e15;
        } else if (_tokenId == 74) {
            // Divine Hourglass 6.3%
            boost = 63e15;
        } else if (_tokenId == 75) {
            // Divine Mask 5.7%
            boost = 57e15;
        } else if (_tokenId == 76) {
            // Donkey 1.2%
            boost = 12e15;
        } else if (_tokenId == 77) {
            // Dragon Tail 0.8%
            boost = 8e15;
        } else if (_tokenId == 79) {
            // Emerald 0.8%
            boost = 8e15;
        } else if (_tokenId == 82) {
            // Favor from the Gods 5.6%
            boost = 56e15;
        } else if (_tokenId == 91) {
            // Framed Butterfly 5.8%
            boost = 58e15;
        } else if (_tokenId == 92) {
            // Gold Coin 0.8%
            boost = 8e15;
        } else if (_tokenId == 93) {
            // Grain 3.2%
            boost = 32e15;
        } else if (_tokenId == 94) {
            // Green Rupee 3.3%
            boost = 33e15;
        } else if (_tokenId == 95) {
            // Grin 15.7%
            boost = 157e15;
        } else if (_tokenId == 96) {
            // Half-Penny 0.8%
            boost = 8e15;
        } else if (_tokenId == 97) {
            // Honeycomb 15.8%
            boost = 158e15;
        } else if (_tokenId == 98) {
            // Immovable Stone 7.2%
            boost = 72e15;
        } else if (_tokenId == 99) {
            // Ivory Breastpin 6.4%
            boost = 64e15;
        } else if (_tokenId == 100) {
            // Jar of Fairies 5.3%
            boost = 53e15;
        } else if (_tokenId == 103) {
            // Lumber 3%
            boost = 30e15;
        } else if (_tokenId == 104) {
            // Military Stipend 6.2%
            boost = 62e15;
        } else if (_tokenId == 105) {
            // Mollusk Shell 6.7%
            boost = 67e15;
        } else if (_tokenId == 114) {
            // Ox 1.6%
            boost = 16e15;
        } else if (_tokenId == 115) {
            // Pearl 0.8%
            boost = 8e15;
        } else if (_tokenId == 116) {
            // Pot of Gold 5.8%
            boost = 58e15;
        } else if (_tokenId == 117) {
            // Quarter-Penny 0.8%
            boost = 8e15;
        } else if (_tokenId == 132) {
            // Red Feather 6.4%
            boost = 64e15;
        } else if (_tokenId == 133) {
            // Red Rupee 0.8%
            boost = 8e15;
        } else if (_tokenId == 141) {
            // Score of Ivory 6%
            boost = 60e15;
        } else if (_tokenId == 151) {
            // Silver Coin 0.8%
            boost = 8e15;
        } else if (_tokenId == 152) {
            // Small Bird 6%
            boost = 60e15;
        } else if (_tokenId == 153) {
            // Snow White Feather 6.4%
            boost = 64e15;
        } else if (_tokenId == 161) {
            // Thread of Divine Silk 7.3%
            boost = 73e15;
        } else if (_tokenId == 162) {
            // Unbreakable Pocketwatch 5.9%
            boost = 59e15;
        } else if (_tokenId == 164) {
            // Witches Broom 5.1%
            boost = 51e15;
        }

        boost = boost * _amount;
    }

    function _vestedPrincipal(address _user, uint256 _depositId) internal virtual returns (uint256 amount) {
        amount = calcualteVestedPrincipal(_user, _depositId);
        UserInfo storage user = userInfo[_user][_depositId];
        user.vestingLastUpdate = block.timestamp;
    }

    function _addDeposit(address _user) internal virtual returns (UserInfo storage user, uint256 newDepositId) {
        // start depositId from 1
        newDepositId = ++currentId[_user];
        allUserDepositIds[_user].add(newDepositId);
        user = userInfo[_user][newDepositId];
    }

    function _removeDeposit(address _user, uint256 _depositId) internal virtual {
        require(allUserDepositIds[_user].remove(_depositId), "depositId !exists");
    }
}

File 2 of 122 : IERC20Upgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20Upgradeable {
    /**
     * @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);

    /**
     * @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);
}

File 3 of 122 : IERC721Upgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721.sol)

pragma solidity ^0.8.0;

import "../../utils/introspection/IERC165Upgradeable.sol";

/**
 * @dev Required interface of an ERC721 compliant contract.
 */
interface IERC721Upgradeable is IERC165Upgradeable {
    /**
     * @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`.
     *
     * 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;

    /**
     * @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 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 the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) external view returns (address operator);

    /**
     * @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);
}

File 4 of 122 : IERC1155Upgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC1155/IERC1155.sol)

pragma solidity ^0.8.0;

import "../../utils/introspection/IERC165Upgradeable.sol";

/**
 * @dev Required interface of an ERC1155 compliant contract, as defined in the
 * https://eips.ethereum.org/EIPS/eip-1155[EIP].
 *
 * _Available since v3.1._
 */
interface IERC1155Upgradeable is IERC165Upgradeable {
    /**
     * @dev Emitted when `value` tokens of token type `id` are transferred from `from` to `to` by `operator`.
     */
    event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);

    /**
     * @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all
     * transfers.
     */
    event TransferBatch(
        address indexed operator,
        address indexed from,
        address indexed to,
        uint256[] ids,
        uint256[] values
    );

    /**
     * @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to
     * `approved`.
     */
    event ApprovalForAll(address indexed account, address indexed operator, bool approved);

    /**
     * @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI.
     *
     * If an {URI} event was emitted for `id`, the standard
     * https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value
     * returned by {IERC1155MetadataURI-uri}.
     */
    event URI(string value, uint256 indexed id);

    /**
     * @dev Returns the amount of tokens of token type `id` owned by `account`.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     */
    function balanceOf(address account, uint256 id) external view returns (uint256);

    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}.
     *
     * Requirements:
     *
     * - `accounts` and `ids` must have the same length.
     */
    function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids)
        external
        view
        returns (uint256[] memory);

    /**
     * @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`,
     *
     * Emits an {ApprovalForAll} event.
     *
     * Requirements:
     *
     * - `operator` cannot be the caller.
     */
    function setApprovalForAll(address operator, bool approved) external;

    /**
     * @dev Returns true if `operator` is approved to transfer ``account``'s tokens.
     *
     * See {setApprovalForAll}.
     */
    function isApprovedForAll(address account, address operator) external view returns (bool);

    /**
     * @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
     *
     * Emits a {TransferSingle} event.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - If the caller is not `from`, it must be have been approved to spend ``from``'s tokens via {setApprovalForAll}.
     * - `from` must have a balance of tokens of type `id` of at least `amount`.
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
     * acceptance magic value.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes calldata data
    ) external;

    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}.
     *
     * Emits a {TransferBatch} event.
     *
     * Requirements:
     *
     * - `ids` and `amounts` must have the same length.
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
     * acceptance magic value.
     */
    function safeBatchTransferFrom(
        address from,
        address to,
        uint256[] calldata ids,
        uint256[] calldata amounts,
        bytes calldata data
    ) external;
}

File 5 of 122 : EnumerableSetUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (utils/structs/EnumerableSet.sol)

pragma solidity ^0.8.0;

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 */
library EnumerableSetUpgradeable {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.

    struct Set {
        // Storage of set values
        bytes32[] _values;
        // Position of the value in the `values` array, plus 1 because index 0
        // means a value is not in the set.
        mapping(bytes32 => uint256) _indexes;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function _add(Set storage set, bytes32 value) private returns (bool) {
        if (!_contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._indexes[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function _remove(Set storage set, bytes32 value) private returns (bool) {
        // We read and store the value's index to prevent multiple reads from the same storage slot
        uint256 valueIndex = set._indexes[value];

        if (valueIndex != 0) {
            // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.

            uint256 toDeleteIndex = valueIndex - 1;
            uint256 lastIndex = set._values.length - 1;

            if (lastIndex != toDeleteIndex) {
                bytes32 lastValue = set._values[lastIndex];

                // Move the last value to the index where the value to delete is
                set._values[toDeleteIndex] = lastValue;
                // Update the index for the moved value
                set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
            }

            // Delete the slot where the moved value was stored
            set._values.pop();

            // Delete the index for the deleted slot
            delete set._indexes[value];

            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function _contains(Set storage set, bytes32 value) private view returns (bool) {
        return set._indexes[value] != 0;
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function _at(Set storage set, uint256 index) private view returns (bytes32) {
        return set._values[index];
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function _values(Set storage set) private view returns (bytes32[] memory) {
        return set._values;
    }

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _add(set._inner, value);
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _remove(set._inner, value);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
        return _contains(set._inner, value);
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(Bytes32Set storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
        return _at(set._inner, index);
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
        return _values(set._inner);
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(AddressSet storage set, address value) internal returns (bool) {
        return _add(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(AddressSet storage set, address value) internal returns (bool) {
        return _remove(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(AddressSet storage set, address value) internal view returns (bool) {
        return _contains(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(AddressSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(AddressSet storage set, uint256 index) internal view returns (address) {
        return address(uint160(uint256(_at(set._inner, index))));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(AddressSet storage set) internal view returns (address[] memory) {
        bytes32[] memory store = _values(set._inner);
        address[] memory result;

        assembly {
            result := store
        }

        return result;
    }

    // UintSet

    struct UintSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(UintSet storage set, uint256 value) internal returns (bool) {
        return _add(set._inner, bytes32(value));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(UintSet storage set, uint256 value) internal returns (bool) {
        return _remove(set._inner, bytes32(value));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(UintSet storage set, uint256 value) internal view returns (bool) {
        return _contains(set._inner, bytes32(value));
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function length(UintSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(UintSet storage set, uint256 index) internal view returns (uint256) {
        return uint256(_at(set._inner, index));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(UintSet storage set) internal view returns (uint256[] memory) {
        bytes32[] memory store = _values(set._inner);
        uint256[] memory result;

        assembly {
            result := store
        }

        return result;
    }
}

File 6 of 122 : SafeCastUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)

pragma solidity ^0.8.0;

/**
 * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
 * checks.
 *
 * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
 * easily result in undesired exploitation or bugs, since developers usually
 * assume that overflows raise errors. `SafeCast` restores this intuition by
 * reverting the transaction when such an operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 *
 * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
 * all math on `uint256` and `int256` and then downcasting.
 */
library SafeCastUpgradeable {
    /**
     * @dev Returns the downcasted uint224 from uint256, reverting on
     * overflow (when the input is greater than largest uint224).
     *
     * Counterpart to Solidity's `uint224` operator.
     *
     * Requirements:
     *
     * - input must fit into 224 bits
     */
    function toUint224(uint256 value) internal pure returns (uint224) {
        require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits");
        return uint224(value);
    }

    /**
     * @dev Returns the downcasted uint128 from uint256, reverting on
     * overflow (when the input is greater than largest uint128).
     *
     * Counterpart to Solidity's `uint128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     */
    function toUint128(uint256 value) internal pure returns (uint128) {
        require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits");
        return uint128(value);
    }

    /**
     * @dev Returns the downcasted uint96 from uint256, reverting on
     * overflow (when the input is greater than largest uint96).
     *
     * Counterpart to Solidity's `uint96` operator.
     *
     * Requirements:
     *
     * - input must fit into 96 bits
     */
    function toUint96(uint256 value) internal pure returns (uint96) {
        require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits");
        return uint96(value);
    }

    /**
     * @dev Returns the downcasted uint64 from uint256, reverting on
     * overflow (when the input is greater than largest uint64).
     *
     * Counterpart to Solidity's `uint64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     */
    function toUint64(uint256 value) internal pure returns (uint64) {
        require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits");
        return uint64(value);
    }

    /**
     * @dev Returns the downcasted uint32 from uint256, reverting on
     * overflow (when the input is greater than largest uint32).
     *
     * Counterpart to Solidity's `uint32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     */
    function toUint32(uint256 value) internal pure returns (uint32) {
        require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits");
        return uint32(value);
    }

    /**
     * @dev Returns the downcasted uint16 from uint256, reverting on
     * overflow (when the input is greater than largest uint16).
     *
     * Counterpart to Solidity's `uint16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     */
    function toUint16(uint256 value) internal pure returns (uint16) {
        require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits");
        return uint16(value);
    }

    /**
     * @dev Returns the downcasted uint8 from uint256, reverting on
     * overflow (when the input is greater than largest uint8).
     *
     * Counterpart to Solidity's `uint8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits.
     */
    function toUint8(uint256 value) internal pure returns (uint8) {
        require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits");
        return uint8(value);
    }

    /**
     * @dev Converts a signed int256 into an unsigned uint256.
     *
     * Requirements:
     *
     * - input must be greater than or equal to 0.
     */
    function toUint256(int256 value) internal pure returns (uint256) {
        require(value >= 0, "SafeCast: value must be positive");
        return uint256(value);
    }

    /**
     * @dev Returns the downcasted int128 from int256, reverting on
     * overflow (when the input is less than smallest int128 or
     * greater than largest int128).
     *
     * Counterpart to Solidity's `int128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     *
     * _Available since v3.1._
     */
    function toInt128(int256 value) internal pure returns (int128) {
        require(value >= type(int128).min && value <= type(int128).max, "SafeCast: value doesn't fit in 128 bits");
        return int128(value);
    }

    /**
     * @dev Returns the downcasted int64 from int256, reverting on
     * overflow (when the input is less than smallest int64 or
     * greater than largest int64).
     *
     * Counterpart to Solidity's `int64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     *
     * _Available since v3.1._
     */
    function toInt64(int256 value) internal pure returns (int64) {
        require(value >= type(int64).min && value <= type(int64).max, "SafeCast: value doesn't fit in 64 bits");
        return int64(value);
    }

    /**
     * @dev Returns the downcasted int32 from int256, reverting on
     * overflow (when the input is less than smallest int32 or
     * greater than largest int32).
     *
     * Counterpart to Solidity's `int32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     *
     * _Available since v3.1._
     */
    function toInt32(int256 value) internal pure returns (int32) {
        require(value >= type(int32).min && value <= type(int32).max, "SafeCast: value doesn't fit in 32 bits");
        return int32(value);
    }

    /**
     * @dev Returns the downcasted int16 from int256, reverting on
     * overflow (when the input is less than smallest int16 or
     * greater than largest int16).
     *
     * Counterpart to Solidity's `int16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     *
     * _Available since v3.1._
     */
    function toInt16(int256 value) internal pure returns (int16) {
        require(value >= type(int16).min && value <= type(int16).max, "SafeCast: value doesn't fit in 16 bits");
        return int16(value);
    }

    /**
     * @dev Returns the downcasted int8 from int256, reverting on
     * overflow (when the input is less than smallest int8 or
     * greater than largest int8).
     *
     * Counterpart to Solidity's `int8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits.
     *
     * _Available since v3.1._
     */
    function toInt8(int256 value) internal pure returns (int8) {
        require(value >= type(int8).min && value <= type(int8).max, "SafeCast: value doesn't fit in 8 bits");
        return int8(value);
    }

    /**
     * @dev Converts an unsigned uint256 into a signed int256.
     *
     * Requirements:
     *
     * - input must be less than or equal to maxInt256.
     */
    function toInt256(uint256 value) internal pure returns (int256) {
        // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
        require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256");
        return int256(value);
    }
}

File 7 of 122 : SafeERC20Upgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20Upgradeable.sol";
import "../../../utils/AddressUpgradeable.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20Upgradeable {
    using AddressUpgradeable for address;

    function safeTransfer(
        IERC20Upgradeable token,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    function safeTransferFrom(
        IERC20Upgradeable token,
        address from,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(
        IERC20Upgradeable token,
        address spender,
        uint256 value
    ) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        require(
            (value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    function safeIncreaseAllowance(
        IERC20Upgradeable token,
        address spender,
        uint256 value
    ) internal {
        uint256 newAllowance = token.allowance(address(this), spender) + value;
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    function safeDecreaseAllowance(
        IERC20Upgradeable token,
        address spender,
        uint256 value
    ) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            uint256 newAllowance = oldAllowance - value;
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20Upgradeable token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        if (returndata.length > 0) {
            // Return data is optional
            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
    }
}

File 8 of 122 : Initializable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.2;

import "../../utils/AddressUpgradeable.sol";

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
 * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
 * case an upgrade adds a module that needs to be initialized.
 *
 * For example:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * contract MyToken is ERC20Upgradeable {
 *     function initialize() initializer public {
 *         __ERC20_init("MyToken", "MTK");
 *     }
 * }
 * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
 *     function initializeV2() reinitializer(2) public {
 *         __ERC20Permit_init("MyToken");
 *     }
 * }
 * ```
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 *
 * [CAUTION]
 * ====
 * Avoid leaving a contract uninitialized.
 *
 * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
 * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
 * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * /// @custom:oz-upgrades-unsafe-allow constructor
 * constructor() {
 *     _disableInitializers();
 * }
 * ```
 * ====
 */
abstract contract Initializable {
    /**
     * @dev Indicates that the contract has been initialized.
     * @custom:oz-retyped-from bool
     */
    uint8 private _initialized;

    /**
     * @dev Indicates that the contract is in the process of being initialized.
     */
    bool private _initializing;

    /**
     * @dev Triggered when the contract has been initialized or reinitialized.
     */
    event Initialized(uint8 version);

    /**
     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
     * `onlyInitializing` functions can be used to initialize parent contracts. Equivalent to `reinitializer(1)`.
     */
    modifier initializer() {
        bool isTopLevelCall = _setInitializedVersion(1);
        if (isTopLevelCall) {
            _initializing = true;
        }
        _;
        if (isTopLevelCall) {
            _initializing = false;
            emit Initialized(1);
        }
    }

    /**
     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
     * used to initialize parent contracts.
     *
     * `initializer` is equivalent to `reinitializer(1)`, so a reinitializer may be used after the original
     * initialization step. This is essential to configure modules that are added through upgrades and that require
     * initialization.
     *
     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
     * a contract, executing them in the right order is up to the developer or operator.
     */
    modifier reinitializer(uint8 version) {
        bool isTopLevelCall = _setInitializedVersion(version);
        if (isTopLevelCall) {
            _initializing = true;
        }
        _;
        if (isTopLevelCall) {
            _initializing = false;
            emit Initialized(version);
        }
    }

    /**
     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} and {reinitializer} modifiers, directly or indirectly.
     */
    modifier onlyInitializing() {
        require(_initializing, "Initializable: contract is not initializing");
        _;
    }

    /**
     * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
     * through proxies.
     */
    function _disableInitializers() internal virtual {
        _setInitializedVersion(type(uint8).max);
    }

    function _setInitializedVersion(uint8 version) private returns (bool) {
        // If the contract is initializing we ignore whether _initialized is set in order to support multiple
        // inheritance patterns, but we only do this in the context of a constructor, and for the lowest level
        // of initializers, because in other contexts the contract may have been reentered.
        if (_initializing) {
            require(
                version == 1 && !AddressUpgradeable.isContract(address(this)),
                "Initializable: contract is already initialized"
            );
            return false;
        } else {
            require(_initialized < version, "Initializable: contract is already initialized");
            _initialized = version;
            return true;
        }
    }
}

File 9 of 122 : AccessControlEnumerableUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControlEnumerable.sol)

pragma solidity ^0.8.0;

import "./IAccessControlEnumerableUpgradeable.sol";
import "./AccessControlUpgradeable.sol";
import "../utils/structs/EnumerableSetUpgradeable.sol";
import "../proxy/utils/Initializable.sol";

/**
 * @dev Extension of {AccessControl} that allows enumerating the members of each role.
 */
abstract contract AccessControlEnumerableUpgradeable is Initializable, IAccessControlEnumerableUpgradeable, AccessControlUpgradeable {
    function __AccessControlEnumerable_init() internal onlyInitializing {
    }

    function __AccessControlEnumerable_init_unchained() internal onlyInitializing {
    }
    using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet;

    mapping(bytes32 => EnumerableSetUpgradeable.AddressSet) private _roleMembers;

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IAccessControlEnumerableUpgradeable).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev Returns one of the accounts that have `role`. `index` must be a
     * value between 0 and {getRoleMemberCount}, non-inclusive.
     *
     * Role bearers are not sorted in any particular way, and their ordering may
     * change at any point.
     *
     * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
     * you perform all queries on the same block. See the following
     * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
     * for more information.
     */
    function getRoleMember(bytes32 role, uint256 index) public view virtual override returns (address) {
        return _roleMembers[role].at(index);
    }

    /**
     * @dev Returns the number of accounts that have `role`. Can be used
     * together with {getRoleMember} to enumerate all bearers of a role.
     */
    function getRoleMemberCount(bytes32 role) public view virtual override returns (uint256) {
        return _roleMembers[role].length();
    }

    /**
     * @dev Overload {_grantRole} to track enumerable memberships
     */
    function _grantRole(bytes32 role, address account) internal virtual override {
        super._grantRole(role, account);
        _roleMembers[role].add(account);
    }

    /**
     * @dev Overload {_revokeRole} to track enumerable memberships
     */
    function _revokeRole(bytes32 role, address account) internal virtual override {
        super._revokeRole(role, account);
        _roleMembers[role].remove(account);
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[49] private __gap;
}

File 10 of 122 : ERC1155HolderUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC1155/utils/ERC1155Holder.sol)

pragma solidity ^0.8.0;

import "./ERC1155ReceiverUpgradeable.sol";
import "../../../proxy/utils/Initializable.sol";

/**
 * Simple implementation of `ERC1155Receiver` that will allow a contract to hold ERC1155 tokens.
 *
 * IMPORTANT: When inheriting this contract, you must include a way to use the received tokens, otherwise they will be
 * stuck.
 *
 * @dev _Available since v3.1._
 */
contract ERC1155HolderUpgradeable is Initializable, ERC1155ReceiverUpgradeable {
    function __ERC1155Holder_init() internal onlyInitializing {
    }

    function __ERC1155Holder_init_unchained() internal onlyInitializing {
    }
    function onERC1155Received(
        address,
        address,
        uint256,
        uint256,
        bytes memory
    ) public virtual override returns (bytes4) {
        return this.onERC1155Received.selector;
    }

    function onERC1155BatchReceived(
        address,
        address,
        uint256[] memory,
        uint256[] memory,
        bytes memory
    ) public virtual override returns (bytes4) {
        return this.onERC1155BatchReceived.selector;
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[50] private __gap;
}

File 11 of 122 : IMasterOfCoin.sol
// SPDX-License-Identifier: MIT
pragma solidity >0.8.0;

interface IMasterOfCoin {
    function requestRewards() external returns (uint256 rewardsPaid);

    function getPendingRewards(address _stream) external view returns (uint256 pendingRewards);

    function setWithdrawStamp() external;

    function setStaticAmount(bool set) external;
}

File 12 of 122 : ILegionMetadataStore.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface ILegionMetadataStore {
    // As this will likely change in the future, this should not be used to store state, but rather
    // as parameters and return values from functions.
    struct LegionMetadata {
        LegionGeneration legionGeneration;
        LegionClass legionClass;
        LegionRarity legionRarity;
        uint8 questLevel;
        uint8 craftLevel;
        uint8[6] constellationRanks;
        uint256 oldId;
    }

    enum Constellation {
        FIRE,
        EARTH,
        WIND,
        WATER,
        LIGHT,
        DARK
    }

    enum LegionRarity {
        LEGENDARY,
        RARE,
        SPECIAL,
        UNCOMMON,
        COMMON,
        RECRUIT
    }

    enum LegionClass {
        RECRUIT,
        SIEGE,
        FIGHTER,
        ASSASSIN,
        RANGED,
        SPELLCASTER,
        RIVERMAN,
        NUMERAIRE,
        ALL_CLASS,
        ORIGIN
    }

    enum LegionGeneration {
        GENESIS,
        AUXILIARY,
        RECRUIT
    }

    // Sets the intial metadata for a token id.
    // Admin only.
    function setInitialMetadataForLegion(
        address _owner,
        uint256 _tokenId,
        LegionGeneration _generation,
        LegionClass _class,
        LegionRarity _rarity,
        uint256 _oldId
    ) external;

    // Increases the quest level by one. It is up to the calling contract to regulate the max quest level. No validation.
    // Admin only.
    function increaseQuestLevel(uint256 _tokenId) external;

    // Increases the craft level by one. It is up to the calling contract to regulate the max craft level. No validation.
    // Admin only.
    function increaseCraftLevel(uint256 _tokenId) external;

    // Increases the rank of the given constellation to the given number. It is up to the calling contract to regulate the max constellation rank. No validation.
    // Admin only.
    function increaseConstellationRank(
        uint256 _tokenId,
        Constellation _constellation,
        uint8 _to
    ) external;

    // Returns the metadata for the given legion.
    function metadataForLegion(uint256 _tokenId) external view returns (LegionMetadata memory);

    // Returns the tokenUri for the given token.
    function tokenURI(uint256 _tokenId) external view returns (string memory);
}

File 13 of 122 : IERC165Upgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165Upgradeable {
    /**
     * @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);
}

File 14 of 122 : AddressUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library AddressUpgradeable {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCall(target, data, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        require(isContract(target), "Address: call to non-contract");

        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev 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);
            }
        }
    }
}

File 15 of 122 : IAccessControlEnumerableUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControlEnumerable.sol)

pragma solidity ^0.8.0;

import "./IAccessControlUpgradeable.sol";

/**
 * @dev External interface of AccessControlEnumerable declared to support ERC165 detection.
 */
interface IAccessControlEnumerableUpgradeable is IAccessControlUpgradeable {
    /**
     * @dev Returns one of the accounts that have `role`. `index` must be a
     * value between 0 and {getRoleMemberCount}, non-inclusive.
     *
     * Role bearers are not sorted in any particular way, and their ordering may
     * change at any point.
     *
     * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
     * you perform all queries on the same block. See the following
     * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
     * for more information.
     */
    function getRoleMember(bytes32 role, uint256 index) external view returns (address);

    /**
     * @dev Returns the number of accounts that have `role`. Can be used
     * together with {getRoleMember} to enumerate all bearers of a role.
     */
    function getRoleMemberCount(bytes32 role) external view returns (uint256);
}

File 16 of 122 : AccessControlUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (access/AccessControl.sol)

pragma solidity ^0.8.0;

import "./IAccessControlUpgradeable.sol";
import "../utils/ContextUpgradeable.sol";
import "../utils/StringsUpgradeable.sol";
import "../utils/introspection/ERC165Upgradeable.sol";
import "../proxy/utils/Initializable.sol";

/**
 * @dev Contract module that allows children to implement role-based access
 * control mechanisms. This is a lightweight version that doesn't allow enumerating role
 * members except through off-chain means by accessing the contract event logs. Some
 * applications may benefit from on-chain enumerability, for those cases see
 * {AccessControlEnumerable}.
 *
 * Roles are referred to by their `bytes32` identifier. These should be exposed
 * in the external API and be unique. The best way to achieve this is by
 * using `public constant` hash digests:
 *
 * ```
 * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
 * ```
 *
 * Roles can be used to represent a set of permissions. To restrict access to a
 * function call, use {hasRole}:
 *
 * ```
 * function foo() public {
 *     require(hasRole(MY_ROLE, msg.sender));
 *     ...
 * }
 * ```
 *
 * Roles can be granted and revoked dynamically via the {grantRole} and
 * {revokeRole} functions. Each role has an associated admin role, and only
 * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
 *
 * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
 * that only accounts with this role will be able to grant or revoke other
 * roles. More complex role relationships can be created by using
 * {_setRoleAdmin}.
 *
 * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
 * grant and revoke this role. Extra precautions should be taken to secure
 * accounts that have been granted it.
 */
abstract contract AccessControlUpgradeable is Initializable, ContextUpgradeable, IAccessControlUpgradeable, ERC165Upgradeable {
    function __AccessControl_init() internal onlyInitializing {
    }

    function __AccessControl_init_unchained() internal onlyInitializing {
    }
    struct RoleData {
        mapping(address => bool) members;
        bytes32 adminRole;
    }

    mapping(bytes32 => RoleData) private _roles;

    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;

    /**
     * @dev Modifier that checks that an account has a specific role. Reverts
     * with a standardized message including the required role.
     *
     * The format of the revert reason is given by the following regular expression:
     *
     *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
     *
     * _Available since v4.1._
     */
    modifier onlyRole(bytes32 role) {
        _checkRole(role);
        _;
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IAccessControlUpgradeable).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) public view virtual override returns (bool) {
        return _roles[role].members[account];
    }

    /**
     * @dev Revert with a standard message if `_msgSender()` is missing `role`.
     * Overriding this function changes the behavior of the {onlyRole} modifier.
     *
     * Format of the revert message is described in {_checkRole}.
     *
     * _Available since v4.6._
     */
    function _checkRole(bytes32 role) internal view virtual {
        _checkRole(role, _msgSender());
    }

    /**
     * @dev Revert with a standard message if `account` is missing `role`.
     *
     * The format of the revert reason is given by the following regular expression:
     *
     *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
     */
    function _checkRole(bytes32 role, address account) internal view virtual {
        if (!hasRole(role, account)) {
            revert(
                string(
                    abi.encodePacked(
                        "AccessControl: account ",
                        StringsUpgradeable.toHexString(uint160(account), 20),
                        " is missing role ",
                        StringsUpgradeable.toHexString(uint256(role), 32)
                    )
                )
            );
        }
    }

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {
        return _roles[role].adminRole;
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
        _grantRole(role, account);
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
        _revokeRole(role, account);
    }

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been revoked `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     */
    function renounceRole(bytes32 role, address account) public virtual override {
        require(account == _msgSender(), "AccessControl: can only renounce roles for self");

        _revokeRole(role, account);
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event. Note that unlike {grantRole}, this function doesn't perform any
     * checks on the calling account.
     *
     * [WARNING]
     * ====
     * This function should only be called from the constructor when setting
     * up the initial roles for the system.
     *
     * Using this function in any other way is effectively circumventing the admin
     * system imposed by {AccessControl}.
     * ====
     *
     * NOTE: This function is deprecated in favor of {_grantRole}.
     */
    function _setupRole(bytes32 role, address account) internal virtual {
        _grantRole(role, account);
    }

    /**
     * @dev Sets `adminRole` as ``role``'s admin role.
     *
     * Emits a {RoleAdminChanged} event.
     */
    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
        bytes32 previousAdminRole = getRoleAdmin(role);
        _roles[role].adminRole = adminRole;
        emit RoleAdminChanged(role, previousAdminRole, adminRole);
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * Internal function without access restriction.
     */
    function _grantRole(bytes32 role, address account) internal virtual {
        if (!hasRole(role, account)) {
            _roles[role].members[account] = true;
            emit RoleGranted(role, account, _msgSender());
        }
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * Internal function without access restriction.
     */
    function _revokeRole(bytes32 role, address account) internal virtual {
        if (hasRole(role, account)) {
            _roles[role].members[account] = false;
            emit RoleRevoked(role, account, _msgSender());
        }
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[49] private __gap;
}

File 17 of 122 : IAccessControlUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)

pragma solidity ^0.8.0;

/**
 * @dev External interface of AccessControl declared to support ERC165 detection.
 */
interface IAccessControlUpgradeable {
    /**
     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
     *
     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
     * {RoleAdminChanged} not being emitted signaling this.
     *
     * _Available since v3.1._
     */
    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);

    /**
     * @dev Emitted when `account` is granted `role`.
     *
     * `sender` is the account that originated the contract call, an admin role
     * bearer except when using {AccessControl-_setupRole}.
     */
    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Emitted when `account` is revoked `role`.
     *
     * `sender` is the account that originated the contract call:
     *   - if using `revokeRole`, it is the admin role bearer
     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
     */
    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) external view returns (bool);

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {AccessControl-_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) external view returns (bytes32);

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been granted `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     */
    function renounceRole(bytes32 role, address account) external;
}

File 18 of 122 : ContextUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;
import "../proxy/utils/Initializable.sol";

/**
 * @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 ContextUpgradeable is Initializable {
    function __Context_init() internal onlyInitializing {
    }

    function __Context_init_unchained() internal onlyInitializing {
    }
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[50] private __gap;
}

File 19 of 122 : StringsUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Strings.sol)

pragma solidity ^0.8.0;

/**
 * @dev String operations.
 */
library StringsUpgradeable {
    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);
    }
}

File 20 of 122 : ERC165Upgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)

pragma solidity ^0.8.0;

import "./IERC165Upgradeable.sol";
import "../../proxy/utils/Initializable.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 *
 * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
 */
abstract contract ERC165Upgradeable is Initializable, IERC165Upgradeable {
    function __ERC165_init() internal onlyInitializing {
    }

    function __ERC165_init_unchained() internal onlyInitializing {
    }
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IERC165Upgradeable).interfaceId;
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[50] private __gap;
}

File 21 of 122 : ERC1155ReceiverUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC1155/utils/ERC1155Receiver.sol)

pragma solidity ^0.8.0;

import "../IERC1155ReceiverUpgradeable.sol";
import "../../../utils/introspection/ERC165Upgradeable.sol";
import "../../../proxy/utils/Initializable.sol";

/**
 * @dev _Available since v3.1._
 */
abstract contract ERC1155ReceiverUpgradeable is Initializable, ERC165Upgradeable, IERC1155ReceiverUpgradeable {
    function __ERC1155Receiver_init() internal onlyInitializing {
    }

    function __ERC1155Receiver_init_unchained() internal onlyInitializing {
    }
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165Upgradeable, IERC165Upgradeable) returns (bool) {
        return interfaceId == type(IERC1155ReceiverUpgradeable).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[50] private __gap;
}

File 22 of 122 : IERC1155ReceiverUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC1155/IERC1155Receiver.sol)

pragma solidity ^0.8.0;

import "../../utils/introspection/IERC165Upgradeable.sol";

/**
 * @dev _Available since v3.1._
 */
interface IERC1155ReceiverUpgradeable is IERC165Upgradeable {
    /**
     * @dev Handles the receipt of a single ERC1155 token type. This function is
     * called at the end of a `safeTransferFrom` after the balance has been updated.
     *
     * NOTE: To accept the transfer, this must return
     * `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
     * (i.e. 0xf23a6e61, or its own function selector).
     *
     * @param operator The address which initiated the transfer (i.e. msg.sender)
     * @param from The address which previously owned the token
     * @param id The ID of the token being transferred
     * @param value The amount of tokens being transferred
     * @param data Additional data with no specified format
     * @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
     */
    function onERC1155Received(
        address operator,
        address from,
        uint256 id,
        uint256 value,
        bytes calldata data
    ) external returns (bytes4);

    /**
     * @dev Handles the receipt of a multiple ERC1155 token types. This function
     * is called at the end of a `safeBatchTransferFrom` after the balances have
     * been updated.
     *
     * NOTE: To accept the transfer(s), this must return
     * `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
     * (i.e. 0xbc197c81, or its own function selector).
     *
     * @param operator The address which initiated the batch transfer (i.e. msg.sender)
     * @param from The address which previously owned the token
     * @param ids An array containing ids of each token being transferred (order and length must match values array)
     * @param values An array containing amounts of each token being transferred (order and length must match ids array)
     * @param data Additional data with no specified format
     * @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
     */
    function onERC1155BatchReceived(
        address operator,
        address from,
        uint256[] calldata ids,
        uint256[] calldata values,
        bytes calldata data
    ) external returns (bytes4);
}

File 23 of 122 : LegionMetadataStore.sol
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";

import "../interfaces/ILegionMetadataStore.sol";

contract LegionMetadataStore is Initializable, ILegionMetadataStore {
    event LegionQuestLevelUp(uint256 indexed _tokenId, uint8 _questLevel);
    event LegionCraftLevelUp(uint256 indexed _tokenId, uint8 _craftLevel);
    event LegionConstellationRankUp(uint256 indexed _tokenId, Constellation indexed _constellation, uint8 _rank);
    event LegionCreated(
        address indexed _owner,
        uint256 indexed _tokenId,
        LegionGeneration _generation,
        LegionClass _class,
        LegionRarity _rarity
    );

    mapping(uint256 => LegionGeneration) internal idToGeneration;
    mapping(uint256 => LegionClass) internal idToClass;
    mapping(uint256 => LegionRarity) internal idToRarity;
    mapping(uint256 => uint256) internal idToOldId;
    mapping(uint256 => uint8) internal idToQuestLevel;
    mapping(uint256 => uint8) internal idToCraftLevel;
    mapping(uint256 => uint8[6]) internal idToConstellationRanks;

    mapping(LegionGeneration => mapping(LegionClass => mapping(LegionRarity => mapping(uint256 => string))))
        internal _genToClassToRarityToOldIdToUri;

    function initialize() external initializer {}

    function setInitialMetadataForLegion(
        address _owner,
        uint256 _tokenId,
        LegionGeneration _generation,
        LegionClass _class,
        LegionRarity _rarity,
        uint256 _oldId
    ) external override {
        idToGeneration[_tokenId] = _generation;
        idToClass[_tokenId] = _class;
        idToRarity[_tokenId] = _rarity;
        idToOldId[_tokenId] = _oldId;

        // Initial quest/craft level is 1.
        idToQuestLevel[_tokenId] = 1;
        idToCraftLevel[_tokenId] = 1;

        emit LegionCreated(_owner, _tokenId, _generation, _class, _rarity);
    }

    function increaseQuestLevel(uint256 _tokenId) external override {
        idToQuestLevel[_tokenId]++;

        emit LegionQuestLevelUp(_tokenId, idToQuestLevel[_tokenId]);
    }

    function increaseCraftLevel(uint256 _tokenId) external override {
        idToCraftLevel[_tokenId]++;

        emit LegionCraftLevelUp(_tokenId, idToCraftLevel[_tokenId]);
    }

    function increaseConstellationRank(
        uint256 _tokenId,
        Constellation _constellation,
        uint8 _to
    ) external override {
        idToConstellationRanks[_tokenId][uint256(_constellation)] = _to;

        emit LegionConstellationRankUp(_tokenId, _constellation, _to);
    }

    function metadataForLegion(uint256 _tokenId) external view override returns (LegionMetadata memory) {
        return
            LegionMetadata(
                idToGeneration[_tokenId],
                idToClass[_tokenId],
                idToRarity[_tokenId],
                idToQuestLevel[_tokenId],
                idToCraftLevel[_tokenId],
                idToConstellationRanks[_tokenId],
                idToOldId[_tokenId]
            );
    }

    function tokenURI(uint256 _tokenId) external view override returns (string memory) {
        return
            _genToClassToRarityToOldIdToUri[idToGeneration[_tokenId]][idToClass[_tokenId]][idToRarity[_tokenId]][
                idToOldId[_tokenId]
            ];
    }

    function setTokenUriForGenClassRarityOldId(
        LegionGeneration _gen,
        LegionClass _class,
        LegionRarity _rarity,
        uint256 _oldId,
        string calldata _uri
    ) external {
        _genToClassToRarityToOldIdToUri[_gen][_class][_rarity][_oldId] = _uri;
    }
}

File 24 of 122 : BattleflyTreasuryFlywheelVault.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;

import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/cryptography/MerkleProofUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/structs/EnumerableSetUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";

import "../BattleflyFounderVaultV08.sol";
import "../interfaces/IBattleflyAtlasStakerV02.sol";
import "../interfaces/vaults/IBattleflyTreasuryFlywheelVault.sol";
import "../interfaces/IBattlefly.sol";
import "../interfaces/IAtlasMine.sol";
import "../interfaces/IBattleflyFounderVault.sol";

contract BattleflyTreasuryFlywheelVault is
    IBattleflyTreasuryFlywheelVault,
    Initializable,
    OwnableUpgradeable,
    ReentrancyGuardUpgradeable,
    PausableUpgradeable
{
    using SafeERC20Upgradeable for IERC20Upgradeable;
    using EnumerableSetUpgradeable for EnumerableSetUpgradeable.UintSet;

    /**
     * @dev Immutable states
     */
    IERC20Upgradeable public MAGIC;
    IBattleflyAtlasStakerV02 public ATLAS_STAKER;
    IBattleflyFounderVault public FOUNDER_VAULT_V2;
    address public BATTLEFLY_BOT;
    uint256 public V2_VAULT_PERCENTAGE;
    uint256 public TREASURY_PERCENTAGE;
    uint256 public DENOMINATOR;
    IAtlasMine.Lock public TREASURY_LOCK;
    EnumerableSetUpgradeable.UintSet depositIds;
    uint256 public pendingDeposits;
    uint256 public activeRestakeDepositId;
    uint256 public pendingTreasuryAmountToStake;

    /**
     * @dev User stake data
     *      { depositId } => { User stake data }
     */
    mapping(uint256 => UserStake) public userStakes;

    function initialize(
        address _magic,
        address _atlasStaker,
        address _battleflyFounderVaultV2,
        address _battleflyBot
    ) external initializer {
        __Ownable_init();
        __ReentrancyGuard_init();
        __Pausable_init();

        require(_magic != address(0), "BattleflyTreasuryFlywheelVault: invalid address");
        require(_atlasStaker != address(0), "BattleflyTreasuryFlywheelVault: invalid address");
        require(_battleflyFounderVaultV2 != address(0), "BattleflyTreasuryFlywheelVault: invalid address");
        require(_battleflyBot != address(0), "BattleflyTreasuryFlywheelVault: invalid address");

        MAGIC = IERC20Upgradeable(_magic);
        ATLAS_STAKER = IBattleflyAtlasStakerV02(_atlasStaker);
        FOUNDER_VAULT_V2 = IBattleflyFounderVault(_battleflyFounderVaultV2);
        BATTLEFLY_BOT = _battleflyBot;

        V2_VAULT_PERCENTAGE = 5000;
        TREASURY_PERCENTAGE = 95000;
        DENOMINATOR = 100000;
        TREASURY_LOCK = IAtlasMine.Lock.twoWeeks;
        MAGIC.approve(address(ATLAS_STAKER), 2**256 - 1);
    }

    /**
     * @dev Deposit funds to AtlasStaker
     */
    function deposit(uint128 _amount) external override nonReentrant onlyOwner returns (uint256 atlasStakerDepositId) {
        MAGIC.safeTransferFrom(msg.sender, address(this), _amount);
        atlasStakerDepositId = _deposit(uint256(_amount));
    }

    /**
     * @dev Withdraw staked funds from AtlasStaker
     */
    function withdraw(uint256[] memory _depositIds, address user)
        public
        override
        nonReentrant
        onlyOwner
        returns (uint256 amount)
    {
        for (uint256 i = 0; i < _depositIds.length; i++) {
            amount += _withdraw(_depositIds[i], user);
        }
    }

    /**
     * @dev Withdraw all from AtlasStaker. This is only possible when the retention period of 14 epochs has passed.
     * The retention period is started when a withdrawal for the stake is requested.
     */
    function withdrawAll(address user) public override nonReentrant onlyOwner returns (uint256 amount) {
        uint256[] memory ids = depositIds.values();
        require(ids.length > 0, "BattleflyTreasuryFlywheelVault: No deposited funds");
        for (uint256 i = 0; i < ids.length; i++) {
            if (ATLAS_STAKER.canWithdraw(ids[i])) {
                amount += _withdraw(ids[i], user);
            }
        }
    }

    /**
     * @dev Request a withdrawal from AtlasStaker. This works with a retention period of 14 epochs.
     * Once the retention period has passed, the stake can be withdrawn.
     */
    function requestWithdrawal(uint256[] memory _depositIds) public override onlyOwner {
        for (uint256 i = 0; i < _depositIds.length; i++) {
            ATLAS_STAKER.requestWithdrawal(_depositIds[i]);
            emit RequestWithdrawal(_depositIds[i]);
        }
    }

    /**
     * @dev Claim emission from AtlasStaker
     */
    function claim(uint256 _depositId, address user) public override nonReentrant onlyOwner returns (uint256 emission) {
        emission = _claim(_depositId, user);
    }

    /**
     * @dev Claim all emissions from AtlasStaker
     */
    function claimAll(address user) external override nonReentrant onlyOwner returns (uint256 amount) {
        uint256[] memory ids = depositIds.values();
        require(ids.length > 0, "BattleflyTreasuryFlywheelVault: No deposited funds");

        for (uint256 i = 0; i < ids.length; i++) {
            amount += _claim(ids[i], user);
        }
    }

    /**
     * @dev Claim all emissions from AtlasStaker, send percentage to V2 Vault and restake.
     */
    function claimAllAndRestake() external override nonReentrant onlyBattleflyBot returns (uint256 amount) {
        uint256[] memory ids = depositIds.values();
        for (uint256 i = 0; i < ids.length; i++) {
            if (getClaimableEmission(ids[i]) > 0) {
                amount += _claim(ids[i], address(this));
            }
        }
        amount = amount + pendingDeposits;
        uint256 v2VaultAmount = (amount * V2_VAULT_PERCENTAGE) / DENOMINATOR;
        uint256 treasuryAmount = (amount * TREASURY_PERCENTAGE) / DENOMINATOR;
        if (v2VaultAmount > 0) {
            MAGIC.safeApprove(address(FOUNDER_VAULT_V2), v2VaultAmount);
            FOUNDER_VAULT_V2.topupTodayEmission(v2VaultAmount);
        }
        pendingTreasuryAmountToStake += treasuryAmount;
        if (activeRestakeDepositId == 0 && pendingTreasuryAmountToStake > 0) {
            activeRestakeDepositId = _deposit(pendingTreasuryAmountToStake);
            pendingTreasuryAmountToStake = 0;
        } else if (activeRestakeDepositId != 0 && canWithdraw(activeRestakeDepositId)) {
            uint256 withdrawn = _withdraw(activeRestakeDepositId, address(this));
            uint256 toDeposit = withdrawn + pendingTreasuryAmountToStake;
            activeRestakeDepositId = _deposit(toDeposit);
            pendingTreasuryAmountToStake = 0;
        } else if (activeRestakeDepositId != 0 && canRequestWithdrawal(activeRestakeDepositId)) {
            ATLAS_STAKER.requestWithdrawal(activeRestakeDepositId);
        }
        pendingDeposits = 0;
    }

    function topupMagic(uint256 amount) public override nonReentrant {
        require(amount > 0);
        MAGIC.safeTransferFrom(msg.sender, address(this), amount);
        pendingDeposits += amount;
        emit TopupMagic(msg.sender, amount);
    }

    // ================ INTERNAL ================

    /**
     * @dev Withdraw a stake from AtlasStaker (Only possible when the retention period has passed)
     */
    function _withdraw(uint256 _depositId, address user) internal returns (uint256 amount) {
        require(ATLAS_STAKER.canWithdraw(_depositId), "BattleflyTreasuryFlywheelVault: stake not yet unlocked");
        amount = ATLAS_STAKER.withdraw(_depositId);
        MAGIC.safeTransfer(user, amount);
        depositIds.remove(_depositId);
        delete userStakes[_depositId];
        emit WithdrawPosition(_depositId, amount);
    }

    /**
     * @dev Claim emission from AtlasStaker
     */
    function _claim(uint256 _depositId, address user) internal returns (uint256 emission) {
        emission = ATLAS_STAKER.claim(_depositId);
        MAGIC.safeTransfer(user, emission);
        emit ClaimEmission(_depositId, emission);
    }

    function _deposit(uint256 _amount) internal returns (uint256 atlasStakerDepositId) {
        atlasStakerDepositId = ATLAS_STAKER.deposit(_amount, TREASURY_LOCK);
        IBattleflyAtlasStakerV02.VaultStake memory vaultStake = ATLAS_STAKER.getVaultStake(atlasStakerDepositId);

        UserStake storage userStake = userStakes[atlasStakerDepositId];
        userStake.amount = _amount;
        userStake.lockAt = vaultStake.lockAt;
        userStake.owner = address(this);
        userStake.lock = TREASURY_LOCK;

        depositIds.add(atlasStakerDepositId);

        emit NewUserStake(atlasStakerDepositId, _amount, vaultStake.unlockAt, address(this), TREASURY_LOCK);
    }

    // ================== VIEW ==================

    /**
     * @dev Get allowed lock periods from AtlasStaker
     */
    function getAllowedLocks() public view override returns (IAtlasMine.Lock[] memory) {
        return ATLAS_STAKER.getAllowedLocks();
    }

    /**
     * @dev Get claimed emission
     */
    function getClaimableEmission(uint256 _depositId) public view override returns (uint256 emission) {
        (emission, ) = ATLAS_STAKER.getClaimableEmission(_depositId);
    }

    /**
     * @dev Check if a vaultStake is eligible for requesting a withdrawal.
     * This is 14 epochs before the end of the initial lock period.
     */
    function canRequestWithdrawal(uint256 _depositId) public view override returns (bool requestable) {
        return ATLAS_STAKER.canRequestWithdrawal(_depositId);
    }

    /**
     * @dev Check if a vaultStake is eligible for a withdrawal
     * This is when the retention period has passed
     */
    function canWithdraw(uint256 _depositId) public view override returns (bool withdrawable) {
        return ATLAS_STAKER.canWithdraw(_depositId);
    }

    /**
     * @dev Check the epoch in which the initial lock period of the vaultStake expires.
     * This is at the end of the lock period
     */
    function initialUnlock(uint256 _depositId) public view override returns (uint64 epoch) {
        return ATLAS_STAKER.getVaultStake(_depositId).unlockAt;
    }

    /**
     * @dev Check the epoch in which the retention period of the vaultStake expires.
     * This is 14 epochs after the withdrawal request has taken place
     */
    function retentionUnlock(uint256 _depositId) public view override returns (uint64 epoch) {
        return ATLAS_STAKER.getVaultStake(_depositId).retentionUnlock;
    }

    /**
     * @dev Get the currently active epoch
     */
    function getCurrentEpoch() public view override returns (uint64 epoch) {
        return ATLAS_STAKER.currentEpoch();
    }

    /**
     * @dev Get the deposit ids
     */
    function getDepositIds() public view override returns (uint256[] memory ids) {
        ids = depositIds.values();
    }

    /**
     * @dev Return the name of the vault
     */
    function getName() public pure override returns (string memory) {
        return "Treasury Flywheel Vault";
    }

    // ================== MODIFIERS ==================

    modifier onlyBattleflyBot() {
        require(msg.sender == BATTLEFLY_BOT, "BattleflyTreasuryFlywheelVault: caller is not a battlefly bot");
        _;
    }

    // ================== EVENTS ==================
    event NewUserStake(uint256 depositId, uint256 amount, uint256 unlockAt, address owner, IAtlasMine.Lock lock);
    event UpdateUserStake(uint256 depositId, uint256 amount, uint256 unlockAt, address owner, IAtlasMine.Lock lock);
    event ClaimEmission(uint256 depositId, uint256 emission);
    event WithdrawPosition(uint256 depositId, uint256 amount);
    event RequestWithdrawal(uint256 depositId);
    event TopupMagic(address sender, uint256 amount);

    event AddedUser(address vault);
    event RemovedUser(address vault);
}

File 25 of 122 : OwnableUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)

pragma solidity ^0.8.0;

import "../utils/ContextUpgradeable.sol";
import "../proxy/utils/Initializable.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 OwnableUpgradeable is Initializable, ContextUpgradeable {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    function __Ownable_init() internal onlyInitializing {
        __Ownable_init_unchained();
    }

    function __Ownable_init_unchained() internal onlyInitializing {
        _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);
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[49] private __gap;
}

File 26 of 122 : PausableUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)

pragma solidity ^0.8.0;

import "../utils/ContextUpgradeable.sol";
import "../proxy/utils/Initializable.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 PausableUpgradeable is Initializable, ContextUpgradeable {
    /**
     * @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.
     */
    function __Pausable_init() internal onlyInitializing {
        __Pausable_init_unchained();
    }

    function __Pausable_init_unchained() internal onlyInitializing {
        _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());
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[49] private __gap;
}

File 27 of 122 : ReentrancyGuardUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol)

pragma solidity ^0.8.0;
import "../proxy/utils/Initializable.sol";

/**
 * @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 ReentrancyGuardUpgradeable is Initializable {
    // 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;

    function __ReentrancyGuard_init() internal onlyInitializing {
        __ReentrancyGuard_init_unchained();
    }

    function __ReentrancyGuard_init_unchained() internal onlyInitializing {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        // On the first call to nonReentrant, _notEntered will be true
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;

        _;

        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[49] private __gap;
}

File 28 of 122 : MerkleProofUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (utils/cryptography/MerkleProof.sol)

pragma solidity ^0.8.0;

/**
 * @dev These functions deal with verification of Merkle Trees proofs.
 *
 * The proofs can be generated using the JavaScript library
 * https://github.com/miguelmota/merkletreejs[merkletreejs].
 * Note: the hashing algorithm should be keccak256 and pair sorting should be enabled.
 *
 * See `test/utils/cryptography/MerkleProof.test.js` for some examples.
 *
 * WARNING: You should avoid using leaf values that are 64 bytes long prior to
 * hashing, or use a hash function other than keccak256 for hashing leaves.
 * This is because the concatenation of a sorted pair of internal nodes in
 * the merkle tree could be reinterpreted as a leaf value.
 */
library MerkleProofUpgradeable {
    /**
     * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
     * defined by `root`. For this, a `proof` must be provided, containing
     * sibling hashes on the branch from the leaf to the root of the tree. Each
     * pair of leaves and each pair of pre-images are assumed to be sorted.
     */
    function verify(
        bytes32[] memory proof,
        bytes32 root,
        bytes32 leaf
    ) internal pure returns (bool) {
        return processProof(proof, leaf) == root;
    }

    /**
     * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
     * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
     * hash matches the root of the tree. When processing the proof, the pairs
     * of leafs & pre-images are assumed to be sorted.
     *
     * _Available since v4.4._
     */
    function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) {
        bytes32 computedHash = leaf;
        for (uint256 i = 0; i < proof.length; i++) {
            bytes32 proofElement = proof[i];
            if (computedHash <= proofElement) {
                // Hash(current computed hash + current element of the proof)
                computedHash = _efficientHash(computedHash, proofElement);
            } else {
                // Hash(current element of the proof + current computed hash)
                computedHash = _efficientHash(proofElement, computedHash);
            }
        }
        return computedHash;
    }

    function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) {
        assembly {
            mstore(0x00, a)
            mstore(0x20, b)
            value := keccak256(0x00, 0x40)
        }
    }
}

File 29 of 122 : BattleflyFounderVaultV08.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;

import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC1155/IERC1155Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC1155/utils/ERC1155HolderUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/utils/ERC721HolderUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/structs/EnumerableSetUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";

import "./interfaces/IBattleflyAtlasStaker.sol";
import "./interfaces/IBattleflyAtlasStakerV02.sol";
import "./interfaces/IAtlasMine.sol";
import "./interfaces/ISpecialNFT.sol";
import "./interfaces/IBattleflyFounderVault.sol";
import "./interfaces/IBattleflyFlywheelVault.sol";
import "./interfaces/vaults/IBattleflyFoundersFlywheelVault.sol";
import "./interfaces/vaults/IBattleflyTreasuryFlywheelVault.sol";

contract BattleflyFounderVaultV08 is
    Initializable,
    OwnableUpgradeable,
    ERC1155HolderUpgradeable,
    ERC721HolderUpgradeable,
    ReentrancyGuardUpgradeable
{
    using SafeERC20Upgradeable for IERC20Upgradeable;
    using AddressUpgradeable for address;
    using SafeCastUpgradeable for uint256;
    using SafeCastUpgradeable for int256;
    using EnumerableSetUpgradeable for EnumerableSetUpgradeable.UintSet;

    // ============================================ STATE ==============================================
    struct FounderStake {
        uint256 amount;
        uint256 stakeTimestamp;
        address owner;
        uint256 lastClaimedDay;
    }
    struct DailyFounderEmission {
        uint256 totalEmission;
        uint256 totalFounders;
    }
    // ============= Global Immutable State ==============

    /// @notice MAGIC token
    /// @dev functionally immutable
    IERC20Upgradeable public magic;
    ISpecialNFT public founderNFT;
    uint256 public founderTypeID;
    // ---- !!! Not Used Anymore !!! ----
    IBattleflyAtlasStaker public BattleflyStaker;
    uint256 public startTimestamp;
    // ============= Global mutable State ==============
    uint256 totalEmission;
    uint256 claimedEmission;
    uint256 pendingFounderEmission;

    mapping(address => EnumerableSetUpgradeable.UintSet) private FounderStakeOfOwner;
    uint256 lastStakeTimestamp;
    mapping(uint256 => FounderStake) public FounderStakes;
    uint256 lastStakeId;
    mapping(address => bool) private adminAccess;
    uint256 public DaysSinceStart;
    mapping(uint256 => DailyFounderEmission) public DailyFounderEmissions;

    uint256 withdrawnOldFounder;
    uint256 unupdatedStakeIdFrom;

    uint256 public stakeBackPercent;
    uint256 public treasuryPercent;
    uint256 public v2VaultPercent;

    IBattleflyFounderVault battleflyFounderVaultV2;
    // ---- !!! Not Used Anymore !!! ----
    IBattleflyFlywheelVault battleflyFlywheelVault;
    // ============= Constant ==============
    address public constant TREASURY_WALLET = 0xF5411006eEfD66c213d2fd2033a1d340458B7226;
    uint256 public constant PERCENT_DENOMINATOR = 10000;
    IAtlasMine.Lock public constant DEFAULT_STAKE_BACK_LOCK = IAtlasMine.Lock.twoWeeks;

    mapping(uint256 => bool) public claimedPastEmission;
    uint256 public pastEmissionPerFounder;
    mapping(uint256 => uint256) public stakeIdOfFounder;
    mapping(uint256 => EnumerableSetUpgradeable.UintSet) stakingFounderOfStakeId;

    // ============================================ EVENTS ==============================================
    event ClaimDailyEmission(
        uint256 dayTotalEmission,
        uint256 totalFounderEmission,
        uint256 totalFounders,
        uint256 stakeBackAmount,
        uint256 treasuryAmount,
        uint256 v2VaultAmount
    );
    event Claim(address user, uint256 stakeId, uint256 amount);
    event Withdraw(address user, uint256 stakeId, uint256 founderId);
    event Stake(address user, uint256 stakeId, uint256[] founderNFTIDs);
    event TopupMagicToStaker(address user, uint256 amount, IAtlasMine.Lock lock);
    event TopupTodayEmission(address user, uint256 amount);
    event ClaimPastEmission(address user, uint256 amount, uint256[] tokenIds);

    // Upgrade Atlas Staker Start

    bool public claimingIsPaused;

    EnumerableSetUpgradeable.UintSet depositIds;
    // ---- !!! New Versions !!! ----
    IBattleflyAtlasStakerV02 public BattleflyStakerV2;
    IBattleflyFoundersFlywheelVault public BattleflyFoundersFlywheelVault;
    IBattleflyTreasuryFlywheelVault public TREASURY_VAULT;

    uint256 public activeDepositId;
    uint256 public activeRestakeDepositId;
    uint256 public pendingStakeBackAmount;
    address public BattleflyBot;

    event WithdrawalFromStaker(uint256 depositId);
    event RequestWithdrawalFromStaker(uint256 depositId);

    // Upgrade Atlas Staker End

    // ============================================ INITIALIZE ==============================================
    function initialize(
        address _magicAddress,
        address _BattleflyStakerAddress,
        uint256 _founderTypeID,
        address _founderNFTAddress,
        uint256 _startTimestamp,
        address _battleflyFounderVaultV2Address,
        uint256 _stakeBackPercent,
        uint256 _treasuryPercent,
        uint256 _v2VaultPercent
    ) external initializer {
        __ERC1155Holder_init();
        __ERC721Holder_init();
        __Ownable_init();
        __ReentrancyGuard_init();

        magic = IERC20Upgradeable(_magicAddress);
        BattleflyStaker = IBattleflyAtlasStaker(_BattleflyStakerAddress);
        founderNFT = (ISpecialNFT(_founderNFTAddress));
        founderTypeID = _founderTypeID;
        lastStakeTimestamp = block.timestamp;
        lastStakeId = 0;
        startTimestamp = _startTimestamp;
        DaysSinceStart = 0;
        stakeBackPercent = _stakeBackPercent;
        treasuryPercent = _treasuryPercent;
        v2VaultPercent = _v2VaultPercent;
        if (_battleflyFounderVaultV2Address == address(0))
            battleflyFounderVaultV2 = IBattleflyFounderVault(address(this));
        else battleflyFounderVaultV2 = IBattleflyFounderVault(_battleflyFounderVaultV2Address);

        require(stakeBackPercent + treasuryPercent + v2VaultPercent <= PERCENT_DENOMINATOR);

        // Approve the AtlasStaker contract to spend the magic
        magic.safeApprove(address(BattleflyStaker), 2**256 - 1);
    }

    // ============================================ USER OPERATIONS ==============================================

    /**
     * @dev Claim past emissions for all owned founders tokens
     */
    function claimPastEmission() external {
        require(pastEmissionPerFounder != 0, "No past founder emission to claim");
        uint256[] memory tokenIds = getPastEmissionClaimableTokens(msg.sender);
        require(tokenIds.length > 0, "No tokens to claim");
        for (uint256 i = 0; i < tokenIds.length; i++) {
            claimedPastEmission[tokenIds[i]] = true;
        }
        magic.safeTransfer(msg.sender, pastEmissionPerFounder * tokenIds.length);
        emit ClaimPastEmission(msg.sender, pastEmissionPerFounder * tokenIds.length, tokenIds);
    }

    /**
     * @dev get all tokens eligible for cliaming past emissions
     */
    function getPastEmissionClaimableTokens(address user) public view returns (uint256[] memory) {
        uint256 balance = founderNFT.balanceOf(user);
        uint256[] memory tokenIds = new uint256[](balance);
        uint256 countClaimable = 0;
        for (uint256 i = 0; i < balance; i++) {
            uint256 tokenId = founderNFT.tokenOfOwnerByIndex(user, i);
            uint256 tokenType = founderNFT.getSpecialNFTType(tokenId);
            if (tokenType == founderTypeID && claimedPastEmission[tokenId] == false) {
                tokenIds[countClaimable] = tokenId;
                countClaimable++;
            }
        }
        (, uint256[][] memory stakeTokens) = stakesOf(user);
        uint256 countClaimableStaked = 0;
        uint256 balanceStaked = 0;
        for (uint256 i = 0; i < stakeTokens.length; i++) {
            balanceStaked += stakeTokens[i].length;
        }
        uint256[] memory stakingTokenIds = new uint256[](balanceStaked);
        for (uint256 i = 0; i < stakeTokens.length; i++) {
            uint256[] memory stakeTokenIds = stakeTokens[i];
            for (uint256 j = 0; j < stakeTokenIds.length; j++) {
                uint256 tokenId = stakeTokenIds[j];
                uint256 tokenType = founderNFT.getSpecialNFTType(tokenId);
                if (tokenType == founderTypeID && claimedPastEmission[tokenId] == false) {
                    stakingTokenIds[countClaimableStaked] = tokenId;
                    countClaimableStaked++;
                }
            }
        }

        uint256[] memory result = new uint256[](countClaimable + countClaimableStaked);
        for (uint256 i = 0; i < countClaimable; i++) {
            result[i] = tokenIds[i];
        }
        for (uint256 i = countClaimable; i < countClaimable + countClaimableStaked; i++) {
            result[i] = stakingTokenIds[i - countClaimable];
        }
        return result;
    }

    /**
     * @dev set founder tokens that can claim past emissions
     */
    function setTokenClaimedPastEmission(uint256[] memory tokenIds, bool isClaimed) external onlyOwner {
        for (uint256 i = 0; i < tokenIds.length; i++) {
            claimedPastEmission[tokenIds[i]] = isClaimed;
        }
    }

    /**
     * @dev set the amount of past emissions per founder token
     */
    function setPastEmission(uint256 amount) external onlyOwner {
        pastEmissionPerFounder = amount;
    }

    /**
     * @dev returns the stake objects and the corresponding founder tokens in the stakes of a specific owner
     */
    function stakesOf(address owner) public view returns (FounderStake[] memory, uint256[][] memory) {
        FounderStake[] memory stakes = new FounderStake[](FounderStakeOfOwner[owner].length());
        uint256[][] memory _founderIDsOfStake = new uint256[][](FounderStakeOfOwner[owner].length());
        for (uint256 i = 0; i < FounderStakeOfOwner[owner].length(); i++) {
            stakes[i] = FounderStakes[FounderStakeOfOwner[owner].at(i)];
            _founderIDsOfStake[i] = stakingFounderOfStakeId[FounderStakeOfOwner[owner].at(i)].values();
        }
        return (stakes, _founderIDsOfStake);
    }

    function isOwner(address owner, uint256 tokenId) public view returns (bool) {
        (, uint256[][] memory tokensPerStake) = stakesOf(owner);
        for (uint256 i = 0; i < tokensPerStake.length; i++) {
            for (uint256 j = 0; j < tokensPerStake[i].length; j++) {
                if (tokensPerStake[i][j] == tokenId) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * @dev Returns the founder tokens balance of an owner
     */
    function balanceOf(address owner) external view returns (uint256 balance) {
        require(owner != address(0), "ERC721: balance query for the zero address");

        uint256 balanceOfUser = 0;
        uint256 founderStakeCount = FounderStakeOfOwner[owner].length();

        for (uint256 i = 0; i < founderStakeCount; i++) {
            balanceOfUser += FounderStakes[FounderStakeOfOwner[owner].at(i)].amount;
        }

        return balanceOfUser;
    }

    /**
     * @dev Stake a list of founder tokens
     */
    function stakeFounderNFT(uint256[] memory ids) external {
        require(ids.length != 0, "Must provide at least one founder NFT ID");
        for (uint256 i = 0; i < ids.length; i++) {
            require(founderNFT.getSpecialNFTType(ids[i]) == founderTypeID, "Not valid founder NFT");
            founderNFT.safeTransferFrom(msg.sender, address(this), ids[i]);
        }
        uint256 currentDay = DaysSinceStart;
        lastStakeId++;
        FounderStakes[lastStakeId] = (
            FounderStake({
                amount: ids.length,
                stakeTimestamp: block.timestamp,
                owner: msg.sender,
                lastClaimedDay: currentDay
            })
        );
        for (uint256 i = 0; i < ids.length; i++) {
            stakeIdOfFounder[ids[i]] = lastStakeId;
            stakingFounderOfStakeId[lastStakeId].add(ids[i]);
        }
        FounderStakeOfOwner[msg.sender].add(lastStakeId);
        emit Stake(msg.sender, lastStakeId, ids);
    }

    /**
     * @dev Indicates if claiming is paused
     */
    function isPaused() external view returns (bool) {
        return claimingIsPaused;
    }

    /**
     * @dev Claim all emissions for the founder tokens owned by the sender
     */
    function claimAll() external nonReentrant {
        if (claimingIsPaused) {
            revert("Claiming is currently paused, please try again later");
        }

        uint256 totalReward = 0;

        for (uint256 i = 0; i < FounderStakeOfOwner[msg.sender].length(); i++) {
            totalReward += _claimByStakeId(FounderStakeOfOwner[msg.sender].at(i));
        }

        require(totalReward > 0, "No reward to claim");
    }

    /**
     * @dev Withdraw all founder tokens of the sender
     */
    function withdrawAll() external nonReentrant {
        require(FounderStakeOfOwner[msg.sender].length() > 0, "No STAKE to withdraw");
        uint256 totalWithdraw = 0;
        uint256[] memory stakeIds = FounderStakeOfOwner[msg.sender].values();
        for (uint256 i = 0; i < stakeIds.length; i++) {
            uint256 stakeId = stakeIds[i];
            _claimByStakeId(stakeId);
            totalWithdraw += FounderStakes[stakeId].amount;
            _withdrawByStakeId(stakeId);
        }
        _checkStakingAmount(totalWithdraw);
    }

    function _withdrawByStakeId(uint256 stakeId) internal {
        FounderStake storage stake = FounderStakes[stakeId];
        _claimByStakeId(stakeId);
        for (uint256 i = 0; i < stakingFounderOfStakeId[stakeId].length(); i++) {
            founderNFT.safeTransferFrom(address(this), stake.owner, stakingFounderOfStakeId[stakeId].at(i));
            emit Withdraw(stake.owner, stakeId, stakingFounderOfStakeId[stakeId].at(i));
        }
        if (stake.stakeTimestamp < (startTimestamp + (DaysSinceStart) * 24 hours - 24 hours)) {
            withdrawnOldFounder += stakingFounderOfStakeId[stakeId].length();
        }
        FounderStakeOfOwner[stake.owner].remove(stakeId);
        delete FounderStakes[stakeId];
        delete stakingFounderOfStakeId[stakeId];
    }

    function _claimByStakeId(uint256 stakeId) internal returns (uint256) {
        require(stakeId != 0, "No stake to claim");
        FounderStake storage stake = FounderStakes[stakeId];
        uint256 totalReward = _getClaimableEmissionOf(stakeId);
        claimedEmission += totalReward;
        stake.lastClaimedDay = DaysSinceStart;
        magic.safeTransfer(stake.owner, totalReward);
        emit Claim(stake.owner, stakeId, totalReward);
        return totalReward;
    }

    /**
     * @dev Withdraw a list of founder tokens
     */
    function withdraw(uint256[] memory founderIds) external nonReentrant {
        require(founderIds.length > 0, "No Founder to withdraw");
        uint256 totalWithdraw = 0;
        for (uint256 i = 0; i < founderIds.length; i++) {
            uint256 stakeId = stakeIdOfFounder[founderIds[i]];
            require(FounderStakes[stakeId].owner == msg.sender, "Not your stake");
            _claimBeforeWithdraw(founderIds[i]);
            _withdraw(founderIds[i]);
            totalWithdraw++;
        }
        _checkStakingAmount(totalWithdraw);
    }

    function _checkStakingAmount(uint256 totalWithdraw) internal view {
        uint256 stakeableAmountPerFounder = founderTypeID == 150
            ? BattleflyFoundersFlywheelVault.STAKING_LIMIT_V1()
            : BattleflyFoundersFlywheelVault.STAKING_LIMIT_V2();
        uint256 currentlyRemaining = BattleflyFoundersFlywheelVault.remainingStakeableAmount(msg.sender);
        uint256 currentlyStaked = BattleflyFoundersFlywheelVault.getStakedAmount(msg.sender);
        uint256 remainingAfterSubstraction = (stakeableAmountPerFounder * totalWithdraw) <= currentlyRemaining
            ? currentlyRemaining - stakeableAmountPerFounder * totalWithdraw
            : 0;
        require(currentlyStaked <= remainingAfterSubstraction, "Pls withdraw from FlywheelVault first");
    }

    function _claimBeforeWithdraw(uint256 founderId) internal returns (uint256) {
        uint256 stakeId = stakeIdOfFounder[founderId];
        FounderStake storage stake = FounderStakes[stakeId];
        uint256 founderReward = _getClaimableEmissionOf(stakeId) / stake.amount;
        claimedEmission += founderReward;
        magic.safeTransfer(stake.owner, founderReward);
        emit Claim(stake.owner, stakeId, founderReward);
        return founderReward;
    }

    /**
     * @dev Get the emissions claimable by a certain user
     */
    function getClaimableEmissionOf(address user) public view returns (uint256) {
        uint256 totalReward = 0;
        for (uint256 i = 0; i < FounderStakeOfOwner[user].length(); i++) {
            totalReward += _getClaimableEmissionOf(FounderStakeOfOwner[user].at(i));
        }
        return totalReward;
    }

    function _getClaimableEmissionOf(uint256 stakeId) internal view returns (uint256) {
        uint256 totalReward = 0;
        FounderStake memory stake = FounderStakes[stakeId];

        if (stake.lastClaimedDay == DaysSinceStart) return 0;

        for (uint256 j = stake.lastClaimedDay + 1; j <= DaysSinceStart; j++) {
            if (DailyFounderEmissions[j].totalFounders == 0 || stake.amount == 0) continue;
            totalReward +=
                (DailyFounderEmissions[j].totalEmission / DailyFounderEmissions[j].totalFounders) *
                stake.amount;
        }
        return totalReward;
    }

    function _withdraw(uint256 founderId) internal {
        uint256 stakeId = stakeIdOfFounder[founderId];
        FounderStake storage stake = FounderStakes[stakeId];

        founderNFT.safeTransferFrom(address(this), stake.owner, founderId);

        stake.amount--;
        delete stakeIdOfFounder[founderId];
        stakingFounderOfStakeId[stakeId].remove(founderId);
        if (stake.stakeTimestamp < (startTimestamp + (DaysSinceStart) * 24 hours - 24 hours)) {
            withdrawnOldFounder += 1;
        }
        if (stake.amount == 0) {
            FounderStakeOfOwner[stake.owner].remove(stakeId);
            delete FounderStakes[stakeId];
        }
        emit Withdraw(stake.owner, stakeId, founderId);
    }

    function _depositToStaker(uint256 amount, IAtlasMine.Lock lock) internal returns (uint256 depositId) {
        depositId = BattleflyStakerV2.deposit(amount, lock);
        depositIds.add(depositId);
    }

    function _updateTotalStakingFounders(uint256 currentDay) private returns (uint256) {
        uint256 result = DailyFounderEmissions[DaysSinceStart].totalFounders - withdrawnOldFounder;
        withdrawnOldFounder = 0;
        uint256 to = startTimestamp + currentDay * 24 hours;
        uint256 i = unupdatedStakeIdFrom;
        for (; i <= lastStakeId; i++) {
            if (FounderStakes[i].stakeTimestamp == 0) {
                continue;
            }
            if (FounderStakes[i].stakeTimestamp > to) {
                break;
            }
            result += FounderStakes[i].amount;
        }
        unupdatedStakeIdFrom = i;
        return result;
    }

    function _claimAllFromStaker() private returns (uint256 amount) {
        uint256[] memory ids = depositIds.values();
        for (uint256 i = 0; i < ids.length; i++) {
            (uint256 pending, ) = BattleflyStakerV2.getClaimableEmission(ids[i]);
            if (pending > 0) {
                amount += BattleflyStakerV2.claim(ids[i]);
            }
        }
    }

    function _stakeBack(uint256 stakeBackAmount) internal {
        pendingStakeBackAmount += stakeBackAmount;
        if (activeRestakeDepositId == 0 && pendingStakeBackAmount > 0) {
            activeRestakeDepositId = _depositToStaker(pendingStakeBackAmount, DEFAULT_STAKE_BACK_LOCK);
            pendingStakeBackAmount = 0;
        } else if (activeRestakeDepositId > 0 && BattleflyStakerV2.canWithdraw(activeRestakeDepositId)) {
            uint256 withdrawn = BattleflyStakerV2.withdraw(activeRestakeDepositId);
            depositIds.remove(activeRestakeDepositId);
            uint256 toDeposit = withdrawn + pendingStakeBackAmount;
            activeRestakeDepositId = _depositToStaker(toDeposit, DEFAULT_STAKE_BACK_LOCK);
            depositIds.add(activeRestakeDepositId);
            pendingStakeBackAmount = 0;
        } else if (activeRestakeDepositId > 0 && BattleflyStakerV2.canRequestWithdrawal(activeRestakeDepositId)) {
            BattleflyStakerV2.requestWithdrawal(activeRestakeDepositId);
        }
        pendingFounderEmission = 0;
    }

    /**
     * @dev Return the name of the vault
     */
    function getName() public view returns (string memory) {
        if (founderTypeID == 150) {
            return "V1 Stakers Vault";
        } else {
            return "V2 Stakers Vault";
        }
    }

    // ============================================ ADMIN OPERATIONS ==============================================

    /**
     * @dev Topup magic directly to the atlas staker
     */
    function topupMagicToStaker(uint256 amount, IAtlasMine.Lock lock) external onlyAdminAccess {
        require(amount > 0);
        magic.safeTransferFrom(msg.sender, address(this), amount);
        _depositToStaker(amount, lock);
        emit TopupMagicToStaker(msg.sender, amount, lock);
    }

    /**
     * @dev Topup magic to be staked in the daily emission batch
     */
    function topupTodayEmission(uint256 amount) external onlyAdminAccess {
        require(amount > 0);
        magic.safeTransferFrom(msg.sender, address(this), amount);
        pendingFounderEmission += amount;
        emit TopupTodayEmission(msg.sender, amount);
    }

    // to support initial staking period, only to be run after staking period is over
    function setFounderStakesToStart() public onlyAdminAccess nonReentrant {
        uint256 length = lastStakeId;

        for (uint256 i = 0; i <= length; i++) {
            FounderStakes[i].stakeTimestamp = startTimestamp;
            FounderStakes[i].lastClaimedDay = 0;
        }
    }

    /**
     * @dev Update the claimed founder emission for a certain day
     */
    function updateClaimedFounderEmission(uint256 amount, uint256 currentDay) external onlyAdminAccess {
        DaysSinceStart = currentDay;
        uint256 todayTotalFounderNFTs = _updateTotalStakingFounders(currentDay);
        DailyFounderEmissions[DaysSinceStart] = DailyFounderEmission({
            totalEmission: amount,
            totalFounders: todayTotalFounderNFTs
        });
    }

    /**
     * @dev Get the current day
     */
    function getCurrentDay() public view onlyAdminAccess returns (uint256) {
        return DaysSinceStart;
    }

    /**
     * @dev Get the daily founder emission for a specific day
     */
    function getDailyFounderEmission(uint256 currentDay) public view onlyAdminAccess returns (uint256[2] memory) {
        return [DailyFounderEmissions[currentDay].totalEmission, DailyFounderEmissions[currentDay].totalFounders];
    }

    /**
     * @dev set the start timestamp
     */
    function setStartTimestamp(uint256 newTimestamp) public onlyAdminAccess {
        startTimestamp = newTimestamp;
    }

    /**
     * @dev Simulate a claim for a specific token id
     */
    function simulateClaim(uint256 tokenId) public view onlyAdminAccess returns (uint256) {
        uint256 stakeId = stakeIdOfFounder[tokenId];
        return _getClaimableEmissionOf(stakeId);
    }

    /**
     * @dev Pause or unpause claiming
     */
    function pauseClaim(bool doPause) external onlyAdminAccess {
        claimingIsPaused = doPause;
    }

    /**
     * @dev Reduce the total emission
     */
    function reduceTotalEmission(uint256 amount) external onlyAdminAccess {
        totalEmission -= amount;
    }

    /**
     * @dev Increase the total emission
     */
    function increaseTotalEmission(uint256 amount) external onlyAdminAccess {
        totalEmission += amount;
    }

    /**
     * @dev Recalculate the total amount of founders to be included for every day starting from a specific day
     */
    function recalculateTotalFounders(uint256 dayToStart) external onlyAdminAccess {
        uint256 base = DailyFounderEmissions[dayToStart].totalFounders;

        for (uint256 index = dayToStart + 1; index <= DaysSinceStart; index++) {
            DailyFounderEmission storage daily = DailyFounderEmissions[index];

            daily.totalFounders += base;
        }
    }

    /**
     * @dev Claim daily emissions from AtlasStaker and distribute over founder token stakers
     */
    function claimDailyEmission() public onlyBattleflyBot nonReentrant {
        uint256 currentDay = DaysSinceStart + 1;

        uint256 todayTotalEmission = _claimAllFromStaker();

        uint256 todayTotalFounderNFTs = _updateTotalStakingFounders(currentDay);

        uint256 stakeBackAmount;
        uint256 v2VaultAmount;
        uint256 treasuryAmount;
        uint256 founderEmission;
        if (todayTotalEmission != 0) {
            stakeBackAmount = ((todayTotalEmission * stakeBackPercent) / PERCENT_DENOMINATOR);
            _stakeBack(stakeBackAmount + pendingFounderEmission);

            v2VaultAmount = (todayTotalEmission * v2VaultPercent) / PERCENT_DENOMINATOR;
            if (v2VaultAmount != 0) battleflyFounderVaultV2.topupTodayEmission(v2VaultAmount);

            treasuryAmount = (todayTotalEmission * treasuryPercent) / PERCENT_DENOMINATOR;
            if (treasuryAmount != 0) {
                magic.approve(address(TREASURY_VAULT), treasuryAmount);
                TREASURY_VAULT.topupMagic(treasuryAmount);
            }
            founderEmission += todayTotalEmission - stakeBackAmount - v2VaultAmount - treasuryAmount;
        } else if (pendingFounderEmission > 0) {
            _stakeBack(pendingFounderEmission);
        } else {
            _stakeBack(0);
        }
        totalEmission += founderEmission;
        DaysSinceStart = currentDay;
        DailyFounderEmissions[DaysSinceStart] = DailyFounderEmission({
            totalEmission: founderEmission,
            totalFounders: todayTotalFounderNFTs
        });
        emit ClaimDailyEmission(
            todayTotalEmission,
            founderEmission,
            todayTotalFounderNFTs,
            stakeBackAmount,
            treasuryAmount,
            v2VaultAmount
        );
    }

    /**
     * @dev Withdraw all withdrawable deposit ids from the vault in the Atlas Staker
     */
    function withdrawAllFromStaker() external onlyAdminAccess {
        uint256[] memory ids = depositIds.values();
        withdrawFromStaker(ids);
    }

    function withdrawFromStaker(uint256[] memory ids) public onlyAdminAccess {
        claimDailyEmission();
        require(ids.length > 0, "BattleflyFlywheelVault: No deposited funds");
        for (uint256 i = 0; i < ids.length; i++) {
            if (BattleflyStakerV2.canWithdraw(ids[i])) {
                BattleflyStakerV2.withdraw(ids[i]);
                depositIds.remove(ids[i]);
                emit WithdrawalFromStaker(ids[i]);
            }
        }
    }

    /**
     * @dev Request a withdrawal from Atlas Staker for all claimable deposit ids
     */
    function requestWithdrawAllFromStaker() external onlyAdminAccess {
        uint256[] memory ids = depositIds.values();
        requestWithdrawFromStaker(ids);
    }

    /**
     * @dev Request a withdrawal from Atlas Staker for specific deposit ids
     */
    function requestWithdrawFromStaker(uint256[] memory ids) public onlyAdminAccess {
        for (uint256 i = 0; i < ids.length; i++) {
            if (BattleflyStakerV2.canRequestWithdrawal(ids[i])) {
                BattleflyStakerV2.requestWithdrawal(ids[i]);
                emit RequestWithdrawalFromStaker(ids[i]);
            }
        }
    }

    /**
     * @dev Withdraw a specific magic amount from the vault and send it to a receiver
     */
    function withdrawFromVault(address receiver, uint256 amount) external onlyAdminAccess {
        magic.safeTransfer(receiver, amount);
    }

    /**
     * @dev Set the daily founder emissions for a specific day
     */
    function setDailyFounderEmissions(
        uint256 day,
        uint256 amount,
        uint256 stakers
    ) external onlyAdminAccess {
        DailyFounderEmissions[day] = DailyFounderEmission(amount, stakers);
    }

    /**
     * @dev Set the treasury vault address
     */
    function setTreasuryVault(address _treasuryAddress) external onlyAdminAccess {
        require(_treasuryAddress != address(0));
        TREASURY_VAULT = IBattleflyTreasuryFlywheelVault(_treasuryAddress);
    }

    //Must be called right after init
    /**
     * @dev Set the flywheel vault address
     */
    function setFlywheelVault(address vault) external onlyOwner {
        require(vault != address(0));
        BattleflyFoundersFlywheelVault = IBattleflyFoundersFlywheelVault(vault);
    }

    //Must be called right after init
    /**
     * @dev Set the battlefly bot address
     */
    function setBattleflyBot(address _battleflyBot) external onlyOwner {
        require(_battleflyBot != address(0));
        BattleflyBot = _battleflyBot;
    }

    //Must be called right after init
    /**
     * @dev Set the battlefly staker address
     */
    function setBattleflyStaker(address staker) external onlyOwner {
        require(staker != address(0));
        BattleflyStakerV2 = IBattleflyAtlasStakerV02(staker);
        // Approve the AtlasStaker contract to spend the magic
        magic.approve(address(BattleflyStakerV2), 2**256 - 1);
    }

    //Must be called right after init
    /**
     * @dev Set the founder vault address
     */
    function setFounderVaultV2(address founderVault) external onlyOwner {
        require(founderVault != address(0));
        battleflyFounderVaultV2 = IBattleflyFounderVault(founderVault);
        // Approve the FounderVault contract to spend the magic
        magic.approve(address(battleflyFounderVaultV2), 2**256 - 1);
    }

    //Must be called right after init
    /**
     * @dev Set the distribution percentages
     */
    function setPercentages(
        uint256 _stakeBackPercent,
        uint256 _treasuryPercent,
        uint256 _v2VaultPercent
    ) external onlyOwner {
        require(_stakeBackPercent + _treasuryPercent + _v2VaultPercent <= PERCENT_DENOMINATOR);
        stakeBackPercent = _stakeBackPercent;
        treasuryPercent = _treasuryPercent;
        v2VaultPercent = _v2VaultPercent;
    }

    /**
     * @dev Set admin access for a specific user
     */
    function setAdminAccess(address user, bool access) external onlyOwner {
        adminAccess[user] = access;
    }

    modifier onlyAdminAccess() {
        require(adminAccess[_msgSender()] || _msgSender() == owner(), "Require admin access");
        _;
    }

    modifier onlyBattleflyBot() {
        require(msg.sender == BattleflyBot, "Require battlefly bot");
        _;
    }
}

File 30 of 122 : IBattleflyAtlasStakerV02.sol
// SPDX-License-Identifier: MIT
pragma solidity >0.8.0;

import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import "./IAtlasMine.sol";

interface IBattleflyAtlasStakerV02 {
    struct Vault {
        uint16 fee;
        uint16 claimRate;
        bool enabled;
    }

    struct VaultStake {
        uint64 lockAt;
        uint64 unlockAt;
        uint64 retentionUnlock;
        uint256 amount;
        uint256 paidEmission;
        address vault;
        IAtlasMine.Lock lock;
    }

    function MAGIC() external returns (IERC20Upgradeable);

    function deposit(uint256, IAtlasMine.Lock) external returns (uint256);

    function withdraw(uint256) external returns (uint256);

    function claim(uint256) external returns (uint256);

    function requestWithdrawal(uint256) external returns (uint64);

    function currentDepositId() external view returns (uint256);

    function getAllowedLocks() external view returns (IAtlasMine.Lock[] memory);

    function getVaultStake(uint256) external view returns (VaultStake memory);

    function getClaimableEmission(uint256) external view returns (uint256, uint256);

    function canWithdraw(uint256 _depositId) external view returns (bool withdrawable);

    function canRequestWithdrawal(uint256 _depositId) external view returns (bool requestable);

    function currentEpoch() external view returns (uint64 epoch);

    function getLockPeriod(IAtlasMine.Lock) external view returns (uint64 epoch);

    function setPause(bool _paused) external;

    function depositIdsOfVault(address vault) external view returns (uint256[] memory depositIds);

    // ========== Events ==========
    event AddedSuperAdmin(address who);
    event RemovedSuperAdmin(address who);

    event AddedVault(address indexed vault, uint16 fee, uint16 claimRate);
    event RemovedVault(address indexed vault);

    event StakedTreasure(address staker, uint256 tokenId, uint256 amount);
    event UnstakedTreasure(address staker, uint256 tokenId, uint256 amount);

    event StakedLegion(address staker, uint256 tokenId);
    event UnstakedLegion(address staker, uint256 tokenId);

    event SetTreasury(address treasury);
    event SetBattleflyBot(address bot);

    event NewDeposit(address indexed vault, uint256 amount, uint256 unlockedAt, uint256 indexed depositId);
    event WithdrawPosition(address indexed vault, uint256 amount, uint256 indexed depositId);
    event ClaimEmission(address indexed vault, uint256 amount, uint256 indexed depositId);
    event RequestWithdrawal(address indexed vault, uint64 withdrawalEpoch, uint256 indexed depositId);

    event DepositedAllToMine(uint256 amount);

    event SetPause(bool paused);
}

File 31 of 122 : IBattleflyTreasuryFlywheelVault.sol
// SPDX-License-Identifier: MIT
pragma solidity >0.8.0;
import "../IAtlasMine.sol";

interface IBattleflyTreasuryFlywheelVault {
    struct UserStake {
        uint64 lockAt;
        uint256 amount;
        address owner;
        IAtlasMine.Lock lock;
    }

    function deposit(uint128 _amount) external returns (uint256 atlasStakerDepositId);

    function withdraw(uint256[] memory _depositIds, address user) external returns (uint256 amount);

    function withdrawAll(address user) external returns (uint256 amount);

    function requestWithdrawal(uint256[] memory _depositIds) external;

    function claim(uint256 _depositId, address user) external returns (uint256 emission);

    function claimAll(address user) external returns (uint256 amount);

    function claimAllAndRestake() external returns (uint256 amount);

    function topupMagic(uint256 amount) external;

    function getAllowedLocks() external view returns (IAtlasMine.Lock[] memory);

    function getClaimableEmission(uint256) external view returns (uint256);

    function canRequestWithdrawal(uint256 _depositId) external view returns (bool requestable);

    function canWithdraw(uint256 _depositId) external view returns (bool withdrawable);

    function initialUnlock(uint256 _depositId) external view returns (uint64 epoch);

    function retentionUnlock(uint256 _depositId) external view returns (uint64 epoch);

    function getCurrentEpoch() external view returns (uint64 epoch);

    function getDepositIds() external view returns (uint256[] memory ids);

    function getName() external pure returns (string memory);
}

File 32 of 122 : IBattlefly.sol
//SPDX-License-Identifier: Unlicense
pragma solidity >=0.6.0;
import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/IERC721EnumerableUpgradeable.sol";

interface IBattlefly is IERC721EnumerableUpgradeable {
    function mintBattlefly(address receiver, uint256 battleflyType) external returns (uint256);

    function mintBattleflies(
        address receiver,
        uint256 _battleflyType,
        uint256 amount
    ) external returns (uint256[] memory);

    function getBattleflyType(uint256) external view returns (uint256);
}

File 33 of 122 : IAtlasMine.sol
// SPDX-License-Identifier: MIT
pragma solidity >0.8.0;

interface IAtlasMine {
    enum Lock {
        twoWeeks,
        oneMonth,
        threeMonths,
        sixMonths,
        twelveMonths
    }
    struct UserInfo {
        uint256 originalDepositAmount;
        uint256 depositAmount;
        uint256 lpAmount;
        uint256 lockedUntil;
        uint256 vestingLastUpdate;
        int256 rewardDebt;
        Lock lock;
    }

    function treasure() external view returns (address);

    function legion() external view returns (address);

    function unlockAll() external view returns (bool);

    function boosts(address user) external view returns (uint256);

    function userInfo(address user, uint256 depositId)
        external
        view
        returns (
            uint256 originalDepositAmount,
            uint256 depositAmount,
            uint256 lpAmount,
            uint256 lockedUntil,
            uint256 vestingLastUpdate,
            int256 rewardDebt,
            Lock lock
        );

    function getLockBoost(Lock _lock) external pure returns (uint256 boost, uint256 timelock);

    function getVestingTime(Lock _lock) external pure returns (uint256 vestingTime);

    function stakeTreasure(uint256 _tokenId, uint256 _amount) external;

    function unstakeTreasure(uint256 _tokenId, uint256 _amount) external;

    function stakeLegion(uint256 _tokenId) external;

    function unstakeLegion(uint256 _tokenId) external;

    function withdrawPosition(uint256 _depositId, uint256 _amount) external returns (bool);

    function withdrawAll() external;

    function pendingRewardsAll(address _user) external view returns (uint256 pending);

    function deposit(uint256 _amount, Lock _lock) external;

    function harvestAll() external;

    function harvestPosition(uint256 _depositId) external;

    function currentId(address _user) external view returns (uint256);

    function pendingRewardsPosition(address _user, uint256 _depositId) external view returns (uint256);

    function getAllUserDepositIds(address) external view returns (uint256[] memory);
}

File 34 of 122 : IBattleflyFounderVault.sol
//SPDX-License-Identifier: Unlicense
pragma solidity >=0.6.0;
import "./IAtlasMine.sol";

interface IBattleflyFounderVault {
    struct FounderStake {
        uint256 amount;
        uint256 stakeTimestamp;
        address owner;
        uint256[] founderNFTIDs;
        uint256 lastClaimedDay;
    }

    function topupTodayEmission(uint256 amount) external;

    function topupMagicToStaker(uint256 amount, IAtlasMine.Lock lock) external;

    function depositToStaker(uint256 amount, IAtlasMine.Lock lock) external;

    function stakesOf(address owner) external view returns (FounderStake[] memory);

    function isOwner(address owner, uint256 tokenId) external view returns (bool);

    function balanceOf(address owner) external view returns (uint256 balance);

    function getName() external view returns (string memory);
}

File 35 of 122 : ERC721HolderUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/utils/ERC721Holder.sol)

pragma solidity ^0.8.0;

import "../IERC721ReceiverUpgradeable.sol";
import "../../../proxy/utils/Initializable.sol";

/**
 * @dev Implementation of the {IERC721Receiver} interface.
 *
 * Accepts all token transfers.
 * Make sure the contract is able to use its token with {IERC721-safeTransferFrom}, {IERC721-approve} or {IERC721-setApprovalForAll}.
 */
contract ERC721HolderUpgradeable is Initializable, IERC721ReceiverUpgradeable {
    function __ERC721Holder_init() internal onlyInitializing {
    }

    function __ERC721Holder_init_unchained() internal onlyInitializing {
    }
    /**
     * @dev See {IERC721Receiver-onERC721Received}.
     *
     * Always returns `IERC721Receiver.onERC721Received.selector`.
     */
    function onERC721Received(
        address,
        address,
        uint256,
        bytes memory
    ) public virtual override returns (bytes4) {
        return this.onERC721Received.selector;
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[50] private __gap;
}

File 36 of 122 : IBattleflyAtlasStaker.sol
// SPDX-License-Identifier: MIT
pragma solidity >0.8.0;
import "./IAtlasMine.sol";

interface IBattleflyAtlasStaker {
    // ============= Events ==============

    event VaultDeposit(
        address indexed vault,
        uint256 indexed depositId,
        uint256 amount,
        uint256 unlockAt,
        IAtlasMine.Lock lock
    );
    event VaultWithdraw(address indexed vault, uint256 indexed depositId, uint256 amount, uint256 reward);
    event VaultClaim(address indexed vault, uint256 indexed depositId, uint256 reward);
    event MineStake(uint256 currentDepositId, uint256 unlockTime);
    event MineHarvest(uint256 earned, uint256 feeEarned, uint256 feeRefunded);
    event StakeNFT(address indexed vault, address indexed nft, uint256 tokenId, uint256 amount, uint256 currentBoost);
    event UnstakeNFT(address indexed vault, address indexed nft, uint256 tokenId, uint256 amount, uint256 currentBoost);
    event StakingPauseToggle(bool paused);
    event WithdrawFeesToTreasury(uint256 amount);
    event SetFeeWhitelistVault(address vault, bool isSet);
    event SetBattleflyVault(address vault, bool isSet);

    // ================= Data Types ==================

    struct Stake {
        uint256 amount;
        uint256 unlockAt;
        uint256 depositId;
    }

    struct VaultStake {
        uint256 amount;
        uint256 unlockAt;
        int256 rewardDebt;
        IAtlasMine.Lock lock;
    }
    struct VaultOwner {
        uint256 share;
        int256 rewardDebt;
        address owner;
        uint256 unclaimedReward;
    }

    // =============== View Functions ================

    function getVaultStake(address vault, uint256 depositId) external returns (VaultStake memory);

    // function vaultTotalStake(address vault) external returns (uint256);

    function pendingRewards(address vault, uint256 depositId) external view returns (uint256);

    function pendingRewardsAll(address vault) external returns (uint256);

    function totalMagic() external returns (uint256);

    // function totalPendingStake() external returns (uint256);

    function totalWithdrawableMagic() external returns (uint256);

    // ============= Staking Operations ==============

    function deposit(uint256 _amount, IAtlasMine.Lock lock) external returns (uint256);

    function withdraw(uint256 depositId) external;

    function withdrawAll() external;

    function claim(uint256 depositId) external returns (uint256);

    function claimAll() external returns (uint256);

    // function withdrawEmergency() external;

    function stakeScheduled() external;

    // ============= Owner Operations ==============

    function unstakeAllFromMine() external;

    function unstakeToTarget(uint256 target) external;

    // function emergencyUnstakeAllFromMine() external;

    function setBoostAdmin(address _hoard, bool isSet) external;

    function approveNFTs() external;

    // function revokeNFTApprovals() external;

    // function setMinimumStakingWait(uint256 wait) external;

    function toggleSchedulePause(bool paused) external;
}

File 37 of 122 : ISpecialNFT.sol
//SPDX-License-Identifier: Unlicense
pragma solidity >=0.6.0;
import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/IERC721EnumerableUpgradeable.sol";
import "./IMod.sol";

interface ISpecialNFT is IERC721EnumerableUpgradeable {
    function mintSpecialNFT(address receiver, uint256 specialNFTType) external returns (uint256);

    function mintSpecialNFTs(
        address receiver,
        uint256 _specialNFTType,
        uint256 amount
    ) external returns (uint256[] memory);

    function getSpecialNFTType(uint256 tokenId) external view returns (uint256);
}

File 38 of 122 : IBattleflyFlywheelVault.sol
//SPDX-License-Identifier: Unlicense
pragma solidity >=0.6.0;

interface IBattleflyFlywheelVault {
    function getStakeAmount(address user) external view returns (uint256, uint256);

    function stakeableAmountPerV1() external view returns (uint256);

    function stakeableAmountPerV2() external view returns (uint256);

    function stakeableAmountPerFounder(address vault) external view returns (uint256);
}

File 39 of 122 : IBattleflyFoundersFlywheelVault.sol
// SPDX-License-Identifier: MIT
pragma solidity >0.8.0;
import "../IAtlasMine.sol";

interface IBattleflyFoundersFlywheelVault {
    struct UserStake {
        uint64 lockAt;
        uint256 amount;
        address owner;
        IAtlasMine.Lock lock;
    }

    function deposit(uint128, IAtlasMine.Lock) external returns (uint256);

    function withdraw(uint256[] memory _depositIds) external returns (uint256);

    function withdrawAll() external returns (uint256);

    function requestWithdrawal(uint256[] memory _depositIds) external;

    function claim(uint256) external returns (uint256);

    function claimAll() external returns (uint256);

    function getAllowedLocks() external view returns (IAtlasMine.Lock[] memory);

    function getClaimableEmission(uint256) external view returns (uint256);

    function canRequestWithdrawal(uint256 _depositId) external view returns (bool requestable);

    function canWithdraw(uint256 _depositId) external view returns (bool withdrawable);

    function initialUnlock(uint256 _depositId) external view returns (uint64 epoch);

    function retentionUnlock(uint256 _depositId) external view returns (uint64 epoch);

    function getCurrentEpoch() external view returns (uint64 epoch);

    function remainingStakeableAmount(address user) external view returns (uint256 remaining);

    function getStakedAmount(address user) external view returns (uint256 amount);

    function getDepositIdsOfUser(address user) external view returns (uint256[] memory depositIds);

    function getName() external pure returns (string memory);

    function STAKING_LIMIT_V1() external view returns (uint256);

    function STAKING_LIMIT_V2() external view returns (uint256);
}

File 40 of 122 : IERC721ReceiverUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol)

pragma solidity ^0.8.0;

/**
 * @title ERC721 token receiver interface
 * @dev Interface for any contract that wants to support safeTransfers
 * from ERC721 asset contracts.
 */
interface IERC721ReceiverUpgradeable {
    /**
     * @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 `IERC721Receiver.onERC721Received.selector`.
     */
    function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes calldata data
    ) external returns (bytes4);
}

File 41 of 122 : IERC721EnumerableUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC721/extensions/IERC721Enumerable.sol)

pragma solidity ^0.8.0;

import "../IERC721Upgradeable.sol";

/**
 * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
 * @dev See https://eips.ethereum.org/EIPS/eip-721
 */
interface IERC721EnumerableUpgradeable is IERC721Upgradeable {
    /**
     * @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);

    /**
     * @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);
}

File 42 of 122 : IMod.sol
//SPDX-License-Identifier: Unlicense
pragma solidity >=0.6.0;
import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/IERC721EnumerableUpgradeable.sol";

interface IMod is IERC721EnumerableUpgradeable {
    function mintMod(address receiver) external returns (uint256);
}

File 43 of 122 : BattleflyFoundersFlywheelVault.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;

import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/cryptography/MerkleProofUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/structs/EnumerableSetUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";

import "../BattleflyFounderVaultV08.sol";
import "../interfaces/IBattleflyAtlasStakerV02.sol";
import "../interfaces/vaults/IBattleflyFoundersFlywheelVault.sol";
import "../interfaces/IBattlefly.sol";
import "../interfaces/IAtlasMine.sol";
import "../interfaces/IBattleflyFounderVault.sol";

contract BattleflyFoundersFlywheelVault is
    IBattleflyFoundersFlywheelVault,
    Initializable,
    OwnableUpgradeable,
    ReentrancyGuardUpgradeable,
    PausableUpgradeable
{
    using SafeERC20Upgradeable for IERC20Upgradeable;
    using EnumerableSetUpgradeable for EnumerableSetUpgradeable.UintSet;

    /**
     * @dev Immutable states
     */
    IERC20Upgradeable public MAGIC;
    IBattleflyAtlasStakerV02 public ATLAS_STAKER;
    IBattleflyFounderVault public FOUNDER_VAULT_V1;
    IBattleflyFounderVault public FOUNDER_VAULT_V2;
    uint256 public override STAKING_LIMIT_V1;
    uint256 public override STAKING_LIMIT_V2;

    /**
     * @dev User stake data
     *      { depositId } => { User stake data }
     */
    mapping(uint256 => UserStake) public userStakes;

    /**
     * @dev User's depositIds
     *      { user } => { depositIds }
     */
    mapping(address => EnumerableSetUpgradeable.UintSet) private depositIdByUser;

    /**
     * @dev Whitelisted users
     *      { user } => { is whitelisted }
     */
    mapping(address => bool) public whitelistedUsers;

    function initialize(
        address _magic,
        address _atlasStaker,
        address _battleflyFounderVaultV1,
        address _battleflyFounderVaultV2
    ) external initializer {
        __Ownable_init();
        __ReentrancyGuard_init();
        __Pausable_init();

        require(_magic != address(0), "BattleflyFlywheelVault: invalid address");
        require(_atlasStaker != address(0), "BattleflyFlywheelVault: invalid address");
        require(_battleflyFounderVaultV1 != address(0), "BattleflyFlywheelVault: invalid address");
        require(_battleflyFounderVaultV2 != address(0), "BattleflyFlywheelVault: invalid address");

        MAGIC = IERC20Upgradeable(_magic);
        ATLAS_STAKER = IBattleflyAtlasStakerV02(_atlasStaker);
        FOUNDER_VAULT_V1 = IBattleflyFounderVault(_battleflyFounderVaultV1);
        FOUNDER_VAULT_V2 = IBattleflyFounderVault(_battleflyFounderVaultV2);

        STAKING_LIMIT_V1 = 20000e18;
        STAKING_LIMIT_V2 = 10000e18;
    }

    /**
     * @dev Deposit funds to AtlasStaker
     */
    function deposit(uint128 _amount, IAtlasMine.Lock _lock)
        external
        override
        nonReentrant
        onlyMembers
        returns (uint256 atlasStakerDepositId)
    {
        if (!whitelistedUsers[msg.sender]) {
            require(
                remainingStakeableAmount(msg.sender) >= _amount,
                "BattleflyFlywheelVault: amount exceeds stakeable amount"
            );
        }
        MAGIC.safeTransferFrom(msg.sender, address(this), _amount);
        MAGIC.safeApprove(address(ATLAS_STAKER), _amount);

        atlasStakerDepositId = ATLAS_STAKER.deposit(uint256(_amount), _lock);
        IBattleflyAtlasStakerV02.VaultStake memory vaultStake = ATLAS_STAKER.getVaultStake(atlasStakerDepositId);

        UserStake storage userStake = userStakes[atlasStakerDepositId];
        userStake.amount = _amount;
        userStake.lockAt = vaultStake.lockAt;
        userStake.owner = msg.sender;
        userStake.lock = _lock;

        depositIdByUser[msg.sender].add(atlasStakerDepositId);

        emit NewUserStake(atlasStakerDepositId, _amount, vaultStake.unlockAt, msg.sender, _lock);
    }

    /**
     * @dev Withdraw staked funds from AtlasStaker
     */
    function withdraw(uint256[] memory _depositIds) public override nonReentrant returns (uint256 amount) {
        for (uint256 i = 0; i < _depositIds.length; i++) {
            amount += _withdraw(_depositIds[i]);
        }
    }

    /**
     * @dev Withdraw all from AtlasStaker. This is only possible when the retention period of 14 epochs has passed.
     * The retention period is started when a withdrawal for the stake is requested.
     */
    function withdrawAll() public override nonReentrant returns (uint256 amount) {
        uint256[] memory depositIds = depositIdByUser[msg.sender].values();
        require(depositIds.length > 0, "BattleflyFlywheelVault: No deposited funds");
        for (uint256 i = 0; i < depositIds.length; i++) {
            if (ATLAS_STAKER.canWithdraw(depositIds[i])) {
                amount += _withdraw(depositIds[i]);
            }
        }
    }

    /**
     * @dev Request a withdrawal from AtlasStaker. This works with a retention period of 14 epochs.
     * Once the retention period has passed, the stake can be withdrawn.
     */
    function requestWithdrawal(uint256[] memory _depositIds) public override {
        for (uint256 i = 0; i < _depositIds.length; i++) {
            UserStake memory userStake = userStakes[_depositIds[i]];
            require(userStake.owner == msg.sender, "BattleflyFlywheelVault: caller is not the owner");
            ATLAS_STAKER.requestWithdrawal(_depositIds[i]);
            emit RequestWithdrawal(_depositIds[i]);
        }
    }

    /**
     * @dev Claim emission from AtlasStaker
     */
    function claim(uint256 _depositId) public override nonReentrant returns (uint256 emission) {
        emission = _claim(_depositId);
    }

    /**
     * @dev Claim all emissions from AtlasStaker
     */
    function claimAll() external override nonReentrant returns (uint256 amount) {
        uint256[] memory depositIds = depositIdByUser[msg.sender].values();
        require(depositIds.length > 0, "BattleflyFlywheelVault: No deposited funds");

        for (uint256 i = 0; i < depositIds.length; i++) {
            amount += _claim(depositIds[i]);
        }
    }

    /**
     * @dev Whitelist user
     */
    function whitelistUser(address _who) public onlyOwner {
        require(!whitelistedUsers[_who], "BattlefalyWheelVault: Already whitelisted");
        whitelistedUsers[_who] = true;
        emit AddedUser(_who);
    }

    /**
     * @dev Whitelist users
     */
    function whitelistUsers(address[] memory _users) external onlyOwner {
        for (uint256 i = 0; i < _users.length; i++) {
            whitelistUser(_users[i]);
        }
    }

    /**
     * @dev Remove user from whitelist
     */
    function removeUser(address _who) public onlyOwner {
        require(whitelistedUsers[_who], "BattlefalyWheelVault: Not whitelisted yet");
        whitelistedUsers[_who] = false;
        emit RemovedUser(_who);
    }

    /**
     * @dev Remove users from whitelist
     */
    function removeUsers(address[] memory _users) external onlyOwner {
        for (uint256 i = 0; i < _users.length; i++) {
            removeUser(_users[i]);
        }
    }

    // ================ INTERNAL ================

    /**
     * @dev Withdraw a stake from AtlasStaker (Only possible when the retention period has passed)
     */
    function _withdraw(uint256 _depositId) internal returns (uint256 amount) {
        UserStake memory userStake = userStakes[_depositId];
        require(userStake.owner == msg.sender, "BattleflyFlywheelVault: caller is not the owner");
        require(ATLAS_STAKER.canWithdraw(_depositId), "BattleflyFlywheelVault: stake not yet unlocked");
        amount = ATLAS_STAKER.withdraw(_depositId);
        MAGIC.safeTransfer(msg.sender, amount);
        depositIdByUser[msg.sender].remove(_depositId);
        delete userStakes[_depositId];
        emit WithdrawPosition(_depositId, amount);
    }

    /**
     * @dev Claim emission from AtlasStaker
     */
    function _claim(uint256 _depositId) internal returns (uint256 emission) {
        UserStake memory userStake = userStakes[_depositId];
        require(userStake.owner == msg.sender, "BattleflyFlywheelVault: caller is not the owner");

        emission = ATLAS_STAKER.claim(_depositId);
        MAGIC.safeTransfer(msg.sender, emission);
        emit ClaimEmission(_depositId, emission);
    }

    // ================== VIEW ==================

    /**
     * @dev Get allowed lock periods from AtlasStaker
     */
    function getAllowedLocks() public view override returns (IAtlasMine.Lock[] memory) {
        return ATLAS_STAKER.getAllowedLocks();
    }

    /**
     * @dev Get claimed emission
     */
    function getClaimableEmission(uint256 _depositId) public view override returns (uint256 emission) {
        (emission, ) = ATLAS_STAKER.getClaimableEmission(_depositId);
    }

    /**
     * @dev Check if a vaultStake is eligible for requesting a withdrawal.
     * This is 14 epochs before the end of the initial lock period.
     */
    function canRequestWithdrawal(uint256 _depositId) public view override returns (bool requestable) {
        return ATLAS_STAKER.canRequestWithdrawal(_depositId);
    }

    /**
     * @dev Check if a vaultStake is eligible for a withdrawal
     * This is when the retention period has passed
     */
    function canWithdraw(uint256 _depositId) public view override returns (bool withdrawable) {
        return ATLAS_STAKER.canWithdraw(_depositId);
    }

    /**
     * @dev Check the epoch in which the initial lock period of the vaultStake expires.
     * This is at the end of the lock period
     */
    function initialUnlock(uint256 _depositId) public view override returns (uint64 epoch) {
        return ATLAS_STAKER.getVaultStake(_depositId).unlockAt;
    }

    /**
     * @dev Check the epoch in which the retention period of the vaultStake expires.
     * This is 14 epochs after the withdrawal request has taken place
     */
    function retentionUnlock(uint256 _depositId) public view override returns (uint64 epoch) {
        return ATLAS_STAKER.getVaultStake(_depositId).retentionUnlock;
    }

    /**
     * @dev Get the currently active epoch
     */
    function getCurrentEpoch() public view override returns (uint64 epoch) {
        return ATLAS_STAKER.currentEpoch();
    }

    /**
     * @dev Get the remaining stakeable MAGIC amount.
     */
    function remainingStakeableAmount(address user) public view override returns (uint256 remaining) {
        uint256 v1Amount = FOUNDER_VAULT_V1.balanceOf(user);
        uint256 v2Amount = FOUNDER_VAULT_V2.balanceOf(user);
        uint256 eligible = (v1Amount * STAKING_LIMIT_V1) + (v2Amount * STAKING_LIMIT_V2);
        uint256 staked = getStakedAmount(user);
        remaining = eligible >= staked ? eligible - staked : 0;
    }

    /**
     * @dev Get the staked amount of a particular user.
     */
    function getStakedAmount(address user) public view override returns (uint256 amount) {
        uint256[] memory depositIds = depositIdByUser[user].values();
        for (uint256 i = 0; i < depositIds.length; i++) {
            amount += userStakes[depositIds[i]].amount;
        }
    }

    /**
     * @dev Get the deposit ids of a user.
     */
    function getDepositIdsOfUser(address user) public view override returns (uint256[] memory depositIds) {
        depositIds = depositIdByUser[user].values();
    }

    /**
     * @dev Return the name of the vault
     */
    function getName() public pure override returns (string memory) {
        return "Founders Flywheel Vault";
    }

    // ================== MODIFIERS ==================

    modifier onlyMembers() {
        if (!whitelistedUsers[msg.sender]) {
            require(
                FOUNDER_VAULT_V1.balanceOf(msg.sender) + FOUNDER_VAULT_V2.balanceOf(msg.sender) > 0,
                "BattleflyWheelVault: caller has no staked Founder NFTs"
            );
        }
        _;
    }

    // ================== EVENTS ==================
    event NewUserStake(uint256 depositId, uint256 amount, uint256 unlockAt, address owner, IAtlasMine.Lock lock);
    event UpdateUserStake(uint256 depositId, uint256 amount, uint256 unlockAt, address owner, IAtlasMine.Lock lock);
    event ClaimEmission(uint256 depositId, uint256 emission);
    event WithdrawPosition(uint256 depositId, uint256 amount);
    event RequestWithdrawal(uint256 depositId);

    event AddedUser(address vault);
    event RemovedUser(address vault);
}

File 44 of 122 : VaultMock.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;

import "../interfaces/IVault.sol";
import "../interfaces/IAtlasMine.sol";
import "../interfaces/IBattleflyAtlasStakerV02.sol";
import "../interfaces/ITestERC20.sol";

contract VaultMock is IVault {
    IBattleflyAtlasStakerV02 public STAKER;

    constructor(address _staker, address _magic) {
        STAKER = IBattleflyAtlasStakerV02(_staker);
        ITestERC20(_magic).approve(_staker, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
        ITestERC20(_magic).mint(100000e18, address(this));
    }

    function deposit(uint128 _amount, IAtlasMine.Lock lock) public {
        STAKER.deposit(_amount, lock);
    }

    function withdraw(uint256 depositId) public {
        STAKER.withdraw(depositId);
    }

    function requestWithdrawal(uint256 depositId) public {
        STAKER.requestWithdrawal(depositId);
    }

    function claim(uint256 depositId) public {
        STAKER.claim(depositId);
    }

    function isAutoCompounded(uint256) public pure override returns (bool) {
        return false;
    }

    function updatePosition(uint256) public override {}
}

File 45 of 122 : IVault.sol
// SPDX-License-Identifier: MIT
pragma solidity >0.8.0;

interface IVault {
    function isAutoCompounded(uint256) external view returns (bool);

    function updatePosition(uint256) external;
}

File 46 of 122 : ITestERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.2;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

interface ITestERC20 is IERC20 {
    function mint(uint256 amount, address receiver) external;
}

File 47 of 122 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @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);

    /**
     * @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);
}

File 48 of 122 : MasterOfCoinMock.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "../interfaces/IMasterOfCoin.sol";
import "../interfaces/ITestERC20.sol";

contract MasterOfCoinMock is IMasterOfCoin {
    ITestERC20 public immutable magic;
    uint256 public previousWithdrawStamp;
    bool public staticAmount;

    constructor(address magic_) {
        magic = ITestERC20(magic_);
        previousWithdrawStamp = block.timestamp;
        staticAmount = true;
    }

    function requestRewards() external override returns (uint256 rewardsPaid) {
        if (staticAmount) {
            magic.mint(500 ether, msg.sender);
            return 500 ether;
        }
        uint256 secondsPassed = block.timestamp - previousWithdrawStamp;
        uint256 rewards = secondsPassed * 11574074e8;
        previousWithdrawStamp = block.timestamp;
        magic.mint(rewards, msg.sender);
        return rewards;
    }

    function getPendingRewards(address) external view override returns (uint256 pendingRewards) {
        if (staticAmount) {
            return 500 ether;
        }
        uint256 secondsPassed = block.timestamp - previousWithdrawStamp;
        uint256 rewards = secondsPassed * 11574074e8;
        return rewards;
    }

    function setWithdrawStamp() external override {
        previousWithdrawStamp = block.timestamp;
    }

    function setStaticAmount(bool set) external override {
        staticAmount = set;
    }
}

File 49 of 122 : IMagicToken.sol
//SPDX-License-Identifier: Unlicense
pragma solidity >=0.6.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

interface IMagicToken is IERC20 {}

File 50 of 122 : IERC20Metadata.sol
// 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);
}

File 51 of 122 : ERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.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, allowance(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 = allowance(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 Updates `owner` s allowance for `spender` based on spent `amount`.
     *
     * 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 {}
}

File 52 of 122 : Context.sol
// 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 53 of 122 : TestERC20.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";

contract TestERC20 is ERC20 {
    using SafeERC20Upgradeable for IERC20Upgradeable;

    constructor() ERC20("TestERC20", "MAGIC") {}

    function mint(uint256 amount, address receiver) public {
        _mint(receiver, amount);
    }
}

File 54 of 122 : RevealStaking.sol
// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.2;

import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/math/SafeMathUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/structs/EnumerableSetUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import "./interfaces/IBattlefly.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/utils/ERC721HolderUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/cryptography/ECDSAUpgradeable.sol";
import "./interfaces/IBattleflyGame.sol";

contract RevealStaking is ERC721HolderUpgradeable, OwnableUpgradeable, ReentrancyGuardUpgradeable {
    using SafeMathUpgradeable for uint256;
    using AddressUpgradeable for address;
    using EnumerableSetUpgradeable for EnumerableSetUpgradeable.UintSet;
    using SafeERC20Upgradeable for IERC20Upgradeable;
    mapping(address => bool) private adminAccess;
    IBattlefly private BattleflyContract;
    IERC20Upgradeable private MagicToken;
    uint256 public MagicAmountPerBattlefly;
    IBattleflyGame private BattleflyGame;
    mapping(address => EnumerableSetUpgradeable.UintSet) private StakingBattlefliesOfOwner;
    mapping(uint256 => address) public OwnerOfStakingBattlefly;
    mapping(uint256 => uint256) public MagicAmountOfStakingBattlefly;
    mapping(uint256 => bool) public NectarClaimed;

    uint8 constant COCOON_STAGE = 0;
    uint8 constant BATTLEFLY_STAGE = 1;

    uint8 constant NECTAR_ID = 0;

    event SetAdminAccess(address indexed user, bool access);
    event BulkStakeBattlefly(uint256[] tokenIds, address indexed user, uint256 totalMagicAmount);
    event BulkUnstakeBattlefly(uint256[] tokenIds, address indexed user, uint256 totalMagicAmount);

    function initialize(
        address batteflyGameContractAddress,
        address battleflyContractAddress,
        address magicTokenAddress,
        uint256 _MagicAmountPerBattlefly
    ) public initializer {
        __ERC721Holder_init();
        __Ownable_init();
        __ReentrancyGuard_init();
        BattleflyContract = IBattlefly(battleflyContractAddress);
        MagicToken = IERC20Upgradeable(magicTokenAddress);
        MagicAmountPerBattlefly = _MagicAmountPerBattlefly;
        BattleflyGame = IBattleflyGame(batteflyGameContractAddress);
    }

    // ADMIN
    function setAdminAccess(address user, bool access) external onlyOwner {
        adminAccess[user] = access;
        emit SetAdminAccess(user, access);
    }

    //USER
    function stakingBattlefliesOfOwner(address user) external view returns (uint256[] memory) {
        return StakingBattlefliesOfOwner[user].values();
    }

    function bulkStakeBattlefly(uint256[] memory tokenIds) external nonReentrant {
        for (uint256 i = 0; i < tokenIds.length; i++) {
            uint256 tokenId = tokenIds[i];
            OwnerOfStakingBattlefly[tokenId] = _msgSender();
            MagicAmountOfStakingBattlefly[tokenId] = MagicAmountPerBattlefly;
            StakingBattlefliesOfOwner[_msgSender()].add(tokenId);
            BattleflyContract.safeTransferFrom(_msgSender(), address(this), tokenId);
        }
        uint256 totalMagicAmount = MagicAmountPerBattlefly.mul(tokenIds.length);
        MagicToken.safeTransferFrom(_msgSender(), address(this), totalMagicAmount);
        emit BulkStakeBattlefly(tokenIds, _msgSender(), totalMagicAmount);
    }

    function bulkUnstakeBattlefly(
        uint256[] memory tokenIds,
        uint256[] memory battleflyStages,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external nonReentrant {
        uint256 totalMagicAmount = 0;
        uint256 totalNectar = 0;
        address receiver = _msgSender();
        bytes32 payloadHash = keccak256(abi.encodePacked(tokenIds, battleflyStages));
        bytes32 messageHash = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", payloadHash));
        (address admin, ECDSAUpgradeable.RecoverError result) = ECDSAUpgradeable.tryRecover(messageHash, v, r, s);
        require(result == ECDSAUpgradeable.RecoverError.NoError && adminAccess[admin], "Require admin access");
        for (uint256 i = 0; i < tokenIds.length; i++) {
            uint256 tokenId = tokenIds[i];
            require(OwnerOfStakingBattlefly[tokenId] == _msgSender(), "Require Staking Battlefly owner access");
            OwnerOfStakingBattlefly[tokenId] = address(0);
            totalMagicAmount = totalMagicAmount.add(MagicAmountOfStakingBattlefly[tokenId]);
            MagicAmountOfStakingBattlefly[tokenId] = 0;
            StakingBattlefliesOfOwner[_msgSender()].remove(tokenId);
            if (battleflyStages[i] == BATTLEFLY_STAGE && NectarClaimed[tokenId] == false) {
                NectarClaimed[tokenId] = true;
                totalNectar = totalNectar.add(10);
            }
            BattleflyContract.safeTransferFrom(address(this), receiver, tokenId);
        }
        if (totalMagicAmount != 0) MagicToken.safeTransfer(receiver, totalMagicAmount);
        if (totalNectar != 0) BattleflyGame.mintItems(NECTAR_ID, receiver, totalNectar);
        emit BulkUnstakeBattlefly(tokenIds, receiver, totalMagicAmount);
    }

    /**
     * @dev Returns the number of staking tokens in ``owner``'s account.
     */
    function balanceOf(address owner) public view returns (uint256) {
        require(owner != address(0), "ERC721: balance query for the zero address");
        return StakingBattlefliesOfOwner[owner].length();
    }

    /**
     * @dev Returns the owner of the `tokenId` staking token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function ownerOf(uint256 tokenId) external view returns (address owner) {
        return OwnerOfStakingBattlefly[tokenId];
    }

    //modifier
    modifier onlyAdminAccess() {
        require(adminAccess[_msgSender()] == true || _msgSender() == owner(), "Require admin access");
        _;
    }
}

File 55 of 122 : SafeMathUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (utils/math/SafeMath.sol)

pragma solidity ^0.8.0;

// CAUTION
// This version of SafeMath should only be used with Solidity 0.8 or later,
// because it relies on the compiler's built in overflow checks.

/**
 * @dev Wrappers over Solidity's arithmetic operations.
 *
 * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler
 * now has built in overflow checking.
 */
library SafeMathUpgradeable {
    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b > a) return (false, 0);
            return (true, a - b);
        }
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
            // benefit is lost if 'b' is also tested.
            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
            if (a == 0) return (true, 0);
            uint256 c = a * b;
            if (c / a != b) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a / b);
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a % b);
        }
    }

    /**
     * @dev Returns the addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     *
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        return a + b;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        return a - b;
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     *
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        return a * b;
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator.
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        return a / b;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        return a % b;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {trySub}.
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(
        uint256 a,
        uint256 b,
        string memory errorMessage
    ) internal pure returns (uint256) {
        unchecked {
            require(b <= a, errorMessage);
            return a - b;
        }
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting with custom message on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(
        uint256 a,
        uint256 b,
        string memory errorMessage
    ) internal pure returns (uint256) {
        unchecked {
            require(b > 0, errorMessage);
            return a / b;
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting with custom message when dividing by zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryMod}.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(
        uint256 a,
        uint256 b,
        string memory errorMessage
    ) internal pure returns (uint256) {
        unchecked {
            require(b > 0, errorMessage);
            return a % b;
        }
    }
}

File 56 of 122 : ECDSAUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (utils/cryptography/ECDSA.sol)

pragma solidity ^0.8.0;

import "../StringsUpgradeable.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 ECDSAUpgradeable {
    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:\n32", 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:\n", StringsUpgradeable.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));
    }
}

File 57 of 122 : IBattleflyGame.sol
//SPDX-License-Identifier: Unlicense
pragma solidity >=0.6.0;

interface IBattleflyGame {
    function mintBattlefly(address receiver, uint256 battleflyType) external returns (uint256);

    function mintSpecialNFT(address receiver, uint256 specialNFTType) external returns (uint256);

    function mintBattleflies(
        address receiver,
        uint256 battleflyType,
        uint256 amount
    ) external returns (uint256[] memory);

    function mintSpecialNFTs(
        address receiver,
        uint256 specialNFTType,
        uint256 amount
    ) external returns (uint256[] memory);

    function mintItems(
        uint256 itemId,
        address receiver,
        uint256 amount
    ) external;
}

File 58 of 122 : FounderGenesisV2Sale.sol
// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.2;

import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/math/SafeMathUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
import "./interfaces/IBattleflyGame.sol";
import { MerkleProof } from "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol"; // OZ: MerkleProof

contract FounderGenesisV2Sale is OwnableUpgradeable {
    using SafeMathUpgradeable for uint256;
    using AddressUpgradeable for address;

    mapping(address => bool) private adminAccess;

    IBattleflyGame Game;
    uint256 FounderGenesisV2TokenType;
    uint256 public totalSellAmount;
    uint256 public soldAmount;
    uint256 public timeStart;
    uint256 public price;
    address public fundAddress;

    uint256 public discountPrice;
    uint256 public discountSoldAmount;
    uint256 public discountTimeStart;
    bytes32 public merkleRoot;
    mapping(bytes32 => bool) public ticketUsed;
    mapping(address => uint256) public mintedOfUser;

    event MintFounderGenesisV2(
        address indexed to,
        uint256 price,
        uint256 indexed specialNFTType,
        uint256 tokenId,
        address fundAddress,
        bytes32 indexed ticket
    );

    function initialize(address battleflyGameContractAddress) public initializer {
        __Ownable_init();
        Game = IBattleflyGame(battleflyGameContractAddress);
        FounderGenesisV2TokenType = 151;
        totalSellAmount = 0;
    }

    function setAdminAccess(address user, bool access) external onlyOwner {
        adminAccess[user] = access;
    }

    function setMerkleRoot(bytes32 _merkleRoot) external onlyAdminAccess {
        merkleRoot = _merkleRoot;
    }

    function mintFounderGenesisV2WithDiscount(
        uint256 amount,
        uint256 allocation,
        bytes32[] calldata proof
    ) external payable {
        address to = _msgSender();
        require(discountSoldAmount + soldAmount + amount <= totalSellAmount, "Sold out");
        require(msg.value == discountPrice.mul(amount), "Not enough ETH");
        require(block.timestamp >= discountTimeStart, "Not time yet");
        require(amount + mintedOfUser[to] <= allocation, "Not enough allocation");
        bytes32 leaf = keccak256(abi.encodePacked(to, allocation));
        bool isValidLeaf = MerkleProof.verify(proof, merkleRoot, leaf);
        require(isValidLeaf, "Not in merkle");
        mintedOfUser[to] += amount;
        discountSoldAmount += amount;
        uint256[] memory tokenIds = Game.mintSpecialNFTs(to, FounderGenesisV2TokenType, amount);
        if (fundAddress != address(0)) {
            (bool success, ) = payable(fundAddress).call{ value: address(this).balance }("");
            require(success, "Failed to send Ether");
        }
        for (uint256 i = 0; i < amount; i++) {
            emit MintFounderGenesisV2(to, discountPrice, FounderGenesisV2TokenType, tokenIds[i], fundAddress, "");
        }
    }

    function setDiscountSaleInfo(uint256 _timeStart, uint256 _price) external onlyAdminAccess {
        discountTimeStart = _timeStart;
        discountPrice = _price;
    }

    function setSaleInfo(
        uint256 _totalSellAmount,
        uint256 _timeStart,
        uint256 _price
    ) external onlyAdminAccess {
        totalSellAmount = _totalSellAmount;
        timeStart = _timeStart;
        price = _price;
    }

    // function setfundAddress(address _fundAddress) external onlyAdminAccess {
    //     fundAddress = _fundAddress;
    // }
    function mintFounderGenesisV2(uint256 amount) external payable {
        address to = _msgSender();
        require(discountSoldAmount + soldAmount + amount <= totalSellAmount, "Sold out");
        require(msg.value == price.mul(amount), "Not enough ETH");
        require(block.timestamp >= timeStart, "Not time yet");
        soldAmount += amount;
        uint256[] memory tokenIds = Game.mintSpecialNFTs(to, FounderGenesisV2TokenType, amount);
        if (fundAddress != address(0)) {
            (bool success, ) = payable(fundAddress).call{ value: address(this).balance }("");
            require(success, "Failed to send Ether");
        }
        for (uint256 i = 0; i < amount; i++) {
            emit MintFounderGenesisV2(to, price, FounderGenesisV2TokenType, tokenIds[i], fundAddress, "");
        }
    }

    // function withdraw(uint256 amount) external onlyAdminAccess {
    //     require(amount <= address(this).balance, "Not enough balance");
    //     (bool success, ) = payable(fundAddress).call{value: amount}("");
    //     require(success, "Failed to send Ether");
    // }
    modifier onlyAdminAccess() {
        require(adminAccess[_msgSender()] == true || _msgSender() == owner(), "Require admin access");
        _;
    }
}

File 59 of 122 : MerkleProof.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (utils/cryptography/MerkleProof.sol)

pragma solidity ^0.8.0;

/**
 * @dev These functions deal with verification of Merkle Trees proofs.
 *
 * The proofs can be generated using the JavaScript library
 * https://github.com/miguelmota/merkletreejs[merkletreejs].
 * Note: the hashing algorithm should be keccak256 and pair sorting should be enabled.
 *
 * See `test/utils/cryptography/MerkleProof.test.js` for some examples.
 *
 * WARNING: You should avoid using leaf values that are 64 bytes long prior to
 * hashing, or use a hash function other than keccak256 for hashing leaves.
 * This is because the concatenation of a sorted pair of internal nodes in
 * the merkle tree could be reinterpreted as a leaf value.
 */
library MerkleProof {
    /**
     * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
     * defined by `root`. For this, a `proof` must be provided, containing
     * sibling hashes on the branch from the leaf to the root of the tree. Each
     * pair of leaves and each pair of pre-images are assumed to be sorted.
     */
    function verify(
        bytes32[] memory proof,
        bytes32 root,
        bytes32 leaf
    ) internal pure returns (bool) {
        return processProof(proof, leaf) == root;
    }

    /**
     * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
     * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
     * hash matches the root of the tree. When processing the proof, the pairs
     * of leafs & pre-images are assumed to be sorted.
     *
     * _Available since v4.4._
     */
    function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) {
        bytes32 computedHash = leaf;
        for (uint256 i = 0; i < proof.length; i++) {
            bytes32 proofElement = proof[i];
            if (computedHash <= proofElement) {
                // Hash(current computed hash + current element of the proof)
                computedHash = _efficientHash(computedHash, proofElement);
            } else {
                // Hash(current element of the proof + current computed hash)
                computedHash = _efficientHash(proofElement, computedHash);
            }
        }
        return computedHash;
    }

    function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) {
        assembly {
            mstore(0x00, a)
            mstore(0x20, b)
            value := keccak256(0x00, 0x40)
        }
    }
}

File 60 of 122 : BattleflyWhitelist.sol
// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.2;

import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/math/SafeMathUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
import "./interfaces/IBattleflyGame.sol";
import { MerkleProof } from "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol"; // OZ: MerkleProof

contract BattleflyWhitelist is OwnableUpgradeable {
    using SafeMathUpgradeable for uint256;
    using AddressUpgradeable for address;
    mapping(address => mapping(uint256 => uint256)) public hasClaimedSpecialNFT;
    mapping(address => mapping(uint256 => uint256)) public hasClaimedBattlefly;
    bytes32 public merkleRootBattlefly;
    bytes32 public merkleRootSpecialNFT;

    mapping(address => bool) private adminAccess;
    IBattleflyGame Game;
    uint256 StartTime;
    uint256 EndTime;

    event ClaimBattlefly(address indexed to, uint256 amount, uint256 indexed battleflyType);
    event ClaimSpecialNFT(address indexed to, uint256 amount, uint256 indexed specialNFTType);

    function initialize(address battleflyGameContractAddress) public initializer {
        __Ownable_init();
        Game = IBattleflyGame(battleflyGameContractAddress);
    }

    function setAdminAccess(address user, bool access) external onlyOwner {
        adminAccess[user] = access;
    }

    function setMerkleRootBattlefly(bytes32 merkleRoot) external onlyAdminAccess {
        merkleRootBattlefly = merkleRoot;
    }

    function setMerkleRootSpecialNFT(bytes32 merkleRoot) external onlyAdminAccess {
        merkleRootSpecialNFT = merkleRoot;
    }

    function setHasClaimedBattlefly(
        address user,
        uint256 battleflyType,
        uint256 value
    ) external onlyAdminAccess {
        hasClaimedBattlefly[user][battleflyType] = value;
    }

    function setHasClaimedSpecialNFT(
        address user,
        uint256 specialNFTType,
        uint256 value
    ) external onlyAdminAccess {
        hasClaimedSpecialNFT[user][specialNFTType] = value;
    }

    function setMintingTime(uint256 start, uint256 end) external onlyAdminAccess {
        StartTime = start;
        EndTime = end;
    }

    function claimBattlefly(
        uint256 allocatedAmount,
        uint256 mintingAmount,
        uint256 battleflyType,
        bytes32[] calldata proof
    ) external {
        address to = _msgSender();
        if (StartTime != 0) {
            require(block.timestamp >= StartTime, "Not start yet");
        }
        if (EndTime != 0) {
            require(block.timestamp <= EndTime, "Already finished");
        }
        require(hasClaimedBattlefly[to][battleflyType] + mintingAmount <= allocatedAmount, "Not enough allocation");
        bytes32 leaf = keccak256(abi.encodePacked(to, allocatedAmount, battleflyType));
        bool isValidLeaf = MerkleProof.verify(proof, merkleRootBattlefly, leaf);
        require(isValidLeaf, "Not in merkle");

        hasClaimedBattlefly[to][battleflyType] += mintingAmount;

        for (uint256 i = 0; i < mintingAmount; i++) {
            Game.mintBattlefly(to, battleflyType);
        }
        emit ClaimBattlefly(to, mintingAmount, battleflyType);
    }

    function claimSpecialNFT(
        uint256 allocatedAmount,
        uint256 mintingAmount,
        uint256 specialNFTType,
        bytes32[] calldata proof
    ) external {
        address to = _msgSender();
        require(hasClaimedSpecialNFT[to][specialNFTType] + mintingAmount <= allocatedAmount, "Not enough allocation");
        bytes32 leaf = keccak256(abi.encodePacked(to, allocatedAmount, specialNFTType));
        bool isValidLeaf = MerkleProof.verify(proof, merkleRootSpecialNFT, leaf);
        require(isValidLeaf, "Not in merkle");

        hasClaimedSpecialNFT[to][specialNFTType] += mintingAmount;

        for (uint256 i = 0; i < mintingAmount; i++) {
            Game.mintSpecialNFT(to, specialNFTType);
        }
        emit ClaimSpecialNFT(to, mintingAmount, specialNFTType);
    }

    modifier onlyAdminAccess() {
        require(adminAccess[_msgSender()] == true || _msgSender() == owner(), "Require admin access");
        _;
    }
}

File 61 of 122 : MockAtlasStaker.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;

import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC1155/IERC1155Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC1155/utils/ERC1155HolderUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/utils/ERC721HolderUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/structs/EnumerableSetUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";

import "./interfaces/IAtlasMine.sol";
import "./interfaces/IBattleflyAtlasStaker.sol";

contract MockAtlasStaker is
    IBattleflyAtlasStaker,
    Initializable,
    OwnableUpgradeable,
    ERC1155HolderUpgradeable,
    ERC721HolderUpgradeable,
    ReentrancyGuardUpgradeable
{
    using SafeERC20Upgradeable for IERC20Upgradeable;
    using AddressUpgradeable for address;
    using SafeCastUpgradeable for uint256;
    using SafeCastUpgradeable for int256;
    using EnumerableSetUpgradeable for EnumerableSetUpgradeable.UintSet;
    using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet;

    // ============================================ STATE ==============================================

    // ============= Global Immutable State ==============

    /// @notice MAGIC token
    /// @dev functionally immutable
    IERC20Upgradeable public magic;
    /// @notice The IAtlasMine
    /// @dev functionally immutable
    IAtlasMine public mine;

    // ============= Global Staking State ==============
    uint256 public constant ONE = 1e30;

    /// @notice Whether new stakes will get staked on the contract as scheduled. For emergencies
    bool public schedulePaused;
    /// @notice The total amount of staked token
    uint256 public totalStaked;
    /// @notice The total amount of share
    uint256 public totalShare;
    /// @notice All stakes currently active
    Stake[] public stakes;
    /// @notice Deposit ID of last stake. Also tracked in atlas mine
    uint256 public lastDepositId;
    /// @notice Rewards accumulated per share
    uint256 public accRewardsPerShare;

    // ============= Vault Staking State ==============
    mapping(address => bool) public battleflyVaults;

    /// @notice Each vault stake, keyed by vault contract address => deposit ID
    mapping(address => mapping(uint256 => VaultStake)) public vaultStake;
    /// @notice All deposit IDs fro a vault, enumerated
    mapping(address => EnumerableSetUpgradeable.UintSet) private allVaultDepositIds;
    /// @notice The current ID of the vault's last deposited stake
    mapping(address => uint256) public currentId;

    // ============= NFT Boosting State ==============

    /// @notice Holder of treasures and legions
    mapping(uint256 => bool) public legionsStaked;
    mapping(uint256 => uint256) public treasuresStaked;

    // ============= Operator State ==============

    IAtlasMine.Lock[] public allowedLocks;
    /// @notice Fee to contract operator. Only assessed on rewards.
    uint256 public fee;
    /// @notice Amount of fees reserved for withdrawal by the operator.
    uint256 public feeReserve;
    /// @notice Max fee the owner can ever take - 10%
    uint256 public constant MAX_FEE = 1000;
    uint256 public constant FEE_DENOMINATOR = 10000;

    mapping(address => mapping(uint256 => int256)) refundedFeeDebts;
    uint256 accRefundedFeePerShare;
    uint256 totalWhitelistedFeeShare;
    EnumerableSetUpgradeable.AddressSet whitelistedFeeVaults;
    mapping(address => bool) public superAdmins;

    /// @notice deposited but unstaked
    uint256 public unstakedDeposits;
    mapping(IAtlasMine.Lock => uint256) public unstakedDepositsByLock;
    address public constant TREASURY_WALLET = 0xF5411006eEfD66c213d2fd2033a1d340458B7226;
    /// @notice Intra-tx buffer for pending payouts
    uint256 public tokenBuffer;

    // ===========================================
    // ============== Post Upgrade ===============
    // ===========================================

    // ========================================== INITIALIZER ===========================================

    /**
     * @param _magic                The MAGIC token address.
     * @param _mine                 The IAtlasMine contract.
     *                              Maps to a timelock for IAtlasMine deposits.
     */
    function initialize(
        IERC20Upgradeable _magic,
        IAtlasMine _mine,
        IAtlasMine.Lock[] memory _allowedLocks
    ) external initializer {
        __ERC1155Holder_init();
        __ERC721Holder_init();
        __Ownable_init();
        __ReentrancyGuard_init();

        magic = _magic;
        mine = _mine;
        allowedLocks = _allowedLocks;
        fee = 1000;
        // Approve the mine
        magic.safeApprove(address(mine), 2**256 - 1);
        // approveNFTs();
    }

    // ======================================== VAULT OPERATIONS ========================================

    /**
     * @notice Make a new deposit into the Staker. The Staker will collect
     *         the tokens, to be later staked in atlas mine by the owner,
     *         according to the stake/unlock schedule.
     * @dev    Specified amount of token must be approved by the caller.
     *
     * @param _amount               The amount of tokens to deposit.
     */
    function deposit(uint256 _amount, IAtlasMine.Lock lock)
        public
        virtual
        override
        onlyBattleflyVaultOrOwner
        nonReentrant
        returns (uint256)
    {
        require(!schedulePaused, "new staking paused");
        _updateRewards();
        // Collect tokens
        uint256 newDepositId = _deposit(_amount, msg.sender, lock);
        magic.safeTransferFrom(msg.sender, address(this), _amount);
        return (newDepositId);
    }

    function _deposit(
        uint256 _amount,
        address _vault,
        IAtlasMine.Lock lock
    ) internal returns (uint256) {
        require(_amount > 0, "Deposit amount 0");
        bool validLock = false;
        for (uint256 i = 0; i < allowedLocks.length; i++) {
            if (allowedLocks[i] == lock) {
                validLock = true;
                break;
            }
        }
        require(validLock, "Lock time not allowed");
        // Add vault stake
        uint256 newDepositId = ++currentId[_vault];
        allVaultDepositIds[_vault].add(newDepositId);
        VaultStake storage s = vaultStake[_vault][newDepositId];

        s.amount = _amount;
        (uint256 boost, uint256 lockTime) = getLockBoost(lock);
        uint256 share = (_amount * (100e16 + boost)) / 100e16;

        uint256 vestingTime = mine.getVestingTime(lock);
        s.unlockAt = block.timestamp + lockTime + vestingTime + 1 days;
        s.rewardDebt = ((share * accRewardsPerShare) / ONE).toInt256();
        s.lock = lock;

        // Update global accounting
        totalStaked += _amount;
        totalShare += share;
        if (whitelistedFeeVaults.contains(_vault)) {
            totalWhitelistedFeeShare += share;
            refundedFeeDebts[_vault][newDepositId] = ((share * accRefundedFeePerShare) / ONE).toInt256();
        }
        // MAGIC tokens sit in contract. Added to pending stakes
        unstakedDeposits += _amount;
        unstakedDepositsByLock[lock] += _amount;
        emit VaultDeposit(_vault, newDepositId, _amount, s.unlockAt, s.lock);
        return newDepositId;
    }

    /**
     * @notice Withdraw a deposit from the Staker contract. Calculates
     *         pro rata share of accumulated MAGIC and distributes any
     *         earned rewards in addition to original deposit.
     *         There must be enough unlocked tokens to withdraw.
     *
     * @param depositId             The ID of the deposit to withdraw from.
     *
     */
    function withdraw(uint256 depositId) public virtual override onlyBattleflyVaultOrOwner nonReentrant {
        // Distribute tokens
        _updateRewards();
        VaultStake storage s = vaultStake[msg.sender][depositId];
        require(s.amount > 0, "No deposit");
        require(block.timestamp >= s.unlockAt, "Deposit locked");

        uint256 payout = _withdraw(s, depositId);
        magic.safeTransfer(msg.sender, payout);
    }

    /**
     * @notice Withdraw all eligible deposits from the staker contract.
     *         Will skip any deposits not yet unlocked. Will also
     *         distribute rewards for all stakes via 'withdraw'.
     *
     */
    function withdrawAll() public virtual override onlyBattleflyVaultOrOwner nonReentrant {
        // Distribute tokens
        _updateRewards();
        uint256[] memory depositIds = allVaultDepositIds[msg.sender].values();
        for (uint256 i = 0; i < depositIds.length; i++) {
            VaultStake storage s = vaultStake[msg.sender][depositIds[i]];

            if (s.amount > 0 && s.unlockAt > 0 && s.unlockAt <= block.timestamp) {
                tokenBuffer += _withdraw(s, depositIds[i]);
            }
        }
        magic.safeTransfer(msg.sender, tokenBuffer);
        tokenBuffer = 0;
    }

    /**
     * @dev Logic for withdrawing a deposit. Calculates pro rata share of
     *      accumulated MAGIC and dsitributed any earned rewards in addition
     *      to original deposit.
     *
     * @dev An _amount argument larger than the total deposit amount will
     *      withdraw the entire deposit.
     *
     * @param s                     The VaultStake struct to withdraw from.
     * @param depositId             The ID of the deposit to withdraw from (for event).
     */
    function _withdraw(VaultStake storage s, uint256 depositId) internal returns (uint256 payout) {
        uint256 _amount = s.amount;

        // Unstake if we need to to ensure we can withdraw
        (uint256 boost, ) = getLockBoost(s.lock);
        uint256 share = (_amount * (100e16 + boost)) / 100e16;
        int256 accumulatedRewards = ((share * accRewardsPerShare) / ONE).toInt256();
        if (whitelistedFeeVaults.contains(msg.sender)) {
            accumulatedRewards += ((share * accRefundedFeePerShare) / ONE).toInt256();
            accumulatedRewards -= refundedFeeDebts[msg.sender][depositId];
            totalWhitelistedFeeShare -= share;
            refundedFeeDebts[msg.sender][depositId] = 0;
        }
        uint256 reward = (accumulatedRewards - s.rewardDebt).toUint256();
        payout = _amount + reward;

        // // Update vault accounting
        // s.amount -= _amount;
        // s.rewardDebt = 0;
        ///comment Archethect: Consider deleting the VaultStake object for gas optimization. s.unlockAt and s.lock can be zeroed as well.
        delete vaultStake[msg.sender][depositId];

        // Update global accounting
        totalStaked -= _amount;

        totalShare -= share;

        // If we need to unstake, unstake until we have enough
        if (payout > _totalUsableMagic()) {
            _unstakeToTarget(payout - _totalUsableMagic());
        }
        emit VaultWithdraw(msg.sender, depositId, _amount, reward);
    }

    /**
     * @notice Claim rewards without unstaking. Will fail if there
     *         are not enough tokens in the contract to claim rewards.
     *         Does not attempt to unstake.
     *
     * @param depositId             The ID of the deposit to claim rewards from.
     *
     */
    function claim(uint256 depositId) public virtual override onlyBattleflyVaultOrOwner nonReentrant returns (uint256) {
        _updateRewards();
        VaultStake storage s = vaultStake[msg.sender][depositId];
        require(s.amount > 0, "No deposit");
        uint256 reward = _claim(s, depositId);
        magic.safeTransfer(msg.sender, reward);
        return reward;
    }

    /**
     * @notice Claim all possible rewards from the staker contract.
     *         Will apply to both locked and unlocked deposits.
     *
     */
    function claimAll() public virtual override onlyBattleflyVaultOrOwner nonReentrant returns (uint256) {
        return 0;
    }

    /**
     * @notice Claim all possible rewards from the staker contract then restake.
     *         Will apply to both locked and unlocked deposits.
     *
     */
    function claimAllAndRestake(IAtlasMine.Lock lock) public onlyBattleflyVaultOrOwner nonReentrant returns (uint256) {
        _updateRewards();
        uint256[] memory depositIds = allVaultDepositIds[msg.sender].values();
        uint256 totalReward = 0;
        for (uint256 i = 0; i < depositIds.length; i++) {
            VaultStake storage s = vaultStake[msg.sender][depositIds[i]];
            uint256 reward = _claim(s, depositIds[i]);
            tokenBuffer += reward;
        }
        _deposit(tokenBuffer, msg.sender, lock);
        tokenBuffer = 0;
        return totalReward;
    }

    /**
     * @dev Logic for claiming rewards on a deposit. Calculates pro rata share of
     *      accumulated MAGIC and dsitributed any earned rewards in addition
     *      to original deposit.
     *
     * @param s                     The VaultStake struct to claim from.
     * @param depositId             The ID of the deposit to claim from (for event).
     */
    function _claim(VaultStake storage s, uint256 depositId) internal returns (uint256) {
        // Update accounting
        (uint256 boost, ) = getLockBoost(s.lock);
        uint256 share = (s.amount * (100e16 + boost)) / 100e16;

        int256 accumulatedRewards = ((share * accRewardsPerShare) / ONE).toInt256();

        uint256 reward = (accumulatedRewards - s.rewardDebt).toUint256();
        if (whitelistedFeeVaults.contains(msg.sender)) {
            int256 accumulatedRefundedFee = ((share * accRefundedFeePerShare) / ONE).toInt256();
            reward += accumulatedRefundedFee.toUint256();
            reward -= refundedFeeDebts[msg.sender][depositId].toUint256();
            refundedFeeDebts[msg.sender][depositId] = accumulatedRefundedFee;
        }
        s.rewardDebt = accumulatedRewards;

        // Unstake if we need to to ensure we can withdraw
        if (reward > _totalUsableMagic()) {
            _unstakeToTarget(reward - _totalUsableMagic());
        }

        require(reward <= _totalUsableMagic(), "Not enough rewards to claim");
        emit VaultClaim(msg.sender, depositId, reward);
        return reward;
    }

    // ======================================= SUPER ADMIN OPERATIONS ========================================

    /**
     * @notice Stake a Treasure owned by the superAdmin into the Atlas Mine.
     *         Staked treasures will boost all vault deposits.
     * @dev    Any treasure must be approved for withdrawal by the caller.
     *
     * @param _tokenId              The tokenId of the specified treasure.
     * @param _amount               The amount of treasures to stake.
     */
    function stakeTreasure(uint256 _tokenId, uint256 _amount) external onlySuperAdminOrOwner {
        address treasureAddr = mine.treasure();
        require(IERC1155Upgradeable(treasureAddr).balanceOf(msg.sender, _tokenId) >= _amount, "Not enough treasures");
        treasuresStaked[_tokenId] += _amount;
        // First withdraw and approve
        IERC1155Upgradeable(treasureAddr).safeTransferFrom(msg.sender, address(this), _tokenId, _amount, bytes(""));
        mine.stakeTreasure(_tokenId, _amount);
        uint256 boost = mine.boosts(address(this));

        emit StakeNFT(msg.sender, treasureAddr, _tokenId, _amount, boost);
    }

    /**
     * @notice Unstake a Treasure from the Atlas Mine adn transfer to receiver.
     *
     * @param _receiver              The receiver .
     * @param _tokenId              The tokenId of the specified treasure.
     * @param _amount               The amount of treasures to stake.
     */
    function unstakeTreasure(
        address _receiver,
        uint256 _tokenId,
        uint256 _amount
    ) external onlySuperAdminOrOwner {
        require(treasuresStaked[_tokenId] >= _amount, "Not enough treasures");
        treasuresStaked[_tokenId] -= _amount;
        address treasureAddr = mine.treasure();
        mine.unstakeTreasure(_tokenId, _amount);
        IERC1155Upgradeable(treasureAddr).safeTransferFrom(address(this), _receiver, _tokenId, _amount, bytes(""));
        uint256 boost = mine.boosts(address(this));
        emit UnstakeNFT(_receiver, treasureAddr, _tokenId, _amount, boost);
    }

    /**
     * @notice Stake a Legion owned by the superAdmin into the Atlas Mine.
     *         Staked legions will boost all vault deposits.
     * @dev    Any legion be approved for withdrawal by the caller.
     *
     * @param _tokenId              The tokenId of the specified legion.
     */
    function stakeLegion(uint256 _tokenId) external onlySuperAdminOrOwner {
        address legionAddr = mine.legion();
        require(IERC721Upgradeable(legionAddr).ownerOf(_tokenId) == msg.sender, "Not owner of legion");
        legionsStaked[_tokenId] = true;
        IERC721Upgradeable(legionAddr).safeTransferFrom(msg.sender, address(this), _tokenId);

        mine.stakeLegion(_tokenId);

        uint256 boost = mine.boosts(address(this));

        emit StakeNFT(msg.sender, legionAddr, _tokenId, 1, boost);
    }

    /**
     * @notice Unstake a Legion from the Atlas Mine and return it to the superAdmin.
     *
     * @param _tokenId              The tokenId of the specified legion.
     */
    function unstakeLegion(address _receiver, uint256 _tokenId) external onlySuperAdminOrOwner {
        require(legionsStaked[_tokenId], "No legion");
        address legionAddr = mine.legion();
        delete legionsStaked[_tokenId];
        mine.unstakeLegion(_tokenId);

        // Distribute to superAdmin
        IERC721Upgradeable(legionAddr).safeTransferFrom(address(this), _receiver, _tokenId);
        uint256 boost = mine.boosts(address(this));

        emit UnstakeNFT(_receiver, legionAddr, _tokenId, 1, boost);
    }

    /**
     * @notice Stake any pending stakes before the current day. Callable
     *         by anybody. Any pending stakes will unlock according
     *         to the time this method is called, and the contract's defined
     *         lock time.
     */
    function stakeScheduled() external virtual override onlySuperAdminOrOwner {
        for (uint256 i = 0; i < allowedLocks.length; i++) {
            IAtlasMine.Lock lock = allowedLocks[i];
            _stakeInMine(unstakedDepositsByLock[lock], lock);
            unstakedDepositsByLock[lock] = 0;
        }
        unstakedDeposits = 0;
    }

    /**
     * @notice Unstake everything eligible for unstaking from Atlas Mine.
     *         Callable by owner. Should only be used in case of emergency
     *         or migration to a new contract, or if there is a need to service
     *         an unexpectedly large amount of withdrawals.
     *
     *         If unlockAll is set to true in the Atlas Mine, this can withdraw
     *         all stake.
     */
    function unstakeAllFromMine() external override onlySuperAdminOrOwner {
        // Unstake everything eligible
        _updateRewards();

        for (uint256 i = 0; i < stakes.length; i++) {
            Stake memory s = stakes[i];

            if (s.unlockAt > block.timestamp) {
                continue;
            }

            // Withdraw position - auto-harvest
            mine.withdrawPosition(s.depositId, s.amount);
        }

        // Only check for removal after, so we don't mutate while looping
        _removeZeroStakes();
    }

    /**
     * @notice Let owner unstake a specified amount as needed to make sure the contract is funded.
     *         Can be used to facilitate expected future withdrawals.
     *
     * @param target                The amount of tokens to reclaim from the mine.
     */
    function unstakeToTarget(uint256 target) external override onlySuperAdminOrOwner {
        _updateRewards();
        _unstakeToTarget(target);
    }

    /**
     * @notice Withdraw any accumulated reward fees to the treasury
     */
    function withdrawFeesToTreasury() external virtual onlySuperAdminOrOwner {
        uint256 amount = feeReserve;
        feeReserve = 0;
        magic.safeTransfer(TREASURY_WALLET, amount);
        emit WithdrawFeesToTreasury(amount);
    }

    function stakeBackFeeTreasury(IAtlasMine.Lock lock) external virtual onlySuperAdminOrOwner {
        uint256 amount = feeReserve;
        feeReserve = 0;
        emit WithdrawFeesToTreasury(amount);
        // magic.safeTransfer(TREASURY_WALLET, amount);
        _deposit(amount, TREASURY_WALLET, lock);
    }

    /**
     * @notice Whitelist vault from fees.
     *
     * @param _vault                Vault address.
     * @param isSet                 Whether to enable or disable the vault whitelist.
     */
    function setFeeWhitelistVault(address _vault, bool isSet) external onlyOwner {
        require(_vault != address(0), "Invalid Vault");
        if (isSet) {
            whitelistedFeeVaults.add(_vault);
            totalWhitelistedFeeShare += totalShareOf(_vault);
        } else {
            whitelistedFeeVaults.remove(_vault);
            totalWhitelistedFeeShare -= totalShareOf(_vault);
        }
        emit SetFeeWhitelistVault(_vault, isSet);
    }

    // ======================================= OWNER OPERATIONS =======================================

    function setBattleflyVault(address _vaultAddress, bool isSet) external onlyOwner {
        require(_vaultAddress != address(0), "Invalid vault");
        if (isSet) {
            require(battleflyVaults[_vaultAddress] == false, "Vault already set");
            battleflyVaults[_vaultAddress] = isSet;
        } else {
            require(allVaultDepositIds[_vaultAddress].length() == 0, "Vault is still active");
            delete battleflyVaults[_vaultAddress];
        }
        emit SetBattleflyVault(_vaultAddress, isSet);
    }

    /**
     * @notice Change the designated superAdmin, the address where treasures and
     *         legions are held. Staked NFTs can only be
     *         withdrawn to the current superAdmin address, regardless of which
     *         address the superAdmin was set to when it was staked.
     *
     * @param _superAdmin                The new superAdmin address.
     * @param isSet                 Whether to enable or disable the superAdmin address.
     */
    function setBoostAdmin(address _superAdmin, bool isSet) external override onlyOwner {
        require(_superAdmin != address(0), "Invalid superAdmin");

        superAdmins[_superAdmin] = isSet;
    }

    /**
     * @notice Change the designated super admin, who manage the fee reverse
     *
     * @param _superAdmin                The new superAdmin address.
     * @param isSet                 Whether to enable or disable the super admin address.
     */
    function setSuperAdmin(address _superAdmin, bool isSet) external onlyOwner {
        require(_superAdmin != address(0), "Invalid address");
        superAdmins[_superAdmin] = isSet;
    }

    /**
     * @notice Approve treasures and legions for withdrawal from the atlas mine.
     *         Called on startup, and should be called again in case contract
     *         addresses for treasures and legions ever change.
     *
     */
    function approveNFTs() public override onlyOwner {
        address treasureAddr = mine.treasure();
        IERC1155Upgradeable(treasureAddr).setApprovalForAll(address(mine), true);

        address legionAddr = mine.legion();
        IERC1155Upgradeable(legionAddr).setApprovalForAll(address(mine), true);
    }

    /**
     * @notice EMERGENCY ONLY - toggle pausing new scheduled stakes.
     *         If on, vaults can deposit, but stakes won't go to Atlas Mine.
     *         Can be used in case of Atlas Mine issues or forced migration
     *         to new contract.
     */
    function toggleSchedulePause(bool paused) external virtual override onlyOwner {
        schedulePaused = paused;

        emit StakingPauseToggle(paused);
    }

    // ======================================== VIEW FUNCTIONS =========================================
    function getLockBoost(IAtlasMine.Lock _lock) public pure virtual returns (uint256 boost, uint256 timelock) {
        if (_lock == IAtlasMine.Lock.twoWeeks) {
            // 10%
            return (10e16, 14 days);
        } else if (_lock == IAtlasMine.Lock.oneMonth) {
            // 25%
            return (25e16, 30 days);
        } else if (_lock == IAtlasMine.Lock.threeMonths) {
            // 80%
            return (80e16, 3 * 30 days);
        } else if (_lock == IAtlasMine.Lock.sixMonths) {
            // 180%
            return (180e16, 6 * 30 days);
        } else if (_lock == IAtlasMine.Lock.twelveMonths) {
            // 400%
            return (400e16, 365 days);
        } else {
            revert("Invalid lock value");
        }
    }

    /**
     * @notice Returns all magic either unstaked, staked, or pending rewards in Atlas Mine.
     *         Best proxy for TVL.
     *
     * @return total               The total amount of MAGIC in the staker.
     */
    function totalMagic() external view override returns (uint256) {
        return _totalControlledMagic() + mine.pendingRewardsAll(address(this));
    }

    /**
     * @notice Returns all magic that has been deposited, but not staked, and is eligible
     *         to be staked (deposit time < current day).
     *
     * @return total               The total amount of MAGIC available to stake.
     */
    // removed, will read the unstakedDeposits directly
    // function totalPendingStake() external view override returns (uint256) {
    //     return unstakedDeposits;
    // }

    /**
     * @notice Returns all magic that has been deposited, but not staked, and is eligible
     *         to be staked (deposit time < current day).
     *
     * @return total               The total amount of MAGIC that can be withdrawn.
     */
    function totalWithdrawableMagic() external view override returns (uint256) {
        uint256 totalPendingRewards;

        // IAtlasMine attempts to divide by 0 if there are no deposits
        try mine.pendingRewardsAll(address(this)) returns (uint256 _pending) {
            totalPendingRewards = _pending;
        } catch Panic(uint256) {
            totalPendingRewards = 0;
        }

        // uint256 vestedPrincipal;
        // for (uint256 i = 0; i < stakes.length; i++) {
        //     vestedPrincipal += mine.calcualteVestedPrincipal(address(this), stakes[i].depositId);
        // }

        return _totalUsableMagic() + totalPendingRewards;
    }

    /**
     * @notice Returns the details of a vault stake.
     *
     * @return vaultStake           The details of a vault stake.
     */
    function getVaultStake(address vault, uint256 depositId) external view override returns (VaultStake memory) {
        return vaultStake[vault][depositId];
    }

    /**
     * @notice Returns the total amount staked by a vault.
     *
     * @return totalStake           The total amount of MAGIC staked by a vault.
     */
    // we will read it from the subgraph
    // function vaultTotalStake(address vault) external view override returns (uint256 totalStake) {
    //     uint256[] memory depositIds = allVaultDepositIds[vault].values();
    //     for (uint256 i = 0; i < depositIds.length; i++) {
    //         VaultStake storage s = vaultStake[vault][depositIds[i]];
    //         totalStake += s.amount;
    //     }
    // }

    /**
     * @notice Returns the pending, claimable rewards for a deposit.
     * @dev    This does not update rewards, so out of date if rewards not recently updated.
     *         Needed to maintain 'view' function type.
     *
     * @param vault              The vault to check rewards for.
     * @param depositId         The specific deposit to check rewards for.
     *
     * @return reward           The total amount of MAGIC reward pending.
     */
    function pendingRewards(address vault, uint256 depositId) public view override returns (uint256 reward) {
        if (totalShare == 0) {
            return 0;
        }
        VaultStake storage s = vaultStake[vault][depositId];
        (uint256 boost, ) = getLockBoost(s.lock);
        uint256 share = (s.amount * (100e16 + boost)) / 100e16;

        uint256 unupdatedReward = mine.pendingRewardsAll(address(this));
        (uint256 founderReward, , uint256 feeRefund) = _calculateHarvestRewardFee(unupdatedReward);
        uint256 realAccRewardsPerShare = accRewardsPerShare + (founderReward * ONE) / totalShare;
        uint256 accumulatedRewards = (share * realAccRewardsPerShare) / ONE;
        if (whitelistedFeeVaults.contains(vault) && totalWhitelistedFeeShare > 0) {
            uint256 realAccRefundedFeePerShare = accRefundedFeePerShare + (feeRefund * ONE) / totalWhitelistedFeeShare;
            uint256 accumulatedRefundedFee = (share * realAccRefundedFeePerShare) / ONE;
            accumulatedRewards = accumulatedRewards + accumulatedRefundedFee;
            accumulatedRewards -= refundedFeeDebts[vault][depositId].toUint256();
        }
        reward = accumulatedRewards - s.rewardDebt.toUint256();
    }

    /**
     * @notice Returns the pending, claimable rewards for all of a vault's deposits.
     * @dev    This does not update rewards, so out of date if rewards not recently updated.
     *         Needed to maintain 'view' function type.
     *
     * @param vault              The vault to check rewards for.
     *
     * @return reward           The total amount of MAGIC reward pending.
     */
    function pendingRewardsAll(address vault) external view override returns (uint256 reward) {
        uint256[] memory depositIds = allVaultDepositIds[vault].values();

        for (uint256 i = 0; i < depositIds.length; i++) {
            reward += pendingRewards(vault, depositIds[i]);
        }
    }

    /**
     * @notice Returns the total Share of a vault.
     *
     * @param vault              The vault to check rewards for.
     *
     * @return _totalShare           The total share of a vault.
     */
    function totalShareOf(address vault) public view returns (uint256 _totalShare) {
        uint256[] memory depositIds = allVaultDepositIds[vault].values();
        for (uint256 i = 0; i < depositIds.length; i++) {
            (uint256 boost, ) = getLockBoost(vaultStake[vault][depositIds[i]].lock);
            uint256 share = (vaultStake[vault][depositIds[i]].amount * (100e16 + boost)) / 100e16;
            _totalShare += share;
        }
    }

    // ============================================ HELPERS ============================================

    /**
     * @dev Stake tokens held by staker in the Atlas Mine, according to
     *      the predefined lock value. Schedules for staking will be managed by a queue.
     *
     * @param _amount               Number of tokens to stake
     */
    function _stakeInMine(uint256 _amount, IAtlasMine.Lock lock) internal {
        require(_amount <= _totalUsableMagic(), "Not enough funds");

        uint256 depositId = ++lastDepositId;
        (, uint256 lockTime) = getLockBoost(lock);
        uint256 vestingPeriod = mine.getVestingTime(lock);
        uint256 unlockAt = block.timestamp + lockTime + vestingPeriod;

        stakes.push(Stake({ amount: _amount, unlockAt: unlockAt, depositId: depositId }));

        mine.deposit(_amount, lock);
    }

    /**
     * @dev Unstakes until we have enough unstaked tokens to meet a specific target.
     *      Used to make sure we can service withdrawals.
     *
     * @param target                The amount of tokens we want to have unstaked.
     */
    function _unstakeToTarget(uint256 target) internal {
        uint256 unstaked = 0;

        for (uint256 i = 0; i < stakes.length; i++) {
            Stake memory s = stakes[i];

            if (s.unlockAt > block.timestamp && !mine.unlockAll()) {
                // This stake is not unlocked - stop looking
                continue;
            }

            // Withdraw position - auto-harvest
            uint256 preclaimBalance = _totalUsableMagic();
            uint256 targetLeft = target - unstaked;
            uint256 amount = targetLeft > s.amount ? s.amount : targetLeft;

            // Do not harvest rewards - if this is running, we've already
            // harvested in the same fn call
            mine.withdrawPosition(s.depositId, amount);
            uint256 postclaimBalance = _totalUsableMagic();

            // Increment amount unstaked
            unstaked += postclaimBalance - preclaimBalance;

            if (unstaked >= target) {
                // We unstaked enough
                break;
            }
        }

        require(unstaked >= target, "Cannot unstake enough");
        require(_totalUsableMagic() >= target, "Not enough in contract after unstaking");

        // Only check for removal after, so we don't mutate while looping
        _removeZeroStakes();
    }

    /**
     * @dev Harvest rewards from the IAtlasMine and send them back to
     *      this contract.
     *
     * @return earned               The amount of rewards earned for depositors, minus the fee.
     * @return feeEearned           The amount of fees earned for the contract operator.
     */
    function _harvestMine() internal returns (uint256, uint256) {
        uint256 preclaimBalance = magic.balanceOf(address(this));

        try mine.harvestAll() {
            uint256 postclaimBalance = magic.balanceOf(address(this));

            uint256 earned = postclaimBalance - preclaimBalance;
            // Reserve the 'fee' amount of what is earned
            (, uint256 feeEarned, uint256 feeRefunded) = _calculateHarvestRewardFee(earned);
            feeReserve += feeEarned - feeRefunded;
            emit MineHarvest(earned - feeEarned, feeEarned - feeRefunded, feeRefunded);
            return (earned - feeEarned, feeRefunded);
        } catch {
            // Failed because of reward debt calculation - should be 0
            return (0, 0);
        }
    }

    function _calculateHarvestRewardFee(uint256 earned)
        internal
        view
        returns (
            uint256,
            uint256,
            uint256
        )
    {
        uint256 feeEarned = (earned * fee) / FEE_DENOMINATOR;
        uint256 accFeePerShare = (feeEarned * ONE) / totalShare;
        uint256 feeRefunded = (accFeePerShare * totalWhitelistedFeeShare) / ONE;
        return (earned - feeEarned, feeEarned, feeRefunded);
    }

    /**
     * @dev Harvest rewards from the mine so that stakers can claim.
     *      Recalculate how many rewards are distributed to each share.
     */
    function _updateRewards() internal {
        if (totalStaked == 0 || totalShare == 0) return;
        (uint256 newRewards, uint256 feeRefunded) = _harvestMine();
        accRewardsPerShare += (newRewards * ONE) / totalShare;
        if (totalWhitelistedFeeShare > 0) accRefundedFeePerShare += (feeRefunded * ONE) / totalWhitelistedFeeShare;
    }

    /**
     * @dev After mutating a stake (by withdrawing fully or partially),
     *      get updated data from the staking contract, and update the stake amounts
     *
     * @param stakeIndex           The index of the stake in the Stakes storage array.
     *
     * @return amount              The current, updated amount of the stake.
     */
    function _updateStakeDepositAmount(uint256 stakeIndex) internal returns (uint256) {
        Stake storage s = stakes[stakeIndex];

        (, uint256 depositAmount, , , , , ) = mine.userInfo(address(this), s.depositId);
        s.amount = depositAmount;

        return s.amount;
    }

    /**
     * @dev Find stakes with zero deposit amount and remove them from tracking.
     *      Uses recursion to stop from mutating an array we are currently looping over.
     *      If a zero stake is found, it is removed, and the function is restarted,
     *      such that it is always working from a 'clean' array.
     *
     */
    function _removeZeroStakes() internal {
        bool shouldRecurse = stakes.length > 0;

        for (uint256 i = 0; i < stakes.length; i++) {
            _updateStakeDepositAmount(i);

            Stake storage s = stakes[i];

            if (s.amount == 0) {
                _removeStake(i);
                // Stop looping and start again - we will skip
                // out of the look and recurse
                break;
            }

            if (i == stakes.length - 1) {
                // We didn't remove anything, so stop recursing
                shouldRecurse = false;
            }
        }

        if (shouldRecurse) {
            _removeZeroStakes();
        }
    }

    /**
     * @dev Calculate total amount of MAGIC usable by the contract.
     *      'Usable' means available for either withdrawal or re-staking.
     *      Counts unstaked magic less fee reserve.
     *
     * @return amount               The amount of usable MAGIC.
     */
    function _totalUsableMagic() internal view returns (uint256) {
        // Current magic held in contract
        uint256 unstaked = magic.balanceOf(address(this));

        return unstaked - tokenBuffer - feeReserve;
    }

    /**
     * @dev Calculate total amount of MAGIC under control of the contract.
     *      Counts staked and unstaked MAGIC. Does _not_ count accumulated
     *      but unclaimed rewards.
     *
     * @return amount               The total amount of MAGIC under control of the contract.
     */
    function _totalControlledMagic() internal view returns (uint256) {
        // Current magic staked in mine
        uint256 staked = 0;

        for (uint256 i = 0; i < stakes.length; i++) {
            staked += stakes[i].amount;
        }

        return staked + _totalUsableMagic();
    }

    /**
     * @dev Remove a tracked stake from any position in the stakes array.
     *      Used when a stake is no longer relevant i.e. fully withdrawn.
     *      Mutates the Stakes array in storage.
     *
     * @param index                 The index of the stake to remove.
     */
    function _removeStake(uint256 index) internal {
        if (index >= stakes.length) return;

        for (uint256 i = index; i < stakes.length - 1; i++) {
            stakes[i] = stakes[i + 1];
        }

        delete stakes[stakes.length - 1];

        stakes.pop();
    }

    modifier onlySuperAdminOrOwner() {
        require(msg.sender == owner() || superAdmins[msg.sender], "Not Super Admin");
        _;
    }
    modifier onlyBattleflyVaultOrOwner() {
        require(msg.sender == owner() || battleflyVaults[msg.sender], "Not BattleflyVault");
        _;
    }
}

File 62 of 122 : BattleflyVault.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;

import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC1155/IERC1155Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/structs/EnumerableSetUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";

import "./interfaces/IBattleflyAtlasStaker.sol";
import "./interfaces/IAtlasMine.sol";
import "./interfaces/ISpecialNFT.sol";
import "./interfaces/IBattleflyVault.sol";

contract BattleflyVault is Initializable, OwnableUpgradeable, ReentrancyGuardUpgradeable {
    using SafeERC20Upgradeable for IERC20Upgradeable;
    using AddressUpgradeable for address;
    using SafeCastUpgradeable for uint256;
    using SafeCastUpgradeable for int256;
    using EnumerableSetUpgradeable for EnumerableSetUpgradeable.UintSet;

    // ============================================ STATE ==============================================
    struct UserStake {
        uint256 amount;
        uint256 unlockAt;
        uint256 withdrawAt;
        IAtlasMine.Lock lock;
        uint256 battleflyStakerDepositId;
        address owner;
    }
    // ============= Global Immutable State ==============
    IERC20Upgradeable public magic;
    IBattleflyAtlasStaker public BattleflyStaker;
    // ============= Global Staking State ==============
    mapping(uint256 => UserStake) public userStakes;
    mapping(address => EnumerableSetUpgradeable.UintSet) private stakesOfOwner;
    uint256 nextStakeId;
    uint256 public totalStaked;

    // ============= Global Admin ==============
    mapping(address => bool) private adminAccess;
    // ============================================ EVENT ==============================================
    event Claim(address indexed user, uint256 stakeId, uint256 amount);
    event Stake(address indexed user, uint256 stakeId, uint256 amount, IAtlasMine.Lock lock);
    event Withdraw(address indexed user, uint256 stakeId, uint256 amount);
    event SetFee(uint256 oldFee, uint256 newFee, uint256 denominator);
    event WithdrawFee(address receiver, uint256 amount);

    event SetAdminAccess(address indexed user, bool access);

    // ============================================ INITIALIZE ==============================================
    function initialize(address _magicAddress, address _BattleflyStakerAddress) external initializer {
        __Ownable_init();
        __ReentrancyGuard_init();

        magic = IERC20Upgradeable(_magicAddress);
        BattleflyStaker = IBattleflyAtlasStaker(_BattleflyStakerAddress);
        nextStakeId = 0;
        // Approve the AtlasStaker contract to spend the magic
        magic.safeApprove(address(BattleflyStaker), 2**256 - 1);
    }

    // ============================================ USER FUNCTIONS ==============================================
    function stake(uint256 amount, IAtlasMine.Lock lock) external {
        magic.safeTransferFrom(msg.sender, address(this), amount);
        _stake(amount, lock);
    }

    function _stake(uint256 amount, IAtlasMine.Lock lock) internal returns (uint256) {
        require(amount > 0, "Amount must be greater than 0");
        uint256 battleflyStakerDepositId = BattleflyStaker.deposit(amount, lock);
        IBattleflyAtlasStaker.VaultStake memory vaultStake = BattleflyStaker.getVaultStake(
            address(this),
            battleflyStakerDepositId
        );
        UserStake storage s = userStakes[nextStakeId];
        s.amount = amount;
        s.unlockAt = vaultStake.unlockAt;
        s.lock = lock;
        s.battleflyStakerDepositId = battleflyStakerDepositId;
        s.owner = msg.sender;
        stakesOfOwner[msg.sender].add(nextStakeId);
        emit Stake(msg.sender, nextStakeId, amount, lock);
        nextStakeId++;
        totalStaked += amount;
        return nextStakeId - 1;
    }

    function claimAll() public nonReentrant {
        uint256 totalReward = 0;
        for (uint256 i = 0; i < stakesOfOwner[msg.sender].length(); i++) {
            uint256 stakeId = stakesOfOwner[msg.sender].at(i);
            if (_getStakeClaimableAmount(stakeId) > 0) {
                totalReward += _claim(stakeId);
            }
        }
        require(totalReward > 0, "No rewards to claim");
        magic.safeTransfer(msg.sender, totalReward);
    }

    function claimAllAndRestake(IAtlasMine.Lock lock) external {
        uint256 totalReward = 0;
        for (uint256 i = 0; i < stakesOfOwner[msg.sender].length(); i++) {
            uint256 stakeId = stakesOfOwner[msg.sender].at(i);
            if (_getStakeClaimableAmount(stakeId) > 0) {
                totalReward += _claim(stakeId);
            }
        }
        require(totalReward > 0, "No rewards to claim");
        _stake(totalReward, lock);
    }

    function claim(uint256[] memory stakeIds) external nonReentrant {
        uint256 totalReward = 0;
        for (uint256 i = 0; i < stakeIds.length; i++) {
            UserStake storage s = userStakes[stakeIds[i]];
            require(s.owner == msg.sender, "Only the owner can claim");
            if (_getStakeClaimableAmount(stakeIds[i]) > 0) {
                totalReward += _claim(stakeIds[i]);
            }
        }
        require(totalReward > 0, "No rewards to claim");
        magic.safeTransfer(msg.sender, totalReward);
    }

    function _claim(uint256 stakeId) internal returns (uint256) {
        UserStake memory s = userStakes[stakeId];
        uint256 claimedAmount = BattleflyStaker.claim(s.battleflyStakerDepositId);
        emit Claim(s.owner, stakeId, claimedAmount);
        return claimedAmount;
    }

    function _getStakeClaimableAmount(uint256 stakeId) internal view returns (uint256) {
        UserStake memory s = userStakes[stakeId];
        uint256 claimAmount = BattleflyStaker.pendingRewards(address(this), s.battleflyStakerDepositId);
        return claimAmount;
    }

    function getUserClaimableAmount(address user) external view returns (uint256) {
        uint256 total = 0;
        for (uint256 i = 0; i < stakesOfOwner[user].length(); i++) {
            uint256 stakeId = stakesOfOwner[user].at(i);
            total += _getStakeClaimableAmount(stakeId);
        }
        return total;
    }

    function getUserStakes(address user) external view returns (UserStake[] memory) {
        UserStake[] memory stakes = new UserStake[](stakesOfOwner[user].length());
        for (uint256 i = 0; i < stakesOfOwner[user].length(); i++) {
            uint256 stakeId = stakesOfOwner[user].at(i);
            stakes[i] = userStakes[stakeId];
        }
        return stakes;
    }

    function withdrawAll() external nonReentrant {
        require(stakesOfOwner[msg.sender].length() > 0, "No stakes to withdraw");
        uint256 receiveAmount;
        for (uint256 i = 0; i < stakesOfOwner[msg.sender].length(); i++) {
            uint256 stakeId = stakesOfOwner[msg.sender].at(i);
            if (userStakes[stakeId].unlockAt < block.timestamp) {
                receiveAmount += _withdraw(stakeId);
            }
        }
        require(receiveAmount > 0, "No stakes to withdraw");
        magic.safeTransfer(msg.sender, receiveAmount);
    }

    function withdraw(uint256[] memory stakeIds) external nonReentrant {
        uint256 receiveAmount;
        for (uint256 i = 0; i < stakeIds.length; i++) {
            UserStake storage s = userStakes[stakeIds[i]];
            require(s.owner == msg.sender, "Only the owner can withdraw");
            receiveAmount += _withdraw(stakeIds[i]);
        }
        require(receiveAmount > 0, "No stakes to withdraw");
        magic.safeTransfer(msg.sender, receiveAmount);
    }

    function _withdraw(uint256 stakeId) internal returns (uint256 withdrawAmount) {
        UserStake storage s = userStakes[stakeId];
        withdrawAmount = s.amount;
        require(s.unlockAt < block.timestamp, "Cannot withdraw before the lock time");
        uint256 claimableAmount = _getStakeClaimableAmount(stakeId);
        if (claimableAmount > 0) {
            withdrawAmount += _claim(stakeId);
        }
        BattleflyStaker.withdraw(s.battleflyStakerDepositId);
        totalStaked -= s.amount;
        stakesOfOwner[msg.sender].remove(stakeId);
        s.withdrawAt = block.timestamp;
        emit Withdraw(msg.sender, stakeId, withdrawAmount);
    }

    // ============================================ OWNER FUNCTIONS ==============================================
    function setAdminAccess(address user, bool access) external onlyOwner {
        adminAccess[user] = access;
        emit SetAdminAccess(user, access);
    }

    // ============================================ MODIFIER ==============================================
    modifier onlyAdminAccessOrOwner() {
        require(adminAccess[_msgSender()] == true || _msgSender() == owner(), "Require admin access");
        _;
    }
}

File 63 of 122 : IBattleflyVault.sol
//SPDX-License-Identifier: Unlicense
pragma solidity >=0.6.0;

interface IBattleflyVault {}

File 64 of 122 : BattleflyFlywheelVault.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;

import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC1155/IERC1155Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/structs/EnumerableSetUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";

import "./interfaces/IBattleflyAtlasStaker.sol";
import "./interfaces/IAtlasMine.sol";
import "./interfaces/ISpecialNFT.sol";
import "./interfaces/IBattleflyVault.sol";
import "./interfaces/IBattleflyFounderVault.sol";

contract BattleflyFlywheelVault is IBattleflyVault, Initializable, OwnableUpgradeable, ReentrancyGuardUpgradeable {
    using SafeERC20Upgradeable for IERC20Upgradeable;
    using AddressUpgradeable for address;
    using SafeCastUpgradeable for uint256;
    using SafeCastUpgradeable for int256;
    using EnumerableSetUpgradeable for EnumerableSetUpgradeable.UintSet;

    // ============================================ STATE ==============================================
    struct UserStake {
        uint256 amount;
        uint256 unlockAt;
        uint256 withdrawAt;
        uint256 battleflyStakerDepositId;
        address owner;
        IAtlasMine.Lock lock;
    }
    // ============= Global Immutable State ==============
    IERC20Upgradeable public magic;
    IBattleflyAtlasStaker public BattleflyStaker;
    // ============= Global Staking State ==============
    mapping(uint256 => UserStake) public userStakes;
    mapping(address => EnumerableSetUpgradeable.UintSet) private stakesOfOwner;
    uint256 public nextStakeId;
    uint256 public totalStaked;
    uint256 public totalFee;
    uint256 public totalFeeWithdrawn;
    uint256 public FEE;
    uint256 public FEE_DENOMINATOR;
    // ============= Global Admin ==============
    mapping(address => bool) private adminAccess;

    address public TREASURY_WALLET;
    IBattleflyFounderVault public founderVaultV1;
    IBattleflyFounderVault public founderVaultV2;
    uint256 public stakeableAmountPerV1;
    uint256 public stakeableAmountPerV2;
    // ============================================ EVENT ==============================================
    event Claim(address indexed user, uint256 stakeId, uint256 amount, uint256 fee);
    event Stake(address indexed user, uint256 stakeId, uint256 amount, IAtlasMine.Lock lock);
    event Withdraw(address indexed user, uint256 stakeId, uint256 amount);
    event SetFee(uint256 oldFee, uint256 newFee, uint256 denominator);
    event WithdrawFee(address receiver, uint256 amount);

    event SetAdminAccess(address indexed user, bool access);

    // ============================================ INITIALIZE ==============================================
    function initialize(
        address _magicAddress,
        address _BattleflyStakerAddress,
        uint256 _fee,
        uint256 _feeDominator,
        address _founderVaultV1Address,
        address _founderVaultV2Address,
        uint256 _stakeableAmountPerV1,
        uint256 _stakeableAmountPerV2
    ) external initializer {
        __Ownable_init();
        __ReentrancyGuard_init();

        magic = IERC20Upgradeable(_magicAddress);
        BattleflyStaker = IBattleflyAtlasStaker(_BattleflyStakerAddress);
        nextStakeId = 0;
        FEE_DENOMINATOR = _feeDominator;
        founderVaultV1 = IBattleflyFounderVault(_founderVaultV1Address);
        founderVaultV2 = IBattleflyFounderVault(_founderVaultV2Address);
        stakeableAmountPerV1 = _stakeableAmountPerV1;
        stakeableAmountPerV2 = _stakeableAmountPerV2;
        TREASURY_WALLET = 0xF5411006eEfD66c213d2fd2033a1d340458B7226;
        // Approve the AtlasStaker contract to spend the magic
        magic.safeApprove(address(BattleflyStaker), 2**256 - 1);

        _setFee(_fee);
    }

    // ============================================ USER FUNCTIONS ==============================================
    function stake(uint256 amount, IAtlasMine.Lock lock) external {
        magic.safeTransferFrom(msg.sender, address(this), amount);
        _stake(amount, lock);
    }

    function _stake(uint256 amount, IAtlasMine.Lock lock) internal returns (uint256) {
        require(amount > 0, "Amount must be greater than 0");
        uint256 battleflyStakerDepositId = BattleflyStaker.deposit(amount, lock);
        IBattleflyAtlasStaker.VaultStake memory vaultStake = BattleflyStaker.getVaultStake(
            address(this),
            battleflyStakerDepositId
        );
        UserStake storage s = userStakes[nextStakeId];
        s.amount = amount;
        s.unlockAt = vaultStake.unlockAt;
        s.battleflyStakerDepositId = battleflyStakerDepositId;
        s.owner = msg.sender;
        s.lock = lock;
        stakesOfOwner[msg.sender].add(nextStakeId);
        emit Stake(msg.sender, nextStakeId, amount, lock);
        nextStakeId++;
        totalStaked += amount;
        return nextStakeId - 1;
    }

    function getStakeAmount(address user) public view returns (uint256, uint256) {
        uint256 totalStakingV1;
        uint256 totalStakingV2;
        IBattleflyFounderVault.FounderStake[] memory v1Stakes = founderVaultV1.stakesOf(user);
        IBattleflyFounderVault.FounderStake[] memory v2Stakes = founderVaultV2.stakesOf(user);
        for (uint256 i = 0; i < v1Stakes.length; i++) {
            totalStakingV1 += v1Stakes[i].amount;
        }
        for (uint256 i = 0; i < v2Stakes.length; i++) {
            totalStakingV2 += v2Stakes[i].amount;
        }
        uint256 totalUserStaking = 0;
        for (uint256 i = 0; i < stakesOfOwner[user].length(); i++) {
            uint256 stakeId = stakesOfOwner[user].at(i);
            totalUserStaking += userStakes[stakeId].amount;
        }
        uint256 stakedAmount = totalStakingV1 * stakeableAmountPerV1 + totalStakingV2 * stakeableAmountPerV2;
        return (stakedAmount, totalUserStaking);
    }

    function claimAll() public nonReentrant {
        uint256 totalReward = 0;
        for (uint256 i = 0; i < stakesOfOwner[msg.sender].length(); i++) {
            uint256 stakeId = stakesOfOwner[msg.sender].at(i);
            if (_getStakeClaimableAmount(stakeId) > 0) {
                totalReward += _claim(stakeId);
            }
        }
        require(totalReward > 0, "No rewards to claim");
        magic.safeTransfer(msg.sender, totalReward);
    }

    function claimAllAndRestake(IAtlasMine.Lock lock) external {
        uint256 totalReward = 0;
        for (uint256 i = 0; i < stakesOfOwner[msg.sender].length(); i++) {
            uint256 stakeId = stakesOfOwner[msg.sender].at(i);
            if (_getStakeClaimableAmount(stakeId) > 0) {
                totalReward += _claim(stakeId);
            }
        }
        require(totalReward > 0, "No rewards to claim");
        _stake(totalReward, lock);
    }

    function claim(uint256[] memory stakeIds) external nonReentrant {
        uint256 totalReward = 0;
        for (uint256 i = 0; i < stakeIds.length; i++) {
            UserStake storage s = userStakes[stakeIds[i]];
            require(s.owner == msg.sender, "Only the owner can claim");
            if (_getStakeClaimableAmount(stakeIds[i]) > 0) {
                totalReward += _claim(stakeIds[i]);
            }
        }
        require(totalReward > 0, "No rewards to claim");
        magic.safeTransfer(msg.sender, totalReward);
    }

    function _claim(uint256 stakeId) internal returns (uint256) {
        UserStake memory s = userStakes[stakeId];
        uint256 claimedAmount = BattleflyStaker.claim(s.battleflyStakerDepositId);
        uint256 fee = (claimedAmount * FEE) / FEE_DENOMINATOR;
        uint256 userClaimAmount = claimedAmount - fee;
        totalFee += fee;
        emit Claim(s.owner, stakeId, userClaimAmount, fee);
        return userClaimAmount;
    }

    function _getStakeClaimableAmount(uint256 stakeId) internal view returns (uint256) {
        UserStake memory s = userStakes[stakeId];
        uint256 claimAmount = BattleflyStaker.pendingRewards(address(this), s.battleflyStakerDepositId);
        uint256 fee = (claimAmount * FEE) / FEE_DENOMINATOR;
        uint256 userClaimAmount = claimAmount - fee;
        return userClaimAmount;
    }

    function getUserClaimableAmount(address user) external view returns (uint256) {
        uint256 total = 0;
        for (uint256 i = 0; i < stakesOfOwner[user].length(); i++) {
            uint256 stakeId = stakesOfOwner[user].at(i);
            total += _getStakeClaimableAmount(stakeId);
        }
        return total;
    }

    function getUserStakes(address user) external view returns (UserStake[] memory) {
        UserStake[] memory stakes = new UserStake[](stakesOfOwner[user].length());
        for (uint256 i = 0; i < stakesOfOwner[user].length(); i++) {
            uint256 stakeId = stakesOfOwner[user].at(i);
            stakes[i] = userStakes[stakeId];
        }
        return stakes;
    }

    function withdrawAll() external nonReentrant {
        require(stakesOfOwner[msg.sender].length() > 0, "No stakes to withdraw");
        uint256 receiveAmount;
        for (uint256 i = 0; i < stakesOfOwner[msg.sender].length(); i++) {
            uint256 stakeId = stakesOfOwner[msg.sender].at(i);
            if (userStakes[stakeId].unlockAt < block.timestamp) {
                receiveAmount += _withdraw(stakeId);
            }
        }
        require(receiveAmount > 0, "No stakes to withdraw");
        magic.safeTransfer(msg.sender, receiveAmount);
    }

    function withdraw(uint256[] memory stakeIds) external nonReentrant {
        uint256 receiveAmount;
        for (uint256 i = 0; i < stakeIds.length; i++) {
            UserStake storage s = userStakes[stakeIds[i]];
            require(s.owner == msg.sender, "Only the owner can withdraw");
            receiveAmount += _withdraw(stakeIds[i]);
        }
        require(receiveAmount > 0, "No stakes to withdraw");
        magic.safeTransfer(msg.sender, receiveAmount);
    }

    function _withdraw(uint256 stakeId) internal returns (uint256 withdrawAmount) {
        UserStake storage s = userStakes[stakeId];
        withdrawAmount = s.amount;
        require(s.unlockAt < block.timestamp, "Cannot withdraw before the lock time");
        uint256 claimableAmount = _getStakeClaimableAmount(stakeId);
        if (claimableAmount > 0) {
            withdrawAmount += _claim(stakeId);
        }
        BattleflyStaker.withdraw(s.battleflyStakerDepositId);
        totalStaked -= s.amount;
        stakesOfOwner[msg.sender].remove(stakeId);
        s.withdrawAt = block.timestamp;
        emit Withdraw(msg.sender, stakeId, withdrawAmount);
    }

    function stakeableAmountPerFounder(address vault) external view returns (uint256) {
        if (vault == address(founderVaultV1)) {
            return stakeableAmountPerV1;
        }
        if (vault == address(founderVaultV2)) {
            return stakeableAmountPerV2;
        }
        return 0;
    }

    // ============================================ ADMIN FUNCTIONS ==============================================
    function setFee(uint256 _fee) external onlyAdminAccessOrOwner {
        _setFee(_fee);
        require(totalStaked == 0, "Fee can only be updated without any stakers");
        emit SetFee(FEE, _fee, FEE_DENOMINATOR);
    }

    function _setFee(uint256 _fee) private {
        require(_fee < FEE_DENOMINATOR, "Fee must be less than the fee dominator");

        FEE = _fee;
    }

    function setTreasuryWallet(address _treasuryWallet) external onlyAdminAccessOrOwner {
        TREASURY_WALLET = _treasuryWallet;
    }

    function withdrawFeeToTreasury() external onlyAdminAccessOrOwner {
        uint256 amount = totalFee - totalFeeWithdrawn;
        require(amount > 0, "No fee to withdraw");
        totalFeeWithdrawn += amount;
        magic.safeTransfer(TREASURY_WALLET, amount);
        emit WithdrawFee(TREASURY_WALLET, amount);
    }

    // ============================================ OWNER FUNCTIONS ==============================================
    function setAdminAccess(address user, bool access) external onlyOwner {
        adminAccess[user] = access;
        emit SetAdminAccess(user, access);
    }

    // ============================================ MODIFIER ==============================================
    modifier onlyAdminAccessOrOwner() {
        require(adminAccess[_msgSender()] == true || _msgSender() == owner(), "Require admin access");
        _;
    }
}

File 65 of 122 : SpecialNFTRouter.sol
// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.2;
import "./interfaces/ISpecialNFT.sol";

contract SpecialNFTRouter {
    ISpecialNFT token;

    constructor(address _tokenAddress) {
        token = ISpecialNFT(_tokenAddress);
    }

    function getAllBalance(address owner) external view returns (uint256[] memory, uint256[] memory) {
        uint256 balance = token.balanceOf(owner);
        uint256[] memory tokenIds = new uint256[](balance);
        uint256[] memory tokenTypes = new uint256[](balance);

        for (uint256 i = 0; i < balance; i++) {
            tokenIds[i] = token.tokenOfOwnerByIndex(owner, i);
            tokenTypes[i] = token.getSpecialNFTType(tokenIds[i]);
        }
        return (tokenIds, tokenTypes);
    }

    function getBalanceOf(address owner, uint256 typeId) external view returns (uint256[] memory) {
        uint256 balance = token.balanceOf(owner);
        uint256[] memory tokenIds = new uint256[](balance);
        uint256 count = 0;
        for (uint256 i = 0; i < balance; i++) {
            uint256 tokenId = token.tokenOfOwnerByIndex(owner, i);
            uint256 tokenType = token.getSpecialNFTType(tokenId);
            if (tokenType == typeId) {
                tokenIds[count] = tokenId;
                count++;
            }
        }
        uint256[] memory result = new uint256[](count);
        for (uint256 i = 0; i < count; i++) {
            result[i] = tokenIds[i];
        }
        return (result);
    }
}

File 66 of 122 : BattleflyGame.sol
// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.2;

import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/math/SafeMathUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/utils/ERC721HolderUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC1155/utils/ERC1155HolderUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/cryptography/ECDSAUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";

import "./interfaces/IHyperdome.sol";
import "./interfaces/IBattlefly.sol";
import "./interfaces/ISpecialNFT.sol";
import "./interfaces/IMod.sol";
import "./interfaces/IScrapToken.sol";
import "./interfaces/IItem.sol";

contract BattleflyGame is OwnableUpgradeable, ERC1155HolderUpgradeable, ERC721HolderUpgradeable {
    using SafeMathUpgradeable for uint256;
    using AddressUpgradeable for address;
    using SafeERC20Upgradeable for IERC20Upgradeable;

    mapping(address => bool) private adminAccess;
    IHyperdome private HyperdomeContract; //removed
    IBattlefly private BattleflyContract;
    ISpecialNFT private SpecialNFTContract;
    IMod private ModContract; // removed
    IERC20Upgradeable private MagicToken;
    IScrapToken private ScrapToken; // removed
    IItem private ItemContract;

    mapping(uint256 => bool) public ProcessedTransactions;
    uint8 constant TRANSACTION_TYPE_WITHDRAW_BATTLEFLY = 0;
    uint8 constant TRANSACTION_TYPE_WITHDRAW_MAGIC = 1;
    uint8 constant TRANSACTION_TYPE_WITHDRAW_ITEM = 2;

    event DepositHyperdome(uint256 indexed tokenId, address indexed user, uint256 timestamp);
    event WithdrawHyperdome(uint256 indexed tokenId, address indexed receiver, uint256 timestamp);

    event DepositBattlefly(uint256 indexed tokenId, address indexed user, uint256 timestamp);
    event WithdrawBattlefly(uint256 indexed tokenId, address indexed receiver, uint256 timestamp);

    event DepositItems(uint256 indexed tokenId, address indexed user, uint256 amount, uint256 timestamp);
    event WithdrawItems(uint256 indexed tokenId, address indexed receiver, uint256 amount, uint256 timestamp);

    event DepositMagic(uint256 amount, address indexed user, uint256 timestamp);
    event WithdrawMagic(uint256 amount, address indexed receiver, uint256 timestamp);

    event MintHyperdome(address indexed receiver, uint256 tokenId);
    event MintBattlefly(address indexed receiver, uint256 tokenId, uint256 battleflyType);
    event MintBattleflies(address[] receivers, uint256[] tokenIds, uint256[] battleflyTypes);

    event MintSpecialNFT(address indexed receiver, uint256 tokenId, uint256 specialNFTType);
    event MintSpecialNFTs(address[] receivers, uint256[] tokenIds, uint256[] specialNFTTypes);

    event MintMod(address indexed receiver, uint256 tokenId);

    event SetAdminAccess(address indexed user, bool access);
    event MintItems(uint256 indexed itemId, address indexed receiver, uint256 amount);

    function initialize(
        address hyperdomeContractAddress,
        address battleflyContractAddress,
        address specialNFTContractAddress,
        address modContractAddress,
        address magicTokenAddress,
        address scrapTokenAddress
    ) public initializer {
        __Ownable_init();
        HyperdomeContract = IHyperdome(hyperdomeContractAddress);
        BattleflyContract = IBattlefly(battleflyContractAddress);
        SpecialNFTContract = ISpecialNFT(specialNFTContractAddress);
        ModContract = IMod(modContractAddress);
        MagicToken = IERC20Upgradeable(magicTokenAddress);
        ScrapToken = IScrapToken(scrapTokenAddress);
    }

    function initializeUpgrade(address itemContractAddress) external onlyOwner {
        ItemContract = IItem(itemContractAddress);
    }

    function setMagic(address magicAddress) external onlyOwner {
        MagicToken = IERC20Upgradeable(magicAddress);
    }

    function getBattlefliesOfOwner(address user) external view returns (uint256[] memory) {
        uint256 balance = BattleflyContract.balanceOf(user);
        uint256[] memory tokenIds = new uint256[](balance);
        for (uint256 i = 0; i < balance; i++) {
            tokenIds[i] = BattleflyContract.tokenOfOwnerByIndex(user, i);
        }
        return tokenIds;
    }

    function getSpecialNFTsOfOwner(address user) external view returns (uint256[] memory) {
        uint256 balance = SpecialNFTContract.balanceOf(user);
        uint256[] memory tokenIds = new uint256[](balance);
        for (uint256 i = 0; i < balance; i++) {
            tokenIds[i] = SpecialNFTContract.tokenOfOwnerByIndex(user, i);
        }
        return tokenIds;
    }

    // ADMIN
    function setAdminAccess(address user, bool access) external onlyOwner {
        adminAccess[user] = access;
        emit SetAdminAccess(user, access);
    }

    function mintSpecialNFT(address receiver, uint256 specialNFTType) external onlyAdminAccess returns (uint256) {
        uint256 tokenId = SpecialNFTContract.mintSpecialNFT(receiver, specialNFTType);
        emit MintSpecialNFT(receiver, tokenId, specialNFTType);
        return tokenId;
    }

    function mintSpecialNFTs(
        address receiver,
        uint256 specialNFTType,
        uint256 amount
    ) external onlyAdminAccess returns (uint256[] memory) {
        uint256[] memory tokenIds = SpecialNFTContract.mintSpecialNFTs(receiver, specialNFTType, amount);
        for (uint256 i = 0; i < amount; i++) {
            emit MintSpecialNFT(receiver, tokenIds[i], specialNFTType);
        }
        return tokenIds;
    }

    function mintItems(
        uint256 itemId,
        address receiver,
        uint256 amount
    ) external onlyAdminAccess {
        ItemContract.mintItems(itemId, receiver, amount, "");
        emit MintItems(itemId, receiver, amount);
    }

    function mintBattlefly(address receiver, uint256 battleflyType) external onlyAdminAccess returns (uint256) {
        uint256 tokenId = BattleflyContract.mintBattlefly(receiver, battleflyType);
        emit MintBattlefly(receiver, tokenId, battleflyType);
        return tokenId;
    }

    function mintBattleflies(
        address receiver,
        uint256 battleflyType,
        uint256 amount
    ) external onlyAdminAccess returns (uint256[] memory) {
        uint256[] memory tokenIds = BattleflyContract.mintBattleflies(receiver, battleflyType, amount);
        for (uint256 i = 0; i < amount; i++) {
            emit MintBattlefly(receiver, tokenIds[i], battleflyType);
        }
        return tokenIds;
    }

    // Battlefly
    function bulkDepositBattlefly(uint256[] memory tokenIds) external {
        for (uint256 i = 0; i < tokenIds.length; i++) {
            depositBattlefly(tokenIds[i]);
        }
    }

    function depositBattlefly(uint256 tokenId) public {
        BattleflyContract.safeTransferFrom(_msgSender(), address(this), tokenId);
        emit DepositBattlefly(tokenId, _msgSender(), block.timestamp);
    }

    function withdrawBattlefly(uint256 tokenId, address receiver) external onlyAdminAccess {
        BattleflyContract.safeTransferFrom(address(this), receiver, tokenId);
        emit WithdrawBattlefly(tokenId, receiver, block.timestamp);
    }

    function claimWithdrawBattleflies(
        uint256[] memory tokenIds,
        uint256 transactionId,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external {
        require(ProcessedTransactions[transactionId] == false, "Transaction have been processed");
        ProcessedTransactions[transactionId] = true;
        bytes32 payloadHash = keccak256(
            abi.encodePacked(_msgSender(), tokenIds, transactionId, TRANSACTION_TYPE_WITHDRAW_BATTLEFLY)
        );
        bytes32 messageHash = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", payloadHash));
        (address admin, ECDSAUpgradeable.RecoverError result) = ECDSAUpgradeable.tryRecover(messageHash, v, r, s);
        require(result == ECDSAUpgradeable.RecoverError.NoError && adminAccess[admin], "Require admin access");
        for (uint256 i = 0; i < tokenIds.length; i++) {
            BattleflyContract.safeTransferFrom(address(this), _msgSender(), tokenIds[i]);
            emit WithdrawBattlefly(tokenIds[i], _msgSender(), block.timestamp);
        }
    }

    //Magic token
    function depositMagic(uint256 amount) external {
        MagicToken.safeTransferFrom(_msgSender(), address(this), amount);
        emit DepositMagic(amount, _msgSender(), block.timestamp);
    }

    function withdrawMagic(uint256 amount, address receiver) external onlyAdminAccess {
        MagicToken.safeTransfer(receiver, amount);
        emit WithdrawMagic(amount, receiver, block.timestamp);
    }

    function claimWithdrawMagic(
        uint256 amount,
        uint256 transactionId,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external {
        require(ProcessedTransactions[transactionId] == false, "Transaction have been processed");
        ProcessedTransactions[transactionId] = true;
        bytes32 payloadHash = keccak256(
            abi.encodePacked(_msgSender(), amount, transactionId, TRANSACTION_TYPE_WITHDRAW_MAGIC)
        );
        bytes32 messageHash = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", payloadHash));
        (address admin, ECDSAUpgradeable.RecoverError result) = ECDSAUpgradeable.tryRecover(messageHash, v, r, s);
        require(result == ECDSAUpgradeable.RecoverError.NoError && adminAccess[admin], "Require admin access");
        MagicToken.safeTransfer(_msgSender(), amount);
        emit WithdrawMagic(amount, _msgSender(), block.timestamp);
    }

    //Item
    function depositItems(uint256 itemId, uint256 amount) external {
        ItemContract.safeTransferFrom(_msgSender(), address(this), itemId, amount, "");
        emit DepositItems(itemId, _msgSender(), amount, block.timestamp);
    }

    function withdrawItems(
        uint256 itemId,
        uint256 amount,
        address receiver
    ) external onlyAdminAccess {
        ItemContract.safeTransferFrom(address(this), receiver, itemId, amount, "");
        emit WithdrawItems(itemId, receiver, amount, block.timestamp);
    }

    function claimWithdrawItems(
        uint256 itemId,
        uint256 amount,
        uint256 transactionId,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external {
        require(ProcessedTransactions[transactionId] == false, "Transaction have been processed");
        ProcessedTransactions[transactionId] = true;
        bytes32 payloadHash = keccak256(
            abi.encodePacked(_msgSender(), itemId, amount, transactionId, TRANSACTION_TYPE_WITHDRAW_ITEM)
        );
        bytes32 messageHash = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", payloadHash));
        (address admin, ECDSAUpgradeable.RecoverError result) = ECDSAUpgradeable.tryRecover(messageHash, v, r, s);
        require(result == ECDSAUpgradeable.RecoverError.NoError && adminAccess[admin], "Require admin access");
        ItemContract.safeTransferFrom(address(this), _msgSender(), itemId, amount, "");
        emit WithdrawItems(itemId, _msgSender(), amount, block.timestamp);
    }

    //modifier
    modifier onlyAdminAccess() {
        require(adminAccess[_msgSender()] == true || _msgSender() == owner(), "Require admin access");
        _;
    }
}

File 67 of 122 : IHyperdome.sol
//SPDX-License-Identifier: Unlicense
pragma solidity >=0.6.0;
import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/IERC721EnumerableUpgradeable.sol";
import "./IBattlefly.sol";

interface IHyperdome is IERC721EnumerableUpgradeable {
    function mintHyperdome(address receiver) external returns (uint256);
}

File 68 of 122 : IScrapToken.sol
//SPDX-License-Identifier: Unlicense
pragma solidity >=0.6.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

interface IScrapToken is IERC20 {
    function mint(uint256 amount, address receiver) external returns (uint256);
}

File 69 of 122 : IItem.sol
//SPDX-License-Identifier: Unlicense
pragma solidity >=0.6.0;
import "@openzeppelin/contracts-upgradeable/token/ERC1155/IERC1155Upgradeable.sol";

interface IItem is IERC1155Upgradeable {
    function mintItems(
        uint256 itemId,
        address receiver,
        uint256 amount,
        bytes memory data
    ) external;
}

File 70 of 122 : BattleflyFounderVault.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;

import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC1155/IERC1155Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC1155/utils/ERC1155HolderUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/utils/ERC721HolderUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/structs/EnumerableSetUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";

import "./interfaces/IBattleflyAtlasStaker.sol";
import "./interfaces/IAtlasMine.sol";
import "./interfaces/ISpecialNFT.sol";
import "./interfaces/IBattleflyFounderVault.sol";
import "./interfaces/IBattleflyFlywheelVault.sol";

contract BattleflyFounderVault is
    Initializable,
    OwnableUpgradeable,
    ERC1155HolderUpgradeable,
    ERC721HolderUpgradeable,
    ReentrancyGuardUpgradeable
{
    using SafeERC20Upgradeable for IERC20Upgradeable;
    using AddressUpgradeable for address;
    using SafeCastUpgradeable for uint256;
    using SafeCastUpgradeable for int256;
    using EnumerableSetUpgradeable for EnumerableSetUpgradeable.UintSet;

    // ============================================ STATE ==============================================
    struct FounderStake {
        uint256 amount;
        uint256 stakeTimestamp;
        address owner;
        uint256 lastClaimedDay;
    }
    struct DailyFounderEmission {
        uint256 totalEmission;
        uint256 totalFounders;
    }
    // ============= Global Immutable State ==============

    /// @notice MAGIC token
    /// @dev functionally immutable
    IERC20Upgradeable public magic;
    ISpecialNFT public founderNFT;
    uint256 public founderTypeID;
    IBattleflyAtlasStaker public BattleflyStaker;
    uint256 public startTimestamp;
    // ============= Global mutable State ==============
    uint256 totalEmission;
    uint256 claimedEmission;
    uint256 pendingFounderEmission;

    mapping(address => EnumerableSetUpgradeable.UintSet) private FounderStakeOfOwner;
    uint256 lastStakeTimestamp;
    mapping(uint256 => FounderStake) public FounderStakes;
    uint256 lastStakeId;
    mapping(address => bool) private adminAccess;
    uint256 public DaysSinceStart;
    mapping(uint256 => DailyFounderEmission) public DailyFounderEmissions;

    uint256 withdrawnOldFounder;
    uint256 unupdatedStakeIdFrom;

    uint256 public stakeBackPercent;
    uint256 public treasuryPercent;
    uint256 public v2VaultPercent;

    IBattleflyFounderVault battleflyFounderVaultV2;
    IBattleflyFlywheelVault battleflyFlywheelVault;
    // ============= Constant ==============
    address public constant TREASURY_WALLET = 0xF5411006eEfD66c213d2fd2033a1d340458B7226;
    uint256 public constant PERCENT_DENOMINATOR = 10000;
    IAtlasMine.Lock public constant DEFAULT_STAKE_BACK_LOCK = IAtlasMine.Lock.twoWeeks;

    mapping(uint256 => bool) public claimedPastEmission;
    uint256 public pastEmissionPerFounder;
    mapping(uint256 => uint256) public stakeIdOfFounder;
    mapping(uint256 => EnumerableSetUpgradeable.UintSet) stakingFounderOfStakeId;

    // ============================================ EVENTS ==============================================
    event ClaimDailyEmission(
        uint256 dayTotalEmission,
        uint256 totalFounderEmission,
        uint256 totalFounders,
        uint256 stakeBackAmount,
        uint256 treasuryAmount,
        uint256 v2VaultAmount
    );
    event Claim(address user, uint256 stakeId, uint256 amount);
    event Withdraw(address user, uint256 stakeId, uint256 founderId);
    event Stake(address user, uint256 stakeId, uint256[] founderNFTIDs);
    event TopupMagicToStaker(address user, uint256 amount, IAtlasMine.Lock lock);
    event TopupTodayEmission(address user, uint256 amount);
    event ClaimPastEmission(address user, uint256 amount, uint256[] tokenIds);

    // ============================================ INITIALIZE ==============================================
    function initialize(
        address _magicAddress,
        address _BattleflyStakerAddress,
        uint256 _founderTypeID,
        address _founderNFTAddress,
        uint256 _startTimestamp,
        address _battleflyFounderVaultV2Address,
        uint256 _stakeBackPercent,
        uint256 _treasuryPercent,
        uint256 _v2VaultPercent
    ) external initializer {
        __ERC1155Holder_init();
        __ERC721Holder_init();
        __Ownable_init();
        __ReentrancyGuard_init();

        magic = IERC20Upgradeable(_magicAddress);
        BattleflyStaker = IBattleflyAtlasStaker(_BattleflyStakerAddress);
        founderNFT = (ISpecialNFT(_founderNFTAddress));
        founderTypeID = _founderTypeID;
        lastStakeTimestamp = block.timestamp;
        lastStakeId = 0;
        startTimestamp = _startTimestamp;
        DaysSinceStart = 0;
        stakeBackPercent = _stakeBackPercent;
        treasuryPercent = _treasuryPercent;
        v2VaultPercent = _v2VaultPercent;
        if (_battleflyFounderVaultV2Address == address(0))
            battleflyFounderVaultV2 = IBattleflyFounderVault(address(this));
        else battleflyFounderVaultV2 = IBattleflyFounderVault(_battleflyFounderVaultV2Address);

        require(stakeBackPercent + treasuryPercent + v2VaultPercent < PERCENT_DENOMINATOR);

        // Approve the AtlasStaker contract to spend the magic
        magic.safeApprove(address(BattleflyStaker), 2**256 - 1);
    }

    // ============================================ USER OPERATIONS ==============================================
    function claimPastEmission() external {
        require(pastEmissionPerFounder != 0, "No past founder emission to claim");
        uint256[] memory tokenIds = getPastEmissionClaimableTokens(msg.sender);
        require(tokenIds.length > 0, "No tokens to claim");
        for (uint256 i = 0; i < tokenIds.length; i++) {
            claimedPastEmission[tokenIds[i]] = true;
        }
        magic.safeTransfer(msg.sender, pastEmissionPerFounder * tokenIds.length);
        emit ClaimPastEmission(msg.sender, pastEmissionPerFounder * tokenIds.length, tokenIds);
    }

    function getPastEmissionClaimableTokens(address user) public view returns (uint256[] memory) {
        uint256 balance = founderNFT.balanceOf(user);
        uint256[] memory tokenIds = new uint256[](balance);
        uint256 countClaimable = 0;
        for (uint256 i = 0; i < balance; i++) {
            uint256 tokenId = founderNFT.tokenOfOwnerByIndex(user, i);
            uint256 tokenType = founderNFT.getSpecialNFTType(tokenId);
            if (tokenType == founderTypeID && claimedPastEmission[tokenId] == false) {
                tokenIds[countClaimable] = tokenId;
                countClaimable++;
            }
        }
        (, uint256[][] memory stakeTokens) = stakesOf(user);
        uint256 countClaimableStaked = 0;
        uint256 balanceStaked = 0;
        for (uint256 i = 0; i < stakeTokens.length; i++) {
            balanceStaked += stakeTokens[i].length;
        }
        uint256[] memory stakingTokenIds = new uint256[](balanceStaked);
        for (uint256 i = 0; i < stakeTokens.length; i++) {
            uint256[] memory stakeTokenIds = stakeTokens[i];
            for (uint256 j = 0; j < stakeTokenIds.length; j++) {
                uint256 tokenId = stakeTokenIds[j];
                uint256 tokenType = founderNFT.getSpecialNFTType(tokenId);
                if (tokenType == founderTypeID && claimedPastEmission[tokenId] == false) {
                    stakingTokenIds[countClaimableStaked] = tokenId;
                    countClaimableStaked++;
                }
            }
        }

        uint256[] memory result = new uint256[](countClaimable + countClaimableStaked);
        for (uint256 i = 0; i < countClaimable; i++) {
            result[i] = tokenIds[i];
        }
        for (uint256 i = countClaimable; i < countClaimable + countClaimableStaked; i++) {
            result[i] = stakingTokenIds[i - countClaimable];
        }
        return result;
    }

    function setTokenClaimedPastEmission(uint256[] memory tokenIds, bool isClaimed) external onlyOwner {
        for (uint256 i = 0; i < tokenIds.length; i++) {
            claimedPastEmission[tokenIds[i]] = isClaimed;
        }
    }

    function setPastEmission(uint256 amount) external onlyOwner {
        pastEmissionPerFounder = amount;
    }

    function stakesOf(address owner) public view returns (FounderStake[] memory, uint256[][] memory) {
        FounderStake[] memory stakes = new FounderStake[](FounderStakeOfOwner[owner].length());
        uint256[][] memory _founderIDsOfStake = new uint256[][](FounderStakeOfOwner[owner].length());
        for (uint256 i = 0; i < FounderStakeOfOwner[owner].length(); i++) {
            stakes[i] = FounderStakes[FounderStakeOfOwner[owner].at(i)];
            _founderIDsOfStake[i] = stakingFounderOfStakeId[FounderStakeOfOwner[owner].at(i)].values();
        }
        return (stakes, _founderIDsOfStake);
    }

    function stakeFounderNFT(uint256[] memory ids) external {
        require(ids.length != 0, "Must provide at least one founder NFT ID");
        for (uint256 i = 0; i < ids.length; i++) {
            require(founderNFT.getSpecialNFTType(ids[i]) == founderTypeID, "Not valid founder NFT");
            founderNFT.safeTransferFrom(msg.sender, address(this), ids[i]);
        }
        uint256 currentDay = (block.timestamp - startTimestamp) / 24 hours;
        lastStakeId++;
        FounderStakes[lastStakeId] = (
            FounderStake({
                amount: ids.length,
                stakeTimestamp: block.timestamp,
                owner: msg.sender,
                lastClaimedDay: currentDay
            })
        );
        for (uint256 i = 0; i < ids.length; i++) {
            stakeIdOfFounder[ids[i]] = lastStakeId;
            stakingFounderOfStakeId[lastStakeId].add(ids[i]);
        }
        FounderStakeOfOwner[msg.sender].add(lastStakeId);
        emit Stake(msg.sender, lastStakeId, ids);
    }

    function claimAll() external nonReentrant {
        uint256 totalReward = 0;
        for (uint256 i = 0; i < FounderStakeOfOwner[msg.sender].length(); i++) {
            totalReward += _claimByStakeId(FounderStakeOfOwner[msg.sender].at(i));
        }
        require(totalReward > 0, "No reward to claim");
    }

    function withdrawAll() external nonReentrant {
        require(FounderStakeOfOwner[msg.sender].length() > 0, "No STAKE to withdraw");
        uint256 totalWithdraw = 0;
        for (uint256 i = 0; i < FounderStakeOfOwner[msg.sender].length(); i++) {
            uint256 stakeId = FounderStakeOfOwner[msg.sender].at(i);
            _claimByStakeId(stakeId);
            totalWithdraw += FounderStakes[stakeId].amount;
            _withdrawByStakeId(stakeId);
        }
        (uint256 stakeableAmount, uint256 stakingAmount) = battleflyFlywheelVault.getStakeAmount(msg.sender);
        uint256 stakeableAmountPerFounder = battleflyFlywheelVault.stakeableAmountPerFounder(address(this));
        require(
            stakingAmount <= stakeableAmount - stakeableAmountPerFounder * totalWithdraw,
            "Pls withdraw FlywheelVault first"
        );
    }

    function _withdrawByStakeId(uint256 stakeId) internal {
        FounderStake storage stake = FounderStakes[stakeId];
        _claimByStakeId(stakeId);
        for (uint256 i = 0; i < stakingFounderOfStakeId[stakeId].length(); i++) {
            founderNFT.safeTransferFrom(address(this), stake.owner, stakingFounderOfStakeId[stakeId].at(i));
            emit Withdraw(stake.owner, stakeId, stakingFounderOfStakeId[stakeId].at(i));
        }
        if (stake.stakeTimestamp < (startTimestamp + (DaysSinceStart) * 24 hours - 24 hours)) {
            withdrawnOldFounder += stakingFounderOfStakeId[stakeId].length();
        }
        FounderStakeOfOwner[stake.owner].remove(stakeId);
        delete FounderStakes[stakeId];
        delete stakingFounderOfStakeId[stakeId];
    }

    function _claimByStakeId(uint256 stakeId) internal returns (uint256) {
        require(stakeId != 0, "No stake to claim");
        FounderStake storage stake = FounderStakes[stakeId];
        uint256 totalReward = _getClaimableEmissionOf(stakeId);
        claimedEmission += totalReward;
        stake.lastClaimedDay = DaysSinceStart;
        magic.safeTransfer(stake.owner, totalReward);
        emit Claim(stake.owner, stakeId, totalReward);
        return totalReward;
    }

    function withdraw(uint256[] memory founderIds) external nonReentrant {
        require(founderIds.length > 0, "No Founder to withdraw");
        uint256 totalWithdraw = 0;
        for (uint256 i = 0; i < founderIds.length; i++) {
            uint256 stakeId = stakeIdOfFounder[founderIds[i]];
            require(FounderStakes[stakeId].owner == msg.sender, "Not your stake");
            _claimBeforeWithdraw(founderIds[i]);
            _withdraw(founderIds[i]);
            totalWithdraw++;
        }
        // (uint256 stakeableAmount, uint256 stakingAmount) = battleflyFlywheelVault.getStakeAmount(msg.sender);
        // uint256 stakeableAmountPerFounder = battleflyFlywheelVault.stakeableAmountPerFounder(address(this));
        // require(stakingAmount <= stakeableAmount - stakeableAmountPerFounder * totalWithdraw, "Pls withdraw FlywheelVault first");
    }

    function _claimBeforeWithdraw(uint256 founderId) internal returns (uint256) {
        uint256 stakeId = stakeIdOfFounder[founderId];
        FounderStake storage stake = FounderStakes[stakeId];
        uint256 founderReward = _getClaimableEmissionOf(stakeId) / stake.amount;
        claimedEmission += founderReward;
        magic.safeTransfer(stake.owner, founderReward);
        emit Claim(stake.owner, stakeId, founderReward);
        return founderReward;
    }

    function getClaimableEmissionOf(address user) public view returns (uint256) {
        uint256 totalReward = 0;
        for (uint256 i = 0; i < FounderStakeOfOwner[user].length(); i++) {
            totalReward += _getClaimableEmissionOf(FounderStakeOfOwner[user].at(i));
        }
        return totalReward;
    }

    function _getClaimableEmissionOf(uint256 stakeId) internal view returns (uint256) {
        uint256 totalReward = 0;
        FounderStake memory stake = FounderStakes[stakeId];
        if (stake.lastClaimedDay == DaysSinceStart) return 0;
        for (uint256 j = stake.lastClaimedDay + 1; j <= DaysSinceStart; j++) {
            if (DailyFounderEmissions[j].totalFounders == 0 || stake.amount == 0) continue;
            totalReward +=
                (DailyFounderEmissions[j].totalEmission / DailyFounderEmissions[j].totalFounders) *
                stake.amount;
        }
        return totalReward;
    }

    function _withdraw(uint256 founderId) internal {
        uint256 stakeId = stakeIdOfFounder[founderId];
        FounderStake storage stake = FounderStakes[stakeId];
        // _claim(founderId);
        founderNFT.safeTransferFrom(address(this), stake.owner, founderId);
        stake.amount--;
        delete stakeIdOfFounder[founderId];
        stakingFounderOfStakeId[stakeId].remove(founderId);
        if (stake.stakeTimestamp < (startTimestamp + (DaysSinceStart) * 24 hours - 24 hours)) {
            withdrawnOldFounder += 1;
        }
        if (stake.amount == 0) {
            FounderStakeOfOwner[stake.owner].remove(stakeId);
            delete FounderStakes[stakeId];
        }
        emit Withdraw(stake.owner, stakeId, founderId);
    }

    // ============================================ ADMIN OPERATIONS ==============================================
    function topupMagicToStaker(uint256 amount, IAtlasMine.Lock lock) external onlyAdminAccess {
        require(amount > 0);
        magic.safeTransferFrom(msg.sender, address(this), amount);
        _depositToStaker(amount, lock);
        emit TopupMagicToStaker(msg.sender, amount, lock);
    }

    function topupTodayEmission(uint256 amount) external onlyAdminAccess {
        require(amount > 0);
        magic.safeTransferFrom(msg.sender, address(this), amount);
        pendingFounderEmission += amount;
        emit TopupTodayEmission(msg.sender, amount);
    }

    function depositToStaker(uint256 amount, IAtlasMine.Lock lock) external onlyAdminAccess {
        require(magic.balanceOf(address(this)) >= amount);
        require(amount > 0);
        _depositToStaker(amount, lock);
    }

    function _depositToStaker(uint256 amount, IAtlasMine.Lock lock) internal {
        BattleflyStaker.deposit(amount, lock);
    }

    function claimDailyEmission() public onlyAdminAccess nonReentrant {
        uint256 currentDay = (block.timestamp - startTimestamp) / 24 hours;
        require(currentDay > DaysSinceStart, "Cant claim again for today");
        uint256 todayTotalEmission = BattleflyStaker.claimAll();
        uint256 todayTotalFounderNFTs = _updateTotalStakingFounders(currentDay);

        uint256 stakeBackAmount;
        uint256 v2VaultAmount;
        uint256 treasuryAmount;
        uint256 founderEmission;
        if (todayTotalEmission != 0) {
            stakeBackAmount = (todayTotalEmission * stakeBackPercent) / PERCENT_DENOMINATOR;
            if (stakeBackAmount != 0) _depositToStaker(stakeBackAmount, DEFAULT_STAKE_BACK_LOCK);

            v2VaultAmount = (todayTotalEmission * v2VaultPercent) / PERCENT_DENOMINATOR;
            if (v2VaultAmount != 0) battleflyFounderVaultV2.topupMagicToStaker(v2VaultAmount, DEFAULT_STAKE_BACK_LOCK);

            treasuryAmount = (todayTotalEmission * treasuryPercent) / PERCENT_DENOMINATOR;
            if (treasuryAmount != 0) magic.safeTransfer(TREASURY_WALLET, treasuryAmount);

            founderEmission += todayTotalEmission - stakeBackAmount - v2VaultAmount - treasuryAmount;
        }
        if (pendingFounderEmission > 0) {
            founderEmission += pendingFounderEmission;
            pendingFounderEmission = 0;
        }
        totalEmission += founderEmission;
        DaysSinceStart = currentDay;
        DailyFounderEmissions[DaysSinceStart] = DailyFounderEmission({
            totalEmission: founderEmission,
            totalFounders: todayTotalFounderNFTs
        });
        emit ClaimDailyEmission(
            todayTotalEmission,
            founderEmission,
            todayTotalFounderNFTs,
            stakeBackAmount,
            treasuryAmount,
            v2VaultAmount
        );
    }

    function withdrawAllFromStaker() external onlyAdminAccess {
        claimDailyEmission();
        BattleflyStaker.withdrawAll();
    }

    function withdrawFromVault(address receiver, uint256 amount) external onlyAdminAccess {
        magic.safeTransfer(receiver, amount);
    }

    function _updateTotalStakingFounders(uint256 currentDay) private returns (uint256) {
        uint256 result = DailyFounderEmissions[DaysSinceStart].totalFounders - withdrawnOldFounder;
        withdrawnOldFounder = 0;
        uint256 to = startTimestamp + currentDay * 24 hours;
        uint256 i = unupdatedStakeIdFrom;
        for (; i <= lastStakeId; i++) {
            if (FounderStakes[i].stakeTimestamp == 0) {
                continue;
            }
            if (FounderStakes[i].stakeTimestamp > to) {
                break;
            }
            result += FounderStakes[i].amount;
        }
        unupdatedStakeIdFrom = i;
        return result;
    }

    //Must be called right after init
    function setFlywheelVault(address vault) external onlyOwner {
        require(vault != address(0));
        battleflyFlywheelVault = IBattleflyFlywheelVault(vault);
    }

    function setAdminAccess(address user, bool access) external onlyOwner {
        adminAccess[user] = access;
    }

    modifier onlyAdminAccess() {
        require(adminAccess[_msgSender()] || _msgSender() == owner(), "Require admin access");
        _;
    }
}

File 71 of 122 : BattleflyAtlasStakerV02.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;

import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC1155/IERC1155Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC1155/utils/ERC1155HolderUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/utils/ERC721HolderUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/structs/EnumerableSetUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";

import "./interfaces/IAtlasMine.sol";
import "./interfaces/IBattleflyAtlasStakerV02.sol";
import "./interfaces/IVault.sol";
import "./libraries/BattleflyAtlasStakerUtils.sol";
import "./interfaces/vaults/IBattleflyTreasuryFlywheelVault.sol";

/// @custom:oz-upgrades-unsafe-allow external-library-linking
contract BattleflyAtlasStakerV02 is
    IBattleflyAtlasStakerV02,
    Initializable,
    OwnableUpgradeable,
    ERC1155HolderUpgradeable,
    ERC721HolderUpgradeable,
    ReentrancyGuardUpgradeable
{
    using AddressUpgradeable for address;
    using SafeCastUpgradeable for uint256;
    using SafeCastUpgradeable for int256;
    using SafeERC20Upgradeable for IERC20Upgradeable;
    using EnumerableSetUpgradeable for EnumerableSetUpgradeable.UintSet;

    // ========== CONSTANTS ==========

    uint96 public constant FEE_DENOMINATOR = 10000;
    uint256 public constant ONE = 1e28;
    address public BATTLEFLY_BOT;

    IERC20Upgradeable public override MAGIC;
    IAtlasMine public ATLAS_MINE;
    IBattleflyTreasuryFlywheelVault public TREASURY_VAULT;

    IAtlasMine.Lock[] public LOCKS;
    IAtlasMine.Lock[] public allowedLocks;

    // ========== Operator States ==========
    uint256 public override currentDepositId;
    uint64 public override currentEpoch;
    uint256 public pausedUntil;
    uint256 public nextExecution;

    /**
     * @dev active positionIds in AtlasMine for this contract.
     *
     */
    EnumerableSetUpgradeable.UintSet private activePositionIds;

    /**
     * @dev Total emissions harvested from Atlas Mine for a particular lock period and epoch
     *      { lock } => { epoch } => { emissions }
     */
    mapping(IAtlasMine.Lock => mapping(uint64 => uint256)) private totalEmissionsAtEpochForLock;

    /**
     * @dev Total amount of magic staked in Atlas Mine for a particular lock period and epoch
     *      { lock } => { epoch } => { magic }
     */
    mapping(IAtlasMine.Lock => mapping(uint64 => uint256)) private totalStakedAtEpochForLock;

    /**
     * @dev Total amount of emissions per share in magic for a particular lock period and epoch
     *      { lock } => { epoch } => { emissionsPerShare }
     */
    mapping(IAtlasMine.Lock => mapping(uint64 => uint256)) private totalPerShareAtEpochForLock;

    /**
     * @dev Total amount of unstaked Magic at a particular epoch
     *      { epoch } => { unstaked magic }
     */
    mapping(uint64 => uint256) private unstakeAmountAtEpoch;

    /**
     * @dev Legion ERC721 NFT stakers data
     *      { tokenId } => { depositor }
     */
    mapping(uint256 => address) public legionStakers;

    /**
     * @dev TREASURE ERC1155 NFT stakers data
     *      { tokenId } => { depositor } => { deposit amount }
     */
    mapping(uint256 => mapping(address => uint256)) public treasureStakers;

    /**
     * @dev Vaultstakes per depositId
     *      { depositId } => { VaultStake }
     */
    mapping(uint256 => VaultStake) public vaultStakes;

    /**
     * @dev Vaults' all deposits
     *      { address } => { depositId }
     */
    mapping(address => EnumerableSetUpgradeable.UintSet) private depositIdByVault;

    /**
     * @dev Magic amount that is not staked to AtlasMine
     *      { Lock } => { unstaked amount }
     */
    mapping(IAtlasMine.Lock => uint256) public unstakedAmount;

    // ========== Access Control States ==========
    mapping(address => bool) public superAdmins;

    /**
     * @dev Whitelisted vaults
     *      { vault address } => { Vault }
     */
    mapping(address => Vault) public vaults;

    function initialize(
        address _magic,
        address _atlasMine,
        address _treasury,
        address _battleflyBot,
        IAtlasMine.Lock[] memory _allowedLocks
    ) external initializer {
        __ERC1155Holder_init();
        __ERC721Holder_init();
        __Ownable_init();
        __ReentrancyGuard_init();

        require(_magic != address(0), "BattleflyAtlasStaker: invalid address");
        require(_atlasMine != address(0), "BattleflyAtlasStaker: invalid address");
        MAGIC = IERC20Upgradeable(_magic);
        ATLAS_MINE = IAtlasMine(_atlasMine);

        superAdmins[msg.sender] = true;
        LOCKS = [
            IAtlasMine.Lock.twoWeeks,
            IAtlasMine.Lock.oneMonth,
            IAtlasMine.Lock.threeMonths,
            IAtlasMine.Lock.sixMonths,
            IAtlasMine.Lock.twelveMonths
        ];

        nextExecution = block.timestamp;

        setTreasury(_treasury);
        setBattleflyBot(_battleflyBot);
        setAllowedLocks(_allowedLocks);

        approveLegion(true);
        approveTreasure(true);
    }

    // ============================== Vault Operations ==============================

    /**
     * @dev deposit an amount of MAGIC in the AtlasStaker for a particular lock period
     */
    function deposit(uint256 _amount, IAtlasMine.Lock _lock)
        external
        override
        onlyWhitelistedVaults
        nonReentrant
        whenNotPaused
        onlyAvailableLock(_lock)
        returns (uint256)
    {
        require(_amount > 0, "BattflyAtlasStaker: cannot deposit 0");
        // Transfer MAGIC from Vault
        MAGIC.safeTransferFrom(msg.sender, address(this), _amount);

        uint256 newDepositId = ++currentDepositId;
        _deposit(newDepositId, _amount, _lock);
        return newDepositId;
    }

    /**
     * @dev withdraw a vaultstake from the AtlasStaker with a specific depositId
     */
    function withdraw(uint256 _depositId) public override nonReentrant whenNotPaused returns (uint256 amount) {
        VaultStake memory vaultStake = vaultStakes[_depositId];
        require(vaultStake.vault == msg.sender, "BattleflyAtlasStaker: caller is not a correct vault");
        // withdraw can only happen if the retention period has passed.
        require(canWithdraw(_depositId), "BattleflyAtlasStaker: position is locked");

        amount = vaultStake.amount;
        // Withdraw MAGIC to user
        MAGIC.safeTransfer(msg.sender, amount);

        // claim remaining emissions
        (uint256 emission, ) = getClaimableEmission(_depositId);
        if (emission > 0) {
            amount += _claim(_depositId);
        }

        // Reset vault stake data
        delete vaultStakes[_depositId];
        depositIdByVault[msg.sender].remove(_depositId);
        emit WithdrawPosition(msg.sender, amount, _depositId);
    }

    /**
     * @dev Claim emissions from a vaultstake in the AtlasStaker with a specific depositId
     */
    function claim(uint256 _depositId) public override nonReentrant whenNotPaused returns (uint256 amount) {
        amount = _claim(_depositId);
    }

    /**
     * @dev Request a withdrawal for a specific depositId. This is required because the vaultStake will be restaked in blocks of 2 weeks after the unlock period has passed.
     * This function is used to notify the AtlasStaker that it should not restake the vaultStake on the next iteration and the initial stake becomes unlocked.
     */
    function requestWithdrawal(uint256 _depositId) public override nonReentrant whenNotPaused returns (uint64) {
        VaultStake storage vaultStake = vaultStakes[_depositId];
        require(vaultStake.vault == msg.sender, "BattleflyAtlasStaker: caller is not a correct vault");
        require(vaultStake.retentionUnlock == 0, "BattleflyAtlasStaker: withdrawal already requested");
        // Every epoch is 1 day; We can start requesting for a withdrawal 14 days before the unlock period.
        require(currentEpoch >= (vaultStake.unlockAt - 14), "BattleflyAtlasStaker: position not yet unlockable");

        // We set the retention period before the withdrawal can happen to the nearest epoch in the future
        uint64 retentionUnlock = currentEpoch < vaultStake.unlockAt
            ? vaultStake.unlockAt
            : currentEpoch + (14 - ((currentEpoch - vaultStake.unlockAt) % 14));
        vaultStake.retentionUnlock = retentionUnlock - 1 == currentEpoch ? retentionUnlock + 14 : retentionUnlock;
        unstakeAmountAtEpoch[vaultStake.retentionUnlock - 1] += vaultStake.amount;
        emit RequestWithdrawal(msg.sender, vaultStake.retentionUnlock, _depositId);
        return vaultStake.retentionUnlock;
    }

    // ============================== Super Admin Operations ==============================

    /**
     * @dev Execute the daily cron job to deposit funds to AtlasMine & claim emissions from AtlasMine
     *      The Battlefly CRON BOT will use this function to execute deposit/claim.
     */
    function executeAll() external onlyBattleflyBot {
        require(block.timestamp >= nextExecution, "BattleflyAtlasStaker: Executed less than 24h ago");
        // set to 24 hours - 5 minutes to take blockchain tx delays into account.
        nextExecution = block.timestamp + 86100;

        // Harvest all positions from AtlasMine
        _executeHarvestAll();

        // Withdraw all positions from AtlasMine
        _executeWithdrawAll();

        // Possibly correct the amount to be deposited due to users withdrawing their stake.
        _correctForUserWithdrawals();

        // Stake all funds to AtlasMine
        _executeDepositAll();
    }

    /**
     * @dev Approve TREASURE ERC1155 NFT transfer to deposit into AtlasMine contract
     */
    function approveTreasure(bool _approve) public onlySuperAdmin {
        getTREASURE().setApprovalForAll(address(ATLAS_MINE), _approve);
    }

    /**
     * @dev Approve LEGION ERC721 NFT transfer to deposit into AtlasMine contract
     */
    function approveLegion(bool _approve) public onlySuperAdmin {
        getLEGION().setApprovalForAll(address(ATLAS_MINE), _approve);
    }

    /**
     * @dev Stake TREASURE ERC1155 NFT
     */
    function stakeTreasure(uint256 _tokenId, uint256 _amount) external onlySuperAdmin nonReentrant {
        require(_amount > 0, "BattleflyAtlasStaker: Invalid TREASURE amount");

        // Caller's balance check already implemented in _safeTransferFrom() in ERC1155Upgradeable contract
        getTREASURE().safeTransferFrom(msg.sender, address(this), _tokenId, _amount, "");
        treasureStakers[_tokenId][msg.sender] += _amount;

        // Token Approval is already done in constructor
        ATLAS_MINE.stakeTreasure(_tokenId, _amount);
        emit StakedTreasure(msg.sender, _tokenId, _amount);
    }

    /**
     * @dev Unstake TREASURE ERC1155 NFT
     */
    function unstakeTreasure(uint256 _tokenId, uint256 _amount) external onlySuperAdmin nonReentrant {
        require(_amount > 0, "BattleflyAtlasStaker: Invalid TREASURE amount");
        require(treasureStakers[_tokenId][msg.sender] >= _amount, "BattleflyAtlasStaker: Invalid TREASURE amount");
        // Unstake TREASURE from AtlasMine
        ATLAS_MINE.unstakeTreasure(_tokenId, _amount);

        // Transfer TREASURE to the staker
        getTREASURE().safeTransferFrom(address(this), msg.sender, _tokenId, _amount, "");
        treasureStakers[_tokenId][msg.sender] -= _amount;
        emit UnstakedTreasure(msg.sender, _tokenId, _amount);
    }

    /**
     * @dev Stake LEGION ERC721 NFT
     */
    function stakeLegion(uint256 _tokenId) external onlySuperAdmin nonReentrant {
        // TokenId ownership validation is already implemented in safeTransferFrom function
        getLEGION().safeTransferFrom(msg.sender, address(this), _tokenId, "");
        legionStakers[_tokenId] = msg.sender;

        // Token Approval is already done in constructor
        ATLAS_MINE.stakeLegion(_tokenId);
        emit StakedLegion(msg.sender, _tokenId);
    }

    /**
     * @dev Unstake LEGION ERC721 NFT
     */
    function unstakeLegion(uint256 _tokenId) external onlySuperAdmin nonReentrant {
        require(legionStakers[_tokenId] == msg.sender, "BattleflyAtlasStaker: Invalid staker");
        // Unstake LEGION from AtlasMine
        ATLAS_MINE.unstakeLegion(_tokenId);

        // Transfer LEGION to the staker
        getLEGION().safeTransferFrom(address(this), msg.sender, _tokenId, "");
        legionStakers[_tokenId] = address(0);
        emit UnstakedLegion(msg.sender, _tokenId);
    }

    // ============================== Owner Operations ==============================

    /**
     * @dev Add super admin permission
     */
    function addSuperAdmin(address _admin) public onlyOwner {
        require(!superAdmins[_admin], "BattleflyAtlasStaker: admin already exists");
        superAdmins[_admin] = true;
        emit AddedSuperAdmin(_admin);
    }

    /**
     * @dev Batch adding super admin permission
     */
    function addSuperAdmins(address[] calldata _admins) external onlyOwner {
        for (uint256 i = 0; i < _admins.length; i++) {
            addSuperAdmin(_admins[i]);
        }
    }

    /**
     * @dev Remove super admin permission
     */
    function removeSuperAdmin(address _admin) public onlyOwner {
        require(superAdmins[_admin], "BattleflyAtlasStaker: admin does not exist");
        superAdmins[_admin] = false;
        emit RemovedSuperAdmin(_admin);
    }

    /**
     * @dev Batch removing super admin permission
     */
    function removeSuperAdmins(address[] calldata _admins) external onlyOwner {
        for (uint256 i = 0; i < _admins.length; i++) {
            removeSuperAdmin(_admins[i]);
        }
    }

    /**
     * @dev Add vault address
     */
    function addVault(address _vault, Vault calldata _vaultData) public onlyOwner {
        require(!vaults[_vault].enabled, "BattleflyAtlasStaker: vault is already added");
        require(_vaultData.fee + _vaultData.claimRate == FEE_DENOMINATOR, "BattleflyAtlasStaker: invalid vault info");

        Vault storage vault = vaults[_vault];
        vault.fee = _vaultData.fee;
        vault.claimRate = _vaultData.claimRate;
        vault.enabled = true;
        emit AddedVault(_vault, vault.fee, vault.claimRate);
    }

    /**
     * @dev Remove vault address
     */
    function removeVault(address _vault) public onlyOwner {
        Vault storage vault = vaults[_vault];
        require(vault.enabled, "BattleflyAtlasStaker: vault does not exist");
        vault.enabled = false;
        emit RemovedVault(_vault);
    }

    /**
     * @dev Set allowed locks
     */
    function setAllowedLocks(IAtlasMine.Lock[] memory _locks) public onlyOwner {
        allowedLocks = _locks;
    }

    /**
     * @dev Set treasury wallet address
     */
    function setTreasury(address _treasury) public onlyOwner {
        require(_treasury != address(0), "BattleflyAtlasStaker: invalid address");
        TREASURY_VAULT = IBattleflyTreasuryFlywheelVault(_treasury);
        emit SetTreasury(_treasury);
    }

    /**
     * @dev Set daily bot address
     */
    function setBattleflyBot(address _battleflyBot) public onlyOwner {
        require(_battleflyBot != address(0), "BattleflyAtlasStaker: invalid address");
        BATTLEFLY_BOT = _battleflyBot;
        emit SetBattleflyBot(_battleflyBot);
    }

    function setPause(bool _paused) external override onlyOwner {
        pausedUntil = _paused ? block.timestamp + 48 hours : 0;
        emit SetPause(_paused);
    }

    // ============================== VIEW ==============================

    /**
     * @dev Validate the lock period
     */
    function isValidLock(IAtlasMine.Lock _lock) public view returns (bool) {
        for (uint256 i = 0; i < allowedLocks.length; i++) {
            if (allowedLocks[i] == _lock) {
                return true;
            }
        }
        return false;
    }

    /**
     * @dev Get AtlasMine TREASURE ERC1155 NFT address
     */
    function getTREASURE() public view returns (IERC1155Upgradeable) {
        return IERC1155Upgradeable(ATLAS_MINE.treasure());
    }

    /**
     * @dev Get AtlasMine LEGION ERC721 NFT address
     */
    function getLEGION() public view returns (IERC721Upgradeable) {
        return IERC721Upgradeable(ATLAS_MINE.legion());
    }

    /**
     * @dev Get Unstaked MAGIC amount
     */
    function getUnstakedAmount() public view returns (uint256 amount) {
        IAtlasMine.Lock[] memory locks = LOCKS;
        for (uint256 i = 0; i < locks.length; i++) {
            amount += unstakedAmount[locks[i]];
        }
    }

    /**
     * @dev Get claimable MAGIC emission.
     * Emissions are:
     *      Emissions from normal lock period +
     *      Emissions from retention period -
     *      Already received emissions
     */
    function getClaimableEmission(uint256 _depositId) public view override returns (uint256 emission, uint256 fee) {
        VaultStake memory vaultStake = vaultStakes[_depositId];
        if (currentEpoch > 0) {
            uint64 retentionLock = vaultStake.retentionUnlock == 0 ? currentEpoch + 1 : vaultStake.retentionUnlock;
            uint64 x = vaultStake.retentionUnlock == 0 || vaultStake.retentionUnlock != vaultStake.unlockAt ? 0 : 1;
            emission =
                _getEmissionsForPeriod(vaultStake.amount, vaultStake.lockAt, vaultStake.unlockAt - x, vaultStake.lock) +
                _getEmissionsForPeriod(vaultStake.amount, vaultStake.unlockAt, retentionLock, vaultStake.lock) -
                vaultStake.paidEmission;
        }
        Vault memory vault = vaults[vaultStake.vault];
        fee = (emission * vault.fee) / FEE_DENOMINATOR;
        emission -= fee;
    }

    /**
     * @dev Get staked amount
     */
    function getDepositedAmount(uint256[] memory _depositIds) public view returns (uint256 amount) {
        for (uint256 i = 0; i < _depositIds.length; i++) {
            amount += vaultStakes[_depositIds[i]].amount;
        }
    }

    /**
     * @dev Get allowed locks
     */
    function getAllowedLocks() public view override returns (IAtlasMine.Lock[] memory) {
        return allowedLocks;
    }

    /**
     * @dev Get vault staked data
     */
    function getVaultStake(uint256 _depositId) public view override returns (VaultStake memory) {
        return vaultStakes[_depositId];
    }

    /**
     * @dev Gets the lock period in epochs
     */
    function getLockPeriod(IAtlasMine.Lock _lock) external view override returns (uint64 epoch) {
        return BattleflyAtlasStakerUtils.getLockPeriod(_lock, ATLAS_MINE) / 1 days;
    }

    /**
     * @dev Check if a vaultStake can be withdrawn
     */
    function canWithdraw(uint256 _depositId) public view override returns (bool withdrawable) {
        VaultStake memory vaultStake = vaultStakes[_depositId];
        withdrawable = (vaultStake.retentionUnlock > 0) && (vaultStake.retentionUnlock <= currentEpoch);
    }

    /**
     * @dev Check if a vaultStake can request a withdrawal
     */
    function canRequestWithdrawal(uint256 _depositId) public view override returns (bool requestable) {
        VaultStake memory vaultStake = vaultStakes[_depositId];
        requestable = (vaultStake.retentionUnlock == 0) && (currentEpoch >= (vaultStake.unlockAt - 14));
    }

    /**
     * @dev Get the depositIds of a user
     */
    function depositIdsOfVault(address vault) public view override returns (uint256[] memory depositIds) {
        return depositIdByVault[vault].values();
    }

    // ============================== Internal ==============================

    /**
     * @dev recalculate and update the emissions per share per lock period and per epoch
     *      1 share is 1 wei of Magic.
     */
    function _updateEmissionsForEpoch() private returns (uint256 totalEmission) {
        uint256[] memory positionIds = activePositionIds.values();

        // Total emissions of the current epoch are at least as much as the total emissions of the previous epoch
        for (uint256 i = 0; i < LOCKS.length; i++) {
            totalEmissionsAtEpochForLock[LOCKS[i]][currentEpoch] = currentEpoch > 0
                ? totalEmissionsAtEpochForLock[LOCKS[i]][currentEpoch - 1]
                : 0;
        }

        // Calculate the total amount of pending emissions and the total deposited amount for each lock period in the current epoch
        for (uint256 j = 0; j < positionIds.length; j++) {
            uint256 pendingRewards = ATLAS_MINE.pendingRewardsPosition(address(this), positionIds[j]);
            (, uint256 currentAmount, , , , , IAtlasMine.Lock _lock) = ATLAS_MINE.userInfo(
                address(this),
                positionIds[j]
            );
            totalEmission += pendingRewards;
            totalEmissionsAtEpochForLock[_lock][currentEpoch] += pendingRewards;
            totalStakedAtEpochForLock[_lock][currentEpoch] += currentAmount;
        }

        // Calculate the accrued emissions per share by (totalEmission * 1e18) / totalStaked
        // Set the total emissions to the accrued emissions of the current epoch + the previous epochs
        for (uint256 k = 0; k < LOCKS.length; k++) {
            uint256 totalStaked = totalStakedAtEpochForLock[LOCKS[k]][currentEpoch];
            if (totalStaked > 0) {
                uint256 accruedRewardsPerShare = (totalEmission * ONE) / totalStaked;
                totalPerShareAtEpochForLock[LOCKS[k]][currentEpoch] = currentEpoch > 0
                    ? totalPerShareAtEpochForLock[LOCKS[k]][currentEpoch - 1] + accruedRewardsPerShare
                    : accruedRewardsPerShare;
            } else {
                totalPerShareAtEpochForLock[LOCKS[k]][currentEpoch] = currentEpoch > 0
                    ? totalPerShareAtEpochForLock[LOCKS[k]][currentEpoch - 1]
                    : 0;
            }
        }
    }

    /**
     * @dev get the total amount of emissions of a certain period between two epochs.
     */
    function _getEmissionsForPeriod(
        uint256 amount,
        uint64 startEpoch,
        uint64 stopEpoch,
        IAtlasMine.Lock lock
    ) private view returns (uint256 emissions) {
        if (stopEpoch >= startEpoch && currentEpoch >= startEpoch) {
            uint256 totalEmissions = (amount * totalPerShareAtEpochForLock[lock][currentEpoch - 1]);
            uint256 emissionsTillExclusion = (amount * totalPerShareAtEpochForLock[lock][stopEpoch - 1]);
            uint256 emissionsTillInclusion = (amount * totalPerShareAtEpochForLock[lock][startEpoch - 1]);
            uint256 emissionsFromExclusion = emissionsTillExclusion > 0 ? (totalEmissions - emissionsTillExclusion) : 0;
            emissions = (totalEmissions - emissionsFromExclusion - emissionsTillInclusion) / ONE;
        }
    }

    /**
     * @dev Deposit MAGIC to AtlasMine
     */
    function _deposit(
        uint256 _depositId,
        uint256 _amount,
        IAtlasMine.Lock _lock
    ) private returns (uint256) {
        // We only deposit to AtlasMine in the next epoch. We can unlock after the lock period has passed.
        uint64 lockAt = currentEpoch + 1;
        uint64 unlockAt = currentEpoch + 1 + (BattleflyAtlasStakerUtils.getLockPeriod(_lock, ATLAS_MINE) / 1 days);

        vaultStakes[_depositId] = VaultStake(lockAt, unlockAt, 0, _amount, 0, msg.sender, _lock);
        // Updated unstaked MAGIC amount
        unstakedAmount[_lock] += _amount;
        depositIdByVault[msg.sender].add(_depositId);
        emit NewDeposit(msg.sender, _amount, unlockAt, _depositId);
        return unlockAt;
    }

    /**
     * @dev Claim emissions for a depositId
     */
    function _claim(uint256 _depositId) internal returns (uint256) {
        VaultStake storage vaultStake = vaultStakes[_depositId];
        require(vaultStake.vault == msg.sender, "BattleflyAtlasStaker: caller is not a correct vault");

        (uint256 emission, uint256 fee) = getClaimableEmission(_depositId);
        if (emission > 0) {
            MAGIC.safeTransfer(msg.sender, emission);
            if (fee > 0) {
                MAGIC.approve(address(TREASURY_VAULT), fee);
                TREASURY_VAULT.topupMagic(fee);
            }
            uint256 amount = emission + fee;
            vaultStake.paidEmission += amount;
            emit ClaimEmission(msg.sender, emission, _depositId);
        }
        return emission;
    }

    /**
     * @dev Execute the daily cron job to harvest all emissions from AtlasMine
     */
    function _executeHarvestAll() internal {
        uint256 pendingHarvest = _updateEmissionsForEpoch();
        uint256 preHarvest = MAGIC.balanceOf(address(this));
        for (uint64 i = 0; i < activePositionIds.length(); i++) {
            ATLAS_MINE.harvestPosition(activePositionIds.at(i));
        }
        uint256 harvested = MAGIC.balanceOf(address(this)) - preHarvest;
        require(pendingHarvest == harvested, "BattleflyAtlasStaker: pending harvest and actual harvest are not equal");
        // Increment the epoch
        currentEpoch++;
    }

    /**
     * @dev Possibly correct the amount to be deposited due to users withdrawing their stake.
     */
    function _correctForUserWithdrawals() internal {
        if (unstakedAmount[IAtlasMine.Lock.twoWeeks] >= unstakeAmountAtEpoch[currentEpoch]) {
            unstakedAmount[IAtlasMine.Lock.twoWeeks] -= unstakeAmountAtEpoch[currentEpoch];
        } else {
            //If not enough withdrawals available from current epoch, request more from the next epoch
            unstakeAmountAtEpoch[currentEpoch + 1] += (unstakeAmountAtEpoch[currentEpoch] -
                unstakedAmount[IAtlasMine.Lock.twoWeeks]);
            unstakedAmount[IAtlasMine.Lock.twoWeeks] = 0;
        }
    }

    /**
     * @dev Execute the daily cron job to deposit all to AtlasMine
     */
    function _executeDepositAll() internal {
        uint256 unstaked;
        for (uint256 i = 0; i < LOCKS.length; i++) {
            uint256 amount = unstakedAmount[LOCKS[i]];
            if (amount > 0) {
                unstaked += amount;
                MAGIC.safeApprove(address(ATLAS_MINE), amount);
                ATLAS_MINE.deposit(amount, LOCKS[i]);
                activePositionIds.add(ATLAS_MINE.currentId(address(this)));
                unstakedAmount[LOCKS[i]] = 0;
            }
        }
        emit DepositedAllToMine(unstaked);
    }

    /**
     * @dev Execute the daily cron job to withdraw all positions from AtlasMine
     */
    function _executeWithdrawAll() internal {
        uint256[] memory depositIds = activePositionIds.values();
        for (uint256 i = 0; i < depositIds.length; i++) {
            (uint256 amount, , , uint256 lockedUntil, , , IAtlasMine.Lock lock) = ATLAS_MINE.userInfo(
                address(this),
                depositIds[i]
            );
            uint256 totalLockedPeriod = lockedUntil + ATLAS_MINE.getVestingTime(lock);

            // If the position is available to withdraw
            if (totalLockedPeriod <= block.timestamp) {
                ATLAS_MINE.withdrawPosition(depositIds[i], type(uint256).max);
                activePositionIds.remove(depositIds[i]);
                // Directly register for restaking, unless a withdrawal is requested (we correct this in _correctForUserWithdrawals())
                unstakedAmount[IAtlasMine.Lock.twoWeeks] += uint256(amount);
            }
        }
    }

    // ============================== Modifiers ==============================

    modifier onlySuperAdmin() {
        require(superAdmins[msg.sender], "BattleflyAtlasStaker: caller is not a super admin");
        _;
    }

    modifier onlyWhitelistedVaults() {
        require(vaults[msg.sender].enabled, "BattleflyAtlasStaker: caller is not whitelisted");
        _;
    }

    modifier onlyAvailableLock(IAtlasMine.Lock _lock) {
        require(isValidLock(_lock), "BattleflyAtlasStaker: invalid lock period");
        _;
    }

    modifier onlyBattleflyBot() {
        require(msg.sender == BATTLEFLY_BOT, "BattleflyAtlasStaker: caller is not a battlefly bot");
        _;
    }

    modifier whenNotPaused() {
        require(block.timestamp > pausedUntil, "BattleflyAtlasStaker: contract paused");
        _;
    }
}

File 72 of 122 : BattleflyAtlasStakerUtils.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "../interfaces/IAtlasMine.sol";

library BattleflyAtlasStakerUtils {
    /**
     * @dev Get lock period
     *      Need to consider about adding 1 more day to lock period regarding the daily cron job
     */
    function getLockPeriod(IAtlasMine.Lock _lock, IAtlasMine ATLAS_MINE) external pure returns (uint64) {
        if (_lock == IAtlasMine.Lock.twoWeeks) {
            return 14 days + 1 days + uint64(ATLAS_MINE.getVestingTime(_lock));
        }
        if (_lock == IAtlasMine.Lock.oneMonth) {
            return 30 days + 1 days + uint64(ATLAS_MINE.getVestingTime(_lock));
        }
        if (_lock == IAtlasMine.Lock.threeMonths) {
            return 90 days + 1 days + uint64(ATLAS_MINE.getVestingTime(_lock));
        }
        if (_lock == IAtlasMine.Lock.sixMonths) {
            return 180 days + 1 days + uint64(ATLAS_MINE.getVestingTime(_lock));
        }
        if (_lock == IAtlasMine.Lock.twelveMonths) {
            return 365 days + 1 days + uint64(ATLAS_MINE.getVestingTime(_lock));
        }

        revert("BattleflyAtlasStaker: Invalid Lock");
    }
}

File 73 of 122 : BattleflyFlywheelVaultV02.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;

import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/cryptography/MerkleProofUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/structs/EnumerableSetUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";

import "./BattleflyFounderVaultV08.sol";
import "./interfaces/IBattleflyAtlasStakerV02.sol";
import "./interfaces/IBattleflyFlywheelVaultV02.sol";
import "./interfaces/IAtlasMine.sol";

contract BattleflyFlywheelVaultV02 is
    IBattleflyFlywheelVaultV02,
    Initializable,
    OwnableUpgradeable,
    ReentrancyGuardUpgradeable
{
    using SafeERC20Upgradeable for IERC20Upgradeable;
    using EnumerableSetUpgradeable for EnumerableSetUpgradeable.UintSet;

    /**
     * @dev Immutable states
     */
    IERC20Upgradeable public MAGIC;
    IBattleflyAtlasStakerV02 public ATLAS_STAKER;

    string public name;

    /**
     * @dev User stake data
     *      { depositId } => { User stake data }
     */
    mapping(uint256 => UserStake) public userStakes;

    /**
     * @dev User's depositIds
     *      { user } => { depositIds }
     */
    mapping(address => EnumerableSetUpgradeable.UintSet) private depositIdByUser;

    /**
     * @dev Whitelisted users
     *      { user } => { is whitelisted }
     */
    mapping(address => bool) public whitelistedUsers;

    function initialize(address _atlasStaker, string memory _name) external initializer {
        __Ownable_init();
        __ReentrancyGuard_init();

        require(_atlasStaker != address(0), "BattleflyFlywheelVault: invalid address");
        require(bytes(_name).length > 0, "BattleflyFlywheelVault: invalid name");

        ATLAS_STAKER = IBattleflyAtlasStakerV02(_atlasStaker);
        MAGIC = ATLAS_STAKER.MAGIC();

        name = _name;
    }

    /**
     * @dev Deposit funds to AtlasStaker
     */
    function deposit(uint128 _amount, IAtlasMine.Lock _lock)
        external
        override
        nonReentrant
        onlyMembers
        returns (uint256 atlasStakerDepositId)
    {
        MAGIC.safeTransferFrom(msg.sender, address(this), _amount);
        MAGIC.safeApprove(address(ATLAS_STAKER), _amount);

        atlasStakerDepositId = ATLAS_STAKER.deposit(uint256(_amount), _lock);
        IBattleflyAtlasStakerV02.VaultStake memory vaultStake = ATLAS_STAKER.getVaultStake(atlasStakerDepositId);

        UserStake storage userStake = userStakes[atlasStakerDepositId];
        userStake.amount = _amount;
        userStake.lockAt = vaultStake.lockAt;
        userStake.owner = msg.sender;
        userStake.lock = _lock;

        depositIdByUser[msg.sender].add(atlasStakerDepositId);

        emit NewUserStake(atlasStakerDepositId, _amount, vaultStake.unlockAt, msg.sender, _lock);
    }

    /**
     * @dev Withdraw staked funds from AtlasStaker
     */
    function withdraw(uint256[] calldata _depositIds) public override nonReentrant returns (uint256 amount) {
        for (uint256 i = 0; i < _depositIds.length; i++) {
            amount += _withdraw(_depositIds[i]);
        }
    }

    /**
     * @dev Withdraw all from AtlasStaker. This is only possible when the retention period of 14 epochs has passed.
     * The retention period is started when a withdrawal for the stake is requested.
     */
    function withdrawAll() public override nonReentrant returns (uint256 amount) {
        uint256[] memory depositIds = depositIdByUser[msg.sender].values();
        require(depositIds.length > 0, "BattleflyFlywheelVault: No deposited funds");
        for (uint256 i = 0; i < depositIds.length; i++) {
            if (ATLAS_STAKER.canWithdraw(depositIds[i])) {
                amount += _withdraw(depositIds[i]);
            }
        }
    }

    /**
     * @dev Request a withdrawal from AtlasStaker. This works with a retention period of 14 epochs.
     * Once the retention period has passed, the stake can be withdrawn.
     */
    function requestWithdrawal(uint256[] calldata _depositIds) public override {
        for (uint256 i = 0; i < _depositIds.length; i++) {
            UserStake memory userStake = userStakes[_depositIds[i]];
            require(userStake.owner == msg.sender, "BattleflyFlywheelVault: caller is not the owner");
            ATLAS_STAKER.requestWithdrawal(_depositIds[i]);
            emit RequestWithdrawal(_depositIds[i]);
        }
    }

    /**
     * @dev Claim emission from AtlasStaker
     */
    function claim(uint256 _depositId) public override nonReentrant returns (uint256 emission) {
        emission = _claim(_depositId);
    }

    /**
     * @dev Claim all emissions from AtlasStaker
     */
    function claimAll() external override nonReentrant returns (uint256 amount) {
        uint256[] memory depositIds = depositIdByUser[msg.sender].values();
        require(depositIds.length > 0, "BattleflyFlywheelVault: No deposited funds");

        for (uint256 i = 0; i < depositIds.length; i++) {
            amount += _claim(depositIds[i]);
        }
    }

    /**
     * @dev Whitelist user
     */
    function whitelistUser(address _who) public onlyOwner {
        require(!whitelistedUsers[_who], "BattlefalyWheelVault: Already whitelisted");
        whitelistedUsers[_who] = true;
        emit AddedUser(_who);
    }

    /**
     * @dev Whitelist users
     */
    function whitelistUsers(address[] calldata _users) external onlyOwner {
        for (uint256 i = 0; i < _users.length; i++) {
            whitelistUser(_users[i]);
        }
    }

    /**
     * @dev Remove user from whitelist
     */
    function removeUser(address _who) public onlyOwner {
        require(whitelistedUsers[_who], "BattlefalyWheelVault: Not whitelisted yet");
        whitelistedUsers[_who] = false;
        emit RemovedUser(_who);
    }

    /**
     * @dev Remove users from whitelist
     */
    function removeUsers(address[] calldata _users) external onlyOwner {
        for (uint256 i = 0; i < _users.length; i++) {
            removeUser(_users[i]);
        }
    }

    /**
     * @dev Set the name of the vault
     */
    function setName(string memory _name) public onlyOwner {
        name = _name;
    }

    // ================ INTERNAL ================

    /**
     * @dev Withdraw a stake from AtlasStaker (Only possible when the retention period has passed)
     */
    function _withdraw(uint256 _depositId) internal returns (uint256 amount) {
        UserStake memory userStake = userStakes[_depositId];
        require(userStake.owner == msg.sender, "BattleflyFlywheelVault: caller is not the owner");
        require(ATLAS_STAKER.canWithdraw(_depositId), "BattleflyFlywheelVault: stake not yet unlocked");

        amount = ATLAS_STAKER.withdraw(_depositId);
        MAGIC.safeTransfer(msg.sender, amount);
        depositIdByUser[msg.sender].remove(_depositId);
        delete userStakes[_depositId];
        emit WithdrawPosition(_depositId, amount);
    }

    /**
     * @dev Claim emission from AtlasStaker
     */
    function _claim(uint256 _depositId) internal returns (uint256 emission) {
        UserStake memory userStake = userStakes[_depositId];
        require(userStake.owner == msg.sender, "BattleflyFlywheelVault: caller is not the owner");

        emission = ATLAS_STAKER.claim(_depositId);
        MAGIC.safeTransfer(msg.sender, emission);
        emit ClaimEmission(_depositId, emission);
    }

    // ================== VIEW ==================

    /**
     * @dev Get allowed lock periods from AtlasStaker
     */
    function getAllowedLocks() public view override returns (IAtlasMine.Lock[] memory) {
        return ATLAS_STAKER.getAllowedLocks();
    }

    /**
     * @dev Get claimed emission
     */
    function getClaimableEmission(uint256 _depositId) public view override returns (uint256 emission) {
        (emission, ) = ATLAS_STAKER.getClaimableEmission(_depositId);
    }

    /**
     * @dev Check if a vaultStake is eligible for requesting a withdrawal.
     * This is 14 epochs before the end of the initial lock period.
     */
    function canRequestWithdrawal(uint256 _depositId) public view override returns (bool requestable) {
        return ATLAS_STAKER.canRequestWithdrawal(_depositId);
    }

    /**
     * @dev Check if a vaultStake is eligible for a withdrawal
     * This is when the retention period has passed
     */
    function canWithdraw(uint256 _depositId) public view override returns (bool withdrawable) {
        return ATLAS_STAKER.canWithdraw(_depositId);
    }

    /**
     * @dev Check the epoch in which the initial lock period of the vaultStake expires.
     * This is at the end of the lock period
     */
    function initialUnlock(uint256 _depositId) public view override returns (uint64 epoch) {
        return ATLAS_STAKER.getVaultStake(_depositId).unlockAt;
    }

    /**
     * @dev Check the epoch in which the retention period of the vaultStake expires.
     * This is 14 epochs after the withdrawal request has taken place
     */
    function retentionUnlock(uint256 _depositId) public view override returns (uint64 epoch) {
        return ATLAS_STAKER.getVaultStake(_depositId).retentionUnlock;
    }

    /**
     * @dev Get the currently active epoch
     */
    function getCurrentEpoch() public view override returns (uint64 epoch) {
        return ATLAS_STAKER.currentEpoch();
    }

    /**
     * @dev Get the depositIds of a user
     */
    function depositIdsOfUser(address user) public view override returns (uint256[] memory depositIds) {
        return depositIdByUser[user].values();
    }

    /**
     * @dev Return the name of the vault
     */
    function getName() public view override returns (string memory) {
        return name;
    }

    // ================== MODIFIERS ==================

    modifier onlyMembers() {
        require(whitelistedUsers[msg.sender], "BattleflyWheelVault: caller is not a whitelisted member");
        _;
    }
}

File 74 of 122 : IBattleflyFlywheelVaultV02.sol
// SPDX-License-Identifier: MIT
pragma solidity >0.8.0;
import "./IAtlasMine.sol";

interface IBattleflyFlywheelVaultV02 {
    struct UserStake {
        uint64 lockAt;
        uint256 amount;
        address owner;
        IAtlasMine.Lock lock;
    }

    function deposit(uint128, IAtlasMine.Lock) external returns (uint256);

    function withdraw(uint256[] calldata _depositIds) external returns (uint256);

    function withdrawAll() external returns (uint256);

    function requestWithdrawal(uint256[] calldata _depositIds) external;

    function claim(uint256) external returns (uint256);

    function claimAll() external returns (uint256);

    function getAllowedLocks() external view returns (IAtlasMine.Lock[] memory);

    function getClaimableEmission(uint256) external view returns (uint256);

    function canRequestWithdrawal(uint256 _depositId) external view returns (bool requestable);

    function canWithdraw(uint256 _depositId) external view returns (bool withdrawable);

    function initialUnlock(uint256 _depositId) external view returns (uint64 epoch);

    function retentionUnlock(uint256 _depositId) external view returns (uint64 epoch);

    function getCurrentEpoch() external view returns (uint64 epoch);

    function depositIdsOfUser(address user) external view returns (uint256[] memory depositIds);

    function getName() external view returns (string memory);

    // ================== EVENTS ==================
    event NewUserStake(
        uint256 indexed depositId,
        uint256 amount,
        uint256 unlockAt,
        address indexed owner,
        IAtlasMine.Lock lock
    );
    event UpdateUserStake(
        uint256 indexed depositId,
        uint256 amount,
        uint256 unlockAt,
        address indexed owner,
        IAtlasMine.Lock lock
    );
    event ClaimEmission(uint256 indexed depositId, uint256 emission);
    event WithdrawPosition(uint256 indexed depositId, uint256 amount);
    event RequestWithdrawal(uint256 indexed depositId);

    event AddedUser(address indexed vault);
    event RemovedUser(address indexed vault);
}

File 75 of 122 : HyperdomeContract.sol
// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.2;

import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721EnumerableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/CountersUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/math/SafeMathUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/structs/EnumerableSetUpgradeable.sol";

contract HyperdomeContract is ERC721Upgradeable, ERC721EnumerableUpgradeable, OwnableUpgradeable {
    using SafeMathUpgradeable for uint256;
    using AddressUpgradeable for address;
    using CountersUpgradeable for CountersUpgradeable.Counter;
    using EnumerableSetUpgradeable for EnumerableSetUpgradeable.UintSet;
    using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet;

    CountersUpgradeable.Counter private _tokenIdCounter;
    mapping(address => bool) private adminAccess;

    function initialize() public initializer {
        __ERC721_init("HyperdomeLand", "Hyperdome");
        __Ownable_init();
    }

    function setAdminAccess(address user, bool access) external onlyOwner {
        adminAccess[user] = access;
    }

    function mintHyperdome(address receiver) external onlyAdminAccess returns (uint256) {
        uint256 nextTokenId = _getNextTokenId();
        _mint(receiver, nextTokenId);
        return nextTokenId;
    }

    function _getNextTokenId() private view returns (uint256) {
        return (_tokenIdCounter.current());
    }

    function _mint(address to, uint256 tokenId) internal override(ERC721Upgradeable) {
        super._mint(to, tokenId);
        _tokenIdCounter.increment();
    }

    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 tokenId
    ) internal override(ERC721Upgradeable, ERC721EnumerableUpgradeable) {
        super._beforeTokenTransfer(from, to, tokenId);
    }

    function supportsInterface(bytes4 interfaceId)
        public
        view
        override(ERC721Upgradeable, ERC721EnumerableUpgradeable)
        returns (bool)
    {
        return super.supportsInterface(interfaceId);
    }

    modifier onlyAdminAccess() {
        require(adminAccess[_msgSender()] == true || _msgSender() == owner(), "Require admin access");
        _;
    }
}

File 76 of 122 : ERC721EnumerableUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/ERC721Enumerable.sol)

pragma solidity ^0.8.0;

import "../ERC721Upgradeable.sol";
import "./IERC721EnumerableUpgradeable.sol";
import "../../../proxy/utils/Initializable.sol";

/**
 * @dev This implements an optional extension of {ERC721} defined in the EIP that adds
 * enumerability of all the token ids in the contract as well as all token ids owned by each
 * account.
 */
abstract contract ERC721EnumerableUpgradeable is Initializable, ERC721Upgradeable, IERC721EnumerableUpgradeable {
    function __ERC721Enumerable_init() internal onlyInitializing {
    }

    function __ERC721Enumerable_init_unchained() internal onlyInitializing {
    }
    // Mapping from owner to list of owned token IDs
    mapping(address => mapping(uint256 => uint256)) private _ownedTokens;

    // Mapping from token ID to index of the owner tokens list
    mapping(uint256 => uint256) private _ownedTokensIndex;

    // Array with all token ids, used for enumeration
    uint256[] private _allTokens;

    // Mapping from token id to position in the allTokens array
    mapping(uint256 => uint256) private _allTokensIndex;

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165Upgradeable, ERC721Upgradeable) returns (bool) {
        return interfaceId == type(IERC721EnumerableUpgradeable).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev See {IERC721Enumerable-tokenOfOwnerByIndex}.
     */
    function tokenOfOwnerByIndex(address owner, uint256 index) public view virtual override returns (uint256) {
        require(index < ERC721Upgradeable.balanceOf(owner), "ERC721Enumerable: owner index out of bounds");
        return _ownedTokens[owner][index];
    }

    /**
     * @dev See {IERC721Enumerable-totalSupply}.
     */
    function totalSupply() public view virtual override returns (uint256) {
        return _allTokens.length;
    }

    /**
     * @dev See {IERC721Enumerable-tokenByIndex}.
     */
    function tokenByIndex(uint256 index) public view virtual override returns (uint256) {
        require(index < ERC721EnumerableUpgradeable.totalSupply(), "ERC721Enumerable: global index out of bounds");
        return _allTokens[index];
    }

    /**
     * @dev Hook that is called before any token transfer. This includes minting
     * and burning.
     *
     * Calling conditions:
     *
     * - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be
     * transferred to `to`.
     * - When `from` is zero, `tokenId` will be minted for `to`.
     * - When `to` is zero, ``from``'s `tokenId` will be burned.
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 tokenId
    ) internal virtual override {
        super._beforeTokenTransfer(from, to, tokenId);

        if (from == address(0)) {
            _addTokenToAllTokensEnumeration(tokenId);
        } else if (from != to) {
            _removeTokenFromOwnerEnumeration(from, tokenId);
        }
        if (to == address(0)) {
            _removeTokenFromAllTokensEnumeration(tokenId);
        } else if (to != from) {
            _addTokenToOwnerEnumeration(to, tokenId);
        }
    }

    /**
     * @dev Private function to add a token to this extension's ownership-tracking data structures.
     * @param to address representing the new owner of the given token ID
     * @param tokenId uint256 ID of the token to be added to the tokens list of the given address
     */
    function _addTokenToOwnerEnumeration(address to, uint256 tokenId) private {
        uint256 length = ERC721Upgradeable.balanceOf(to);
        _ownedTokens[to][length] = tokenId;
        _ownedTokensIndex[tokenId] = length;
    }

    /**
     * @dev Private function to add a token to this extension's token tracking data structures.
     * @param tokenId uint256 ID of the token to be added to the tokens list
     */
    function _addTokenToAllTokensEnumeration(uint256 tokenId) private {
        _allTokensIndex[tokenId] = _allTokens.length;
        _allTokens.push(tokenId);
    }

    /**
     * @dev Private function to remove a token from this extension's ownership-tracking data structures. Note that
     * while the token is not assigned a new owner, the `_ownedTokensIndex` mapping is _not_ updated: this allows for
     * gas optimizations e.g. when performing a transfer operation (avoiding double writes).
     * This has O(1) time complexity, but alters the order of the _ownedTokens array.
     * @param from address representing the previous owner of the given token ID
     * @param tokenId uint256 ID of the token to be removed from the tokens list of the given address
     */
    function _removeTokenFromOwnerEnumeration(address from, uint256 tokenId) private {
        // To prevent a gap in from's tokens array, we store the last token in the index of the token to delete, and
        // then delete the last slot (swap and pop).

        uint256 lastTokenIndex = ERC721Upgradeable.balanceOf(from) - 1;
        uint256 tokenIndex = _ownedTokensIndex[tokenId];

        // When the token to delete is the last token, the swap operation is unnecessary
        if (tokenIndex != lastTokenIndex) {
            uint256 lastTokenId = _ownedTokens[from][lastTokenIndex];

            _ownedTokens[from][tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
            _ownedTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index
        }

        // This also deletes the contents at the last position of the array
        delete _ownedTokensIndex[tokenId];
        delete _ownedTokens[from][lastTokenIndex];
    }

    /**
     * @dev Private function to remove a token from this extension's token tracking data structures.
     * This has O(1) time complexity, but alters the order of the _allTokens array.
     * @param tokenId uint256 ID of the token to be removed from the tokens list
     */
    function _removeTokenFromAllTokensEnumeration(uint256 tokenId) private {
        // To prevent a gap in the tokens array, we store the last token in the index of the token to delete, and
        // then delete the last slot (swap and pop).

        uint256 lastTokenIndex = _allTokens.length - 1;
        uint256 tokenIndex = _allTokensIndex[tokenId];

        // When the token to delete is the last token, the swap operation is unnecessary. However, since this occurs so
        // rarely (when the last minted token is burnt) that we still do the swap here to avoid the gas cost of adding
        // an 'if' statement (like in _removeTokenFromOwnerEnumeration)
        uint256 lastTokenId = _allTokens[lastTokenIndex];

        _allTokens[tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
        _allTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index

        // This also deletes the contents at the last position of the array
        delete _allTokensIndex[tokenId];
        _allTokens.pop();
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[46] private __gap;
}

File 77 of 122 : CountersUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Counters.sol)

pragma solidity ^0.8.0;

/**
 * @title Counters
 * @author Matt Condon (@shrugs)
 * @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number
 * of elements in a mapping, issuing ERC721 ids, or counting request ids.
 *
 * Include with `using Counters for Counters.Counter;`
 */
library CountersUpgradeable {
    struct Counter {
        // This variable should never be directly accessed by users of the library: interactions must be restricted to
        // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
        // this feature: see https://github.com/ethereum/solidity/issues/4637
        uint256 _value; // default: 0
    }

    function current(Counter storage counter) internal view returns (uint256) {
        return counter._value;
    }

    function increment(Counter storage counter) internal {
        unchecked {
            counter._value += 1;
        }
    }

    function decrement(Counter storage counter) internal {
        uint256 value = counter._value;
        require(value > 0, "Counter: decrement overflow");
        unchecked {
            counter._value = value - 1;
        }
    }

    function reset(Counter storage counter) internal {
        counter._value = 0;
    }
}

File 78 of 122 : ERC721Upgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/ERC721.sol)

pragma solidity ^0.8.0;

import "./IERC721Upgradeable.sol";
import "./IERC721ReceiverUpgradeable.sol";
import "./extensions/IERC721MetadataUpgradeable.sol";
import "../../utils/AddressUpgradeable.sol";
import "../../utils/ContextUpgradeable.sol";
import "../../utils/StringsUpgradeable.sol";
import "../../utils/introspection/ERC165Upgradeable.sol";
import "../../proxy/utils/Initializable.sol";

/**
 * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
 * the Metadata extension, but not including the Enumerable extension, which is available separately as
 * {ERC721Enumerable}.
 */
contract ERC721Upgradeable is Initializable, ContextUpgradeable, ERC165Upgradeable, IERC721Upgradeable, IERC721MetadataUpgradeable {
    using AddressUpgradeable for address;
    using StringsUpgradeable for uint256;

    // Token name
    string private _name;

    // Token symbol
    string private _symbol;

    // Mapping from token ID to owner address
    mapping(uint256 => address) private _owners;

    // Mapping owner address to token count
    mapping(address => uint256) private _balances;

    // Mapping from token ID to approved address
    mapping(uint256 => address) private _tokenApprovals;

    // Mapping from owner to operator approvals
    mapping(address => mapping(address => bool)) private _operatorApprovals;

    /**
     * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
     */
    function __ERC721_init(string memory name_, string memory symbol_) internal onlyInitializing {
        __ERC721_init_unchained(name_, symbol_);
    }

    function __ERC721_init_unchained(string memory name_, string memory symbol_) internal onlyInitializing {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165Upgradeable, IERC165Upgradeable) returns (bool) {
        return
            interfaceId == type(IERC721Upgradeable).interfaceId ||
            interfaceId == type(IERC721MetadataUpgradeable).interfaceId ||
            super.supportsInterface(interfaceId);
    }

    /**
     * @dev See {IERC721-balanceOf}.
     */
    function balanceOf(address owner) public view virtual override returns (uint256) {
        require(owner != address(0), "ERC721: balance query for the zero address");
        return _balances[owner];
    }

    /**
     * @dev See {IERC721-ownerOf}.
     */
    function ownerOf(uint256 tokenId) public view virtual override returns (address) {
        address owner = _owners[tokenId];
        require(owner != address(0), "ERC721: owner query for nonexistent token");
        return owner;
    }

    /**
     * @dev See {IERC721Metadata-name}.
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }

    /**
     * @dev See {IERC721Metadata-symbol}.
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @dev See {IERC721Metadata-tokenURI}.
     */
    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
        require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");

        string memory baseURI = _baseURI();
        return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : "";
    }

    /**
     * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
     * token will be the concatenation of the `baseURI` and the `tokenId`. Empty
     * by default, can be overridden in child contracts.
     */
    function _baseURI() internal view virtual returns (string memory) {
        return "";
    }

    /**
     * @dev See {IERC721-approve}.
     */
    function approve(address to, uint256 tokenId) public virtual override {
        address owner = ERC721Upgradeable.ownerOf(tokenId);
        require(to != owner, "ERC721: approval to current owner");

        require(
            _msgSender() == owner || isApprovedForAll(owner, _msgSender()),
            "ERC721: approve caller is not owner nor approved for all"
        );

        _approve(to, tokenId);
    }

    /**
     * @dev See {IERC721-getApproved}.
     */
    function getApproved(uint256 tokenId) public view virtual override returns (address) {
        require(_exists(tokenId), "ERC721: approved query for nonexistent token");

        return _tokenApprovals[tokenId];
    }

    /**
     * @dev See {IERC721-setApprovalForAll}.
     */
    function setApprovalForAll(address operator, bool approved) public virtual override {
        _setApprovalForAll(_msgSender(), operator, approved);
    }

    /**
     * @dev See {IERC721-isApprovedForAll}.
     */
    function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
        return _operatorApprovals[owner][operator];
    }

    /**
     * @dev See {IERC721-transferFrom}.
     */
    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) public virtual override {
        //solhint-disable-next-line max-line-length
        require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");

        _transfer(from, to, tokenId);
    }

    /**
     * @dev See {IERC721-safeTransferFrom}.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) public virtual override {
        safeTransferFrom(from, to, tokenId, "");
    }

    /**
     * @dev See {IERC721-safeTransferFrom}.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        bytes memory _data
    ) public virtual override {
        require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");
        _safeTransfer(from, to, tokenId, _data);
    }

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
     *
     * `_data` is additional data, it has no specified format and it is sent in call to `to`.
     *
     * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g.
     * implement alternative mechanisms to perform token transfer, such as signature-based.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function _safeTransfer(
        address from,
        address to,
        uint256 tokenId,
        bytes memory _data
    ) internal virtual {
        _transfer(from, to, tokenId);
        require(_checkOnERC721Received(from, to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer");
    }

    /**
     * @dev Returns whether `tokenId` exists.
     *
     * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
     *
     * Tokens start existing when they are minted (`_mint`),
     * and stop existing when they are burned (`_burn`).
     */
    function _exists(uint256 tokenId) internal view virtual returns (bool) {
        return _owners[tokenId] != address(0);
    }

    /**
     * @dev Returns whether `spender` is allowed to manage `tokenId`.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {
        require(_exists(tokenId), "ERC721: operator query for nonexistent token");
        address owner = ERC721Upgradeable.ownerOf(tokenId);
        return (spender == owner || isApprovedForAll(owner, spender) || getApproved(tokenId) == spender);
    }

    /**
     * @dev Safely mints `tokenId` and transfers it to `to`.
     *
     * Requirements:
     *
     * - `tokenId` must not exist.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function _safeMint(address to, uint256 tokenId) internal virtual {
        _safeMint(to, tokenId, "");
    }

    /**
     * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
     * forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
     */
    function _safeMint(
        address to,
        uint256 tokenId,
        bytes memory _data
    ) internal virtual {
        _mint(to, tokenId);
        require(
            _checkOnERC721Received(address(0), to, tokenId, _data),
            "ERC721: transfer to non ERC721Receiver implementer"
        );
    }

    /**
     * @dev Mints `tokenId` and transfers it to `to`.
     *
     * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
     *
     * Requirements:
     *
     * - `tokenId` must not exist.
     * - `to` cannot be the zero address.
     *
     * Emits a {Transfer} event.
     */
    function _mint(address to, uint256 tokenId) internal virtual {
        require(to != address(0), "ERC721: mint to the zero address");
        require(!_exists(tokenId), "ERC721: token already minted");

        _beforeTokenTransfer(address(0), to, tokenId);

        _balances[to] += 1;
        _owners[tokenId] = to;

        emit Transfer(address(0), to, tokenId);

        _afterTokenTransfer(address(0), to, tokenId);
    }

    /**
     * @dev Destroys `tokenId`.
     * The approval is cleared when the token is burned.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     *
     * Emits a {Transfer} event.
     */
    function _burn(uint256 tokenId) internal virtual {
        address owner = ERC721Upgradeable.ownerOf(tokenId);

        _beforeTokenTransfer(owner, address(0), tokenId);

        // Clear approvals
        _approve(address(0), tokenId);

        _balances[owner] -= 1;
        delete _owners[tokenId];

        emit Transfer(owner, address(0), tokenId);

        _afterTokenTransfer(owner, address(0), tokenId);
    }

    /**
     * @dev Transfers `tokenId` from `from` to `to`.
     *  As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     *
     * Emits a {Transfer} event.
     */
    function _transfer(
        address from,
        address to,
        uint256 tokenId
    ) internal virtual {
        require(ERC721Upgradeable.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner");
        require(to != address(0), "ERC721: transfer to the zero address");

        _beforeTokenTransfer(from, to, tokenId);

        // Clear approvals from the previous owner
        _approve(address(0), tokenId);

        _balances[from] -= 1;
        _balances[to] += 1;
        _owners[tokenId] = to;

        emit Transfer(from, to, tokenId);

        _afterTokenTransfer(from, to, tokenId);
    }

    /**
     * @dev Approve `to` to operate on `tokenId`
     *
     * Emits a {Approval} event.
     */
    function _approve(address to, uint256 tokenId) internal virtual {
        _tokenApprovals[tokenId] = to;
        emit Approval(ERC721Upgradeable.ownerOf(tokenId), to, tokenId);
    }

    /**
     * @dev Approve `operator` to operate on all of `owner` tokens
     *
     * Emits a {ApprovalForAll} event.
     */
    function _setApprovalForAll(
        address owner,
        address operator,
        bool approved
    ) internal virtual {
        require(owner != operator, "ERC721: approve to caller");
        _operatorApprovals[owner][operator] = approved;
        emit ApprovalForAll(owner, operator, approved);
    }

    /**
     * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.
     * The call is not executed if the target address is not a contract.
     *
     * @param from address representing the previous owner of the given token ID
     * @param to target address that will receive the tokens
     * @param tokenId uint256 ID of the token to be transferred
     * @param _data bytes optional data to send along with the call
     * @return bool whether the call correctly returned the expected magic value
     */
    function _checkOnERC721Received(
        address from,
        address to,
        uint256 tokenId,
        bytes memory _data
    ) private returns (bool) {
        if (to.isContract()) {
            try IERC721ReceiverUpgradeable(to).onERC721Received(_msgSender(), from, tokenId, _data) returns (bytes4 retval) {
                return retval == IERC721ReceiverUpgradeable.onERC721Received.selector;
            } catch (bytes memory reason) {
                if (reason.length == 0) {
                    revert("ERC721: transfer to non ERC721Receiver implementer");
                } else {
                    assembly {
                        revert(add(32, reason), mload(reason))
                    }
                }
            }
        } else {
            return true;
        }
    }

    /**
     * @dev Hook that is called before any token transfer. This includes minting
     * and burning.
     *
     * Calling conditions:
     *
     * - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be
     * transferred to `to`.
     * - When `from` is zero, `tokenId` will be minted for `to`.
     * - When `to` is zero, ``from``'s `tokenId` will be burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 tokenId
    ) internal virtual {}

    /**
     * @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.
     * - `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 tokenId
    ) internal virtual {}

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[44] private __gap;
}

File 79 of 122 : IERC721MetadataUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol)

pragma solidity ^0.8.0;

import "../IERC721Upgradeable.sol";

/**
 * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
 * @dev See https://eips.ethereum.org/EIPS/eip-721
 */
interface IERC721MetadataUpgradeable is IERC721Upgradeable {
    /**
     * @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);
}

File 80 of 122 : SpecialNFTContract.sol
// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.2;

import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721EnumerableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/CountersUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/math/SafeMathUpgradeable.sol";

contract SpecialNFTContract is ERC721EnumerableUpgradeable, OwnableUpgradeable {
    using SafeMathUpgradeable for uint256;
    using AddressUpgradeable for address;
    using CountersUpgradeable for CountersUpgradeable.Counter;
    using StringsUpgradeable for uint256;

    mapping(address => bool) private adminAccess;
    mapping(uint256 => uint256) private specialNFTTypes;

    CountersUpgradeable.Counter private _tokenIdCounter;
    event SetAdminAccess(address indexed user, bool access);

    function initialize() public initializer {
        __ERC721Enumerable_init();
        __ERC721_init("Battlefly Special NFTs", "BattleflySNFT");
        __Ownable_init();
    }

    function setAdminAccess(address user, bool access) external onlyOwner {
        adminAccess[user] = access;
        emit SetAdminAccess(user, access);
    }

    function mintSpecialNFTs(
        address receiver,
        uint256 _specialNFTType,
        uint256 amount
    ) external onlyAdminAccess returns (uint256[] memory) {
        uint256[] memory tokenIds = new uint256[](amount);
        for (uint256 i = 0; i < amount; i++) {
            uint256 nextTokenId = _getNextTokenId();
            _mint(receiver, nextTokenId);
            specialNFTTypes[nextTokenId] = _specialNFTType;
            tokenIds[i] = nextTokenId;
        }
        return tokenIds;
    }

    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
        require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
        return string(abi.encodePacked("https://api.battlefly.game/specials/", tokenId.toString(), "/metadata"));
    }

    function mintSpecialNFT(address receiver, uint256 specialNFTType) external onlyAdminAccess returns (uint256) {
        uint256 nextTokenId = _getNextTokenId();
        _mint(receiver, nextTokenId);
        specialNFTTypes[nextTokenId] = specialNFTType;
        return nextTokenId;
    }

    function getSpecialNFTType(uint256 tokenId) external view returns (uint256) {
        return specialNFTTypes[tokenId];
    }

    function _getNextTokenId() private view returns (uint256) {
        return (_tokenIdCounter.current() + 1);
    }

    function _mint(address to, uint256 tokenId) internal override(ERC721Upgradeable) {
        super._mint(to, tokenId);
        _tokenIdCounter.increment();
    }

    modifier onlyAdminAccess() {
        require(adminAccess[_msgSender()] == true || _msgSender() == owner(), "Require admin access");
        _;
    }
}

File 81 of 122 : ModContract.sol
// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.2;

import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721EnumerableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/CountersUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/math/SafeMathUpgradeable.sol";

contract ModContract is ERC721EnumerableUpgradeable, OwnableUpgradeable {
    using SafeMathUpgradeable for uint256;
    using AddressUpgradeable for address;
    using CountersUpgradeable for CountersUpgradeable.Counter;
    mapping(address => bool) private adminAccess;

    CountersUpgradeable.Counter private _tokenIdCounter;
    mapping(uint256 => Mod) private mods;

    struct Mod {
        uint256 modId;
        uint256 item;
        uint256 mountType;
    }

    function initialize() public initializer {
        __ERC721Enumerable_init();
        __ERC721_init("Mod", "Mod");
        __Ownable_init();
    }

    function setAdminAccess(address user, bool access) external onlyOwner {
        adminAccess[user] = access;
    }

    function mintMod(address receiver, Mod memory mod) external onlyAdminAccess returns (uint256) {
        uint256 nextTokenId = _getNextTokenId();
        _mint(receiver, nextTokenId);
        mod.modId = nextTokenId;
        mods[nextTokenId] = mod;
        return nextTokenId;
    }

    function _getNextTokenId() private view returns (uint256) {
        return (_tokenIdCounter.current() + 1);
    }

    function _mint(address to, uint256 tokenId) internal override(ERC721Upgradeable) {
        super._mint(to, tokenId);
        _tokenIdCounter.increment();
    }

    modifier onlyAdminAccess() {
        require(adminAccess[_msgSender()] == true || _msgSender() == owner(), "Require admin access");
        _;
    }

    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 tokenId
    ) internal override(ERC721EnumerableUpgradeable) {
        super._beforeTokenTransfer(from, to, tokenId);
    }

    function supportsInterface(bytes4 interfaceId) public view override(ERC721EnumerableUpgradeable) returns (bool) {
        return super.supportsInterface(interfaceId);
    }
}

File 82 of 122 : ERC1155Upgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC1155/ERC1155.sol)

pragma solidity ^0.8.0;

import "./IERC1155Upgradeable.sol";
import "./IERC1155ReceiverUpgradeable.sol";
import "./extensions/IERC1155MetadataURIUpgradeable.sol";
import "../../utils/AddressUpgradeable.sol";
import "../../utils/ContextUpgradeable.sol";
import "../../utils/introspection/ERC165Upgradeable.sol";
import "../../proxy/utils/Initializable.sol";

/**
 * @dev Implementation of the basic standard multi-token.
 * See https://eips.ethereum.org/EIPS/eip-1155
 * Originally based on code by Enjin: https://github.com/enjin/erc-1155
 *
 * _Available since v3.1._
 */
contract ERC1155Upgradeable is Initializable, ContextUpgradeable, ERC165Upgradeable, IERC1155Upgradeable, IERC1155MetadataURIUpgradeable {
    using AddressUpgradeable for address;

    // Mapping from token ID to account balances
    mapping(uint256 => mapping(address => uint256)) private _balances;

    // Mapping from account to operator approvals
    mapping(address => mapping(address => bool)) private _operatorApprovals;

    // Used as the URI for all token types by relying on ID substitution, e.g. https://token-cdn-domain/{id}.json
    string private _uri;

    /**
     * @dev See {_setURI}.
     */
    function __ERC1155_init(string memory uri_) internal onlyInitializing {
        __ERC1155_init_unchained(uri_);
    }

    function __ERC1155_init_unchained(string memory uri_) internal onlyInitializing {
        _setURI(uri_);
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165Upgradeable, IERC165Upgradeable) returns (bool) {
        return
            interfaceId == type(IERC1155Upgradeable).interfaceId ||
            interfaceId == type(IERC1155MetadataURIUpgradeable).interfaceId ||
            super.supportsInterface(interfaceId);
    }

    /**
     * @dev See {IERC1155MetadataURI-uri}.
     *
     * This implementation returns the same URI for *all* token types. It relies
     * on the token type ID substitution mechanism
     * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP].
     *
     * Clients calling this function must replace the `\{id\}` substring with the
     * actual token type ID.
     */
    function uri(uint256) public view virtual override returns (string memory) {
        return _uri;
    }

    /**
     * @dev See {IERC1155-balanceOf}.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     */
    function balanceOf(address account, uint256 id) public view virtual override returns (uint256) {
        require(account != address(0), "ERC1155: balance query for the zero address");
        return _balances[id][account];
    }

    /**
     * @dev See {IERC1155-balanceOfBatch}.
     *
     * Requirements:
     *
     * - `accounts` and `ids` must have the same length.
     */
    function balanceOfBatch(address[] memory accounts, uint256[] memory ids)
        public
        view
        virtual
        override
        returns (uint256[] memory)
    {
        require(accounts.length == ids.length, "ERC1155: accounts and ids length mismatch");

        uint256[] memory batchBalances = new uint256[](accounts.length);

        for (uint256 i = 0; i < accounts.length; ++i) {
            batchBalances[i] = balanceOf(accounts[i], ids[i]);
        }

        return batchBalances;
    }

    /**
     * @dev See {IERC1155-setApprovalForAll}.
     */
    function setApprovalForAll(address operator, bool approved) public virtual override {
        _setApprovalForAll(_msgSender(), operator, approved);
    }

    /**
     * @dev See {IERC1155-isApprovedForAll}.
     */
    function isApprovedForAll(address account, address operator) public view virtual override returns (bool) {
        return _operatorApprovals[account][operator];
    }

    /**
     * @dev See {IERC1155-safeTransferFrom}.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes memory data
    ) public virtual override {
        require(
            from == _msgSender() || isApprovedForAll(from, _msgSender()),
            "ERC1155: caller is not owner nor approved"
        );
        _safeTransferFrom(from, to, id, amount, data);
    }

    /**
     * @dev See {IERC1155-safeBatchTransferFrom}.
     */
    function safeBatchTransferFrom(
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) public virtual override {
        require(
            from == _msgSender() || isApprovedForAll(from, _msgSender()),
            "ERC1155: transfer caller is not owner nor approved"
        );
        _safeBatchTransferFrom(from, to, ids, amounts, data);
    }

    /**
     * @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
     *
     * Emits a {TransferSingle} event.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - `from` must have a balance of tokens of type `id` of at least `amount`.
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
     * acceptance magic value.
     */
    function _safeTransferFrom(
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes memory data
    ) internal virtual {
        require(to != address(0), "ERC1155: transfer to the zero address");

        address operator = _msgSender();
        uint256[] memory ids = _asSingletonArray(id);
        uint256[] memory amounts = _asSingletonArray(amount);

        _beforeTokenTransfer(operator, from, to, ids, amounts, data);

        uint256 fromBalance = _balances[id][from];
        require(fromBalance >= amount, "ERC1155: insufficient balance for transfer");
        unchecked {
            _balances[id][from] = fromBalance - amount;
        }
        _balances[id][to] += amount;

        emit TransferSingle(operator, from, to, id, amount);

        _afterTokenTransfer(operator, from, to, ids, amounts, data);

        _doSafeTransferAcceptanceCheck(operator, from, to, id, amount, data);
    }

    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_safeTransferFrom}.
     *
     * Emits a {TransferBatch} event.
     *
     * Requirements:
     *
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
     * acceptance magic value.
     */
    function _safeBatchTransferFrom(
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal virtual {
        require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
        require(to != address(0), "ERC1155: transfer to the zero address");

        address operator = _msgSender();

        _beforeTokenTransfer(operator, from, to, ids, amounts, data);

        for (uint256 i = 0; i < ids.length; ++i) {
            uint256 id = ids[i];
            uint256 amount = amounts[i];

            uint256 fromBalance = _balances[id][from];
            require(fromBalance >= amount, "ERC1155: insufficient balance for transfer");
            unchecked {
                _balances[id][from] = fromBalance - amount;
            }
            _balances[id][to] += amount;
        }

        emit TransferBatch(operator, from, to, ids, amounts);

        _afterTokenTransfer(operator, from, to, ids, amounts, data);

        _doSafeBatchTransferAcceptanceCheck(operator, from, to, ids, amounts, data);
    }

    /**
     * @dev Sets a new URI for all token types, by relying on the token type ID
     * substitution mechanism
     * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP].
     *
     * By this mechanism, any occurrence of the `\{id\}` substring in either the
     * URI or any of the amounts in the JSON file at said URI will be replaced by
     * clients with the token type ID.
     *
     * For example, the `https://token-cdn-domain/\{id\}.json` URI would be
     * interpreted by clients as
     * `https://token-cdn-domain/000000000000000000000000000000000000000000000000000000000004cce0.json`
     * for token type ID 0x4cce0.
     *
     * See {uri}.
     *
     * Because these URIs cannot be meaningfully represented by the {URI} event,
     * this function emits no events.
     */
    function _setURI(string memory newuri) internal virtual {
        _uri = newuri;
    }

    /**
     * @dev Creates `amount` tokens of token type `id`, and assigns them to `to`.
     *
     * Emits a {TransferSingle} event.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
     * acceptance magic value.
     */
    function _mint(
        address to,
        uint256 id,
        uint256 amount,
        bytes memory data
    ) internal virtual {
        require(to != address(0), "ERC1155: mint to the zero address");

        address operator = _msgSender();
        uint256[] memory ids = _asSingletonArray(id);
        uint256[] memory amounts = _asSingletonArray(amount);

        _beforeTokenTransfer(operator, address(0), to, ids, amounts, data);

        _balances[id][to] += amount;
        emit TransferSingle(operator, address(0), to, id, amount);

        _afterTokenTransfer(operator, address(0), to, ids, amounts, data);

        _doSafeTransferAcceptanceCheck(operator, address(0), to, id, amount, data);
    }

    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_mint}.
     *
     * Requirements:
     *
     * - `ids` and `amounts` must have the same length.
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
     * acceptance magic value.
     */
    function _mintBatch(
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal virtual {
        require(to != address(0), "ERC1155: mint to the zero address");
        require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");

        address operator = _msgSender();

        _beforeTokenTransfer(operator, address(0), to, ids, amounts, data);

        for (uint256 i = 0; i < ids.length; i++) {
            _balances[ids[i]][to] += amounts[i];
        }

        emit TransferBatch(operator, address(0), to, ids, amounts);

        _afterTokenTransfer(operator, address(0), to, ids, amounts, data);

        _doSafeBatchTransferAcceptanceCheck(operator, address(0), to, ids, amounts, data);
    }

    /**
     * @dev Destroys `amount` tokens of token type `id` from `from`
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `from` must have at least `amount` tokens of token type `id`.
     */
    function _burn(
        address from,
        uint256 id,
        uint256 amount
    ) internal virtual {
        require(from != address(0), "ERC1155: burn from the zero address");

        address operator = _msgSender();
        uint256[] memory ids = _asSingletonArray(id);
        uint256[] memory amounts = _asSingletonArray(amount);

        _beforeTokenTransfer(operator, from, address(0), ids, amounts, "");

        uint256 fromBalance = _balances[id][from];
        require(fromBalance >= amount, "ERC1155: burn amount exceeds balance");
        unchecked {
            _balances[id][from] = fromBalance - amount;
        }

        emit TransferSingle(operator, from, address(0), id, amount);

        _afterTokenTransfer(operator, from, address(0), ids, amounts, "");
    }

    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_burn}.
     *
     * Requirements:
     *
     * - `ids` and `amounts` must have the same length.
     */
    function _burnBatch(
        address from,
        uint256[] memory ids,
        uint256[] memory amounts
    ) internal virtual {
        require(from != address(0), "ERC1155: burn from the zero address");
        require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");

        address operator = _msgSender();

        _beforeTokenTransfer(operator, from, address(0), ids, amounts, "");

        for (uint256 i = 0; i < ids.length; i++) {
            uint256 id = ids[i];
            uint256 amount = amounts[i];

            uint256 fromBalance = _balances[id][from];
            require(fromBalance >= amount, "ERC1155: burn amount exceeds balance");
            unchecked {
                _balances[id][from] = fromBalance - amount;
            }
        }

        emit TransferBatch(operator, from, address(0), ids, amounts);

        _afterTokenTransfer(operator, from, address(0), ids, amounts, "");
    }

    /**
     * @dev Approve `operator` to operate on all of `owner` tokens
     *
     * Emits a {ApprovalForAll} event.
     */
    function _setApprovalForAll(
        address owner,
        address operator,
        bool approved
    ) internal virtual {
        require(owner != operator, "ERC1155: setting approval status for self");
        _operatorApprovals[owner][operator] = approved;
        emit ApprovalForAll(owner, operator, approved);
    }

    /**
     * @dev Hook that is called before any token transfer. This includes minting
     * and burning, as well as batched variants.
     *
     * The same hook is called on both single and batched variants. For single
     * transfers, the length of the `id` and `amount` arrays will be 1.
     *
     * Calling conditions (for each `id` and `amount` pair):
     *
     * - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * of token type `id` will be  transferred to `to`.
     * - When `from` is zero, `amount` tokens of token type `id` will be minted
     * for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens of token type `id`
     * will be burned.
     * - `from` and `to` are never both zero.
     * - `ids` and `amounts` have the same, non-zero length.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(
        address operator,
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal virtual {}

    /**
     * @dev Hook that is called after any token transfer. This includes minting
     * and burning, as well as batched variants.
     *
     * The same hook is called on both single and batched variants. For single
     * transfers, the length of the `id` and `amount` arrays will be 1.
     *
     * Calling conditions (for each `id` and `amount` pair):
     *
     * - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * of token type `id` will be  transferred to `to`.
     * - When `from` is zero, `amount` tokens of token type `id` will be minted
     * for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens of token type `id`
     * will be burned.
     * - `from` and `to` are never both zero.
     * - `ids` and `amounts` have the same, non-zero length.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _afterTokenTransfer(
        address operator,
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal virtual {}

    function _doSafeTransferAcceptanceCheck(
        address operator,
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes memory data
    ) private {
        if (to.isContract()) {
            try IERC1155ReceiverUpgradeable(to).onERC1155Received(operator, from, id, amount, data) returns (bytes4 response) {
                if (response != IERC1155ReceiverUpgradeable.onERC1155Received.selector) {
                    revert("ERC1155: ERC1155Receiver rejected tokens");
                }
            } catch Error(string memory reason) {
                revert(reason);
            } catch {
                revert("ERC1155: transfer to non ERC1155Receiver implementer");
            }
        }
    }

    function _doSafeBatchTransferAcceptanceCheck(
        address operator,
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) private {
        if (to.isContract()) {
            try IERC1155ReceiverUpgradeable(to).onERC1155BatchReceived(operator, from, ids, amounts, data) returns (
                bytes4 response
            ) {
                if (response != IERC1155ReceiverUpgradeable.onERC1155BatchReceived.selector) {
                    revert("ERC1155: ERC1155Receiver rejected tokens");
                }
            } catch Error(string memory reason) {
                revert(reason);
            } catch {
                revert("ERC1155: transfer to non ERC1155Receiver implementer");
            }
        }
    }

    function _asSingletonArray(uint256 element) private pure returns (uint256[] memory) {
        uint256[] memory array = new uint256[](1);
        array[0] = element;

        return array;
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[47] private __gap;
}

File 83 of 122 : IERC1155MetadataURIUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC1155/extensions/IERC1155MetadataURI.sol)

pragma solidity ^0.8.0;

import "../IERC1155Upgradeable.sol";

/**
 * @dev Interface of the optional ERC1155MetadataExtension interface, as defined
 * in the https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[EIP].
 *
 * _Available since v3.1._
 */
interface IERC1155MetadataURIUpgradeable is IERC1155Upgradeable {
    /**
     * @dev Returns the URI for token type `id`.
     *
     * If the `\{id\}` substring is present in the URI, it must be replaced by
     * clients with the actual token type ID.
     */
    function uri(uint256 id) external view returns (string memory);
}

File 84 of 122 : ItemContract.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts-upgradeable/token/ERC1155/ERC1155Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/math/SafeMathUpgradeable.sol";

contract ItemContract is OwnableUpgradeable, ERC1155Upgradeable {
    using SafeMathUpgradeable for uint256;

    mapping(address => bool) private adminAccess;
    event SetAdminAccess(address indexed user, bool access);
    string _contractURI;

    function initialize() public initializer {
        __ERC1155_init("");
        __Ownable_init();
    }

    function name() public view virtual returns (string memory) {
        return "Battlefly Items";
    }

    function symbol() public view virtual returns (string memory) {
        return "Battlefly ITEM";
    }

    function contractURI() public view virtual returns (string memory) {
        return _contractURI;
    }

    function mintItems(
        uint256 itemId,
        address receiver,
        uint256 amount,
        bytes memory data
    ) external onlyAdminAccess {
        _mint(receiver, itemId, amount, data);
    }

    function setAdminAccess(address user, bool access) external onlyOwner {
        adminAccess[user] = access;
        emit SetAdminAccess(user, access);
    }

    function setContractURI(string memory contractURI_) external onlyOwner {
        _contractURI = contractURI_;
    }

    function setURI(string memory uri_) external onlyOwner {
        _setURI(uri_);
    }

    modifier onlyAdminAccess() {
        require(adminAccess[_msgSender()] == true || _msgSender() == owner(), "Require admin access");
        _;
    }
}

File 85 of 122 : BattleflyPublicMint.sol
// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.2;

import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/math/SafeMathUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
import "./interfaces/IBattleflyGame.sol";
import { MerkleProof } from "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol"; // OZ: MerkleProof
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract BattleflyPublicMint is OwnableUpgradeable {
    using SafeMathUpgradeable for uint256;
    using AddressUpgradeable for address;
    mapping(address => bool) public HasMinted;

    bytes32 public merkleRootBattlefly;

    mapping(address => bool) private adminAccess;
    IBattleflyGame Game;
    uint256 public StartTime;
    uint256 public EndTime;
    uint256 public BattleflyType;
    uint256 public MinMagicAmountHolder;
    ERC20 MagicToken;
    mapping(bytes32 => bool) public HasMintedTicket;

    event PublicMintBattlefly(address indexed to, uint256 battleflyType, uint256 battleflyId, bytes32 indexed ticket);

    function initialize(address battleflyGameContractAddress, address magicTokenAddress) public initializer {
        __Ownable_init();
        Game = IBattleflyGame(battleflyGameContractAddress);
        MagicToken = ERC20(magicTokenAddress);
    }

    function setAdminAccess(address user, bool access) external onlyOwner {
        adminAccess[user] = access;
    }

    function setMerkleRootBattlefly(bytes32 merkleRoot) external onlyAdminAccess {
        merkleRootBattlefly = merkleRoot;
    }

    function setHasMinted(address user, bool value) external onlyAdminAccess {
        HasMinted[user] = value;
    }

    function setMinting(
        uint256 start,
        uint256 end,
        uint256 minMagicAmountHolder,
        uint256 battleflyType
    ) external onlyAdminAccess {
        StartTime = start;
        EndTime = end;
        MinMagicAmountHolder = minMagicAmountHolder;
        BattleflyType = battleflyType;
    }

    function mintBattlefly(bytes32 ticket, bytes32[] calldata proof) external {
        address to = _msgSender();
        require(block.timestamp >= StartTime, "Not start yet");
        require(block.timestamp <= EndTime, "Already finished");
        require(HasMinted[to] == false, "Already minted");
        require(HasMintedTicket[ticket] == false, "Already minted - ticket");

        if (MinMagicAmountHolder != 0)
            require(MagicToken.balanceOf(_msgSender()) >= MinMagicAmountHolder, "You must hold an amount of Magic");

        bytes32 leaf = keccak256(abi.encodePacked(ticket));
        bool isValidLeaf = MerkleProof.verify(proof, merkleRootBattlefly, leaf);
        require(isValidLeaf, "Not in merkle");

        HasMinted[to] = true;
        HasMintedTicket[ticket] = true;
        uint256 battleflyId = Game.mintBattlefly(to, BattleflyType);

        emit PublicMintBattlefly(to, BattleflyType, battleflyId, ticket);
    }

    modifier onlyAdminAccess() {
        require(adminAccess[_msgSender()] == true || _msgSender() == owner(), "Require admin access");
        _;
    }
}

File 86 of 122 : BattleflyContract.sol
// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.2;

import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721EnumerableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/CountersUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/math/SafeMathUpgradeable.sol";

contract BattleflyContract is ERC721EnumerableUpgradeable, OwnableUpgradeable {
    using SafeMathUpgradeable for uint256;
    using AddressUpgradeable for address;
    using CountersUpgradeable for CountersUpgradeable.Counter;
    using StringsUpgradeable for uint256;

    mapping(address => bool) private adminAccess;
    mapping(uint256 => uint256) private battleflyTypes;

    CountersUpgradeable.Counter private _tokenIdCounter;
    event SetAdminAccess(address indexed user, bool access);

    function initialize() public initializer {
        __ERC721Enumerable_init();
        __ERC721_init("Battlefly", "Battlefly");
        __Ownable_init();
    }

    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
        require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
        return string(abi.encodePacked("https://api.battlefly.game/battleflies/", tokenId.toString(), "/metadata"));
    }

    function setAdminAccess(address user, bool access) external onlyOwner {
        adminAccess[user] = access;
        emit SetAdminAccess(user, access);
    }

    function mintBattlefly(address receiver, uint256 battleflyType) external onlyAdminAccess returns (uint256) {
        uint256 nextTokenId = _getNextTokenId();
        battleflyTypes[nextTokenId] = battleflyType;
        _mint(receiver, nextTokenId);
        return nextTokenId;
    }

    function mintBattleflies(
        address receiver,
        uint256 _battleflyType,
        uint256 amount
    ) external onlyAdminAccess returns (uint256[] memory) {
        uint256[] memory tokenIds = new uint256[](amount);
        for (uint256 i = 0; i < amount; i++) {
            uint256 nextTokenId = _getNextTokenId();
            battleflyTypes[nextTokenId] = _battleflyType;
            tokenIds[i] = nextTokenId;
            _mint(receiver, nextTokenId);
        }
        return tokenIds;
    }

    function getBattleflyType(uint256 tokenId) external view returns (uint256) {
        return battleflyTypes[tokenId];
    }

    function _getNextTokenId() private view returns (uint256) {
        return (_tokenIdCounter.current() + 1);
    }

    function _mint(address to, uint256 tokenId) internal override(ERC721Upgradeable) {
        super._mint(to, tokenId);
        _tokenIdCounter.increment();
    }

    modifier onlyAdminAccess() {
        require(adminAccess[_msgSender()] == true || _msgSender() == owner(), "Require admin access");
        _;
    }
}

File 87 of 122 : BattleflyAtlasStakerV01.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;

import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC1155/IERC1155Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC1155/utils/ERC1155HolderUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/utils/ERC721HolderUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/structs/EnumerableSetUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";

import "./interfaces/IAtlasMine.sol";
import "./interfaces/IBattleflyAtlasStaker.sol";

import "hardhat/console.sol";

contract BattleflyAtlasStakerV01 is
    IBattleflyAtlasStaker,
    Initializable,
    OwnableUpgradeable,
    ERC1155HolderUpgradeable,
    ERC721HolderUpgradeable,
    ReentrancyGuardUpgradeable
{
    using SafeERC20Upgradeable for IERC20Upgradeable;
    using AddressUpgradeable for address;
    using SafeCastUpgradeable for uint256;
    using SafeCastUpgradeable for int256;
    using EnumerableSetUpgradeable for EnumerableSetUpgradeable.UintSet;
    using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet;

    // ============================================ STATE ==============================================

    // ============= Global Immutable State ==============

    /// @notice MAGIC token
    /// @dev functionally immutable
    IERC20Upgradeable public magic;
    /// @notice The IAtlasMine
    /// @dev functionally immutable
    IAtlasMine public mine;

    // ============= Global Staking State ==============
    uint256 public constant ONE = 1e30;

    /// @notice Whether new stakes will get staked on the contract as scheduled. For emergencies
    bool public schedulePaused;
    /// @notice The total amount of staked token
    uint256 public totalStaked;
    /// @notice The total amount of share
    uint256 public totalShare;
    /// @notice All stakes currently active
    Stake[] public stakes;
    /// @notice Deposit ID of last stake. Also tracked in atlas mine
    uint256 public lastDepositId;
    /// @notice Rewards accumulated per share
    uint256 public accRewardsPerShare;

    // ============= Vault Staking State ==============
    mapping(address => bool) public battleflyVaults;

    /// @notice Each vault stake, keyed by vault contract address => deposit ID
    mapping(address => mapping(uint256 => VaultStake)) public vaultStake;
    /// @notice All deposit IDs fro a vault, enumerated
    mapping(address => EnumerableSetUpgradeable.UintSet) private allVaultDepositIds;
    /// @notice The current ID of the vault's last deposited stake
    mapping(address => uint256) public currentId;

    // ============= NFT Boosting State ==============

    /// @notice Holder of treasures and legions
    mapping(uint256 => bool) public legionsStaked;
    mapping(uint256 => uint256) public treasuresStaked;

    // ============= Operator State ==============

    IAtlasMine.Lock[] public allowedLocks;
    /// @notice Fee to contract operator. Only assessed on rewards.
    uint256 public fee;
    /// @notice Amount of fees reserved for withdrawal by the operator.
    uint256 public feeReserve;
    /// @notice Max fee the owner can ever take - 10%
    uint256 public constant MAX_FEE = 1000;
    uint256 public constant FEE_DENOMINATOR = 10000;

    mapping(address => mapping(uint256 => int256)) refundedFeeDebts;
    uint256 accRefundedFeePerShare;
    uint256 totalWhitelistedFeeShare;
    EnumerableSetUpgradeable.AddressSet whitelistedFeeVaults;
    mapping(address => bool) public superAdmins;

    /// @notice deposited but unstaked
    uint256 public unstakedDeposits;
    mapping(IAtlasMine.Lock => uint256) public unstakedDepositsByLock;
    address public constant TREASURY_WALLET = 0xF5411006eEfD66c213d2fd2033a1d340458B7226;
    /// @notice Intra-tx buffer for pending payouts
    uint256 public tokenBuffer;

    // ===========================================
    // ============== Post Upgrade ===============
    // ===========================================

    // ========================================== INITIALIZER ===========================================

    /**
     * @param _magic                The MAGIC token address.
     * @param _mine                 The IAtlasMine contract.
     *                              Maps to a timelock for IAtlasMine deposits.
     */
    function initialize(
        IERC20Upgradeable _magic,
        IAtlasMine _mine,
        IAtlasMine.Lock[] memory _allowedLocks
    ) external initializer {
        __ERC1155Holder_init();
        __ERC721Holder_init();
        __Ownable_init();
        __ReentrancyGuard_init();

        magic = _magic;
        mine = _mine;
        allowedLocks = _allowedLocks;
        fee = 1000;
        // Approve the mine
        magic.safeApprove(address(mine), 2**256 - 1);
    }

    // ======================================== VAULT OPERATIONS ========================================

    /**
     * @notice Make a new deposit into the Staker. The Staker will collect
     *         the tokens, to be later staked in atlas mine by the owner,
     *         according to the stake/unlock schedule.
     * @dev    Specified amount of token must be approved by the caller.
     *
     * @param _amount               The amount of tokens to deposit.
     */
    function deposit(uint256 _amount, IAtlasMine.Lock lock)
        public
        virtual
        override
        onlyBattleflyVaultOrOwner
        nonReentrant
        returns (uint256)
    {
        require(!schedulePaused, "new staking paused");
        _updateRewards();
        // Collect tokens
        uint256 newDepositId = _deposit(_amount, msg.sender, lock);
        magic.safeTransferFrom(msg.sender, address(this), _amount);
        return (newDepositId);
    }

    function _deposit(
        uint256 _amount,
        address _vault,
        IAtlasMine.Lock lock
    ) internal returns (uint256) {
        require(_amount > 0, "Deposit amount 0");
        bool validLock = false;
        for (uint256 i = 0; i < allowedLocks.length; i++) {
            if (allowedLocks[i] == lock) {
                validLock = true;
                break;
            }
        }
        require(validLock, "Lock time not allowed");
        // Add vault stake
        uint256 newDepositId = ++currentId[_vault];
        allVaultDepositIds[_vault].add(newDepositId);
        VaultStake storage s = vaultStake[_vault][newDepositId];

        s.amount = _amount;
        (uint256 boost, uint256 lockTime) = getLockBoost(lock);
        uint256 share = (_amount * (100e16 + boost)) / 100e16;

        uint256 vestingTime = mine.getVestingTime(lock);
        s.unlockAt = block.timestamp + lockTime + vestingTime + 1 days;
        s.rewardDebt = ((share * accRewardsPerShare) / ONE).toInt256();
        s.lock = lock;

        // Update global accounting
        totalStaked += _amount;
        totalShare += share;
        if (whitelistedFeeVaults.contains(_vault)) {
            totalWhitelistedFeeShare += share;
            refundedFeeDebts[_vault][newDepositId] = ((share * accRefundedFeePerShare) / ONE).toInt256();
        }
        // MAGIC tokens sit in contract. Added to pending stakes
        unstakedDeposits += _amount;
        unstakedDepositsByLock[lock] += _amount;
        emit VaultDeposit(_vault, newDepositId, _amount, s.unlockAt, s.lock);
        return newDepositId;
    }

    /**
     * @notice Withdraw a deposit from the Staker contract. Calculates
     *         pro rata share of accumulated MAGIC and distributes any
     *         earned rewards in addition to original deposit.
     *         There must be enough unlocked tokens to withdraw.
     *
     * @param depositId             The ID of the deposit to withdraw from.
     *
     */
    function withdraw(uint256 depositId) public virtual override onlyBattleflyVaultOrOwner nonReentrant {
        // Distribute tokens
        _updateRewards();
        VaultStake storage s = vaultStake[msg.sender][depositId];
        require(s.amount > 0, "No deposit");
        require(block.timestamp >= s.unlockAt, "Deposit locked");

        uint256 payout = _withdraw(s, depositId);
        magic.safeTransfer(msg.sender, payout);
    }

    /**
     * @notice Withdraw all eligible deposits from the staker contract.
     *         Will skip any deposits not yet unlocked. Will also
     *         distribute rewards for all stakes via 'withdraw'.
     *
     */
    function withdrawAll() public virtual override onlyBattleflyVaultOrOwner nonReentrant {
        // Distribute tokens
        _updateRewards();
        uint256[] memory depositIds = allVaultDepositIds[msg.sender].values();
        for (uint256 i = 0; i < depositIds.length; i++) {
            VaultStake storage s = vaultStake[msg.sender][depositIds[i]];

            if (s.amount > 0 && s.unlockAt > 0 && s.unlockAt <= block.timestamp) {
                tokenBuffer += _withdraw(s, depositIds[i]);
            }
        }
        magic.safeTransfer(msg.sender, tokenBuffer);
        tokenBuffer = 0;
    }

    /**
     * @dev Logic for withdrawing a deposit. Calculates pro rata share of
     *      accumulated MAGIC and dsitributed any earned rewards in addition
     *      to original deposit.
     *
     * @dev An _amount argument larger than the total deposit amount will
     *      withdraw the entire deposit.
     *
     * @param s                     The VaultStake struct to withdraw from.
     * @param depositId             The ID of the deposit to withdraw from (for event).
     */
    function _withdraw(VaultStake storage s, uint256 depositId) internal returns (uint256 payout) {
        uint256 _amount = s.amount;

        // Unstake if we need to to ensure we can withdraw
        (uint256 boost, ) = getLockBoost(s.lock);
        uint256 share = (_amount * (100e16 + boost)) / 100e16;
        int256 accumulatedRewards = ((share * accRewardsPerShare) / ONE).toInt256();
        if (whitelistedFeeVaults.contains(msg.sender)) {
            accumulatedRewards += ((share * accRefundedFeePerShare) / ONE).toInt256();
            accumulatedRewards -= refundedFeeDebts[msg.sender][depositId];
            totalWhitelistedFeeShare -= share;
            refundedFeeDebts[msg.sender][depositId] = 0;
        }
        uint256 reward = (accumulatedRewards - s.rewardDebt).toUint256();
        payout = _amount + reward;

        delete vaultStake[msg.sender][depositId];

        // Update global accounting
        totalStaked -= _amount;

        totalShare -= share;

        // If we need to unstake, unstake until we have enough
        if (payout > _totalUsableMagic()) {
            _unstakeToTarget(payout - _totalUsableMagic());
        }
        emit VaultWithdraw(msg.sender, depositId, _amount, reward);
    }

    /**
     * @notice Claim rewards without unstaking. Will fail if there
     *         are not enough tokens in the contract to claim rewards.
     *         Does not attempt to unstake.
     *
     * @param depositId             The ID of the deposit to claim rewards from.
     *
     */
    function claim(uint256 depositId) public virtual override onlyBattleflyVaultOrOwner nonReentrant returns (uint256) {
        _updateRewards();
        VaultStake storage s = vaultStake[msg.sender][depositId];
        require(s.amount > 0, "No deposit");
        uint256 reward = _claim(s, depositId);
        magic.safeTransfer(msg.sender, reward);
        return reward;
    }

    /**
     * @notice Claim all possible rewards from the staker contract.
     *         Will apply to both locked and unlocked deposits.
     *
     */
    function claimAll() public virtual override onlyBattleflyVaultOrOwner nonReentrant returns (uint256) {
        _updateRewards();
        uint256[] memory depositIds = allVaultDepositIds[msg.sender].values();
        uint256 totalReward = 0;
        for (uint256 i = 0; i < depositIds.length; i++) {
            VaultStake storage s = vaultStake[msg.sender][depositIds[i]];
            uint256 reward = _claim(s, depositIds[i]);
            totalReward += reward;
        }
        magic.safeTransfer(msg.sender, totalReward);
        return totalReward;
    }

    /**
     * @notice Claim all possible rewards from the staker contract then restake.
     *         Will apply to both locked and unlocked deposits.
     *
     */
    function claimAllAndRestake(IAtlasMine.Lock lock) public onlyBattleflyVaultOrOwner nonReentrant returns (uint256) {
        _updateRewards();
        uint256[] memory depositIds = allVaultDepositIds[msg.sender].values();
        uint256 totalReward = 0;
        for (uint256 i = 0; i < depositIds.length; i++) {
            VaultStake storage s = vaultStake[msg.sender][depositIds[i]];
            uint256 reward = _claim(s, depositIds[i]);
            totalReward += reward;
        }
        _deposit(totalReward, msg.sender, lock);
        return totalReward;
    }

    /**
     * @dev Logic for claiming rewards on a deposit. Calculates pro rata share of
     *      accumulated MAGIC and dsitributed any earned rewards in addition
     *      to original deposit.
     *
     * @param s                     The VaultStake struct to claim from.
     * @param depositId             The ID of the deposit to claim from (for event).
     */
    function _claim(VaultStake storage s, uint256 depositId) internal returns (uint256) {
        // Update accounting
        (uint256 boost, ) = getLockBoost(s.lock);
        uint256 share = (s.amount * (100e16 + boost)) / 100e16;

        int256 accumulatedRewards = ((share * accRewardsPerShare) / ONE).toInt256();

        uint256 reward = (accumulatedRewards - s.rewardDebt).toUint256();

        if (whitelistedFeeVaults.contains(msg.sender)) {
            int256 accumulatedRefundedFee = ((share * accRefundedFeePerShare) / ONE).toInt256();
            reward += accumulatedRefundedFee.toUint256();
            reward -= refundedFeeDebts[msg.sender][depositId].toUint256();
            refundedFeeDebts[msg.sender][depositId] = accumulatedRefundedFee;
        }

        s.rewardDebt = accumulatedRewards;

        // Unstake if we need to to ensure we can withdraw
        if (reward > _totalUsableMagic()) {
            _unstakeToTarget(reward - _totalUsableMagic());
        }

        require(reward <= _totalUsableMagic(), "Not enough rewards to claim");
        emit VaultClaim(msg.sender, depositId, reward);
        return reward;
    }

    // ======================================= SUPER ADMIN OPERATIONS ========================================

    /**
     * @notice Stake a Treasure owned by the superAdmin into the Atlas Mine.
     *         Staked treasures will boost all vault deposits.
     * @dev    Any treasure must be approved for withdrawal by the caller.
     *
     * @param _tokenId              The tokenId of the specified treasure.
     * @param _amount               The amount of treasures to stake.
     */
    function stakeTreasure(uint256 _tokenId, uint256 _amount) external onlySuperAdminOrOwner {
        address treasureAddr = mine.treasure();
        require(IERC1155Upgradeable(treasureAddr).balanceOf(msg.sender, _tokenId) >= _amount, "Not enough treasures");
        treasuresStaked[_tokenId] += _amount;
        // First withdraw and approve
        IERC1155Upgradeable(treasureAddr).safeTransferFrom(msg.sender, address(this), _tokenId, _amount, bytes(""));
        mine.stakeTreasure(_tokenId, _amount);
        uint256 boost = mine.boosts(address(this));

        emit StakeNFT(msg.sender, treasureAddr, _tokenId, _amount, boost);
    }

    /**
     * @notice Unstake a Treasure from the Atlas Mine adn transfer to receiver.
     *
     * @param _receiver              The receiver .
     * @param _tokenId              The tokenId of the specified treasure.
     * @param _amount               The amount of treasures to stake.
     */
    function unstakeTreasure(
        address _receiver,
        uint256 _tokenId,
        uint256 _amount
    ) external onlySuperAdminOrOwner {
        require(treasuresStaked[_tokenId] >= _amount, "Not enough treasures");
        treasuresStaked[_tokenId] -= _amount;
        address treasureAddr = mine.treasure();
        mine.unstakeTreasure(_tokenId, _amount);
        IERC1155Upgradeable(treasureAddr).safeTransferFrom(address(this), _receiver, _tokenId, _amount, bytes(""));
        uint256 boost = mine.boosts(address(this));
        emit UnstakeNFT(_receiver, treasureAddr, _tokenId, _amount, boost);
    }

    /**
     * @notice Stake a Legion owned by the superAdmin into the Atlas Mine.
     *         Staked legions will boost all vault deposits.
     * @dev    Any legion be approved for withdrawal by the caller.
     *
     * @param _tokenId              The tokenId of the specified legion.
     */
    function stakeLegion(uint256 _tokenId) external onlySuperAdminOrOwner {
        address legionAddr = mine.legion();

        require(IERC721Upgradeable(legionAddr).ownerOf(_tokenId) == msg.sender, "Not owner of legion");

        legionsStaked[_tokenId] = true;
        IERC721Upgradeable(legionAddr).safeTransferFrom(msg.sender, address(this), _tokenId);

        mine.stakeLegion(_tokenId);

        uint256 boost = mine.boosts(address(this));

        emit StakeNFT(msg.sender, legionAddr, _tokenId, 1, boost);
    }

    /**
     * @notice Unstake a Legion from the Atlas Mine and return it to the superAdmin.
     *
     * @param _tokenId              The tokenId of the specified legion.
     */
    function unstakeLegion(address _receiver, uint256 _tokenId) external onlySuperAdminOrOwner {
        require(legionsStaked[_tokenId], "No legion");
        address legionAddr = mine.legion();
        delete legionsStaked[_tokenId];
        mine.unstakeLegion(_tokenId);

        // Distribute to superAdmin
        IERC721Upgradeable(legionAddr).safeTransferFrom(address(this), _receiver, _tokenId);
        uint256 boost = mine.boosts(address(this));

        emit UnstakeNFT(_receiver, legionAddr, _tokenId, 1, boost);
    }

    /**
     * @notice Stake any pending stakes before the current day. Callable
     *         by anybody. Any pending stakes will unlock according
     *         to the time this method is called, and the contract's defined
     *         lock time.
     */
    function stakeScheduled() external virtual override onlySuperAdminOrOwner {
        for (uint256 i = 0; i < allowedLocks.length; i++) {
            IAtlasMine.Lock lock = allowedLocks[i];
            _stakeInMine(unstakedDepositsByLock[lock], lock);
            unstakedDepositsByLock[lock] = 0;
        }
        unstakedDeposits = 0;
    }

    /**
     * @notice Unstake everything eligible for unstaking from Atlas Mine.
     *         Callable by owner. Should only be used in case of emergency
     *         or migration to a new contract, or if there is a need to service
     *         an unexpectedly large amount of withdrawals.
     *
     *         If unlockAll is set to true in the Atlas Mine, this can withdraw
     *         all stake.
     */
    function unstakeAllFromMine() external override onlySuperAdminOrOwner {
        // Unstake everything eligible
        _updateRewards();

        for (uint256 i = 0; i < stakes.length; i++) {
            Stake memory s = stakes[i];

            if (s.unlockAt > block.timestamp) {
                continue;
            }

            // Withdraw position - auto-harvest
            mine.withdrawPosition(s.depositId, s.amount);
        }

        // Only check for removal after, so we don't mutate while looping
        _removeZeroStakes();
    }

    /**
     * @notice Let owner unstake a specified amount as needed to make sure the contract is funded.
     *         Can be used to facilitate expected future withdrawals.
     *
     * @param target                The amount of tokens to reclaim from the mine.
     */
    function unstakeToTarget(uint256 target) external override onlySuperAdminOrOwner {
        _updateRewards();
        _unstakeToTarget(target);
    }

    /**
     * @notice Withdraw any accumulated reward fees to the treasury
     */
    function withdrawFeesToTreasury() external virtual onlySuperAdminOrOwner {
        uint256 amount = feeReserve;
        feeReserve = 0;
        magic.safeTransfer(TREASURY_WALLET, amount);
        emit WithdrawFeesToTreasury(amount);
    }

    function stakeBackFeeTreasury(IAtlasMine.Lock lock) external virtual onlySuperAdminOrOwner {
        uint256 amount = feeReserve;
        feeReserve = 0;
        emit WithdrawFeesToTreasury(amount);
        _deposit(amount, TREASURY_WALLET, lock);
    }

    /**
     * @notice Whitelist vault from fees.
     *
     * @param _vault                Vault address.
     * @param isSet                 Whether to enable or disable the vault whitelist.
     */
    function setFeeWhitelistVault(address _vault, bool isSet) external onlyOwner {
        require(_vault != address(0), "Invalid Vault");
        if (isSet) {
            whitelistedFeeVaults.add(_vault);
            totalWhitelistedFeeShare += totalShareOf(_vault);
        } else {
            whitelistedFeeVaults.remove(_vault);
            totalWhitelistedFeeShare -= totalShareOf(_vault);
        }
        emit SetFeeWhitelistVault(_vault, isSet);
    }

    // ======================================= OWNER OPERATIONS =======================================

    function setBattleflyVault(address _vaultAddress, bool isSet) external onlyOwner {
        require(_vaultAddress != address(0), "Invalid vault");
        if (isSet) {
            require(battleflyVaults[_vaultAddress] == false, "Vault already set");
            battleflyVaults[_vaultAddress] = isSet;
        } else {
            require(allVaultDepositIds[_vaultAddress].length() == 0, "Vault is still active");
            delete battleflyVaults[_vaultAddress];
        }
        emit SetBattleflyVault(_vaultAddress, isSet);
    }

    /**
     * @notice Change the designated superAdmin, the address where treasures and
     *         legions are held. Staked NFTs can only be
     *         withdrawn to the current superAdmin address, regardless of which
     *         address the superAdmin was set to when it was staked.
     *
     * @param _superAdmin                The new superAdmin address.
     * @param isSet                 Whether to enable or disable the superAdmin address.
     */
    function setBoostAdmin(address _superAdmin, bool isSet) external override onlyOwner {
        require(_superAdmin != address(0), "Invalid superAdmin");

        superAdmins[_superAdmin] = isSet;
    }

    /**
     * @notice Change the designated super admin, who manage the fee reverse
     *
     * @param _superAdmin                The new superAdmin address.
     * @param isSet                 Whether to enable or disable the super admin address.
     */
    function setSuperAdmin(address _superAdmin, bool isSet) external onlyOwner {
        require(_superAdmin != address(0), "Invalid address");
        superAdmins[_superAdmin] = isSet;
    }

    /**
     * @notice Approve treasures and legions for withdrawal from the atlas mine.
     *         Called on startup, and should be called again in case contract
     *         addresses for treasures and legions ever change.
     *
     */
    function approveNFTs() public override onlyOwner {
        address treasureAddr = mine.treasure();
        IERC1155Upgradeable(treasureAddr).setApprovalForAll(address(mine), true);

        address legionAddr = mine.legion();
        IERC1155Upgradeable(legionAddr).setApprovalForAll(address(mine), true);
    }

    /**
     * @notice EMERGENCY ONLY - toggle pausing new scheduled stakes.
     *         If on, vaults can deposit, but stakes won't go to Atlas Mine.
     *         Can be used in case of Atlas Mine issues or forced migration
     *         to new contract.
     */
    function toggleSchedulePause(bool paused) external virtual override onlyOwner {
        schedulePaused = paused;

        emit StakingPauseToggle(paused);
    }

    // ======================================== VIEW FUNCTIONS =========================================
    function getLockBoost(IAtlasMine.Lock _lock) public pure virtual returns (uint256 boost, uint256 timelock) {
        if (_lock == IAtlasMine.Lock.twoWeeks) {
            // 10%
            return (10e16, 14 days);
        } else if (_lock == IAtlasMine.Lock.oneMonth) {
            // 25%
            return (25e16, 30 days);
        } else if (_lock == IAtlasMine.Lock.threeMonths) {
            // 80%
            return (80e16, 13 weeks);
        } else if (_lock == IAtlasMine.Lock.sixMonths) {
            // 180%
            return (180e16, 23 weeks);
        } else if (_lock == IAtlasMine.Lock.twelveMonths) {
            // 400%
            return (400e16, 365 days);
        } else {
            revert("Invalid lock value");
        }
    }

    /**
     * @notice Returns all magic either unstaked, staked, or pending rewards in Atlas Mine.
     *         Best proxy for TVL.
     *
     * @return total               The total amount of MAGIC in the staker.
     */
    function totalMagic() external view override returns (uint256) {
        return _totalControlledMagic() + mine.pendingRewardsAll(address(this));
    }

    /**
     * @notice Returns all magic that has been deposited, but not staked, and is eligible
     *         to be staked (deposit time < current day).
     *
     * @return total               The total amount of MAGIC that can be withdrawn.
     */
    function totalWithdrawableMagic() external view override returns (uint256) {
        uint256 totalPendingRewards;

        // IAtlasMine attempts to divide by 0 if there are no deposits
        try mine.pendingRewardsAll(address(this)) returns (uint256 _pending) {
            totalPendingRewards = _pending;
        } catch Panic(uint256) {
            totalPendingRewards = 0;
        }

        return _totalUsableMagic() + totalPendingRewards;
    }

    /**
     * @notice Returns the details of a vault stake.
     *
     * @return vaultStake           The details of a vault stake.
     */
    function getVaultStake(address vault, uint256 depositId) external view override returns (VaultStake memory) {
        return vaultStake[vault][depositId];
    }

    /**
     * @notice Returns the pending, claimable rewards for a deposit.
     * @dev    This does not update rewards, so out of date if rewards not recently updated.
     *         Needed to maintain 'view' function type.
     *
     * @param vault              The vault to check rewards for.
     * @param depositId         The specific deposit to check rewards for.
     *
     * @return reward           The total amount of MAGIC reward pending.
     */
    function pendingRewards(address vault, uint256 depositId) public view override returns (uint256 reward) {
        if (totalShare == 0) {
            return 0;
        }
        VaultStake storage s = vaultStake[vault][depositId];
        (uint256 boost, ) = getLockBoost(s.lock);
        uint256 share = (s.amount * (100e16 + boost)) / 100e16;

        uint256 unupdatedReward = mine.pendingRewardsAll(address(this));
        (uint256 founderReward, , uint256 feeRefund) = _calculateHarvestRewardFee(unupdatedReward);
        uint256 realAccRewardsPerShare = accRewardsPerShare + (founderReward * ONE) / totalShare;
        uint256 accumulatedRewards = (share * realAccRewardsPerShare) / ONE;
        if (whitelistedFeeVaults.contains(vault) && totalWhitelistedFeeShare > 0) {
            uint256 realAccRefundedFeePerShare = accRefundedFeePerShare + (feeRefund * ONE) / totalWhitelistedFeeShare;
            uint256 accumulatedRefundedFee = (share * realAccRefundedFeePerShare) / ONE;
            accumulatedRewards = accumulatedRewards + accumulatedRefundedFee;
            accumulatedRewards -= refundedFeeDebts[vault][depositId].toUint256();
        }
        reward = accumulatedRewards - s.rewardDebt.toUint256();
    }

    /**
     * @notice Returns the pending, claimable rewards for all of a vault's deposits.
     * @dev    This does not update rewards, so out of date if rewards not recently updated.
     *         Needed to maintain 'view' function type.
     *
     * @param vault              The vault to check rewards for.
     *
     * @return reward           The total amount of MAGIC reward pending.
     */
    function pendingRewardsAll(address vault) external view override returns (uint256 reward) {
        uint256[] memory depositIds = allVaultDepositIds[vault].values();

        for (uint256 i = 0; i < depositIds.length; i++) {
            reward += pendingRewards(vault, depositIds[i]);
        }
    }

    /**
     * @notice Returns the total Share of a vault.
     *
     * @param vault              The vault to check rewards for.
     *
     * @return _totalShare           The total share of a vault.
     */
    function totalShareOf(address vault) public view returns (uint256 _totalShare) {
        uint256[] memory depositIds = allVaultDepositIds[vault].values();
        for (uint256 i = 0; i < depositIds.length; i++) {
            (uint256 boost, ) = getLockBoost(vaultStake[vault][depositIds[i]].lock);
            uint256 share = (vaultStake[vault][depositIds[i]].amount * (100e16 + boost)) / 100e16;
            _totalShare += share;
        }
    }

    // ============================================ HELPERS ============================================

    /**
     * @dev Stake tokens held by staker in the Atlas Mine, according to
     *      the predefined lock value. Schedules for staking will be managed by a queue.
     *
     * @param _amount               Number of tokens to stake
     */
    function _stakeInMine(uint256 _amount, IAtlasMine.Lock lock) internal {
        require(_amount <= _totalUsableMagic(), "Not enough funds");

        uint256 depositId = ++lastDepositId;
        (, uint256 lockTime) = getLockBoost(lock);
        uint256 vestingPeriod = mine.getVestingTime(lock);
        uint256 unlockAt = block.timestamp + lockTime + vestingPeriod;

        stakes.push(Stake({ amount: _amount, unlockAt: unlockAt, depositId: depositId }));

        mine.deposit(_amount, lock);
    }

    /**
     * @dev Unstakes until we have enough unstaked tokens to meet a specific target.
     *      Used to make sure we can service withdrawals.
     *
     * @param target                The amount of tokens we want to have unstaked.
     */
    function _unstakeToTarget(uint256 target) internal {
        uint256 unstaked = 0;

        for (uint256 i = 0; i < stakes.length; i++) {
            Stake memory s = stakes[i];

            if (s.unlockAt > block.timestamp && !mine.unlockAll()) {
                // This stake is not unlocked - stop looking
                continue;
            }

            // Withdraw position - auto-harvest
            uint256 preclaimBalance = _totalUsableMagic();
            uint256 targetLeft = target - unstaked;
            uint256 amount = targetLeft > s.amount ? s.amount : targetLeft;

            // Do not harvest rewards - if this is running, we've already
            // harvested in the same fn call
            mine.withdrawPosition(s.depositId, amount);
            uint256 postclaimBalance = _totalUsableMagic();

            // Increment amount unstaked
            unstaked += postclaimBalance - preclaimBalance;

            if (unstaked >= target) {
                // We unstaked enough
                break;
            }
        }

        require(unstaked >= target, "Cannot unstake enough");
        require(_totalUsableMagic() >= target, "Not enough in contract after unstaking");

        // Only check for removal after, so we don't mutate while looping
        _removeZeroStakes();
    }

    /**
     * @dev Harvest rewards from the IAtlasMine and send them back to
     *      this contract.
     *
     * @return earned               The amount of rewards earned for depositors, minus the fee.
     * @return feeEearned           The amount of fees earned for the contract operator.
     */
    function _harvestMine() internal returns (uint256, uint256) {
        uint256 preclaimBalance = magic.balanceOf(address(this));

        try mine.harvestAll() {
            uint256 postclaimBalance = magic.balanceOf(address(this));

            uint256 earned = postclaimBalance - preclaimBalance;
            // Reserve the 'fee' amount of what is earned
            (, uint256 feeEarned, uint256 feeRefunded) = _calculateHarvestRewardFee(earned);
            feeReserve += feeEarned - feeRefunded;
            emit MineHarvest(earned - feeEarned, feeEarned - feeRefunded, feeRefunded);
            return (earned - feeEarned, feeRefunded);
        } catch {
            // Failed because of reward debt calculation - should be 0
            return (0, 0);
        }
    }

    function _calculateHarvestRewardFee(uint256 earned)
        internal
        view
        returns (
            uint256,
            uint256,
            uint256
        )
    {
        uint256 feeEarned = (earned * fee) / FEE_DENOMINATOR;
        uint256 accFeePerShare = (feeEarned * ONE) / totalShare;
        uint256 feeRefunded = (accFeePerShare * totalWhitelistedFeeShare) / ONE;
        return (earned - feeEarned, feeEarned, feeRefunded);
    }

    /**
     * @dev Harvest rewards from the mine so that stakers can claim.
     *      Recalculate how many rewards are distributed to each share.
     */
    function _updateRewards() internal {
        if (totalStaked == 0 || totalShare == 0) return;
        (uint256 newRewards, uint256 feeRefunded) = _harvestMine();
        accRewardsPerShare += (newRewards * ONE) / totalShare;
        if (totalWhitelistedFeeShare > 0) accRefundedFeePerShare += (feeRefunded * ONE) / totalWhitelistedFeeShare;
    }

    /**
     * @dev After mutating a stake (by withdrawing fully or partially),
     *      get updated data from the staking contract, and update the stake amounts
     *
     * @param stakeIndex           The index of the stake in the Stakes storage array.
     *
     * @return amount              The current, updated amount of the stake.
     */
    function _updateStakeDepositAmount(uint256 stakeIndex) internal returns (uint256) {
        Stake storage s = stakes[stakeIndex];

        (, uint256 depositAmount, , , , , ) = mine.userInfo(address(this), s.depositId);
        s.amount = depositAmount;

        return s.amount;
    }

    /**
     * @dev Find stakes with zero deposit amount and remove them from tracking.
     *      Uses recursion to stop from mutating an array we are currently looping over.
     *      If a zero stake is found, it is removed, and the function is restarted,
     *      such that it is always working from a 'clean' array.
     *
     */
    function _removeZeroStakes() internal {
        bool shouldRecurse = stakes.length > 0;

        for (uint256 i = 0; i < stakes.length; i++) {
            _updateStakeDepositAmount(i);

            Stake storage s = stakes[i];

            if (s.amount == 0) {
                _removeStake(i);
                // Stop looping and start again - we will skip
                // out of the look and recurse
                break;
            }

            if (i == stakes.length - 1) {
                // We didn't remove anything, so stop recursing
                shouldRecurse = false;
            }
        }

        if (shouldRecurse) {
            _removeZeroStakes();
        }
    }

    /**
     * @dev Calculate total amount of MAGIC usable by the contract.
     *      'Usable' means available for either withdrawal or re-staking.
     *      Counts unstaked magic less fee reserve.
     *
     * @return amount               The amount of usable MAGIC.
     */
    function _totalUsableMagic() internal view returns (uint256) {
        // Current magic held in contract
        uint256 unstaked = magic.balanceOf(address(this));

        return unstaked - tokenBuffer - feeReserve;
    }

    /**
     * @dev Calculate total amount of MAGIC under control of the contract.
     *      Counts staked and unstaked MAGIC. Does _not_ count accumulated
     *      but unclaimed rewards.
     *
     * @return amount               The total amount of MAGIC under control of the contract.
     */
    function _totalControlledMagic() internal view returns (uint256) {
        // Current magic staked in mine
        uint256 staked = 0;

        for (uint256 i = 0; i < stakes.length; i++) {
            staked += stakes[i].amount;
        }

        return staked + _totalUsableMagic();
    }

    /**
     * @dev Remove a tracked stake from any position in the stakes array.
     *      Used when a stake is no longer relevant i.e. fully withdrawn.
     *      Mutates the Stakes array in storage.
     *
     * @param index                 The index of the stake to remove.
     */
    function _removeStake(uint256 index) internal {
        if (index >= stakes.length) return;

        for (uint256 i = index; i < stakes.length - 1; i++) {
            stakes[i] = stakes[i + 1];
        }

        delete stakes[stakes.length - 1];

        stakes.pop();
    }

    modifier onlySuperAdminOrOwner() {
        require(msg.sender == owner() || superAdmins[msg.sender], "Not Super Admin");
        _;
    }
    modifier onlyBattleflyVaultOrOwner() {
        require(msg.sender == owner() || battleflyVaults[msg.sender], "Not BattleflyVault");
        _;
    }
}

File 88 of 122 : console.sol
// SPDX-License-Identifier: MIT
pragma solidity >= 0.4.22 <0.9.0;

library console {
	address constant CONSOLE_ADDRESS = address(0x000000000000000000636F6e736F6c652e6c6f67);

	function _sendLogPayload(bytes memory payload) private view {
		uint256 payloadLength = payload.length;
		address consoleAddress = CONSOLE_ADDRESS;
		assembly {
			let payloadStart := add(payload, 32)
			let r := staticcall(gas(), consoleAddress, payloadStart, payloadLength, 0, 0)
		}
	}

	function log() internal view {
		_sendLogPayload(abi.encodeWithSignature("log()"));
	}

	function logInt(int p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(int)", p0));
	}

	function logUint(uint p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint)", p0));
	}

	function logString(string memory p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string)", p0));
	}

	function logBool(bool p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool)", p0));
	}

	function logAddress(address p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address)", p0));
	}

	function logBytes(bytes memory p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes)", p0));
	}

	function logBytes1(bytes1 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes1)", p0));
	}

	function logBytes2(bytes2 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes2)", p0));
	}

	function logBytes3(bytes3 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes3)", p0));
	}

	function logBytes4(bytes4 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes4)", p0));
	}

	function logBytes5(bytes5 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes5)", p0));
	}

	function logBytes6(bytes6 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes6)", p0));
	}

	function logBytes7(bytes7 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes7)", p0));
	}

	function logBytes8(bytes8 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes8)", p0));
	}

	function logBytes9(bytes9 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes9)", p0));
	}

	function logBytes10(bytes10 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes10)", p0));
	}

	function logBytes11(bytes11 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes11)", p0));
	}

	function logBytes12(bytes12 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes12)", p0));
	}

	function logBytes13(bytes13 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes13)", p0));
	}

	function logBytes14(bytes14 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes14)", p0));
	}

	function logBytes15(bytes15 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes15)", p0));
	}

	function logBytes16(bytes16 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes16)", p0));
	}

	function logBytes17(bytes17 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes17)", p0));
	}

	function logBytes18(bytes18 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes18)", p0));
	}

	function logBytes19(bytes19 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes19)", p0));
	}

	function logBytes20(bytes20 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes20)", p0));
	}

	function logBytes21(bytes21 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes21)", p0));
	}

	function logBytes22(bytes22 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes22)", p0));
	}

	function logBytes23(bytes23 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes23)", p0));
	}

	function logBytes24(bytes24 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes24)", p0));
	}

	function logBytes25(bytes25 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes25)", p0));
	}

	function logBytes26(bytes26 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes26)", p0));
	}

	function logBytes27(bytes27 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes27)", p0));
	}

	function logBytes28(bytes28 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes28)", p0));
	}

	function logBytes29(bytes29 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes29)", p0));
	}

	function logBytes30(bytes30 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes30)", p0));
	}

	function logBytes31(bytes31 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes31)", p0));
	}

	function logBytes32(bytes32 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes32)", p0));
	}

	function log(uint p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint)", p0));
	}

	function log(string memory p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string)", p0));
	}

	function log(bool p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool)", p0));
	}

	function log(address p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address)", p0));
	}

	function log(uint p0, uint p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint)", p0, p1));
	}

	function log(uint p0, string memory p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string)", p0, p1));
	}

	function log(uint p0, bool p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool)", p0, p1));
	}

	function log(uint p0, address p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address)", p0, p1));
	}

	function log(string memory p0, uint p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint)", p0, p1));
	}

	function log(string memory p0, string memory p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string)", p0, p1));
	}

	function log(string memory p0, bool p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool)", p0, p1));
	}

	function log(string memory p0, address p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address)", p0, p1));
	}

	function log(bool p0, uint p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint)", p0, p1));
	}

	function log(bool p0, string memory p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string)", p0, p1));
	}

	function log(bool p0, bool p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool)", p0, p1));
	}

	function log(bool p0, address p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address)", p0, p1));
	}

	function log(address p0, uint p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint)", p0, p1));
	}

	function log(address p0, string memory p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string)", p0, p1));
	}

	function log(address p0, bool p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool)", p0, p1));
	}

	function log(address p0, address p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address)", p0, p1));
	}

	function log(uint p0, uint p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint)", p0, p1, p2));
	}

	function log(uint p0, uint p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,string)", p0, p1, p2));
	}

	function log(uint p0, uint p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool)", p0, p1, p2));
	}

	function log(uint p0, uint p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,address)", p0, p1, p2));
	}

	function log(uint p0, string memory p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,uint)", p0, p1, p2));
	}

	function log(uint p0, string memory p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,string)", p0, p1, p2));
	}

	function log(uint p0, string memory p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,bool)", p0, p1, p2));
	}

	function log(uint p0, string memory p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,address)", p0, p1, p2));
	}

	function log(uint p0, bool p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint)", p0, p1, p2));
	}

	function log(uint p0, bool p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,string)", p0, p1, p2));
	}

	function log(uint p0, bool p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool)", p0, p1, p2));
	}

	function log(uint p0, bool p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,address)", p0, p1, p2));
	}

	function log(uint p0, address p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,uint)", p0, p1, p2));
	}

	function log(uint p0, address p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,string)", p0, p1, p2));
	}

	function log(uint p0, address p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,bool)", p0, p1, p2));
	}

	function log(uint p0, address p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,address)", p0, p1, p2));
	}

	function log(string memory p0, uint p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,uint)", p0, p1, p2));
	}

	function log(string memory p0, uint p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,string)", p0, p1, p2));
	}

	function log(string memory p0, uint p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,bool)", p0, p1, p2));
	}

	function log(string memory p0, uint p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,address)", p0, p1, p2));
	}

	function log(string memory p0, string memory p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,uint)", p0, p1, p2));
	}

	function log(string memory p0, string memory p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,string)", p0, p1, p2));
	}

	function log(string memory p0, string memory p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,bool)", p0, p1, p2));
	}

	function log(string memory p0, string memory p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,address)", p0, p1, p2));
	}

	function log(string memory p0, bool p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,uint)", p0, p1, p2));
	}

	function log(string memory p0, bool p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,string)", p0, p1, p2));
	}

	function log(string memory p0, bool p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,bool)", p0, p1, p2));
	}

	function log(string memory p0, bool p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,address)", p0, p1, p2));
	}

	function log(string memory p0, address p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,uint)", p0, p1, p2));
	}

	function log(string memory p0, address p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,string)", p0, p1, p2));
	}

	function log(string memory p0, address p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,bool)", p0, p1, p2));
	}

	function log(string memory p0, address p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,address)", p0, p1, p2));
	}

	function log(bool p0, uint p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint)", p0, p1, p2));
	}

	function log(bool p0, uint p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,string)", p0, p1, p2));
	}

	function log(bool p0, uint p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool)", p0, p1, p2));
	}

	function log(bool p0, uint p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,address)", p0, p1, p2));
	}

	function log(bool p0, string memory p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,uint)", p0, p1, p2));
	}

	function log(bool p0, string memory p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,string)", p0, p1, p2));
	}

	function log(bool p0, string memory p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,bool)", p0, p1, p2));
	}

	function log(bool p0, string memory p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,address)", p0, p1, p2));
	}

	function log(bool p0, bool p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint)", p0, p1, p2));
	}

	function log(bool p0, bool p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,string)", p0, p1, p2));
	}

	function log(bool p0, bool p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool)", p0, p1, p2));
	}

	function log(bool p0, bool p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,address)", p0, p1, p2));
	}

	function log(bool p0, address p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,uint)", p0, p1, p2));
	}

	function log(bool p0, address p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,string)", p0, p1, p2));
	}

	function log(bool p0, address p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,bool)", p0, p1, p2));
	}

	function log(bool p0, address p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,address)", p0, p1, p2));
	}

	function log(address p0, uint p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,uint)", p0, p1, p2));
	}

	function log(address p0, uint p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,string)", p0, p1, p2));
	}

	function log(address p0, uint p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,bool)", p0, p1, p2));
	}

	function log(address p0, uint p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,address)", p0, p1, p2));
	}

	function log(address p0, string memory p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,uint)", p0, p1, p2));
	}

	function log(address p0, string memory p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,string)", p0, p1, p2));
	}

	function log(address p0, string memory p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,bool)", p0, p1, p2));
	}

	function log(address p0, string memory p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,address)", p0, p1, p2));
	}

	function log(address p0, bool p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,uint)", p0, p1, p2));
	}

	function log(address p0, bool p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,string)", p0, p1, p2));
	}

	function log(address p0, bool p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,bool)", p0, p1, p2));
	}

	function log(address p0, bool p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,address)", p0, p1, p2));
	}

	function log(address p0, address p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,uint)", p0, p1, p2));
	}

	function log(address p0, address p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,string)", p0, p1, p2));
	}

	function log(address p0, address p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,bool)", p0, p1, p2));
	}

	function log(address p0, address p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,address)", p0, p1, p2));
	}

	function log(uint p0, uint p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,string)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,address)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,string)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,address)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,string)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,address)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,string)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,address)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,string)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,address)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,string,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,string,string)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,string,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,string,address)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,string)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,address)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,address,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,address,string)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,address,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,address,address)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,string)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,address)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,string)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,address)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,string)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,address)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,string)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,address)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,string)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,address)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,string,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,string,string)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,string,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,string,address)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,string)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,address)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,address,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,address,string)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,address,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,address,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,string,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,string,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,string,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,string,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,address,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,address,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,address,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,address,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,uint,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,uint,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,uint,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,uint,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,string,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,string,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,string,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,string,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,bool,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,bool,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,bool,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,bool,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,address,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,address,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,address,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,address,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,string,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,string,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,string,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,string,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,address,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,address,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,address,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,address,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,uint,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,uint,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,uint,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,uint,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,string,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,string,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,string,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,string,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,bool,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,bool,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,bool,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,bool,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,address,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,address,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,address,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,address,address)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,string)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,address)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,string)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,address)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,string)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,address)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,string)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,address)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,string)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,address)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,string,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,string,string)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,string,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,string,address)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,string)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,address)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,address,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,address,string)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,address,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,address,address)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,string)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,address)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,string)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,address)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,string)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,address)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,string)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,address)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,string)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,address)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,string,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,string,string)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,string,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,string,address)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,string)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,address)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,address,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,address,string)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,address,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,address,address)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,uint)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,string)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,bool)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,address)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,string,uint)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,string,string)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,string,bool)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,string,address)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,uint)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,string)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,bool)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,address)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,address,uint)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,address,string)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,address,bool)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,address,address)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,uint,uint)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,uint,string)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,uint,bool)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,uint,address)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,string,uint)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,string,string)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,string,bool)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,string,address)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,bool,uint)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,bool,string)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,bool,bool)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,bool,address)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,address,uint)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,address,string)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,address,bool)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,address,address)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,uint)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,string)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,bool)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,address)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,string,uint)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,string,string)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,string,bool)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,string,address)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,uint)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,string)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,bool)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,address)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,address,uint)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,address,string)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,address,bool)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,address,address)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,uint,uint)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,uint,string)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,uint,bool)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,uint,address)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,string,uint)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,string,string)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,string,bool)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,string,address)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,bool,uint)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,bool,string)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,bool,bool)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,bool,address)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,address,uint)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,address,string)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,address,bool)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,address,address)", p0, p1, p2, p3));
	}

}

File 89 of 122 : BattleflyComic.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;

import "@openzeppelin/contracts-upgradeable/token/ERC1155/ERC1155Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC1155/extensions/ERC1155SupplyUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";

import "./interfaces/IBattleflyFounderVault.sol";
import "./interfaces/IBattleflyStaker.sol";
import "./interfaces/IBattleflyComic.sol";

contract BattleflyComic is
    ERC1155Upgradeable,
    OwnableUpgradeable,
    ReentrancyGuardUpgradeable,
    ERC1155SupplyUpgradeable,
    IBattleflyComic
{
    using SafeERC20Upgradeable for IERC20Upgradeable;

    uint256 public currentComicId;

    IBattleflyFounderVault public FounderVaultV1;
    IBattleflyFounderVault public FounderVaultV2;
    IBattleflyStaker public BattleflyStaker;
    IERC20Upgradeable public Magic;

    mapping(uint256 => Comic) public comicIdToComic;
    mapping(uint256 => mapping(uint256 => bool)) public usedTokens;
    mapping(uint256 => mapping(address => uint256)) public paidMints;
    mapping(address => bool) public admins;

    function initialize(
        address _magic,
        address _founderVaultV1,
        address _founderVaultV2,
        address _battleflyStake
    ) external initializer {
        __ERC1155_init("");
        __Ownable_init();
        __ReentrancyGuard_init();
        __ERC1155Supply_init();

        require(_magic != address(0), "BattleflyComic: invalid address");
        require(_founderVaultV1 != address(0), "BattleflyComic: invalid address");
        require(_founderVaultV2 != address(0), "BattleflyComic: invalid address");
        require(_battleflyStake != address(0), "BattleflyComic: invalid address");

        admins[msg.sender] = true;

        Magic = IERC20Upgradeable(_magic);
        FounderVaultV1 = IBattleflyFounderVault(_founderVaultV1);
        FounderVaultV2 = IBattleflyFounderVault(_founderVaultV2);
        BattleflyStaker = IBattleflyStaker(_battleflyStake);
    }

    // ---------------- Public methods ----------------- //

    /**
     * @dev Mint comic(s) with staked founders tokens.
     */
    function mintFounders(uint256[] memory tokenIds, uint256 id) public override nonReentrant {
        require(comicIdToComic[id].active, "BattleflyComic: This comic cannot be minted as it is currently paused");
        require(
            comicIdToComic[id].mintType == 1,
            "BattleflyComic: This comic cannot be minted by using founders tokens"
        );
        require(
            comicIdToComic[id].maxMints == 0 || totalSupply(id) + tokenIds.length <= comicIdToComic[id].maxMints,
            "BattleflyComic: Max amount of mints reached for this comic"
        );
        for (uint256 i = 0; i < tokenIds.length; i++) {
            require(
                FounderVaultV1.isOwner(msg.sender, tokenIds[i]) || FounderVaultV2.isOwner(msg.sender, tokenIds[i]),
                "BattleflyComic: Founders token not staked by minter"
            );
            require(!usedTokens[id][tokenIds[i]], "BattleflyComic: Founders token cannot be used twice for minting");
        }
        _mint(msg.sender, id, tokenIds.length, "");
        emit MintComicWithFounder(msg.sender, id, tokenIds);
        for (uint256 i = 0; i < tokenIds.length; i++) {
            usedTokens[id][tokenIds[i]] = true;
        }
    }

    /**
     * @dev Mint comic(s) with staked battlefly tokens.
     */
    function mintBattlefly(uint256[] memory tokenIds, uint256 id) public override nonReentrant {
        require(comicIdToComic[id].active, "BattleflyComic: This comic cannot be minted as it is currently paused");
        require(
            comicIdToComic[id].mintType == 2,
            "BattleflyComic: This comic cannot be minted by using battlefly tokens"
        );
        require(
            comicIdToComic[id].maxMints == 0 || totalSupply(id) + tokenIds.length <= comicIdToComic[id].maxMints,
            "BattleflyComic: Max amount of mints reached for this comic"
        );
        for (uint256 i = 0; i < tokenIds.length; i++) {
            require(
                BattleflyStaker.ownerOf(tokenIds[i]) == msg.sender,
                "BattleflyComic: Battlefly token not staked by minter"
            );
            require(!usedTokens[id][tokenIds[i]], "BattleflyComic: Battlefly token cannot be used twice for minting");
        }
        _mint(msg.sender, id, tokenIds.length, "");
        emit MintComicWithBattlefly(msg.sender, id, tokenIds);
        for (uint256 i = 0; i < tokenIds.length; i++) {
            usedTokens[id][tokenIds[i]] = true;
        }
    }

    /**
     * @dev Mint comic(s) by paying Magic.
     */
    function mintPaid(uint256 amount, uint256 id) public override nonReentrant {
        require(comicIdToComic[id].active, "BattleflyComic: This comic cannot be minted as it is currently paused");
        require(
            comicIdToComic[id].mintType == 1 || comicIdToComic[id].mintType == 2,
            "BattleflyComic: This comic cannot be minted as paid mint"
        );
        require(
            comicIdToComic[id].maxMints == 0 || totalSupply(id) + amount <= comicIdToComic[id].maxMints,
            "BattleflyComic: Max amount of mints reached for this comic"
        );
        require(
            comicIdToComic[id].maxPaidMintsPerWallet == 0 ||
                paidMints[id][msg.sender] + amount <= comicIdToComic[id].maxPaidMintsPerWallet,
            "BattleflyComic: Max mints per address for this comic reached"
        );
        require(
            Magic.balanceOf(msg.sender) >= (amount * comicIdToComic[id].priceInWei),
            "BattleflyComic: Not enough MAGIC in wallet"
        );
        Magic.transferFrom(msg.sender, address(this), amount * comicIdToComic[id].priceInWei);
        _mint(msg.sender, id, amount, "");
        emit MintComicWithPayment(msg.sender, id, amount);
        paidMints[id][msg.sender] = paidMints[id][msg.sender] + amount;
    }

    /**
     * @dev Mint comic(s) by burning other comics.
     */
    function burn(
        uint256 burnId,
        uint256 amount,
        uint256 mintId
    ) public override nonReentrant {
        require(comicIdToComic[mintId].active, "BattleflyComic: This comic cannot be minted as it is currently paused");
        require(comicIdToComic[mintId].mintType == 3, "BattleflyComic: This comic cannot be used for burning");
        require(comicIdToComic[burnId].burnableIn == mintId, "BattleflyComic: This comic cannot be used for burning");
        require(
            balanceOf(msg.sender, burnId) >= comicIdToComic[burnId].burnAmount * amount,
            "BattleflyComic: Not enough comics in wallet to burn"
        );
        safeTransferFrom(
            msg.sender,
            0x000000000000000000000000000000000000dEaD,
            burnId,
            comicIdToComic[burnId].burnAmount * amount,
            ""
        );
        _mint(msg.sender, mintId, amount, "");
        emit MintComicByBurning(msg.sender, burnId, amount, mintId);
    }

    // ---------------- Admin methods ----------------- //

    /**
     * @dev Add a new comic cover
     */
    function addComic(Comic memory comic) public onlyAdmin {
        currentComicId++;
        Comic memory newComic = Comic(
            currentComicId,
            comic.active,
            comic.mintType,
            comic.priceInWei,
            comic.burnableIn,
            comic.burnAmount,
            comic.maxPaidMintsPerWallet,
            comic.maxMints,
            comic.name,
            comic.uri
        );
        comicIdToComic[currentComicId] = newComic;
        emit NewComicAdded(
            currentComicId,
            comic.active,
            comic.mintType,
            comic.priceInWei,
            comic.burnableIn,
            comic.burnAmount,
            comic.maxPaidMintsPerWallet,
            comic.maxMints,
            comic.name,
            comic.uri
        );
    }

    /**
     * @dev Mint comic(s) and send them to the treasury address.
     */
    function mintTreasury(
        uint256 amount,
        uint256 id,
        address treasury
    ) public onlyAdmin nonReentrant {
        require(comicIdToComic[id].active, "BattleflyComic: This comic cannot be minted as it is currently paused");
        require(comicIdToComic[id].mintType == 4, "BattleflyComic: This comic cannot be minted as treasury mint");
        require(
            comicIdToComic[id].maxMints == 0 || totalSupply(id) + amount <= comicIdToComic[id].maxMints,
            "BattleflyComic: Max amount of mints reached for this comic"
        );
        _mint(treasury, id, amount, "");
        emit MintComicWithTreasury(treasury, id, amount);
    }

    /**
     * @dev Withdraw Magic
     */
    function withdrawMagic(uint256 amount, address receiver) public onlyAdmin {
        Magic.transfer(receiver, amount);
    }

    /**
     * @dev Update a comic URI
     */
    function updateURI(uint256 _comicId, string memory _newUri) public override onlyAdmin {
        comicIdToComic[_comicId].uri = _newUri;
        emit UpdateComicURI(_comicId, _newUri);
    }

    /**
     * @dev Activate or deactivate the comic
     */
    function activateComic(uint256 _comicId, bool _activate) public onlyAdmin {
        comicIdToComic[_comicId].active = _activate;
        emit ComicActivated(_comicId, _activate);
    }

    /**
     * @dev Update comic.
     */
    function updateComic(uint256 _comicId, Comic memory _comic) public onlyAdmin {
        require(_comicId > 0 && _comicId <= currentComicId, "BattleflyComic: Invalid comic id");
        _comic.id = _comicId;
        comicIdToComic[_comicId] = _comic;
        emit ComicUpdated(
            _comicId,
            _comic.active,
            _comic.mintType,
            _comic.priceInWei,
            _comic.burnableIn,
            _comic.burnAmount,
            _comic.maxPaidMintsPerWallet,
            _comic.maxMints,
            _comic.name,
            _comic.uri
        );
    }

    /**
     * @dev Batch adding admin permission
     */
    function addAdmins(address[] calldata _admins) external onlyOwner {
        for (uint256 i = 0; i < _admins.length; i++) {
            admins[_admins[i]] = true;
        }
    }

    /**
     * @dev Batch removing admin permission
     */
    function removeAdmins(address[] calldata _admins) external onlyOwner {
        for (uint256 i = 0; i < _admins.length; i++) {
            admins[_admins[i]] = false;
        }
    }

    // ---------------- View methods ----------------- //

    /**
     * @dev et the URI of a comic.
     */
    function uri(uint256 _comicId)
        public
        view
        virtual
        override(ERC1155Upgradeable, IBattleflyComic)
        returns (string memory)
    {
        return comicIdToComic[_comicId].uri;
    }

    // ---------------- Internal methods ----------------- //

    function _beforeTokenTransfer(
        address operator,
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal override(ERC1155Upgradeable, ERC1155SupplyUpgradeable) {
        super._beforeTokenTransfer(operator, from, to, ids, amounts, data);
    }

    modifier onlyAdmin() {
        require(admins[msg.sender], "BattleflyComic: caller is not an admin");
        _;
    }
}

File 90 of 122 : ERC1155SupplyUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC1155/extensions/ERC1155Supply.sol)

pragma solidity ^0.8.0;

import "../ERC1155Upgradeable.sol";
import "../../../proxy/utils/Initializable.sol";

/**
 * @dev Extension of ERC1155 that adds tracking of total supply per id.
 *
 * Useful for scenarios where Fungible and Non-fungible tokens have to be
 * clearly identified. Note: While a totalSupply of 1 might mean the
 * corresponding is an NFT, there is no guarantees that no other token with the
 * same id are not going to be minted.
 */
abstract contract ERC1155SupplyUpgradeable is Initializable, ERC1155Upgradeable {
    function __ERC1155Supply_init() internal onlyInitializing {
    }

    function __ERC1155Supply_init_unchained() internal onlyInitializing {
    }
    mapping(uint256 => uint256) private _totalSupply;

    /**
     * @dev Total amount of tokens in with a given id.
     */
    function totalSupply(uint256 id) public view virtual returns (uint256) {
        return _totalSupply[id];
    }

    /**
     * @dev Indicates whether any token exist with a given id, or not.
     */
    function exists(uint256 id) public view virtual returns (bool) {
        return ERC1155SupplyUpgradeable.totalSupply(id) > 0;
    }

    /**
     * @dev See {ERC1155-_beforeTokenTransfer}.
     */
    function _beforeTokenTransfer(
        address operator,
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal virtual override {
        super._beforeTokenTransfer(operator, from, to, ids, amounts, data);

        if (from == address(0)) {
            for (uint256 i = 0; i < ids.length; ++i) {
                _totalSupply[ids[i]] += amounts[i];
            }
        }

        if (to == address(0)) {
            for (uint256 i = 0; i < ids.length; ++i) {
                uint256 id = ids[i];
                uint256 amount = amounts[i];
                uint256 supply = _totalSupply[id];
                require(supply >= amount, "ERC1155: burn amount exceeds totalSupply");
                unchecked {
                    _totalSupply[id] = supply - amount;
                }
            }
        }
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[49] private __gap;
}

File 91 of 122 : IBattleflyStaker.sol
//SPDX-License-Identifier: Unlicense
pragma solidity >=0.6.0;

interface IBattleflyStaker {
    function stakingBattlefliesOfOwner(address user) external view returns (uint256[] memory);

    function bulkStakeBattlefly(uint256[] memory tokenIds) external;

    function bulkUnstakeBattlefly(
        uint256[] memory tokenIds,
        uint256[] memory battleflyStages,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    function balanceOf(address owner) external view returns (uint256);

    function ownerOf(uint256 tokenId) external view returns (address owner);
}

File 92 of 122 : IBattleflyComic.sol
//SPDX-License-Identifier: Unlicense
pragma solidity >=0.6.0;

interface IBattleflyComic {
    struct Comic {
        uint256 id;
        bool active;
        // 1 = V1/V2 staked + buyable, 2 = battlefly staked + buyable, 3 = burn result, 4 only mintable by treasury
        uint256 mintType;
        uint256 priceInWei;
        uint256 burnableIn;
        uint256 burnAmount;
        uint256 maxPaidMintsPerWallet;
        uint256 maxMints;
        string name;
        string uri;
    }

    function uri(uint256 _comicId) external view returns (string memory);

    function updateURI(uint256 _comicId, string memory _newUri) external;

    function mintFounders(uint256[] memory tokenIds, uint256 id) external;

    function mintBattlefly(uint256[] memory tokenIds, uint256 id) external;

    function mintPaid(uint256 amount, uint256 id) external;

    function burn(
        uint256 burnId,
        uint256 amount,
        uint256 mintId
    ) external;

    event MintComicWithFounder(address indexed sender, uint256 indexed comicId, uint256[] usedFounderIds);
    event MintComicWithBattlefly(address indexed sender, uint256 indexed comicId, uint256[] usedBattleflyIds);
    event MintComicWithPayment(address indexed sender, uint256 indexed comicId, uint256 amount);
    event MintComicByBurning(
        address indexed sender,
        uint256 indexed comicToBeBurnt,
        uint256 amount,
        uint256 indexed comicId
    );
    event MintComicWithTreasury(address indexed treasury, uint256 indexed comicId, uint256 amount);
    event NewComicAdded(
        uint256 indexed comicId,
        bool active,
        uint256 mintType,
        uint256 priceInWei,
        uint256 burnableIn,
        uint256 burnAmount,
        uint256 maxPaidMintsPerWallet,
        uint256 maxMints,
        string name,
        string uri
    );
    event UpdateComicURI(uint256 indexed comicId, string newUri);
    event ComicActivated(uint256 indexed comicId, bool activated);
    event ComicUpdated(
        uint256 comicId,
        bool active,
        uint256 mintType,
        uint256 priceInWei,
        uint256 burnableIn,
        uint256 burnAmount,
        uint256 maxPaidMintsPerWallet,
        uint256 maxMints,
        string name,
        string uri
    );
}

File 93 of 122 : BattleflyAtlasStaker.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;

import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC1155/IERC1155Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC1155/utils/ERC1155HolderUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/utils/ERC721HolderUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/structs/EnumerableSetUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";

import "./interfaces/IAtlasMine.sol";
import "./interfaces/IBattleflyAtlasStaker.sol";

contract BattleflyAtlasStaker is
    IBattleflyAtlasStaker,
    Initializable,
    OwnableUpgradeable,
    ERC1155HolderUpgradeable,
    ERC721HolderUpgradeable,
    ReentrancyGuardUpgradeable
{
    using SafeERC20Upgradeable for IERC20Upgradeable;
    using AddressUpgradeable for address;
    using SafeCastUpgradeable for uint256;
    using SafeCastUpgradeable for int256;
    using EnumerableSetUpgradeable for EnumerableSetUpgradeable.UintSet;
    using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet;

    // ============================================ STATE ==============================================

    // ============= Global Immutable State ==============

    /// @notice MAGIC token
    /// @dev functionally immutable
    IERC20Upgradeable public magic;
    /// @notice The IAtlasMine
    /// @dev functionally immutable
    IAtlasMine public mine;

    // ============= Global Staking State ==============
    uint256 public constant ONE = 1e30;

    /// @notice Whether new stakes will get staked on the contract as scheduled. For emergencies
    bool public schedulePaused;
    /// @notice The total amount of staked token
    uint256 public totalStaked;
    /// @notice The total amount of share
    uint256 public totalShare;
    /// @notice All stakes currently active
    Stake[] public stakes;
    /// @notice Deposit ID of last stake. Also tracked in atlas mine
    uint256 public lastDepositId;
    /// @notice Rewards accumulated per share
    uint256 public accRewardsPerShare;

    // ============= Vault Staking State ==============
    mapping(address => bool) public battleflyVaults;

    /// @notice Each vault stake, keyed by vault contract address => deposit ID
    mapping(address => mapping(uint256 => VaultStake)) public vaultStake;
    /// @notice All deposit IDs fro a vault, enumerated
    mapping(address => EnumerableSetUpgradeable.UintSet) private allVaultDepositIds;
    /// @notice The current ID of the vault's last deposited stake
    mapping(address => uint256) public currentId;

    // ============= NFT Boosting State ==============

    /// @notice Holder of treasures and legions
    mapping(uint256 => bool) public legionsStaked;
    mapping(uint256 => uint256) public treasuresStaked;

    // ============= Operator State ==============

    IAtlasMine.Lock[] public allowedLocks;
    /// @notice Fee to contract operator. Only assessed on rewards.
    uint256 public fee;
    /// @notice Amount of fees reserved for withdrawal by the operator.
    uint256 public feeReserve;
    /// @notice Max fee the owner can ever take - 10%
    uint256 public constant MAX_FEE = 1000;
    uint256 public constant FEE_DENOMINATOR = 10000;

    mapping(address => mapping(uint256 => int256)) refundedFeeDebts;
    uint256 accRefundedFeePerShare;
    uint256 totalWhitelistedFeeShare;
    EnumerableSetUpgradeable.AddressSet whitelistedFeeVaults;
    mapping(address => bool) public superAdmins;

    /// @notice deposited but unstaked
    uint256 public unstakedDeposits;
    mapping(IAtlasMine.Lock => uint256) public unstakedDepositsByLock;
    address public constant TREASURY_WALLET = 0xF5411006eEfD66c213d2fd2033a1d340458B7226;
    /// @notice Intra-tx buffer for pending payouts
    uint256 public tokenBuffer;

    // ===========================================
    // ============== Post Upgrade ===============
    // ===========================================

    // ========================================== INITIALIZER ===========================================

    /**
     * @param _magic                The MAGIC token address.
     * @param _mine                 The IAtlasMine contract.
     *                              Maps to a timelock for IAtlasMine deposits.
     */
    function initialize(
        IERC20Upgradeable _magic,
        IAtlasMine _mine,
        IAtlasMine.Lock[] memory _allowedLocks
    ) external initializer {
        __ERC1155Holder_init();
        __ERC721Holder_init();
        __Ownable_init();
        __ReentrancyGuard_init();

        magic = _magic;
        mine = _mine;
        allowedLocks = _allowedLocks;
        fee = 1000;
        // Approve the mine
        magic.safeApprove(address(mine), 2**256 - 1);
        // approveNFTs();
    }

    // ======================================== VAULT OPERATIONS ========================================

    /**
     * @notice Make a new deposit into the Staker. The Staker will collect
     *         the tokens, to be later staked in atlas mine by the owner,
     *         according to the stake/unlock schedule.
     * @dev    Specified amount of token must be approved by the caller.
     *
     * @param _amount               The amount of tokens to deposit.
     */
    function deposit(uint256 _amount, IAtlasMine.Lock lock)
        public
        virtual
        override
        onlyBattleflyVaultOrOwner
        nonReentrant
        returns (uint256)
    {
        require(!schedulePaused, "new staking paused");
        _updateRewards();
        // Collect tokens
        uint256 newDepositId = _deposit(_amount, msg.sender, lock);
        magic.safeTransferFrom(msg.sender, address(this), _amount);
        return (newDepositId);
    }

    function _deposit(
        uint256 _amount,
        address _vault,
        IAtlasMine.Lock lock
    ) internal returns (uint256) {
        require(_amount > 0, "Deposit amount 0");
        bool validLock = false;
        for (uint256 i = 0; i < allowedLocks.length; i++) {
            if (allowedLocks[i] == lock) {
                validLock = true;
                break;
            }
        }
        require(validLock, "Lock time not allowed");
        // Add vault stake
        uint256 newDepositId = ++currentId[_vault];
        allVaultDepositIds[_vault].add(newDepositId);
        VaultStake storage s = vaultStake[_vault][newDepositId];

        s.amount = _amount;
        (uint256 boost, uint256 lockTime) = getLockBoost(lock);
        uint256 share = (_amount * (100e16 + boost)) / 100e16;

        uint256 vestingTime = mine.getVestingTime(lock);
        s.unlockAt = block.timestamp + lockTime + vestingTime + 1 days;
        s.rewardDebt = ((share * accRewardsPerShare) / ONE).toInt256();
        s.lock = lock;

        // Update global accounting
        totalStaked += _amount;
        totalShare += share;
        if (whitelistedFeeVaults.contains(_vault)) {
            totalWhitelistedFeeShare += share;
            refundedFeeDebts[_vault][newDepositId] = ((share * accRefundedFeePerShare) / ONE).toInt256();
        }
        // MAGIC tokens sit in contract. Added to pending stakes
        unstakedDeposits += _amount;
        unstakedDepositsByLock[lock] += _amount;
        emit VaultDeposit(_vault, newDepositId, _amount, s.unlockAt, s.lock);
        return newDepositId;
    }

    /**
     * @notice Withdraw a deposit from the Staker contract. Calculates
     *         pro rata share of accumulated MAGIC and distributes any
     *         earned rewards in addition to original deposit.
     *         There must be enough unlocked tokens to withdraw.
     *
     * @param depositId             The ID of the deposit to withdraw from.
     *
     */
    function withdraw(uint256 depositId) public virtual override onlyBattleflyVaultOrOwner nonReentrant {
        // Distribute tokens
        _updateRewards();
        VaultStake storage s = vaultStake[msg.sender][depositId];
        require(s.amount > 0, "No deposit");
        require(block.timestamp >= s.unlockAt, "Deposit locked");

        uint256 payout = _withdraw(s, depositId);
        magic.safeTransfer(msg.sender, payout);
    }

    /**
     * @notice Withdraw all eligible deposits from the staker contract.
     *         Will skip any deposits not yet unlocked. Will also
     *         distribute rewards for all stakes via 'withdraw'.
     *
     */
    function withdrawAll() public virtual override onlyBattleflyVaultOrOwner nonReentrant {
        // Distribute tokens
        _updateRewards();
        uint256[] memory depositIds = allVaultDepositIds[msg.sender].values();
        for (uint256 i = 0; i < depositIds.length; i++) {
            VaultStake storage s = vaultStake[msg.sender][depositIds[i]];

            if (s.amount > 0 && s.unlockAt > 0 && s.unlockAt <= block.timestamp) {
                tokenBuffer += _withdraw(s, depositIds[i]);
            }
        }
        magic.safeTransfer(msg.sender, tokenBuffer);
        tokenBuffer = 0;
    }

    /**
     * @dev Logic for withdrawing a deposit. Calculates pro rata share of
     *      accumulated MAGIC and dsitributed any earned rewards in addition
     *      to original deposit.
     *
     * @dev An _amount argument larger than the total deposit amount will
     *      withdraw the entire deposit.
     *
     * @param s                     The VaultStake struct to withdraw from.
     * @param depositId             The ID of the deposit to withdraw from (for event).
     */
    function _withdraw(VaultStake storage s, uint256 depositId) internal returns (uint256 payout) {
        uint256 _amount = s.amount;

        // Unstake if we need to to ensure we can withdraw
        (uint256 boost, ) = getLockBoost(s.lock);
        uint256 share = (_amount * (100e16 + boost)) / 100e16;
        int256 accumulatedRewards = ((share * accRewardsPerShare) / ONE).toInt256();
        if (whitelistedFeeVaults.contains(msg.sender)) {
            accumulatedRewards += ((share * accRefundedFeePerShare) / ONE).toInt256();
            accumulatedRewards -= refundedFeeDebts[msg.sender][depositId];
            totalWhitelistedFeeShare -= share;
            refundedFeeDebts[msg.sender][depositId] = 0;
        }
        uint256 reward = (accumulatedRewards - s.rewardDebt).toUint256();
        payout = _amount + reward;

        // // Update vault accounting
        // s.amount -= _amount;
        // s.rewardDebt = 0;
        ///comment Archethect: Consider deleting the VaultStake object for gas optimization. s.unlockAt and s.lock can be zeroed as well.
        delete vaultStake[msg.sender][depositId];

        // Update global accounting
        totalStaked -= _amount;

        totalShare -= share;

        // If we need to unstake, unstake until we have enough
        if (payout > _totalUsableMagic()) {
            _unstakeToTarget(payout - _totalUsableMagic());
        }
        emit VaultWithdraw(msg.sender, depositId, _amount, reward);
    }

    /**
     * @notice Claim rewards without unstaking. Will fail if there
     *         are not enough tokens in the contract to claim rewards.
     *         Does not attempt to unstake.
     *
     * @param depositId             The ID of the deposit to claim rewards from.
     *
     */
    function claim(uint256 depositId) public virtual override onlyBattleflyVaultOrOwner nonReentrant returns (uint256) {
        _updateRewards();
        VaultStake storage s = vaultStake[msg.sender][depositId];
        require(s.amount > 0, "No deposit");
        uint256 reward = _claim(s, depositId);
        magic.safeTransfer(msg.sender, reward);
        return reward;
    }

    /**
     * @notice Claim all possible rewards from the staker contract.
     *         Will apply to both locked and unlocked deposits.
     *
     */
    function claimAll() public virtual override onlyBattleflyVaultOrOwner nonReentrant returns (uint256) {
        _updateRewards();
        uint256[] memory depositIds = allVaultDepositIds[msg.sender].values();
        uint256 totalReward = 0;
        for (uint256 i = 0; i < depositIds.length; i++) {
            VaultStake storage s = vaultStake[msg.sender][depositIds[i]];
            uint256 reward = _claim(s, depositIds[i]);
            tokenBuffer += reward;
        }
        magic.safeTransfer(msg.sender, tokenBuffer);
        totalReward = tokenBuffer;
        tokenBuffer = 0;
        return totalReward;
    }

    /**
     * @notice Claim all possible rewards from the staker contract then restake.
     *         Will apply to both locked and unlocked deposits.
     *
     */
    function claimAllAndRestake(IAtlasMine.Lock lock) public onlyBattleflyVaultOrOwner nonReentrant returns (uint256) {
        _updateRewards();
        uint256[] memory depositIds = allVaultDepositIds[msg.sender].values();
        uint256 totalReward = 0;
        for (uint256 i = 0; i < depositIds.length; i++) {
            VaultStake storage s = vaultStake[msg.sender][depositIds[i]];
            uint256 reward = _claim(s, depositIds[i]);
            tokenBuffer += reward;
        }
        _deposit(tokenBuffer, msg.sender, lock);
        tokenBuffer = 0;
        return totalReward;
    }

    /**
     * @dev Logic for claiming rewards on a deposit. Calculates pro rata share of
     *      accumulated MAGIC and dsitributed any earned rewards in addition
     *      to original deposit.
     *
     * @param s                     The VaultStake struct to claim from.
     * @param depositId             The ID of the deposit to claim from (for event).
     */
    function _claim(VaultStake storage s, uint256 depositId) internal returns (uint256) {
        // Update accounting
        (uint256 boost, ) = getLockBoost(s.lock);
        uint256 share = (s.amount * (100e16 + boost)) / 100e16;

        int256 accumulatedRewards = ((share * accRewardsPerShare) / ONE).toInt256();

        uint256 reward = (accumulatedRewards - s.rewardDebt).toUint256();
        if (whitelistedFeeVaults.contains(msg.sender)) {
            int256 accumulatedRefundedFee = ((share * accRefundedFeePerShare) / ONE).toInt256();
            reward += accumulatedRefundedFee.toUint256();
            reward -= refundedFeeDebts[msg.sender][depositId].toUint256();
            refundedFeeDebts[msg.sender][depositId] = accumulatedRefundedFee;
        }
        s.rewardDebt = accumulatedRewards;

        // Unstake if we need to to ensure we can withdraw
        if (reward > _totalUsableMagic()) {
            _unstakeToTarget(reward - _totalUsableMagic());
        }

        require(reward <= _totalUsableMagic(), "Not enough rewards to claim");
        emit VaultClaim(msg.sender, depositId, reward);
        return reward;
    }

    // ======================================= SUPER ADMIN OPERATIONS ========================================

    /**
     * @notice Stake a Treasure owned by the superAdmin into the Atlas Mine.
     *         Staked treasures will boost all vault deposits.
     * @dev    Any treasure must be approved for withdrawal by the caller.
     *
     * @param _tokenId              The tokenId of the specified treasure.
     * @param _amount               The amount of treasures to stake.
     */
    function stakeTreasure(uint256 _tokenId, uint256 _amount) external onlySuperAdminOrOwner {
        address treasureAddr = mine.treasure();
        require(IERC1155Upgradeable(treasureAddr).balanceOf(msg.sender, _tokenId) >= _amount, "Not enough treasures");
        treasuresStaked[_tokenId] += _amount;
        // First withdraw and approve
        IERC1155Upgradeable(treasureAddr).safeTransferFrom(msg.sender, address(this), _tokenId, _amount, bytes(""));
        mine.stakeTreasure(_tokenId, _amount);
        uint256 boost = mine.boosts(address(this));

        emit StakeNFT(msg.sender, treasureAddr, _tokenId, _amount, boost);
    }

    /**
     * @notice Unstake a Treasure from the Atlas Mine adn transfer to receiver.
     *
     * @param _receiver              The receiver .
     * @param _tokenId              The tokenId of the specified treasure.
     * @param _amount               The amount of treasures to stake.
     */
    function unstakeTreasure(
        address _receiver,
        uint256 _tokenId,
        uint256 _amount
    ) external onlySuperAdminOrOwner {
        require(treasuresStaked[_tokenId] >= _amount, "Not enough treasures");
        treasuresStaked[_tokenId] -= _amount;
        address treasureAddr = mine.treasure();
        mine.unstakeTreasure(_tokenId, _amount);
        IERC1155Upgradeable(treasureAddr).safeTransferFrom(address(this), _receiver, _tokenId, _amount, bytes(""));
        uint256 boost = mine.boosts(address(this));
        emit UnstakeNFT(_receiver, treasureAddr, _tokenId, _amount, boost);
    }

    /**
     * @notice Stake a Legion owned by the superAdmin into the Atlas Mine.
     *         Staked legions will boost all vault deposits.
     * @dev    Any legion be approved for withdrawal by the caller.
     *
     * @param _tokenId              The tokenId of the specified legion.
     */
    function stakeLegion(uint256 _tokenId) external onlySuperAdminOrOwner {
        address legionAddr = mine.legion();
        require(IERC721Upgradeable(legionAddr).ownerOf(_tokenId) == msg.sender, "Not owner of legion");
        legionsStaked[_tokenId] = true;
        IERC721Upgradeable(legionAddr).safeTransferFrom(msg.sender, address(this), _tokenId);

        mine.stakeLegion(_tokenId);

        uint256 boost = mine.boosts(address(this));

        emit StakeNFT(msg.sender, legionAddr, _tokenId, 1, boost);
    }

    /**
     * @notice Unstake a Legion from the Atlas Mine and return it to the superAdmin.
     *
     * @param _tokenId              The tokenId of the specified legion.
     */
    function unstakeLegion(address _receiver, uint256 _tokenId) external onlySuperAdminOrOwner {
        require(legionsStaked[_tokenId], "No legion");
        address legionAddr = mine.legion();
        delete legionsStaked[_tokenId];
        mine.unstakeLegion(_tokenId);

        // Distribute to superAdmin
        IERC721Upgradeable(legionAddr).safeTransferFrom(address(this), _receiver, _tokenId);
        uint256 boost = mine.boosts(address(this));

        emit UnstakeNFT(_receiver, legionAddr, _tokenId, 1, boost);
    }

    /**
     * @notice Stake any pending stakes before the current day. Callable
     *         by anybody. Any pending stakes will unlock according
     *         to the time this method is called, and the contract's defined
     *         lock time.
     */
    function stakeScheduled() external virtual override onlySuperAdminOrOwner {
        for (uint256 i = 0; i < allowedLocks.length; i++) {
            IAtlasMine.Lock lock = allowedLocks[i];
            _stakeInMine(unstakedDepositsByLock[lock], lock);
            unstakedDepositsByLock[lock] = 0;
        }
        unstakedDeposits = 0;
    }

    /**
     * @notice Unstake everything eligible for unstaking from Atlas Mine.
     *         Callable by owner. Should only be used in case of emergency
     *         or migration to a new contract, or if there is a need to service
     *         an unexpectedly large amount of withdrawals.
     *
     *         If unlockAll is set to true in the Atlas Mine, this can withdraw
     *         all stake.
     */
    function unstakeAllFromMine() external override onlySuperAdminOrOwner {
        // Unstake everything eligible
        _updateRewards();

        for (uint256 i = 0; i < stakes.length; i++) {
            Stake memory s = stakes[i];

            if (s.unlockAt > block.timestamp) {
                continue;
            }

            // Withdraw position - auto-harvest
            mine.withdrawPosition(s.depositId, s.amount);
        }

        // Only check for removal after, so we don't mutate while looping
        _removeZeroStakes();
    }

    /**
     * @notice Let owner unstake a specified amount as needed to make sure the contract is funded.
     *         Can be used to facilitate expected future withdrawals.
     *
     * @param target                The amount of tokens to reclaim from the mine.
     */
    function unstakeToTarget(uint256 target) external override onlySuperAdminOrOwner {
        _updateRewards();
        _unstakeToTarget(target);
    }

    /**
     * @notice Withdraw any accumulated reward fees to the treasury
     */
    function withdrawFeesToTreasury() external virtual onlySuperAdminOrOwner {
        uint256 amount = feeReserve;
        feeReserve = 0;
        magic.safeTransfer(TREASURY_WALLET, amount);
        emit WithdrawFeesToTreasury(amount);
    }

    function stakeBackFeeTreasury(IAtlasMine.Lock lock) external virtual onlySuperAdminOrOwner {
        uint256 amount = feeReserve;
        feeReserve = 0;
        emit WithdrawFeesToTreasury(amount);
        // magic.safeTransfer(TREASURY_WALLET, amount);
        _deposit(amount, TREASURY_WALLET, lock);
    }

    /**
     * @notice Whitelist vault from fees.
     *
     * @param _vault                Vault address.
     * @param isSet                 Whether to enable or disable the vault whitelist.
     */
    function setFeeWhitelistVault(address _vault, bool isSet) external onlyOwner {
        require(_vault != address(0), "Invalid Vault");
        if (isSet) {
            whitelistedFeeVaults.add(_vault);
            totalWhitelistedFeeShare += totalShareOf(_vault);
        } else {
            whitelistedFeeVaults.remove(_vault);
            totalWhitelistedFeeShare -= totalShareOf(_vault);
        }
        emit SetFeeWhitelistVault(_vault, isSet);
    }

    // ======================================= OWNER OPERATIONS =======================================

    function setBattleflyVault(address _vaultAddress, bool isSet) external onlyOwner {
        require(_vaultAddress != address(0), "Invalid vault");
        if (isSet) {
            require(battleflyVaults[_vaultAddress] == false, "Vault already set");
            battleflyVaults[_vaultAddress] = isSet;
        } else {
            require(allVaultDepositIds[_vaultAddress].length() == 0, "Vault is still active");
            delete battleflyVaults[_vaultAddress];
        }
        emit SetBattleflyVault(_vaultAddress, isSet);
    }

    /**
     * @notice Change the designated superAdmin, the address where treasures and
     *         legions are held. Staked NFTs can only be
     *         withdrawn to the current superAdmin address, regardless of which
     *         address the superAdmin was set to when it was staked.
     *
     * @param _superAdmin                The new superAdmin address.
     * @param isSet                 Whether to enable or disable the superAdmin address.
     */
    function setBoostAdmin(address _superAdmin, bool isSet) external override onlyOwner {
        require(_superAdmin != address(0), "Invalid superAdmin");

        superAdmins[_superAdmin] = isSet;
    }

    /**
     * @notice Change the designated super admin, who manage the fee reverse
     *
     * @param _superAdmin                The new superAdmin address.
     * @param isSet                 Whether to enable or disable the super admin address.
     */
    function setSuperAdmin(address _superAdmin, bool isSet) external onlyOwner {
        require(_superAdmin != address(0), "Invalid address");
        superAdmins[_superAdmin] = isSet;
    }

    /**
     * @notice Approve treasures and legions for withdrawal from the atlas mine.
     *         Called on startup, and should be called again in case contract
     *         addresses for treasures and legions ever change.
     *
     */
    function approveNFTs() public override onlyOwner {
        address treasureAddr = mine.treasure();
        IERC1155Upgradeable(treasureAddr).setApprovalForAll(address(mine), true);

        address legionAddr = mine.legion();
        IERC1155Upgradeable(legionAddr).setApprovalForAll(address(mine), true);
    }

    /**
     * @notice EMERGENCY ONLY - toggle pausing new scheduled stakes.
     *         If on, vaults can deposit, but stakes won't go to Atlas Mine.
     *         Can be used in case of Atlas Mine issues or forced migration
     *         to new contract.
     */
    function toggleSchedulePause(bool paused) external virtual override onlyOwner {
        schedulePaused = paused;

        emit StakingPauseToggle(paused);
    }

    // ======================================== VIEW FUNCTIONS =========================================
    function getLockBoost(IAtlasMine.Lock _lock) public pure virtual returns (uint256 boost, uint256 timelock) {
        if (_lock == IAtlasMine.Lock.twoWeeks) {
            // 10%
            return (10e16, 14 days);
        } else if (_lock == IAtlasMine.Lock.oneMonth) {
            // 25%
            return (25e16, 30 days);
        } else if (_lock == IAtlasMine.Lock.threeMonths) {
            // 80%
            return (80e16, 13 weeks);
        } else if (_lock == IAtlasMine.Lock.sixMonths) {
            // 180%
            return (180e16, 26 weeks);
        } else if (_lock == IAtlasMine.Lock.twelveMonths) {
            // 400%
            return (400e16, 365 days);
        } else {
            revert("Invalid lock value");
        }
    }

    /**
     * @notice Returns all magic either unstaked, staked, or pending rewards in Atlas Mine.
     *         Best proxy for TVL.
     *
     * @return total               The total amount of MAGIC in the staker.
     */
    function totalMagic() external view override returns (uint256) {
        return _totalControlledMagic() + mine.pendingRewardsAll(address(this));
    }

    /**
     * @notice Returns all magic that has been deposited, but not staked, and is eligible
     *         to be staked (deposit time < current day).
     *
     * @return total               The total amount of MAGIC that can be withdrawn.
     */
    function totalWithdrawableMagic() external view override returns (uint256) {
        uint256 totalPendingRewards;

        // IAtlasMine attempts to divide by 0 if there are no deposits
        try mine.pendingRewardsAll(address(this)) returns (uint256 _pending) {
            totalPendingRewards = _pending;
        } catch Panic(uint256) {
            totalPendingRewards = 0;
        }

        return _totalUsableMagic() + totalPendingRewards;
    }

    /**
     * @notice Returns the details of a vault stake.
     *
     * @return vaultStake           The details of a vault stake.
     */
    function getVaultStake(address vault, uint256 depositId) external view override returns (VaultStake memory) {
        return vaultStake[vault][depositId];
    }

    /**
     * @notice Returns the pending, claimable rewards for a deposit.
     * @dev    This does not update rewards, so out of date if rewards not recently updated.
     *         Needed to maintain 'view' function type.
     *
     * @param vault              The vault to check rewards for.
     * @param depositId         The specific deposit to check rewards for.
     *
     * @return reward           The total amount of MAGIC reward pending.
     */
    function pendingRewards(address vault, uint256 depositId) public view override returns (uint256 reward) {
        if (totalShare == 0) {
            return 0;
        }
        VaultStake storage s = vaultStake[vault][depositId];
        (uint256 boost, ) = getLockBoost(s.lock);
        uint256 share = (s.amount * (100e16 + boost)) / 100e16;

        uint256 unupdatedReward = mine.pendingRewardsAll(address(this));
        (uint256 founderReward, , uint256 feeRefund) = _calculateHarvestRewardFee(unupdatedReward);
        uint256 realAccRewardsPerShare = accRewardsPerShare + (founderReward * ONE) / totalShare;
        uint256 accumulatedRewards = (share * realAccRewardsPerShare) / ONE;
        if (whitelistedFeeVaults.contains(vault) && totalWhitelistedFeeShare > 0) {
            uint256 realAccRefundedFeePerShare = accRefundedFeePerShare + (feeRefund * ONE) / totalWhitelistedFeeShare;
            uint256 accumulatedRefundedFee = (share * realAccRefundedFeePerShare) / ONE;
            accumulatedRewards = accumulatedRewards + accumulatedRefundedFee;
            accumulatedRewards -= refundedFeeDebts[vault][depositId].toUint256();
        }
        reward = accumulatedRewards - s.rewardDebt.toUint256();
    }

    /**
     * @notice Returns the pending, claimable rewards for all of a vault's deposits.
     * @dev    This does not update rewards, so out of date if rewards not recently updated.
     *         Needed to maintain 'view' function type.
     *
     * @param vault              The vault to check rewards for.
     *
     * @return reward           The total amount of MAGIC reward pending.
     */
    function pendingRewardsAll(address vault) external view override returns (uint256 reward) {
        uint256[] memory depositIds = allVaultDepositIds[vault].values();

        for (uint256 i = 0; i < depositIds.length; i++) {
            reward += pendingRewards(vault, depositIds[i]);
        }
    }

    /**
     * @notice Returns the total Share of a vault.
     *
     * @param vault              The vault to check rewards for.
     *
     * @return _totalShare           The total share of a vault.
     */
    function totalShareOf(address vault) public view returns (uint256 _totalShare) {
        uint256[] memory depositIds = allVaultDepositIds[vault].values();
        for (uint256 i = 0; i < depositIds.length; i++) {
            (uint256 boost, ) = getLockBoost(vaultStake[vault][depositIds[i]].lock);
            uint256 share = (vaultStake[vault][depositIds[i]].amount * (100e16 + boost)) / 100e16;
            _totalShare += share;
        }
    }

    // ============================================ HELPERS ============================================

    /**
     * @dev Stake tokens held by staker in the Atlas Mine, according to
     *      the predefined lock value. Schedules for staking will be managed by a queue.
     *
     * @param _amount               Number of tokens to stake
     */
    function _stakeInMine(uint256 _amount, IAtlasMine.Lock lock) internal {
        require(_amount <= _totalUsableMagic(), "Not enough funds");

        uint256 depositId = ++lastDepositId;
        (, uint256 lockTime) = getLockBoost(lock);
        uint256 vestingPeriod = mine.getVestingTime(lock);
        uint256 unlockAt = block.timestamp + lockTime + vestingPeriod;

        stakes.push(Stake({ amount: _amount, unlockAt: unlockAt, depositId: depositId }));

        mine.deposit(_amount, lock);
    }

    /**
     * @dev Unstakes until we have enough unstaked tokens to meet a specific target.
     *      Used to make sure we can service withdrawals.
     *
     * @param target                The amount of tokens we want to have unstaked.
     */
    function _unstakeToTarget(uint256 target) internal {
        uint256 unstaked = 0;

        for (uint256 i = 0; i < stakes.length; i++) {
            Stake memory s = stakes[i];

            if (s.unlockAt > block.timestamp && !mine.unlockAll()) {
                // This stake is not unlocked - stop looking
                continue;
            }

            // Withdraw position - auto-harvest
            uint256 preclaimBalance = _totalUsableMagic();
            uint256 targetLeft = target - unstaked;
            uint256 amount = targetLeft > s.amount ? s.amount : targetLeft;

            // Do not harvest rewards - if this is running, we've already
            // harvested in the same fn call
            mine.withdrawPosition(s.depositId, amount);
            uint256 postclaimBalance = _totalUsableMagic();

            // Increment amount unstaked
            unstaked += postclaimBalance - preclaimBalance;

            if (unstaked >= target) {
                // We unstaked enough
                break;
            }
        }

        require(unstaked >= target, "Cannot unstake enough");
        require(_totalUsableMagic() >= target, "Not enough in contract after unstaking");

        // Only check for removal after, so we don't mutate while looping
        _removeZeroStakes();
    }

    /**
     * @dev Harvest rewards from the IAtlasMine and send them back to
     *      this contract.
     *
     * @return earned               The amount of rewards earned for depositors, minus the fee.
     * @return feeEearned           The amount of fees earned for the contract operator.
     */
    function _harvestMine() internal returns (uint256, uint256) {
        uint256 preclaimBalance = magic.balanceOf(address(this));

        try mine.harvestAll() {
            uint256 postclaimBalance = magic.balanceOf(address(this));

            uint256 earned = postclaimBalance - preclaimBalance;
            // Reserve the 'fee' amount of what is earned
            (, uint256 feeEarned, uint256 feeRefunded) = _calculateHarvestRewardFee(earned);
            feeReserve += feeEarned - feeRefunded;
            emit MineHarvest(earned - feeEarned, feeEarned - feeRefunded, feeRefunded);
            return (earned - feeEarned, feeRefunded);
        } catch {
            // Failed because of reward debt calculation - should be 0
            return (0, 0);
        }
    }

    function _calculateHarvestRewardFee(uint256 earned)
        internal
        view
        returns (
            uint256,
            uint256,
            uint256
        )
    {
        uint256 feeEarned = (earned * fee) / FEE_DENOMINATOR;
        uint256 accFeePerShare = (feeEarned * ONE) / totalShare;
        uint256 feeRefunded = (accFeePerShare * totalWhitelistedFeeShare) / ONE;
        return (earned - feeEarned, feeEarned, feeRefunded);
    }

    /**
     * @dev Harvest rewards from the mine so that stakers can claim.
     *      Recalculate how many rewards are distributed to each share.
     */
    function _updateRewards() internal {
        if (totalStaked == 0 || totalShare == 0) return;
        (uint256 newRewards, uint256 feeRefunded) = _harvestMine();
        accRewardsPerShare += (newRewards * ONE) / totalShare;
        if (totalWhitelistedFeeShare > 0) accRefundedFeePerShare += (feeRefunded * ONE) / totalWhitelistedFeeShare;
    }

    /**
     * @dev After mutating a stake (by withdrawing fully or partially),
     *      get updated data from the staking contract, and update the stake amounts
     *
     * @param stakeIndex           The index of the stake in the Stakes storage array.
     *
     * @return amount              The current, updated amount of the stake.
     */
    function _updateStakeDepositAmount(uint256 stakeIndex) internal returns (uint256) {
        Stake storage s = stakes[stakeIndex];

        (, uint256 depositAmount, , , , , ) = mine.userInfo(address(this), s.depositId);
        s.amount = depositAmount;

        return s.amount;
    }

    /**
     * @dev Find stakes with zero deposit amount and remove them from tracking.
     *      Uses recursion to stop from mutating an array we are currently looping over.
     *      If a zero stake is found, it is removed, and the function is restarted,
     *      such that it is always working from a 'clean' array.
     *
     */
    function _removeZeroStakes() internal {
        bool shouldRecurse = stakes.length > 0;

        for (uint256 i = 0; i < stakes.length; i++) {
            _updateStakeDepositAmount(i);

            Stake storage s = stakes[i];

            if (s.amount == 0) {
                _removeStake(i);
                // Stop looping and start again - we will skip
                // out of the look and recurse
                break;
            }

            if (i == stakes.length - 1) {
                // We didn't remove anything, so stop recursing
                shouldRecurse = false;
            }
        }

        if (shouldRecurse) {
            _removeZeroStakes();
        }
    }

    /**
     * @dev Calculate total amount of MAGIC usable by the contract.
     *      'Usable' means available for either withdrawal or re-staking.
     *      Counts unstaked magic less fee reserve.
     *
     * @return amount               The amount of usable MAGIC.
     */
    function _totalUsableMagic() internal view returns (uint256) {
        // Current magic held in contract
        uint256 unstaked = magic.balanceOf(address(this));

        return unstaked - tokenBuffer - feeReserve;
    }

    /**
     * @dev Calculate total amount of MAGIC under control of the contract.
     *      Counts staked and unstaked MAGIC. Does _not_ count accumulated
     *      but unclaimed rewards.
     *
     * @return amount               The total amount of MAGIC under control of the contract.
     */
    function _totalControlledMagic() internal view returns (uint256) {
        // Current magic staked in mine
        uint256 staked = 0;

        for (uint256 i = 0; i < stakes.length; i++) {
            staked += stakes[i].amount;
        }

        return staked + _totalUsableMagic();
    }

    /**
     * @dev Remove a tracked stake from any position in the stakes array.
     *      Used when a stake is no longer relevant i.e. fully withdrawn.
     *      Mutates the Stakes array in storage.
     *
     * @param index                 The index of the stake to remove.
     */
    function _removeStake(uint256 index) internal {
        if (index >= stakes.length) return;

        for (uint256 i = index; i < stakes.length - 1; i++) {
            stakes[i] = stakes[i + 1];
        }

        delete stakes[stakes.length - 1];

        stakes.pop();
    }

    modifier onlySuperAdminOrOwner() {
        require(msg.sender == owner() || superAdmins[msg.sender], "Not Super Admin");
        _;
    }
    modifier onlyBattleflyVaultOrOwner() {
        require(msg.sender == owner() || battleflyVaults[msg.sender], "Not BattleflyVault");
        _;
    }
}

File 94 of 122 : MagicTokenContract.sol
// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.2;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract MagicTokenContract is ERC20, Ownable {
    mapping(address => bool) private adminAccess;

    constructor(string memory name_, string memory symbol_) ERC20(name_, symbol_) {}

    function setAdminAccess(address user, bool access) external onlyOwner {
        adminAccess[user] = access;
    }

    function mint(uint256 amount, address receiver) external onlyAdminAccess {
        _mint(receiver, amount);
    }

    modifier onlyAdminAccess() {
        require(adminAccess[_msgSender()] == true || _msgSender() == owner(), "Require admin access");
        _;
    }
}

File 95 of 122 : Ownable.sol
// 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);
    }
}

File 96 of 122 : ERC721BulkTransfer.sol
// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.2;
import "@openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Context.sol";

contract ERC721BulkTransfer is Context {
    mapping(address => bool) private adminAccess;

    constructor() {}

    function bulkTransferERC721(
        uint256[] memory tokenIds,
        address[] memory receivers,
        address tokenAddress
    ) external {
        IERC721 token = IERC721(tokenAddress);
        for (uint256 i = 0; i < tokenIds.length; i++) {
            token.transferFrom(_msgSender(), receivers[i], tokenIds[i]);
        }
    }

    function transferWithAmount(
        address receiver,
        uint256 amount,
        address tokenAddress
    ) external {
        IERC721Enumerable token = IERC721Enumerable(tokenAddress);
        uint256 balance = token.balanceOf(_msgSender());
        require(amount <= balance, "Not enough balance");
        for (uint256 i = 0; i < amount; i++) {
            token.transferFrom(_msgSender(), receiver, token.tokenOfOwnerByIndex(_msgSender(), 0));
        }
    }

    function bulkTransferWithAmount(
        address[] memory receivers,
        uint256[] memory amounts,
        address tokenAddress
    ) external {
        IERC721Enumerable token = IERC721Enumerable(tokenAddress);
        require(receivers.length == amounts.length, "Wrong input");
        for (uint256 i = 0; i < receivers.length; i++) {
            for (uint256 j = 0; j < amounts[i]; j++) {
                token.transferFrom(_msgSender(), receivers[i], token.tokenOfOwnerByIndex(_msgSender(), 0));
            }
        }
    }
}

File 97 of 122 : IERC721Enumerable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC721/extensions/IERC721Enumerable.sol)

pragma solidity ^0.8.0;

import "../IERC721.sol";

/**
 * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
 * @dev See https://eips.ethereum.org/EIPS/eip-721
 */
interface IERC721Enumerable is IERC721 {
    /**
     * @dev Returns the total amount of tokens stored by the contract.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns a token ID owned by `owner` at a given `index` of its token list.
     * Use along with {balanceOf} to enumerate all of ``owner``'s tokens.
     */
    function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);

    /**
     * @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);
}

File 98 of 122 : IERC721.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721.sol)

pragma solidity ^0.8.0;

import "../../utils/introspection/IERC165.sol";

/**
 * @dev Required interface of an ERC721 compliant contract.
 */
interface IERC721 is IERC165 {
    /**
     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
     */
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
     */
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
     */
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /**
     * @dev Returns the number of tokens in ``owner``'s account.
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    /**
     * @dev Returns the owner of the `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function ownerOf(uint256 tokenId) external view returns (address owner);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`.
     *
     * 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;

    /**
     * @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 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 the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) external view returns (address operator);

    /**
     * @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);
}

File 99 of 122 : IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

File 100 of 122 : ERC721.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/ERC721.sol)

pragma solidity ^0.8.0;

import "./IERC721.sol";
import "./IERC721Receiver.sol";
import "./extensions/IERC721Metadata.sol";
import "../../utils/Address.sol";
import "../../utils/Context.sol";
import "../../utils/Strings.sol";
import "../../utils/introspection/ERC165.sol";

/**
 * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
 * the Metadata extension, but not including the Enumerable extension, which is available separately as
 * {ERC721Enumerable}.
 */
contract ERC721 is Context, ERC165, IERC721, IERC721Metadata {
    using Address for address;
    using Strings for uint256;

    // Token name
    string private _name;

    // Token symbol
    string private _symbol;

    // Mapping from token ID to owner address
    mapping(uint256 => address) private _owners;

    // Mapping owner address to token count
    mapping(address => uint256) private _balances;

    // Mapping from token ID to approved address
    mapping(uint256 => address) private _tokenApprovals;

    // Mapping from owner to operator approvals
    mapping(address => mapping(address => bool)) private _operatorApprovals;

    /**
     * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
        return
            interfaceId == type(IERC721).interfaceId ||
            interfaceId == type(IERC721Metadata).interfaceId ||
            super.supportsInterface(interfaceId);
    }

    /**
     * @dev See {IERC721-balanceOf}.
     */
    function balanceOf(address owner) public view virtual override returns (uint256) {
        require(owner != address(0), "ERC721: balance query for the zero address");
        return _balances[owner];
    }

    /**
     * @dev See {IERC721-ownerOf}.
     */
    function ownerOf(uint256 tokenId) public view virtual override returns (address) {
        address owner = _owners[tokenId];
        require(owner != address(0), "ERC721: owner query for nonexistent token");
        return owner;
    }

    /**
     * @dev See {IERC721Metadata-name}.
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }

    /**
     * @dev See {IERC721Metadata-symbol}.
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @dev See {IERC721Metadata-tokenURI}.
     */
    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
        require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");

        string memory baseURI = _baseURI();
        return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : "";
    }

    /**
     * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
     * token will be the concatenation of the `baseURI` and the `tokenId`. Empty
     * by default, can be overridden in child contracts.
     */
    function _baseURI() internal view virtual returns (string memory) {
        return "";
    }

    /**
     * @dev See {IERC721-approve}.
     */
    function approve(address to, uint256 tokenId) public virtual override {
        address owner = ERC721.ownerOf(tokenId);
        require(to != owner, "ERC721: approval to current owner");

        require(
            _msgSender() == owner || isApprovedForAll(owner, _msgSender()),
            "ERC721: approve caller is not owner nor approved for all"
        );

        _approve(to, tokenId);
    }

    /**
     * @dev See {IERC721-getApproved}.
     */
    function getApproved(uint256 tokenId) public view virtual override returns (address) {
        require(_exists(tokenId), "ERC721: approved query for nonexistent token");

        return _tokenApprovals[tokenId];
    }

    /**
     * @dev See {IERC721-setApprovalForAll}.
     */
    function setApprovalForAll(address operator, bool approved) public virtual override {
        _setApprovalForAll(_msgSender(), operator, approved);
    }

    /**
     * @dev See {IERC721-isApprovedForAll}.
     */
    function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
        return _operatorApprovals[owner][operator];
    }

    /**
     * @dev See {IERC721-transferFrom}.
     */
    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) public virtual override {
        //solhint-disable-next-line max-line-length
        require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");

        _transfer(from, to, tokenId);
    }

    /**
     * @dev See {IERC721-safeTransferFrom}.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) public virtual override {
        safeTransferFrom(from, to, tokenId, "");
    }

    /**
     * @dev See {IERC721-safeTransferFrom}.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        bytes memory _data
    ) public virtual override {
        require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");
        _safeTransfer(from, to, tokenId, _data);
    }

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
     *
     * `_data` is additional data, it has no specified format and it is sent in call to `to`.
     *
     * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g.
     * implement alternative mechanisms to perform token transfer, such as signature-based.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function _safeTransfer(
        address from,
        address to,
        uint256 tokenId,
        bytes memory _data
    ) internal virtual {
        _transfer(from, to, tokenId);
        require(_checkOnERC721Received(from, to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer");
    }

    /**
     * @dev Returns whether `tokenId` exists.
     *
     * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
     *
     * Tokens start existing when they are minted (`_mint`),
     * and stop existing when they are burned (`_burn`).
     */
    function _exists(uint256 tokenId) internal view virtual returns (bool) {
        return _owners[tokenId] != address(0);
    }

    /**
     * @dev Returns whether `spender` is allowed to manage `tokenId`.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {
        require(_exists(tokenId), "ERC721: operator query for nonexistent token");
        address owner = ERC721.ownerOf(tokenId);
        return (spender == owner || isApprovedForAll(owner, spender) || getApproved(tokenId) == spender);
    }

    /**
     * @dev Safely mints `tokenId` and transfers it to `to`.
     *
     * Requirements:
     *
     * - `tokenId` must not exist.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function _safeMint(address to, uint256 tokenId) internal virtual {
        _safeMint(to, tokenId, "");
    }

    /**
     * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
     * forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
     */
    function _safeMint(
        address to,
        uint256 tokenId,
        bytes memory _data
    ) internal virtual {
        _mint(to, tokenId);
        require(
            _checkOnERC721Received(address(0), to, tokenId, _data),
            "ERC721: transfer to non ERC721Receiver implementer"
        );
    }

    /**
     * @dev Mints `tokenId` and transfers it to `to`.
     *
     * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
     *
     * Requirements:
     *
     * - `tokenId` must not exist.
     * - `to` cannot be the zero address.
     *
     * Emits a {Transfer} event.
     */
    function _mint(address to, uint256 tokenId) internal virtual {
        require(to != address(0), "ERC721: mint to the zero address");
        require(!_exists(tokenId), "ERC721: token already minted");

        _beforeTokenTransfer(address(0), to, tokenId);

        _balances[to] += 1;
        _owners[tokenId] = to;

        emit Transfer(address(0), to, tokenId);

        _afterTokenTransfer(address(0), to, tokenId);
    }

    /**
     * @dev Destroys `tokenId`.
     * The approval is cleared when the token is burned.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     *
     * Emits a {Transfer} event.
     */
    function _burn(uint256 tokenId) internal virtual {
        address owner = ERC721.ownerOf(tokenId);

        _beforeTokenTransfer(owner, address(0), tokenId);

        // Clear approvals
        _approve(address(0), tokenId);

        _balances[owner] -= 1;
        delete _owners[tokenId];

        emit Transfer(owner, address(0), tokenId);

        _afterTokenTransfer(owner, address(0), tokenId);
    }

    /**
     * @dev Transfers `tokenId` from `from` to `to`.
     *  As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     *
     * Emits a {Transfer} event.
     */
    function _transfer(
        address from,
        address to,
        uint256 tokenId
    ) internal virtual {
        require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner");
        require(to != address(0), "ERC721: transfer to the zero address");

        _beforeTokenTransfer(from, to, tokenId);

        // Clear approvals from the previous owner
        _approve(address(0), tokenId);

        _balances[from] -= 1;
        _balances[to] += 1;
        _owners[tokenId] = to;

        emit Transfer(from, to, tokenId);

        _afterTokenTransfer(from, to, tokenId);
    }

    /**
     * @dev Approve `to` to operate on `tokenId`
     *
     * Emits a {Approval} event.
     */
    function _approve(address to, uint256 tokenId) internal virtual {
        _tokenApprovals[tokenId] = to;
        emit Approval(ERC721.ownerOf(tokenId), to, tokenId);
    }

    /**
     * @dev Approve `operator` to operate on all of `owner` tokens
     *
     * Emits a {ApprovalForAll} event.
     */
    function _setApprovalForAll(
        address owner,
        address operator,
        bool approved
    ) internal virtual {
        require(owner != operator, "ERC721: approve to caller");
        _operatorApprovals[owner][operator] = approved;
        emit ApprovalForAll(owner, operator, approved);
    }

    /**
     * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.
     * The call is not executed if the target address is not a contract.
     *
     * @param from address representing the previous owner of the given token ID
     * @param to target address that will receive the tokens
     * @param tokenId uint256 ID of the token to be transferred
     * @param _data bytes optional data to send along with the call
     * @return bool whether the call correctly returned the expected magic value
     */
    function _checkOnERC721Received(
        address from,
        address to,
        uint256 tokenId,
        bytes memory _data
    ) private returns (bool) {
        if (to.isContract()) {
            try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, _data) returns (bytes4 retval) {
                return retval == IERC721Receiver.onERC721Received.selector;
            } catch (bytes memory reason) {
                if (reason.length == 0) {
                    revert("ERC721: transfer to non ERC721Receiver implementer");
                } else {
                    assembly {
                        revert(add(32, reason), mload(reason))
                    }
                }
            }
        } else {
            return true;
        }
    }

    /**
     * @dev Hook that is called before any token transfer. This includes minting
     * and burning.
     *
     * Calling conditions:
     *
     * - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be
     * transferred to `to`.
     * - When `from` is zero, `tokenId` will be minted for `to`.
     * - When `to` is zero, ``from``'s `tokenId` will be burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 tokenId
    ) internal virtual {}

    /**
     * @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.
     * - `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 tokenId
    ) internal virtual {}
}

File 101 of 122 : IERC721Receiver.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol)

pragma solidity ^0.8.0;

/**
 * @title ERC721 token receiver interface
 * @dev Interface for any contract that wants to support safeTransfers
 * from ERC721 asset contracts.
 */
interface IERC721Receiver {
    /**
     * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
     * by `operator` from `from`, this function is called.
     *
     * It must return its Solidity selector to confirm the token transfer.
     * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
     *
     * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
     */
    function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes calldata data
    ) external returns (bytes4);
}

File 102 of 122 : IERC721Metadata.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol)

pragma solidity ^0.8.0;

import "../IERC721.sol";

/**
 * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
 * @dev See https://eips.ethereum.org/EIPS/eip-721
 */
interface IERC721Metadata is IERC721 {
    /**
     * @dev Returns the token collection name.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the token collection symbol.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
     */
    function tokenURI(uint256 tokenId) external view returns (string memory);
}

File 103 of 122 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCall(target, data, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        require(isContract(target), "Address: call to non-contract");

        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(isContract(target), "Address: delegate call to non-contract");

        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly

                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

File 104 of 122 : Strings.sol
// 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);
    }
}

File 105 of 122 : ERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)

pragma solidity ^0.8.0;

import "./IERC165.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 *
 * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}

File 106 of 122 : IERC1155Receiver.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC1155/IERC1155Receiver.sol)

pragma solidity ^0.8.0;

import "../../utils/introspection/IERC165.sol";

/**
 * @dev _Available since v3.1._
 */
interface IERC1155Receiver is IERC165 {
    /**
     * @dev Handles the receipt of a single ERC1155 token type. This function is
     * called at the end of a `safeTransferFrom` after the balance has been updated.
     *
     * NOTE: To accept the transfer, this must return
     * `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
     * (i.e. 0xf23a6e61, or its own function selector).
     *
     * @param operator The address which initiated the transfer (i.e. msg.sender)
     * @param from The address which previously owned the token
     * @param id The ID of the token being transferred
     * @param value The amount of tokens being transferred
     * @param data Additional data with no specified format
     * @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
     */
    function onERC1155Received(
        address operator,
        address from,
        uint256 id,
        uint256 value,
        bytes calldata data
    ) external returns (bytes4);

    /**
     * @dev Handles the receipt of a multiple ERC1155 token types. This function
     * is called at the end of a `safeBatchTransferFrom` after the balances have
     * been updated.
     *
     * NOTE: To accept the transfer(s), this must return
     * `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
     * (i.e. 0xbc197c81, or its own function selector).
     *
     * @param operator The address which initiated the batch transfer (i.e. msg.sender)
     * @param from The address which previously owned the token
     * @param ids An array containing ids of each token being transferred (order and length must match values array)
     * @param values An array containing amounts of each token being transferred (order and length must match ids array)
     * @param data Additional data with no specified format
     * @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
     */
    function onERC1155BatchReceived(
        address operator,
        address from,
        uint256[] calldata ids,
        uint256[] calldata values,
        bytes calldata data
    ) external returns (bytes4);
}

File 107 of 122 : ERC1155.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC1155/ERC1155.sol)

pragma solidity ^0.8.0;

import "./IERC1155.sol";
import "./IERC1155Receiver.sol";
import "./extensions/IERC1155MetadataURI.sol";
import "../../utils/Address.sol";
import "../../utils/Context.sol";
import "../../utils/introspection/ERC165.sol";

/**
 * @dev Implementation of the basic standard multi-token.
 * See https://eips.ethereum.org/EIPS/eip-1155
 * Originally based on code by Enjin: https://github.com/enjin/erc-1155
 *
 * _Available since v3.1._
 */
contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI {
    using Address for address;

    // Mapping from token ID to account balances
    mapping(uint256 => mapping(address => uint256)) private _balances;

    // Mapping from account to operator approvals
    mapping(address => mapping(address => bool)) private _operatorApprovals;

    // Used as the URI for all token types by relying on ID substitution, e.g. https://token-cdn-domain/{id}.json
    string private _uri;

    /**
     * @dev See {_setURI}.
     */
    constructor(string memory uri_) {
        _setURI(uri_);
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
        return
            interfaceId == type(IERC1155).interfaceId ||
            interfaceId == type(IERC1155MetadataURI).interfaceId ||
            super.supportsInterface(interfaceId);
    }

    /**
     * @dev See {IERC1155MetadataURI-uri}.
     *
     * This implementation returns the same URI for *all* token types. It relies
     * on the token type ID substitution mechanism
     * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP].
     *
     * Clients calling this function must replace the `\{id\}` substring with the
     * actual token type ID.
     */
    function uri(uint256) public view virtual override returns (string memory) {
        return _uri;
    }

    /**
     * @dev See {IERC1155-balanceOf}.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     */
    function balanceOf(address account, uint256 id) public view virtual override returns (uint256) {
        require(account != address(0), "ERC1155: balance query for the zero address");
        return _balances[id][account];
    }

    /**
     * @dev See {IERC1155-balanceOfBatch}.
     *
     * Requirements:
     *
     * - `accounts` and `ids` must have the same length.
     */
    function balanceOfBatch(address[] memory accounts, uint256[] memory ids)
        public
        view
        virtual
        override
        returns (uint256[] memory)
    {
        require(accounts.length == ids.length, "ERC1155: accounts and ids length mismatch");

        uint256[] memory batchBalances = new uint256[](accounts.length);

        for (uint256 i = 0; i < accounts.length; ++i) {
            batchBalances[i] = balanceOf(accounts[i], ids[i]);
        }

        return batchBalances;
    }

    /**
     * @dev See {IERC1155-setApprovalForAll}.
     */
    function setApprovalForAll(address operator, bool approved) public virtual override {
        _setApprovalForAll(_msgSender(), operator, approved);
    }

    /**
     * @dev See {IERC1155-isApprovedForAll}.
     */
    function isApprovedForAll(address account, address operator) public view virtual override returns (bool) {
        return _operatorApprovals[account][operator];
    }

    /**
     * @dev See {IERC1155-safeTransferFrom}.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes memory data
    ) public virtual override {
        require(
            from == _msgSender() || isApprovedForAll(from, _msgSender()),
            "ERC1155: caller is not owner nor approved"
        );
        _safeTransferFrom(from, to, id, amount, data);
    }

    /**
     * @dev See {IERC1155-safeBatchTransferFrom}.
     */
    function safeBatchTransferFrom(
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) public virtual override {
        require(
            from == _msgSender() || isApprovedForAll(from, _msgSender()),
            "ERC1155: transfer caller is not owner nor approved"
        );
        _safeBatchTransferFrom(from, to, ids, amounts, data);
    }

    /**
     * @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
     *
     * Emits a {TransferSingle} event.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - `from` must have a balance of tokens of type `id` of at least `amount`.
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
     * acceptance magic value.
     */
    function _safeTransferFrom(
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes memory data
    ) internal virtual {
        require(to != address(0), "ERC1155: transfer to the zero address");

        address operator = _msgSender();
        uint256[] memory ids = _asSingletonArray(id);
        uint256[] memory amounts = _asSingletonArray(amount);

        _beforeTokenTransfer(operator, from, to, ids, amounts, data);

        uint256 fromBalance = _balances[id][from];
        require(fromBalance >= amount, "ERC1155: insufficient balance for transfer");
        unchecked {
            _balances[id][from] = fromBalance - amount;
        }
        _balances[id][to] += amount;

        emit TransferSingle(operator, from, to, id, amount);

        _afterTokenTransfer(operator, from, to, ids, amounts, data);

        _doSafeTransferAcceptanceCheck(operator, from, to, id, amount, data);
    }

    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_safeTransferFrom}.
     *
     * Emits a {TransferBatch} event.
     *
     * Requirements:
     *
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
     * acceptance magic value.
     */
    function _safeBatchTransferFrom(
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal virtual {
        require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
        require(to != address(0), "ERC1155: transfer to the zero address");

        address operator = _msgSender();

        _beforeTokenTransfer(operator, from, to, ids, amounts, data);

        for (uint256 i = 0; i < ids.length; ++i) {
            uint256 id = ids[i];
            uint256 amount = amounts[i];

            uint256 fromBalance = _balances[id][from];
            require(fromBalance >= amount, "ERC1155: insufficient balance for transfer");
            unchecked {
                _balances[id][from] = fromBalance - amount;
            }
            _balances[id][to] += amount;
        }

        emit TransferBatch(operator, from, to, ids, amounts);

        _afterTokenTransfer(operator, from, to, ids, amounts, data);

        _doSafeBatchTransferAcceptanceCheck(operator, from, to, ids, amounts, data);
    }

    /**
     * @dev Sets a new URI for all token types, by relying on the token type ID
     * substitution mechanism
     * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP].
     *
     * By this mechanism, any occurrence of the `\{id\}` substring in either the
     * URI or any of the amounts in the JSON file at said URI will be replaced by
     * clients with the token type ID.
     *
     * For example, the `https://token-cdn-domain/\{id\}.json` URI would be
     * interpreted by clients as
     * `https://token-cdn-domain/000000000000000000000000000000000000000000000000000000000004cce0.json`
     * for token type ID 0x4cce0.
     *
     * See {uri}.
     *
     * Because these URIs cannot be meaningfully represented by the {URI} event,
     * this function emits no events.
     */
    function _setURI(string memory newuri) internal virtual {
        _uri = newuri;
    }

    /**
     * @dev Creates `amount` tokens of token type `id`, and assigns them to `to`.
     *
     * Emits a {TransferSingle} event.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
     * acceptance magic value.
     */
    function _mint(
        address to,
        uint256 id,
        uint256 amount,
        bytes memory data
    ) internal virtual {
        require(to != address(0), "ERC1155: mint to the zero address");

        address operator = _msgSender();
        uint256[] memory ids = _asSingletonArray(id);
        uint256[] memory amounts = _asSingletonArray(amount);

        _beforeTokenTransfer(operator, address(0), to, ids, amounts, data);

        _balances[id][to] += amount;
        emit TransferSingle(operator, address(0), to, id, amount);

        _afterTokenTransfer(operator, address(0), to, ids, amounts, data);

        _doSafeTransferAcceptanceCheck(operator, address(0), to, id, amount, data);
    }

    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_mint}.
     *
     * Requirements:
     *
     * - `ids` and `amounts` must have the same length.
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
     * acceptance magic value.
     */
    function _mintBatch(
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal virtual {
        require(to != address(0), "ERC1155: mint to the zero address");
        require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");

        address operator = _msgSender();

        _beforeTokenTransfer(operator, address(0), to, ids, amounts, data);

        for (uint256 i = 0; i < ids.length; i++) {
            _balances[ids[i]][to] += amounts[i];
        }

        emit TransferBatch(operator, address(0), to, ids, amounts);

        _afterTokenTransfer(operator, address(0), to, ids, amounts, data);

        _doSafeBatchTransferAcceptanceCheck(operator, address(0), to, ids, amounts, data);
    }

    /**
     * @dev Destroys `amount` tokens of token type `id` from `from`
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `from` must have at least `amount` tokens of token type `id`.
     */
    function _burn(
        address from,
        uint256 id,
        uint256 amount
    ) internal virtual {
        require(from != address(0), "ERC1155: burn from the zero address");

        address operator = _msgSender();
        uint256[] memory ids = _asSingletonArray(id);
        uint256[] memory amounts = _asSingletonArray(amount);

        _beforeTokenTransfer(operator, from, address(0), ids, amounts, "");

        uint256 fromBalance = _balances[id][from];
        require(fromBalance >= amount, "ERC1155: burn amount exceeds balance");
        unchecked {
            _balances[id][from] = fromBalance - amount;
        }

        emit TransferSingle(operator, from, address(0), id, amount);

        _afterTokenTransfer(operator, from, address(0), ids, amounts, "");
    }

    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_burn}.
     *
     * Requirements:
     *
     * - `ids` and `amounts` must have the same length.
     */
    function _burnBatch(
        address from,
        uint256[] memory ids,
        uint256[] memory amounts
    ) internal virtual {
        require(from != address(0), "ERC1155: burn from the zero address");
        require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");

        address operator = _msgSender();

        _beforeTokenTransfer(operator, from, address(0), ids, amounts, "");

        for (uint256 i = 0; i < ids.length; i++) {
            uint256 id = ids[i];
            uint256 amount = amounts[i];

            uint256 fromBalance = _balances[id][from];
            require(fromBalance >= amount, "ERC1155: burn amount exceeds balance");
            unchecked {
                _balances[id][from] = fromBalance - amount;
            }
        }

        emit TransferBatch(operator, from, address(0), ids, amounts);

        _afterTokenTransfer(operator, from, address(0), ids, amounts, "");
    }

    /**
     * @dev Approve `operator` to operate on all of `owner` tokens
     *
     * Emits a {ApprovalForAll} event.
     */
    function _setApprovalForAll(
        address owner,
        address operator,
        bool approved
    ) internal virtual {
        require(owner != operator, "ERC1155: setting approval status for self");
        _operatorApprovals[owner][operator] = approved;
        emit ApprovalForAll(owner, operator, approved);
    }

    /**
     * @dev Hook that is called before any token transfer. This includes minting
     * and burning, as well as batched variants.
     *
     * The same hook is called on both single and batched variants. For single
     * transfers, the length of the `id` and `amount` arrays will be 1.
     *
     * Calling conditions (for each `id` and `amount` pair):
     *
     * - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * of token type `id` will be  transferred to `to`.
     * - When `from` is zero, `amount` tokens of token type `id` will be minted
     * for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens of token type `id`
     * will be burned.
     * - `from` and `to` are never both zero.
     * - `ids` and `amounts` have the same, non-zero length.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(
        address operator,
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal virtual {}

    /**
     * @dev Hook that is called after any token transfer. This includes minting
     * and burning, as well as batched variants.
     *
     * The same hook is called on both single and batched variants. For single
     * transfers, the length of the `id` and `amount` arrays will be 1.
     *
     * Calling conditions (for each `id` and `amount` pair):
     *
     * - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * of token type `id` will be  transferred to `to`.
     * - When `from` is zero, `amount` tokens of token type `id` will be minted
     * for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens of token type `id`
     * will be burned.
     * - `from` and `to` are never both zero.
     * - `ids` and `amounts` have the same, non-zero length.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _afterTokenTransfer(
        address operator,
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal virtual {}

    function _doSafeTransferAcceptanceCheck(
        address operator,
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes memory data
    ) private {
        if (to.isContract()) {
            try IERC1155Receiver(to).onERC1155Received(operator, from, id, amount, data) returns (bytes4 response) {
                if (response != IERC1155Receiver.onERC1155Received.selector) {
                    revert("ERC1155: ERC1155Receiver rejected tokens");
                }
            } catch Error(string memory reason) {
                revert(reason);
            } catch {
                revert("ERC1155: transfer to non ERC1155Receiver implementer");
            }
        }
    }

    function _doSafeBatchTransferAcceptanceCheck(
        address operator,
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) private {
        if (to.isContract()) {
            try IERC1155Receiver(to).onERC1155BatchReceived(operator, from, ids, amounts, data) returns (
                bytes4 response
            ) {
                if (response != IERC1155Receiver.onERC1155BatchReceived.selector) {
                    revert("ERC1155: ERC1155Receiver rejected tokens");
                }
            } catch Error(string memory reason) {
                revert(reason);
            } catch {
                revert("ERC1155: transfer to non ERC1155Receiver implementer");
            }
        }
    }

    function _asSingletonArray(uint256 element) private pure returns (uint256[] memory) {
        uint256[] memory array = new uint256[](1);
        array[0] = element;

        return array;
    }
}

File 108 of 122 : IERC1155.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC1155/IERC1155.sol)

pragma solidity ^0.8.0;

import "../../utils/introspection/IERC165.sol";

/**
 * @dev Required interface of an ERC1155 compliant contract, as defined in the
 * https://eips.ethereum.org/EIPS/eip-1155[EIP].
 *
 * _Available since v3.1._
 */
interface IERC1155 is IERC165 {
    /**
     * @dev Emitted when `value` tokens of token type `id` are transferred from `from` to `to` by `operator`.
     */
    event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);

    /**
     * @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all
     * transfers.
     */
    event TransferBatch(
        address indexed operator,
        address indexed from,
        address indexed to,
        uint256[] ids,
        uint256[] values
    );

    /**
     * @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to
     * `approved`.
     */
    event ApprovalForAll(address indexed account, address indexed operator, bool approved);

    /**
     * @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI.
     *
     * If an {URI} event was emitted for `id`, the standard
     * https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value
     * returned by {IERC1155MetadataURI-uri}.
     */
    event URI(string value, uint256 indexed id);

    /**
     * @dev Returns the amount of tokens of token type `id` owned by `account`.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     */
    function balanceOf(address account, uint256 id) external view returns (uint256);

    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}.
     *
     * Requirements:
     *
     * - `accounts` and `ids` must have the same length.
     */
    function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids)
        external
        view
        returns (uint256[] memory);

    /**
     * @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`,
     *
     * Emits an {ApprovalForAll} event.
     *
     * Requirements:
     *
     * - `operator` cannot be the caller.
     */
    function setApprovalForAll(address operator, bool approved) external;

    /**
     * @dev Returns true if `operator` is approved to transfer ``account``'s tokens.
     *
     * See {setApprovalForAll}.
     */
    function isApprovedForAll(address account, address operator) external view returns (bool);

    /**
     * @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
     *
     * Emits a {TransferSingle} event.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - If the caller is not `from`, it must be have been approved to spend ``from``'s tokens via {setApprovalForAll}.
     * - `from` must have a balance of tokens of type `id` of at least `amount`.
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
     * acceptance magic value.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes calldata data
    ) external;

    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}.
     *
     * Emits a {TransferBatch} event.
     *
     * Requirements:
     *
     * - `ids` and `amounts` must have the same length.
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
     * acceptance magic value.
     */
    function safeBatchTransferFrom(
        address from,
        address to,
        uint256[] calldata ids,
        uint256[] calldata amounts,
        bytes calldata data
    ) external;
}

File 109 of 122 : IERC1155MetadataURI.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC1155/extensions/IERC1155MetadataURI.sol)

pragma solidity ^0.8.0;

import "../IERC1155.sol";

/**
 * @dev Interface of the optional ERC1155MetadataExtension interface, as defined
 * in the https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[EIP].
 *
 * _Available since v3.1._
 */
interface IERC1155MetadataURI is IERC1155 {
    /**
     * @dev Returns the URI for token type `id`.
     *
     * If the `\{id\}` substring is present in the URI, it must be replaced by
     * clients with the actual token type ID.
     */
    function uri(uint256 id) external view returns (string memory);
}

File 110 of 122 : ERC1967Upgrade.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (proxy/ERC1967/ERC1967Upgrade.sol)

pragma solidity ^0.8.2;

import "../beacon/IBeacon.sol";
import "../../interfaces/draft-IERC1822.sol";
import "../../utils/Address.sol";
import "../../utils/StorageSlot.sol";

/**
 * @dev This abstract contract provides getters and event emitting update functions for
 * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
 *
 * _Available since v4.1._
 *
 * @custom:oz-upgrades-unsafe-allow delegatecall
 */
abstract contract ERC1967Upgrade {
    // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1
    bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;

    /**
     * @dev Storage slot with the address of the current implementation.
     * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
     * validated in the constructor.
     */
    bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;

    /**
     * @dev Emitted when the implementation is upgraded.
     */
    event Upgraded(address indexed implementation);

    /**
     * @dev Returns the current implementation address.
     */
    function _getImplementation() internal view returns (address) {
        return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
    }

    /**
     * @dev Stores a new address in the EIP1967 implementation slot.
     */
    function _setImplementation(address newImplementation) private {
        require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
        StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
    }

    /**
     * @dev Perform implementation upgrade
     *
     * Emits an {Upgraded} event.
     */
    function _upgradeTo(address newImplementation) internal {
        _setImplementation(newImplementation);
        emit Upgraded(newImplementation);
    }

    /**
     * @dev Perform implementation upgrade with additional setup call.
     *
     * Emits an {Upgraded} event.
     */
    function _upgradeToAndCall(
        address newImplementation,
        bytes memory data,
        bool forceCall
    ) internal {
        _upgradeTo(newImplementation);
        if (data.length > 0 || forceCall) {
            Address.functionDelegateCall(newImplementation, data);
        }
    }

    /**
     * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
     *
     * Emits an {Upgraded} event.
     */
    function _upgradeToAndCallUUPS(
        address newImplementation,
        bytes memory data,
        bool forceCall
    ) internal {
        // Upgrades from old implementations will perform a rollback test. This test requires the new
        // implementation to upgrade back to the old, non-ERC1822 compliant, implementation. Removing
        // this special case will break upgrade paths from old UUPS implementation to new ones.
        if (StorageSlot.getBooleanSlot(_ROLLBACK_SLOT).value) {
            _setImplementation(newImplementation);
        } else {
            try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) {
                require(slot == _IMPLEMENTATION_SLOT, "ERC1967Upgrade: unsupported proxiableUUID");
            } catch {
                revert("ERC1967Upgrade: new implementation is not UUPS");
            }
            _upgradeToAndCall(newImplementation, data, forceCall);
        }
    }

    /**
     * @dev Storage slot with the admin of the contract.
     * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
     * validated in the constructor.
     */
    bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;

    /**
     * @dev Emitted when the admin account has changed.
     */
    event AdminChanged(address previousAdmin, address newAdmin);

    /**
     * @dev Returns the current admin.
     */
    function _getAdmin() internal view returns (address) {
        return StorageSlot.getAddressSlot(_ADMIN_SLOT).value;
    }

    /**
     * @dev Stores a new address in the EIP1967 admin slot.
     */
    function _setAdmin(address newAdmin) private {
        require(newAdmin != address(0), "ERC1967: new admin is the zero address");
        StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
    }

    /**
     * @dev Changes the admin of the proxy.
     *
     * Emits an {AdminChanged} event.
     */
    function _changeAdmin(address newAdmin) internal {
        emit AdminChanged(_getAdmin(), newAdmin);
        _setAdmin(newAdmin);
    }

    /**
     * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
     * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.
     */
    bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;

    /**
     * @dev Emitted when the beacon is upgraded.
     */
    event BeaconUpgraded(address indexed beacon);

    /**
     * @dev Returns the current beacon.
     */
    function _getBeacon() internal view returns (address) {
        return StorageSlot.getAddressSlot(_BEACON_SLOT).value;
    }

    /**
     * @dev Stores a new beacon in the EIP1967 beacon slot.
     */
    function _setBeacon(address newBeacon) private {
        require(Address.isContract(newBeacon), "ERC1967: new beacon is not a contract");
        require(
            Address.isContract(IBeacon(newBeacon).implementation()),
            "ERC1967: beacon implementation is not a contract"
        );
        StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon;
    }

    /**
     * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does
     * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).
     *
     * Emits a {BeaconUpgraded} event.
     */
    function _upgradeBeaconToAndCall(
        address newBeacon,
        bytes memory data,
        bool forceCall
    ) internal {
        _setBeacon(newBeacon);
        emit BeaconUpgraded(newBeacon);
        if (data.length > 0 || forceCall) {
            Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
        }
    }
}

File 111 of 122 : IBeacon.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol)

pragma solidity ^0.8.0;

/**
 * @dev This is the interface that {BeaconProxy} expects of its beacon.
 */
interface IBeacon {
    /**
     * @dev Must return an address that can be used as a delegate call target.
     *
     * {BeaconProxy} will check that this address is a contract.
     */
    function implementation() external view returns (address);
}

File 112 of 122 : draft-IERC1822.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (interfaces/draft-IERC1822.sol)

pragma solidity ^0.8.0;

/**
 * @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified
 * proxy whose upgrades are fully controlled by the current implementation.
 */
interface IERC1822Proxiable {
    /**
     * @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation
     * address.
     *
     * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
     * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
     * function revert if invoked through a proxy.
     */
    function proxiableUUID() external view returns (bytes32);
}

File 113 of 122 : StorageSlot.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/StorageSlot.sol)

pragma solidity ^0.8.0;

/**
 * @dev Library for reading and writing primitive types to specific storage slots.
 *
 * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
 * This library helps with reading and writing to such slots without the need for inline assembly.
 *
 * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
 *
 * Example usage to set ERC1967 implementation slot:
 * ```
 * contract ERC1967 {
 *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
 *
 *     function _getImplementation() internal view returns (address) {
 *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
 *     }
 *
 *     function _setImplementation(address newImplementation) internal {
 *         require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
 *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
 *     }
 * }
 * ```
 *
 * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._
 */
library StorageSlot {
    struct AddressSlot {
        address value;
    }

    struct BooleanSlot {
        bool value;
    }

    struct Bytes32Slot {
        bytes32 value;
    }

    struct Uint256Slot {
        uint256 value;
    }

    /**
     * @dev Returns an `AddressSlot` with member `value` located at `slot`.
     */
    function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
     */
    function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
     */
    function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
     */
    function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
        assembly {
            r.slot := slot
        }
    }
}

File 114 of 122 : ERC1967Proxy.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (proxy/ERC1967/ERC1967Proxy.sol)

pragma solidity ^0.8.0;

import "../Proxy.sol";
import "./ERC1967Upgrade.sol";

/**
 * @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an
 * implementation address that can be changed. This address is stored in storage in the location specified by
 * https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the
 * implementation behind the proxy.
 */
contract ERC1967Proxy is Proxy, ERC1967Upgrade {
    /**
     * @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`.
     *
     * If `_data` is nonempty, it's used as data in a delegate call to `_logic`. This will typically be an encoded
     * function call, and allows initializating the storage of the proxy like a Solidity constructor.
     */
    constructor(address _logic, bytes memory _data) payable {
        assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1));
        _upgradeToAndCall(_logic, _data, false);
    }

    /**
     * @dev Returns the current implementation address.
     */
    function _implementation() internal view virtual override returns (address impl) {
        return ERC1967Upgrade._getImplementation();
    }
}

File 115 of 122 : Proxy.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (proxy/Proxy.sol)

pragma solidity ^0.8.0;

/**
 * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM
 * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to
 * be specified by overriding the virtual {_implementation} function.
 *
 * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a
 * different contract through the {_delegate} function.
 *
 * The success and return data of the delegated call will be returned back to the caller of the proxy.
 */
abstract contract Proxy {
    /**
     * @dev Delegates the current call to `implementation`.
     *
     * This function does not return to its internal call site, it will return directly to the external caller.
     */
    function _delegate(address implementation) internal virtual {
        assembly {
            // Copy msg.data. We take full control of memory in this inline assembly
            // block because it will not return to Solidity code. We overwrite the
            // Solidity scratch pad at memory position 0.
            calldatacopy(0, 0, calldatasize())

            // Call the implementation.
            // out and outsize are 0 because we don't know the size yet.
            let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)

            // Copy the returned data.
            returndatacopy(0, 0, returndatasize())

            switch result
            // delegatecall returns 0 on error.
            case 0 {
                revert(0, returndatasize())
            }
            default {
                return(0, returndatasize())
            }
        }
    }

    /**
     * @dev This is a virtual function that should be overridden so it returns the address to which the fallback function
     * and {_fallback} should delegate.
     */
    function _implementation() internal view virtual returns (address);

    /**
     * @dev Delegates the current call to the address returned by `_implementation()`.
     *
     * This function does not return to its internal call site, it will return directly to the external caller.
     */
    function _fallback() internal virtual {
        _beforeFallback();
        _delegate(_implementation());
    }

    /**
     * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other
     * function in the contract matches the call data.
     */
    fallback() external payable virtual {
        _fallback();
    }

    /**
     * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data
     * is empty.
     */
    receive() external payable virtual {
        _fallback();
    }

    /**
     * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback`
     * call, or as part of the Solidity `fallback` or `receive` functions.
     *
     * If overridden should call `super._beforeFallback()`.
     */
    function _beforeFallback() internal virtual {}
}

File 116 of 122 : TransparentUpgradeableProxy.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (proxy/transparent/TransparentUpgradeableProxy.sol)

pragma solidity ^0.8.0;

import "../ERC1967/ERC1967Proxy.sol";

/**
 * @dev This contract implements a proxy that is upgradeable by an admin.
 *
 * To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector
 * clashing], which can potentially be used in an attack, this contract uses the
 * https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two
 * things that go hand in hand:
 *
 * 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if
 * that call matches one of the admin functions exposed by the proxy itself.
 * 2. If the admin calls the proxy, it can access the admin functions, but its calls will never be forwarded to the
 * implementation. If the admin tries to call a function on the implementation it will fail with an error that says
 * "admin cannot fallback to proxy target".
 *
 * These properties mean that the admin account can only be used for admin actions like upgrading the proxy or changing
 * the admin, so it's best if it's a dedicated account that is not used for anything else. This will avoid headaches due
 * to sudden errors when trying to call a function from the proxy implementation.
 *
 * Our recommendation is for the dedicated account to be an instance of the {ProxyAdmin} contract. If set up this way,
 * you should think of the `ProxyAdmin` instance as the real administrative interface of your proxy.
 */
contract TransparentUpgradeableProxy is ERC1967Proxy {
    /**
     * @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and
     * optionally initialized with `_data` as explained in {ERC1967Proxy-constructor}.
     */
    constructor(
        address _logic,
        address admin_,
        bytes memory _data
    ) payable ERC1967Proxy(_logic, _data) {
        assert(_ADMIN_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1));
        _changeAdmin(admin_);
    }

    /**
     * @dev Modifier used internally that will delegate the call to the implementation unless the sender is the admin.
     */
    modifier ifAdmin() {
        if (msg.sender == _getAdmin()) {
            _;
        } else {
            _fallback();
        }
    }

    /**
     * @dev Returns the current admin.
     *
     * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyAdmin}.
     *
     * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
     * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
     * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
     */
    function admin() external ifAdmin returns (address admin_) {
        admin_ = _getAdmin();
    }

    /**
     * @dev Returns the current implementation.
     *
     * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyImplementation}.
     *
     * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
     * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
     * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`
     */
    function implementation() external ifAdmin returns (address implementation_) {
        implementation_ = _implementation();
    }

    /**
     * @dev Changes the admin of the proxy.
     *
     * Emits an {AdminChanged} event.
     *
     * NOTE: Only the admin can call this function. See {ProxyAdmin-changeProxyAdmin}.
     */
    function changeAdmin(address newAdmin) external virtual ifAdmin {
        _changeAdmin(newAdmin);
    }

    /**
     * @dev Upgrade the implementation of the proxy.
     *
     * NOTE: Only the admin can call this function. See {ProxyAdmin-upgrade}.
     */
    function upgradeTo(address newImplementation) external ifAdmin {
        _upgradeToAndCall(newImplementation, bytes(""), false);
    }

    /**
     * @dev Upgrade the implementation of the proxy, and then call a function from the new implementation as specified
     * by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the
     * proxied contract.
     *
     * NOTE: Only the admin can call this function. See {ProxyAdmin-upgradeAndCall}.
     */
    function upgradeToAndCall(address newImplementation, bytes calldata data) external payable ifAdmin {
        _upgradeToAndCall(newImplementation, data, true);
    }

    /**
     * @dev Returns the current admin.
     */
    function _admin() internal view virtual returns (address) {
        return _getAdmin();
    }

    /**
     * @dev Makes sure the admin cannot access the fallback function. See {Proxy-_beforeFallback}.
     */
    function _beforeFallback() internal virtual override {
        require(msg.sender != _getAdmin(), "TransparentUpgradeableProxy: admin cannot fallback to proxy target");
        super._beforeFallback();
    }
}

File 117 of 122 : ProxyAdmin.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

/**
 * @dev This is an auxiliary contract meant to be assigned as the admin of a {TransparentUpgradeableProxy}. For an
 * explanation of why you would want to use this see the documentation for {TransparentUpgradeableProxy}.
 */
contract ProxyAdmin is Ownable {
    /**
     * @dev Returns the current implementation of `proxy`.
     *
     * Requirements:
     *
     * - This contract must be the admin of `proxy`.
     */
    function getProxyImplementation(TransparentUpgradeableProxy proxy) public view virtual returns (address) {
        // We need to manually run the static call since the getter cannot be flagged as view
        // bytes4(keccak256("implementation()")) == 0x5c60da1b
        (bool success, bytes memory returndata) = address(proxy).staticcall(hex"5c60da1b");
        require(success);
        return abi.decode(returndata, (address));
    }

    /**
     * @dev Returns the current admin of `proxy`.
     *
     * Requirements:
     *
     * - This contract must be the admin of `proxy`.
     */
    function getProxyAdmin(TransparentUpgradeableProxy proxy) public view virtual returns (address) {
        // We need to manually run the static call since the getter cannot be flagged as view
        // bytes4(keccak256("admin()")) == 0xf851a440
        (bool success, bytes memory returndata) = address(proxy).staticcall(hex"f851a440");
        require(success);
        return abi.decode(returndata, (address));
    }

    /**
     * @dev Changes the admin of `proxy` to `newAdmin`.
     *
     * Requirements:
     *
     * - This contract must be the current admin of `proxy`.
     */
    function changeProxyAdmin(TransparentUpgradeableProxy proxy, address newAdmin) public virtual onlyOwner {
        proxy.changeAdmin(newAdmin);
    }

    /**
     * @dev Upgrades `proxy` to `implementation`. See {TransparentUpgradeableProxy-upgradeTo}.
     *
     * Requirements:
     *
     * - This contract must be the admin of `proxy`.
     */
    function upgrade(TransparentUpgradeableProxy proxy, address implementation) public virtual onlyOwner {
        proxy.upgradeTo(implementation);
    }

    /**
     * @dev Upgrades `proxy` to `implementation` and calls a function on the new implementation. See
     * {TransparentUpgradeableProxy-upgradeToAndCall}.
     *
     * Requirements:
     *
     * - This contract must be the admin of `proxy`.
     */
    function upgradeAndCall(
        TransparentUpgradeableProxy proxy,
        address implementation,
        bytes memory data
    ) public payable virtual onlyOwner {
        proxy.upgradeToAndCall{ value: msg.value }(implementation, data);
    }
}

File 118 of 122 : ITestERC1155.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;

import "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";

interface ITestERC1155 is IERC1155 {
    function mint(
        uint256 amount,
        uint256 id,
        address receiver
    ) external;
}

File 119 of 122 : TestERC1155.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;

import "../interfaces/ITestERC1155.sol";
import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";

contract TestERC1155 is ERC1155, ITestERC1155 {
    constructor() ERC1155("TestToken ERC1155") {}

    function mint(
        uint256 amount,
        uint256 id,
        address receiver
    ) public override {
        _mint(receiver, id, amount, "");
    }
}

File 120 of 122 : ERC721Enumerable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/ERC721Enumerable.sol)

pragma solidity ^0.8.0;

import "../ERC721.sol";
import "./IERC721Enumerable.sol";

/**
 * @dev This implements an optional extension of {ERC721} defined in the EIP that adds
 * enumerability of all the token ids in the contract as well as all token ids owned by each
 * account.
 */
abstract contract ERC721Enumerable is ERC721, IERC721Enumerable {
    // Mapping from owner to list of owned token IDs
    mapping(address => mapping(uint256 => uint256)) private _ownedTokens;

    // Mapping from token ID to index of the owner tokens list
    mapping(uint256 => uint256) private _ownedTokensIndex;

    // Array with all token ids, used for enumeration
    uint256[] private _allTokens;

    // Mapping from token id to position in the allTokens array
    mapping(uint256 => uint256) private _allTokensIndex;

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC721) returns (bool) {
        return interfaceId == type(IERC721Enumerable).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev See {IERC721Enumerable-tokenOfOwnerByIndex}.
     */
    function tokenOfOwnerByIndex(address owner, uint256 index) public view virtual override returns (uint256) {
        require(index < ERC721.balanceOf(owner), "ERC721Enumerable: owner index out of bounds");
        return _ownedTokens[owner][index];
    }

    /**
     * @dev See {IERC721Enumerable-totalSupply}.
     */
    function totalSupply() public view virtual override returns (uint256) {
        return _allTokens.length;
    }

    /**
     * @dev See {IERC721Enumerable-tokenByIndex}.
     */
    function tokenByIndex(uint256 index) public view virtual override returns (uint256) {
        require(index < ERC721Enumerable.totalSupply(), "ERC721Enumerable: global index out of bounds");
        return _allTokens[index];
    }

    /**
     * @dev Hook that is called before any token transfer. This includes minting
     * and burning.
     *
     * Calling conditions:
     *
     * - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be
     * transferred to `to`.
     * - When `from` is zero, `tokenId` will be minted for `to`.
     * - When `to` is zero, ``from``'s `tokenId` will be burned.
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 tokenId
    ) internal virtual override {
        super._beforeTokenTransfer(from, to, tokenId);

        if (from == address(0)) {
            _addTokenToAllTokensEnumeration(tokenId);
        } else if (from != to) {
            _removeTokenFromOwnerEnumeration(from, tokenId);
        }
        if (to == address(0)) {
            _removeTokenFromAllTokensEnumeration(tokenId);
        } else if (to != from) {
            _addTokenToOwnerEnumeration(to, tokenId);
        }
    }

    /**
     * @dev Private function to add a token to this extension's ownership-tracking data structures.
     * @param to address representing the new owner of the given token ID
     * @param tokenId uint256 ID of the token to be added to the tokens list of the given address
     */
    function _addTokenToOwnerEnumeration(address to, uint256 tokenId) private {
        uint256 length = ERC721.balanceOf(to);
        _ownedTokens[to][length] = tokenId;
        _ownedTokensIndex[tokenId] = length;
    }

    /**
     * @dev Private function to add a token to this extension's token tracking data structures.
     * @param tokenId uint256 ID of the token to be added to the tokens list
     */
    function _addTokenToAllTokensEnumeration(uint256 tokenId) private {
        _allTokensIndex[tokenId] = _allTokens.length;
        _allTokens.push(tokenId);
    }

    /**
     * @dev Private function to remove a token from this extension's ownership-tracking data structures. Note that
     * while the token is not assigned a new owner, the `_ownedTokensIndex` mapping is _not_ updated: this allows for
     * gas optimizations e.g. when performing a transfer operation (avoiding double writes).
     * This has O(1) time complexity, but alters the order of the _ownedTokens array.
     * @param from address representing the previous owner of the given token ID
     * @param tokenId uint256 ID of the token to be removed from the tokens list of the given address
     */
    function _removeTokenFromOwnerEnumeration(address from, uint256 tokenId) private {
        // To prevent a gap in from's tokens array, we store the last token in the index of the token to delete, and
        // then delete the last slot (swap and pop).

        uint256 lastTokenIndex = ERC721.balanceOf(from) - 1;
        uint256 tokenIndex = _ownedTokensIndex[tokenId];

        // When the token to delete is the last token, the swap operation is unnecessary
        if (tokenIndex != lastTokenIndex) {
            uint256 lastTokenId = _ownedTokens[from][lastTokenIndex];

            _ownedTokens[from][tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
            _ownedTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index
        }

        // This also deletes the contents at the last position of the array
        delete _ownedTokensIndex[tokenId];
        delete _ownedTokens[from][lastTokenIndex];
    }

    /**
     * @dev Private function to remove a token from this extension's token tracking data structures.
     * This has O(1) time complexity, but alters the order of the _allTokens array.
     * @param tokenId uint256 ID of the token to be removed from the tokens list
     */
    function _removeTokenFromAllTokensEnumeration(uint256 tokenId) private {
        // To prevent a gap in the tokens array, we store the last token in the index of the token to delete, and
        // then delete the last slot (swap and pop).

        uint256 lastTokenIndex = _allTokens.length - 1;
        uint256 tokenIndex = _allTokensIndex[tokenId];

        // When the token to delete is the last token, the swap operation is unnecessary. However, since this occurs so
        // rarely (when the last minted token is burnt) that we still do the swap here to avoid the gas cost of adding
        // an 'if' statement (like in _removeTokenFromOwnerEnumeration)
        uint256 lastTokenId = _allTokens[lastTokenIndex];

        _allTokens[tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
        _allTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index

        // This also deletes the contents at the last position of the array
        delete _allTokensIndex[tokenId];
        _allTokens.pop();
    }
}

File 121 of 122 : TestERC721.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;

import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";

contract TestERC721 is ERC721Enumerable {
    uint256 public tokenId;

    constructor() ERC721("TestToken ERC721", "TTERC721") {
        tokenId = 1;
    }

    function mint(address receiver) public {
        _safeMint(receiver, tokenId);
        tokenId++;
    }
}

File 122 of 122 : ITestERC721.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;

import "@openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol";

interface ITestERC721 is IERC721Enumerable {
    function mint(address receiver) external;
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 100
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "abi"
      ]
    }
  },
  "metadata": {
    "useLiteralContent": true
  }
}

Contract ABI

[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"stakeId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Claim","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"dayTotalEmission","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalFounderEmission","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalFounders","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"stakeBackAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"treasuryAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"v2VaultAmount","type":"uint256"}],"name":"ClaimDailyEmission","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"ClaimPastEmission","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"depositId","type":"uint256"}],"name":"RequestWithdrawalFromStaker","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"stakeId","type":"uint256"},{"indexed":false,"internalType":"uint256[]","name":"founderNFTIDs","type":"uint256[]"}],"name":"Stake","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"enum IAtlasMine.Lock","name":"lock","type":"uint8"}],"name":"TopupMagicToStaker","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TopupTodayEmission","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"stakeId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"founderId","type":"uint256"}],"name":"Withdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"depositId","type":"uint256"}],"name":"WithdrawalFromStaker","type":"event"},{"inputs":[],"name":"BattleflyBot","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"BattleflyFoundersFlywheelVault","outputs":[{"internalType":"contract IBattleflyFoundersFlywheelVault","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"BattleflyStaker","outputs":[{"internalType":"contract IBattleflyAtlasStaker","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"BattleflyStakerV2","outputs":[{"internalType":"contract IBattleflyAtlasStakerV02","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEFAULT_STAKE_BACK_LOCK","outputs":[{"internalType":"enum IAtlasMine.Lock","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"DailyFounderEmissions","outputs":[{"internalType":"uint256","name":"totalEmission","type":"uint256"},{"internalType":"uint256","name":"totalFounders","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DaysSinceStart","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"FounderStakes","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"stakeTimestamp","type":"uint256"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"lastClaimedDay","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERCENT_DENOMINATOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TREASURY_VAULT","outputs":[{"internalType":"contract IBattleflyTreasuryFlywheelVault","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TREASURY_WALLET","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"activeDepositId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"activeRestakeDepositId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"claimAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimDailyEmission","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimPastEmission","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"claimedPastEmission","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"claimingIsPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"founderNFT","outputs":[{"internalType":"contract ISpecialNFT","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"founderTypeID","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getClaimableEmissionOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentDay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"currentDay","type":"uint256"}],"name":"getDailyFounderEmission","outputs":[{"internalType":"uint256[2]","name":"","type":"uint256[2]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getName","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getPastEmissionClaimableTokens","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"increaseTotalEmission","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_magicAddress","type":"address"},{"internalType":"address","name":"_BattleflyStakerAddress","type":"address"},{"internalType":"uint256","name":"_founderTypeID","type":"uint256"},{"internalType":"address","name":"_founderNFTAddress","type":"address"},{"internalType":"uint256","name":"_startTimestamp","type":"uint256"},{"internalType":"address","name":"_battleflyFounderVaultV2Address","type":"address"},{"internalType":"uint256","name":"_stakeBackPercent","type":"uint256"},{"internalType":"uint256","name":"_treasuryPercent","type":"uint256"},{"internalType":"uint256","name":"_v2VaultPercent","type":"uint256"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"isOwner","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"magic","outputs":[{"internalType":"contract IERC20Upgradeable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC1155BatchReceived","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC1155Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pastEmissionPerFounder","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"doPause","type":"bool"}],"name":"pauseClaim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"pendingStakeBackAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"dayToStart","type":"uint256"}],"name":"recalculateTotalFounders","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"reduceTotalEmission","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"requestWithdrawAllFromStaker","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"ids","type":"uint256[]"}],"name":"requestWithdrawFromStaker","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"bool","name":"access","type":"bool"}],"name":"setAdminAccess","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_battleflyBot","type":"address"}],"name":"setBattleflyBot","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"}],"name":"setBattleflyStaker","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"day","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"stakers","type":"uint256"}],"name":"setDailyFounderEmissions","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"vault","type":"address"}],"name":"setFlywheelVault","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"setFounderStakesToStart","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"founderVault","type":"address"}],"name":"setFounderVaultV2","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"setPastEmission","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_stakeBackPercent","type":"uint256"},{"internalType":"uint256","name":"_treasuryPercent","type":"uint256"},{"internalType":"uint256","name":"_v2VaultPercent","type":"uint256"}],"name":"setPercentages","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newTimestamp","type":"uint256"}],"name":"setStartTimestamp","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"internalType":"bool","name":"isClaimed","type":"bool"}],"name":"setTokenClaimedPastEmission","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_treasuryAddress","type":"address"}],"name":"setTreasuryVault","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"simulateClaim","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stakeBackPercent","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"ids","type":"uint256[]"}],"name":"stakeFounderNFT","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"stakeIdOfFounder","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"stakesOf","outputs":[{"components":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"stakeTimestamp","type":"uint256"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"lastClaimedDay","type":"uint256"}],"internalType":"struct BattleflyFounderVaultV08.FounderStake[]","name":"","type":"tuple[]"},{"internalType":"uint256[][]","name":"","type":"uint256[][]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"startTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"enum IAtlasMine.Lock","name":"lock","type":"uint8"}],"name":"topupMagicToStaker","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"topupTodayEmission","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"treasuryPercent","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"currentDay","type":"uint256"}],"name":"updateClaimedFounderEmission","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"v2VaultPercent","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"founderIds","type":"uint256[]"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawAllFromStaker","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"ids","type":"uint256[]"}],"name":"withdrawFromStaker","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawFromVault","outputs":[],"stateMutability":"nonpayable","type":"function"}]

608060405234801561001057600080fd5b506157dc80620000216000396000f3fe608060405234801561001057600080fd5b50600436106103c35760003560e01c80639212567c11610206578063be70ba311161012b578063e327a6af116100c3578063f23a6e6111610087578063f23a6e611461092f578063f2fde38b1461094e578063f65a0b3e14610961578063fc68866114610974578063fe41d0561461098757600080fd5b8063e327a6af146108e2578063e6fd48bc146108f5578063e9d33b8b146108ff578063ea0f57e114610912578063f01e30351461092557600080fd5b8063be70ba31146107dc578063c0ce0bad146107ef578063c3a7ab0514610813578063c44bef751461081d578063c579007714610830578063d1058e5914610843578063d11dea5a1461084b578063d3e210e31461085e578063d5f80c34146108cf57600080fd5b8063a8ad1ca81161019e578063a8ad1ca8146106ff578063a9d3ce9914610720578063aad8750b14610733578063ac6b640a14610746578063afe89b4f14610783578063b187bd2614610796578063b25c6eaf146107a2578063b84c0b6a146107aa578063bc197c81146107bd57600080fd5b80639212567c14610670578063935b7aca1461067857806396e381541461068c578063983d95ce146106a05780639888cc83146106b35780639e6c2959146106c65780639f51563b146106cf578063a073801f146106d9578063a4ade971146106ec57600080fd5b80633d0fc343116102ec57806365ccc5b01161028457806365ccc5b0146105f6578063705aefc31461060b57806370a0823114610615578063715018a6146106285780637765968714610630578063791253b5146106435780637e1464331461064d578063853828b6146106605780638da5cb5b1461066857600080fd5b80633d0fc343146105425780633e6968b6146105555780633fc276cd1461055d5780634295d22d1461057d5780634719f251146105905780634def312d1461059a5780635daf3146146105a8578063610a8aec146105c357806363a454e0146105e357600080fd5b806317d7de7c1161035f57806317d7de7c146104ad57806318b46e9e146104c25780631d093859146104cc57806321116f4e146104d457806321ec4db9146104e757806332bd72e5146104ef57806333b69c4c146104f95780633819a9931461051a5780633a2a3bf21461052e57600080fd5b806301ffc9a7146103c857806304ef9d58146103f0578063076e298214610408578063090cdd1f1461041d5780630a7b4b2e1461043e5780630d854646146104525780630e82867b14610466578063150b7a021461047957806316c44182146104a5575b600080fd5b6103db6103d6366004615190565b61099a565b60405190151581526020015b60405180910390f35b6103fa6101715481565b6040519081526020016103e7565b61041b6104163660046151b8565b6109d1565b005b61017d54610431906001600160a01b031681565b6040516103e7919061531d565b61017e54610431906001600160a01b031681565b61015f54610431906001600160a01b031681565b61041b610474366004614e79565b610a9f565b61048c610487366004614fbc565b610b81565b6040516001600160e01b031990911681526020016103e7565b61041b610b92565b6104b5610c00565b6040516103e791906154f6565b6103fa6101705481565b61041b610c64565b61041b6104e23660046150b9565b610d2c565b61041b610d97565b6103fa61017f5481565b61050c610507366004614e79565b610f2d565b6040516103e79291906153e3565b61016254610431906001600160a01b031681565b61017c54610431906001600160a01b031681565b61041b610550366004615114565b6111c6565b6103fa611267565b61057061056b3660046151b8565b6112c2565b6040516103e791906154a4565b61041b61058b3660046150e2565b611352565b6103fa6101725481565b610179546103db9060ff1681565b61043173f5411006eefd66c213d2fd2033a1d340458b722681565b6105d66105d1366004614e79565b6116f4565b6040516103e791906154d5565b61041b6105f13660046150e2565b611cf6565b6105fe600081565b6040516103e791906154e8565b6103fa6101615481565b6103fa610623366004614e79565b611f2c565b61041b612028565b61041b61063e3660046151b8565b612063565b6103fa61016c5481565b61041b61065b3660046151b8565b612098565b61041b61215b565b610431612283565b61041b612292565b61016054610431906001600160a01b031681565b61018254610431906001600160a01b031681565b61041b6106ae3660046150e2565b612613565b61041b6106c1366004615158565b6127bd565b6103fa61271081565b6103fa6101815481565b61041b6106e7366004614e79565b612824565b61041b6106fa36600461520f565b6128b0565b6103fa61070d3660046151b8565b6101776020526000908152604090205481565b61041b61072e366004615253565b61294a565b61041b6107413660046151b8565b6129ad565b61076e6107543660046151b8565b61016d602052600090815260409020805460019091015482565b604080519283526020830191909152016103e7565b61041b610791366004614f38565b612a1b565b6101795460ff166103db565b61041b612ba8565b61041b6107b83660046151e8565b612c13565b61048c6107cb366004614e93565b63bc197c8160e01b95945050505050565b61041b6107ea366004614e79565b612cca565b6103db6107fd3660046151b8565b6101756020526000908152604090205460ff1681565b6103fa6101765481565b61041b61082b3660046151b8565b612d2f565b6103fa61083e366004614e79565b612d88565b61041b612e07565b61041b610859366004615253565b612f56565b6108a061086c3660046151b8565b610169602052600090815260409020805460018201546002830154600390930154919290916001600160a01b039091169084565b6040516103e7949392919093845260208401929092526001600160a01b03166040830152606082015260800190565b61041b6108dd366004614e79565b612fd8565b6103db6108f03660046150b9565b613061565b6103fa6101635481565b61041b61090d3660046151b8565b613141565b61041b6109203660046150e2565b6131a7565b6103fa6101805481565b61048c61093d366004615021565b63f23a6e6160e01b95945050505050565b61041b61095c366004614e79565b613486565b61041b61096f366004615083565b613523565b6103fa6109823660046151b8565b61357e565b61041b610995366004614e79565b6135f1565b60006001600160e01b03198216630271189760e51b14806109cb57506301ffc9a760e01b6001600160e01b03198316145b92915050565b33600090815261016b602052604090205460ff1680610a0857506109f3612283565b6001600160a01b0316336001600160a01b0316145b610a2d5760405162461bcd60e51b8152600401610a2490615529565b60405180910390fd5b600081815261016d602052604081206001908101549190610a4f9084906156a0565b90505b61016c548111610a9a57600081815261016d6020526040812060018101805491928592610a809084906156a0565b90915550829150610a92905081615751565b915050610a52565b505050565b33610aa8612283565b6001600160a01b031614610ace5760405162461bcd60e51b8152600401610a24906155a5565b6001600160a01b038116610ae157600080fd5b61017380546001600160a01b0319166001600160a01b0383811691821790925561015f5460405163095ea7b360e01b815292169163095ea7b391610b2b9160001990600401615355565b602060405180830381600087803b158015610b4557600080fd5b505af1158015610b59573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b7d9190615174565b5050565b630a85bd0160e11b5b949350505050565b33600090815261016b602052604090205460ff1680610bc95750610bb4612283565b6001600160a01b0316336001600160a01b0316145b610be55760405162461bcd60e51b8152600401610a2490615529565b6000610bf261017a613656565b9050610bfd81611cf6565b50565b60606101615460961415610c39575060408051808201909152601081526f158c4814dd185ad95c9cc815985d5b1d60821b602082015290565b5060408051808201909152601081526f158c8814dd185ad95c9cc815985d5b1d60821b602082015290565b33600090815261016b602052604090205460ff1680610c9b5750610c86612283565b6001600160a01b0316336001600160a01b0316145b610cb75760405162461bcd60e51b8152600401610a2490615529565b600261012d541415610cdb5760405162461bcd60e51b8152600401610a2490615625565b600261012d5561016a5460005b818111610d225761016354600082815261016960205260408120600181019290925560039091015580610d1a81615751565b915050610ce8565b5050600161012d55565b33600090815261016b602052604090205460ff1680610d635750610d4e612283565b6001600160a01b0316336001600160a01b0316145b610d7f5760405162461bcd60e51b8152600401610a2490615529565b61015f54610b7d906001600160a01b03168383613663565b61017654610df15760405162461bcd60e51b815260206004820152602160248201527f4e6f207061737420666f756e64657220656d697373696f6e20746f20636c61696044820152606d60f81b6064820152608401610a24565b6000610dfc336116f4565b90506000815111610e445760405162461bcd60e51b81526020600482015260126024820152714e6f20746f6b656e7320746f20636c61696d60701b6044820152606401610a24565b60005b8151811015610eb75760016101756000848481518110610e7757634e487b7160e01b600052603260045260246000fd5b6020026020010151815260200190815260200160002060006101000a81548160ff0219169083151502179055508080610eaf90615751565b915050610e47565b50610ee033825161017654610ecc91906156d8565b61015f546001600160a01b03169190613663565b7f93f2234b87606ba2c9f5e61b5e95b7ef9d74b574120ae0be68c57fe636e6ef4733825161017654610f1291906156d8565b83604051610f229392919061536e565b60405180910390a150565b6001600160a01b0381166000908152610167602052604081206060918291610f54906136b9565b6001600160401b03811115610f7957634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015610fde57816020015b610fcb6040518060800160405280600081526020016000815260200160006001600160a01b03168152602001600081525090565b815260200190600190039081610f975790505b506001600160a01b03851660009081526101676020526040812091925090611005906136b9565b6001600160401b0381111561102a57634e487b7160e01b600052604160045260246000fd5b60405190808252806020026020018201604052801561105d57816020015b60608152602001906001900390816110485790505b50905060005b6001600160a01b038616600090815261016760205260409020611085906136b9565b8110156111bb576001600160a01b03861660009081526101676020526040812061016991906110b490846136c3565b8152602080820192909252604090810160002081516080810183528154815260018201549381019390935260028101546001600160a01b031691830191909152600301546060820152835184908390811061111f57634e487b7160e01b600052603260045260246000fd5b602002602001018190525061117d610178600061116a8461016760008c6001600160a01b03166001600160a01b031681526020019081526020016000206136c390919063ffffffff16565b8152602001908152602001600020613656565b82828151811061119d57634e487b7160e01b600052603260045260246000fd5b602002602001018190525080806111b390615751565b915050611063565b509094909350915050565b336111cf612283565b6001600160a01b0316146111f55760405162461bcd60e51b8152600401610a24906155a5565b60005b8251811015610a9a5781610175600085848151811061122757634e487b7160e01b600052603260045260246000fd5b6020026020010151815260200190815260200160002060006101000a81548160ff021916908315150217905550808061125f90615751565b9150506111f8565b33600090815261016b602052604081205460ff168061129e5750611289612283565b6001600160a01b0316336001600160a01b0316145b6112ba5760405162461bcd60e51b8152600401610a2490615529565b5061016c5490565b6112ca614d26565b33600090815261016b602052604090205460ff168061130157506112ec612283565b6001600160a01b0316336001600160a01b0316145b61131d5760405162461bcd60e51b8152600401610a2490615529565b50604080518082018252600083815261016d60208181529382208054845291859052835260010154918101919091525b919050565b80516113b15760405162461bcd60e51b815260206004820152602860248201527f4d7573742070726f76696465206174206c65617374206f6e6520666f756e64656044820152671c8813919508125160c21b6064820152608401610a24565b60005b815181101561155b57610161546101605483516001600160a01b039091169063a2d9dd12908590859081106113f957634e487b7160e01b600052603260045260246000fd5b60200260200101516040518263ffffffff1660e01b815260040161141f91815260200190565b60206040518083038186803b15801561143757600080fd5b505afa15801561144b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061146f91906151d0565b146114b45760405162461bcd60e51b8152602060048201526015602482015274139bdd081d985b1a5908199bdd5b99195c88139195605a1b6044820152606401610a24565b6101605482516001600160a01b03909116906342842e0e90339030908690869081106114f057634e487b7160e01b600052603260045260246000fd5b60200260200101516040518463ffffffff1660e01b815260040161151693929190615331565b600060405180830381600087803b15801561153057600080fd5b505af1158015611544573d6000803e3d6000fd5b50505050808061155390615751565b9150506113b4565b5061016c5461016a805490600061157183615751565b90915550506040805160808101825283518152426020808301918252338385019081526060840186815261016a546000908152610169909352948220935184559151600184015590516002830180546001600160a01b0319166001600160a01b0390921691909117905591516003909101555b82518110156116935761016a54610177600085848151811061161657634e487b7160e01b600052603260045260246000fd5b602002602001015181526020019081526020016000208190555061168083828151811061165357634e487b7160e01b600052603260045260246000fd5b6020026020010151610178600061016a5481526020019081526020016000206136cf90919063ffffffff16565b508061168b81615751565b9150506115e4565b5061016a54336000908152610167602052604090206116b1916136cf565b507f55331f3fa9ca07b25728aacdb3badf1eacd93d82f1dfe128cc633a137232f9663361016a54846040516116e89392919061536e565b60405180910390a15050565b610160546040516370a0823160e01b81526060916000916001600160a01b03909116906370a082319061172b90869060040161531d565b60206040518083038186803b15801561174357600080fd5b505afa158015611757573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061177b91906151d0565b90506000816001600160401b038111156117a557634e487b7160e01b600052604160045260246000fd5b6040519080825280602002602001820160405280156117ce578160200160208202803683370190505b5090506000805b8381101561195c5761016054604051632f745c5960e01b81526000916001600160a01b031690632f745c5990611811908a908690600401615355565b60206040518083038186803b15801561182957600080fd5b505afa15801561183d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061186191906151d0565b6101605460405163516cee8960e11b8152600481018390529192506000916001600160a01b039091169063a2d9dd129060240160206040518083038186803b1580156118ac57600080fd5b505afa1580156118c0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118e491906151d0565b9050610161548114801561190857506000828152610175602052604090205460ff16155b15611947578185858151811061192e57634e487b7160e01b600052603260045260246000fd5b60209081029190910101528361194381615751565b9450505b5050808061195490615751565b9150506117d5565b50600061196886610f2d565b91505060008060005b83518110156119c25783818151811061199a57634e487b7160e01b600052603260045260246000fd5b602002602001015151826119ae91906156a0565b9150806119ba81615751565b915050611971565b506000816001600160401b038111156119eb57634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015611a14578160200160208202803683370190505b50905060005b8451811015611b98576000858281518110611a4557634e487b7160e01b600052603260045260246000fd5b6020026020010151905060005b8151811015611b83576000828281518110611a7d57634e487b7160e01b600052603260045260246000fd5b60209081029190910101516101605460405163516cee8960e11b8152600481018390529192506000916001600160a01b039091169063a2d9dd129060240160206040518083038186803b158015611ad357600080fd5b505afa158015611ae7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b0b91906151d0565b90506101615481148015611b2f57506000828152610175602052604090205460ff16155b15611b6e5781868981518110611b5557634e487b7160e01b600052603260045260246000fd5b602090810291909101015287611b6a81615751565b9850505b50508080611b7b90615751565b915050611a52565b50508080611b9090615751565b915050611a1a565b506000611ba584876156a0565b6001600160401b03811115611bca57634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015611bf3578160200160208202803683370190505b50905060005b86811015611c6657878181518110611c2157634e487b7160e01b600052603260045260246000fd5b6020026020010151828281518110611c4957634e487b7160e01b600052603260045260246000fd5b602090810291909101015280611c5e81615751565b915050611bf9565b50855b611c7385886156a0565b811015611ce85782611c8588836156f7565b81518110611ca357634e487b7160e01b600052603260045260246000fd5b6020026020010151828281518110611ccb57634e487b7160e01b600052603260045260246000fd5b602090810291909101015280611ce081615751565b915050611c69565b509998505050505050505050565b33600090815261016b602052604090205460ff1680611d2d5750611d18612283565b6001600160a01b0316336001600160a01b0316145b611d495760405162461bcd60e51b8152600401610a2490615529565b60005b8151811015610b7d5761017c5482516001600160a01b039091169063dd550bb490849084908110611d8d57634e487b7160e01b600052603260045260246000fd5b60200260200101516040518263ffffffff1660e01b8152600401611db391815260200190565b60206040518083038186803b158015611dcb57600080fd5b505afa158015611ddf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e039190615174565b15611f1a5761017c5482516001600160a01b0390911690639ee679e890849084908110611e4057634e487b7160e01b600052603260045260246000fd5b60200260200101516040518263ffffffff1660e01b8152600401611e6691815260200190565b602060405180830381600087803b158015611e8057600080fd5b505af1158015611e94573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611eb8919061527e565b507f21c77cf216591cc6caec60a5dd496cd81759bad01d20fd41961a2df074bd0c4f828281518110611efa57634e487b7160e01b600052603260045260246000fd5b6020026020010151604051611f1191815260200190565b60405180910390a15b80611f2481615751565b915050611d4c565b60006001600160a01b038216611f975760405162461bcd60e51b815260206004820152602a60248201527f4552433732313a2062616c616e636520717565727920666f7220746865207a65604482015269726f206164647265737360b01b6064820152608401610a24565b6001600160a01b0382166000908152610167602052604081208190611fbb906136b9565b905060005b8181101561201f576001600160a01b0385166000908152610167602052604081206101699190611ff090846136c3565b815260208101919091526040016000205461200b90846156a0565b92508061201781615751565b915050611fc0565b50909392505050565b33612031612283565b6001600160a01b0316146120575760405162461bcd60e51b8152600401610a24906155a5565b61206160006136db565b565b3361206c612283565b6001600160a01b0316146120925760405162461bcd60e51b8152600401610a24906155a5565b61017655565b33600090815261016b602052604090205460ff16806120cf57506120ba612283565b6001600160a01b0316336001600160a01b0316145b6120eb5760405162461bcd60e51b8152600401610a2490615529565b600081116120f857600080fd5b61015f54612111906001600160a01b031633308461372d565b80610166600082825461212491906156a0565b90915550506040517f8ae5bc60487649c7a0deec334e85a8f4e5b0f48c67483b3e10a00024d187c8ec90610f229033908490615355565b600261012d54141561217f5760405162461bcd60e51b8152600401610a2490615625565b600261012d553360009081526101676020526040812061219e906136b9565b116121e25760405162461bcd60e51b81526020600482015260146024820152734e6f205354414b4520746f20776974686472617760601b6044820152606401610a24565b3360009081526101676020526040812081906121fd90613656565b905060005b815181101561227957600082828151811061222d57634e487b7160e01b600052603260045260246000fd5b6020026020010151905061224081613754565b506000818152610169602052604090205461225b90856156a0565b935061226681613845565b508061227181615751565b915050612202565b50610d2282613a61565b6033546001600160a01b031690565b610182546001600160a01b031633146122e55760405162461bcd60e51b815260206004820152601560248201527414995c5d5a5c994818985d1d1b19599b1e48189bdd605a1b6044820152606401610a24565b600261012d5414156123095760405162461bcd60e51b8152600401610a2490615625565b600261012d5561016c546000906123219060016156a0565b9050600061232d613d26565b9050600061233a83613ece565b90506000808080851561254057612710610170548761235991906156d8565b61236391906156b8565b935061237c610166548561237791906156a0565b613fa4565b612710610172548761238e91906156d8565b61239891906156b8565b925082156124005761017354604051637e14643360e01b8152600481018590526001600160a01b0390911690637e14643390602401600060405180830381600087803b1580156123e757600080fd5b505af11580156123fb573d6000803e3d6000fd5b505050505b612710610171548761241291906156d8565b61241c91906156b8565b9150811561250f5761015f5461017e5460405163095ea7b360e01b81526001600160a01b039283169263095ea7b39261245c929116908690600401615355565b602060405180830381600087803b15801561247657600080fd5b505af115801561248a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124ae9190615174565b5061017e546040516370055ee160e11b8152600481018490526001600160a01b039091169063e00abdc290602401600060405180830381600087803b1580156124f657600080fd5b505af115801561250a573d6000803e3d6000fd5b505050505b818361251b86896156f7565b61252591906156f7565b61252f91906156f7565b61253990826156a0565b9050612564565b610166541561255a5761255561016654613fa4565b612564565b6125646000613fa4565b80610164600082825461257791906156a0565b909155505061016c879055604080518082018252828152602080820188815260008b815261016d835284902092518355516001909201919091558151888152908101839052908101869052606081018590526080810183905260a081018490527fc87e64b388919dd4e97de1f419aae0bf9ce06b2ff3ee8e776f48a9fd51c067739060c00160405180910390a15050600161012d555050505050565b600261012d5414156126375760405162461bcd60e51b8152600401610a2490615625565b600261012d5580516126845760405162461bcd60e51b81526020600482015260166024820152754e6f20466f756e64657220746f20776974686472617760501b6044820152606401610a24565b6000805b82518110156127b357600061017760008584815181106126b857634e487b7160e01b600052603260045260246000fd5b602090810291909101810151825281810192909252604090810160009081205480825261016990935220600201549091506001600160a01b031633146127315760405162461bcd60e51b815260206004820152600e60248201526d4e6f7420796f7572207374616b6560901b6044820152606401610a24565b61276184838151811061275457634e487b7160e01b600052603260045260246000fd5b6020026020010151614299565b5061279284838151811061278557634e487b7160e01b600052603260045260246000fd5b602002602001015161435b565b8261279c81615751565b9350505080806127ab90615751565b915050612688565b50610d2281613a61565b33600090815261016b602052604090205460ff16806127f457506127df612283565b6001600160a01b0316336001600160a01b0316145b6128105760405162461bcd60e51b8152600401610a2490615529565b610179805460ff1916911515919091179055565b3361282d612283565b6001600160a01b0316146128535760405162461bcd60e51b8152600401610a24906155a5565b6001600160a01b03811661286657600080fd5b61017c80546001600160a01b0319166001600160a01b0383811691821790925561015f5460405163095ea7b360e01b815292169163095ea7b391610b2b9160001990600401615355565b33600090815261016b602052604090205460ff16806128e757506128d2612283565b6001600160a01b0316336001600160a01b0316145b6129035760405162461bcd60e51b8152600401610a2490615529565b61016c819055600061291482613ece565b604080518082018252948552602080860192835261016c54600090815261016d9091522093518455516001909301929092555050565b33612953612283565b6001600160a01b0316146129795760405162461bcd60e51b8152600401610a24906155a5565b6127108161298784866156a0565b61299191906156a0565b111561299c57600080fd5b610170929092556101715561017255565b33600090815261016b602052604090205460ff16806129e457506129cf612283565b6001600160a01b0316336001600160a01b0316145b612a005760405162461bcd60e51b8152600401610a2490615529565b806101646000828254612a1391906156f7565b909155505050565b6000612a27600161452d565b90508015612a3f576000805461ff0019166101001790555b612a476145b5565b612a4f6145b5565b612a576145dc565b612a5f61460b565b61015f80546001600160a01b03808d166001600160a01b03199283161790925561016280548c841690831617905561016080548a841692169190911790556101618990554261016855600061016a81905561016388905561016c556101708590556101718490556101728390558516612aea5761017380546001600160a01b03191630179055612b07565b61017380546001600160a01b0319166001600160a01b0387161790555b612710610172546101715461017054612b2091906156a0565b612b2a91906156a0565b1115612b3557600080fd5b6101625461015f54612b56916001600160a01b03918216911660001961463a565b8015612b9c576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b50505050505050505050565b33600090815261016b602052604090205460ff1680612bdf5750612bca612283565b6001600160a01b0316336001600160a01b0316145b612bfb5760405162461bcd60e51b8152600401610a2490615529565b6000612c0861017a613656565b9050610bfd816131a7565b33600090815261016b602052604090205460ff1680612c4a5750612c35612283565b6001600160a01b0316336001600160a01b0316145b612c665760405162461bcd60e51b8152600401610a2490615529565b60008211612c7357600080fd5b61015f54612c8c906001600160a01b031633308561372d565b612c96828261474d565b507f4c88bc1b2b3af09b7d71bbfba500a369511504bcfcc12131b0f2eb8b736e21893383836040516116e89392919061539e565b33612cd3612283565b6001600160a01b031614612cf95760405162461bcd60e51b8152600401610a24906155a5565b6001600160a01b038116612d0c57600080fd5b61017d80546001600160a01b0319166001600160a01b0392909216919091179055565b33600090815261016b602052604090205460ff1680612d665750612d51612283565b6001600160a01b0316336001600160a01b0316145b612d825760405162461bcd60e51b8152600401610a2490615529565b61016355565b600080805b6001600160a01b038416600090815261016760205260409020612daf906136b9565b811015612e00576001600160a01b038416600090815261016760205260409020612de290612ddd90836136c3565b6147e1565b612dec90836156a0565b915080612df881615751565b915050612d8d565b5092915050565b600261012d541415612e2b5760405162461bcd60e51b8152600401610a2490615625565b600261012d556101795460ff1615612ea25760405162461bcd60e51b815260206004820152603460248201527f436c61696d696e672069732063757272656e746c79207061757365642c20706c60448201527332b0b9b2903a393c9030b3b0b4b7103630ba32b960611b6064820152608401610a24565b6000805b33600090815261016760205260409020612ebf906136b9565b811015612f075733600090815261016760205260409020612ee990612ee490836136c3565b613754565b612ef390836156a0565b915080612eff81615751565b915050612ea6565b5060008111612f4d5760405162461bcd60e51b81526020600482015260126024820152714e6f2072657761726420746f20636c61696d60701b6044820152606401610a24565b50600161012d55565b33600090815261016b602052604090205460ff1680612f8d5750612f78612283565b6001600160a01b0316336001600160a01b0316145b612fa95760405162461bcd60e51b8152600401610a2490615529565b6040805180820182529283526020808401928352600094855261016d9052909220905181559051600190910155565b33600090815261016b602052604090205460ff168061300f5750612ffa612283565b6001600160a01b0316336001600160a01b0316145b61302b5760405162461bcd60e51b8152600401610a2490615529565b6001600160a01b03811661303e57600080fd5b61017e80546001600160a01b0319166001600160a01b0392909216919091179055565b60008061306d84610f2d565b91505060005b81518110156131365760005b82828151811061309f57634e487b7160e01b600052603260045260246000fd5b60200260200101515181101561312357848383815181106130d057634e487b7160e01b600052603260045260246000fd5b602002602001015182815181106130f757634e487b7160e01b600052603260045260246000fd5b6020026020010151141561311157600193505050506109cb565b8061311b81615751565b91505061307f565b508061312e81615751565b915050613073565b506000949350505050565b33600090815261016b602052604090205460ff16806131785750613163612283565b6001600160a01b0316336001600160a01b0316145b6131945760405162461bcd60e51b8152600401610a2490615529565b806101646000828254612a1391906156a0565b33600090815261016b602052604090205460ff16806131de57506131c9612283565b6001600160a01b0316336001600160a01b0316145b6131fa5760405162461bcd60e51b8152600401610a2490615529565b613202612292565b60008151116132665760405162461bcd60e51b815260206004820152602a60248201527f426174746c65666c79466c79776865656c5661756c743a204e6f206465706f73604482015269697465642066756e647360b01b6064820152608401610a24565b60005b8151811015610b7d5761017c5482516001600160a01b039091169063fbe85f06908490849081106132aa57634e487b7160e01b600052603260045260246000fd5b60200260200101516040518263ffffffff1660e01b81526004016132d091815260200190565b60206040518083038186803b1580156132e857600080fd5b505afa1580156132fc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133209190615174565b156134745761017c5482516001600160a01b0390911690632e1a7d4d9084908490811061335d57634e487b7160e01b600052603260045260246000fd5b60200260200101516040518263ffffffff1660e01b815260040161338391815260200190565b602060405180830381600087803b15801561339d57600080fd5b505af11580156133b1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133d591906151d0565b506134128282815181106133f957634e487b7160e01b600052603260045260246000fd5b602002602001015161017a6148d590919063ffffffff16565b507f91db1283de26ad94dd00d7a30f701d5d8fd048ea13ca51d2ad90b2bef7368dba82828151811061345457634e487b7160e01b600052603260045260246000fd5b602002602001015160405161346b91815260200190565b60405180910390a15b8061347e81615751565b915050613269565b3361348f612283565b6001600160a01b0316146134b55760405162461bcd60e51b8152600401610a24906155a5565b6001600160a01b03811661351a5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610a24565b610bfd816136db565b3361352c612283565b6001600160a01b0316146135525760405162461bcd60e51b8152600401610a24906155a5565b6001600160a01b0391909116600090815261016b60205260409020805460ff1916911515919091179055565b33600090815261016b602052604081205460ff16806135b557506135a0612283565b6001600160a01b0316336001600160a01b0316145b6135d15760405162461bcd60e51b8152600401610a2490615529565b600082815261017760205260409020546135ea816147e1565b9392505050565b336135fa612283565b6001600160a01b0316146136205760405162461bcd60e51b8152600401610a24906155a5565b6001600160a01b03811661363357600080fd5b61018280546001600160a01b0319166001600160a01b0392909216919091179055565b606060006135ea836148e1565b610a9a8363a9059cbb60e01b8484604051602401613682929190615355565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b03199093169290921790915261493d565b60006109cb825490565b60006135ea8383614a0f565b60006135ea8383614a47565b603380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b61374e846323b872dd60e01b85858560405160240161368293929190615331565b50505050565b6000816137975760405162461bcd60e51b81526020600482015260116024820152704e6f207374616b6520746f20636c61696d60781b6044820152606401610a24565b600082815261016960205260408120906137b0846147e1565b90508061016560008282546137c591906156a0565b909155505061016c546003830155600282015461015f546137f3916001600160a01b03918216911683613663565b60028201546040517f34fcbac0073d7c3d388e51312faf357774904998eeb8fca628b9e6f65ee1cbf791613836916001600160a01b0390911690879085906153c2565b60405180910390a19392505050565b60008181526101696020526040902061385d82613754565b5060005b600083815261017860205260409020613879906136b9565b81101561397e576101605460028301546000858152610178602052604090206001600160a01b03928316926342842e0e9230929116906138b990866136c3565b6040518463ffffffff1660e01b81526004016138d793929190615331565b600060405180830381600087803b1580156138f157600080fd5b505af1158015613905573d6000803e3d6000fd5b5050505060028201546000848152610178602052604090207ff279e6a1f5e320cca91135676d9cb6e44ca8a08c0b88342bcdb1144f6511b568916001600160a01b031690859061395590856136c3565b604051613964939291906153c2565b60405180910390a18061397681615751565b915050613861565b506201518061016c546201518061399591906156d8565b610163546139a391906156a0565b6139ad91906156f7565b816001015410156139e8576000828152610178602052604090206139d0906136b9565b61016e60008282546139e291906156a0565b90915550505b60028101546001600160a01b0316600090815261016760205260409020613a0f90836148d5565b50600082815261016960209081526040808320838155600181018490556002810180546001600160a01b03191690556003018390556101789091528120908181613a598282614d44565b505050505050565b600061016154609614613afa5761017d60009054906101000a90046001600160a01b03166001600160a01b031663508ca1a76040518163ffffffff1660e01b815260040160206040518083038186803b158015613abd57600080fd5b505afa158015613ad1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613af591906151d0565b613b81565b61017d60009054906101000a90046001600160a01b03166001600160a01b0316633583d8b86040518163ffffffff1660e01b815260040160206040518083038186803b158015613b4957600080fd5b505afa158015613b5d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b8191906151d0565b61017d54604051630e7e736d60e21b81529192506000916001600160a01b03909116906339f9cdb490613bb890339060040161531d565b60206040518083038186803b158015613bd057600080fd5b505afa158015613be4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c0891906151d0565b61017d546040516326d352ab60e11b81529192506000916001600160a01b0390911690634da6a55690613c3f90339060040161531d565b60206040518083038186803b158015613c5757600080fd5b505afa158015613c6b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c8f91906151d0565b9050600082613c9e86866156d8565b1115613cab576000613cbf565b613cb585856156d8565b613cbf90846156f7565b905080821115613d1f5760405162461bcd60e51b815260206004820152602560248201527f506c732077697468647261772066726f6d20466c79776865656c5661756c7420604482015264199a5c9cdd60da1b6064820152608401610a24565b5050505050565b600080613d3461017a613656565b905060005b8151811015613ec95761017c5482516000916001600160a01b03169063d387c08b90859085908110613d7b57634e487b7160e01b600052603260045260246000fd5b60200260200101516040518263ffffffff1660e01b8152600401613da191815260200190565b604080518083038186803b158015613db857600080fd5b505afa158015613dcc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613df09190615230565b5090508015613eb65761017c5483516001600160a01b039091169063379607f590859085908110613e3157634e487b7160e01b600052603260045260246000fd5b60200260200101516040518263ffffffff1660e01b8152600401613e5791815260200190565b602060405180830381600087803b158015613e7157600080fd5b505af1158015613e85573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ea991906151d0565b613eb390856156a0565b93505b5080613ec181615751565b915050613d39565b505090565b61016e5461016c54600090815261016d602052604081206001015490918291613ef791906156f7565b600061016e819055909150613f0f84620151806156d8565b61016354613f1d91906156a0565b61016f549091505b61016a548111613f995760008181526101696020526040902060010154613f4b57613f87565b60008181526101696020526040902060010154821015613f6a57613f99565b60008181526101696020526040902054613f8490846156a0565b92505b80613f9181615751565b915050613f25565b61016f555092915050565b806101816000828254613fb791906156a0565b909155505061018054158015613fd05750600061018154115b15613ff257613fe361018154600061474d565b61018055600061018155614290565b600061018054118015614087575061017c5461018054604051637df42f8360e11b81526001600160a01b039092169163fbe85f06916140379160040190815260200190565b60206040518083038186803b15801561404f57600080fd5b505afa158015614063573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906140879190615174565b1561416b5761017c5461018054604051632e1a7d4d60e01b815260048101919091526000916001600160a01b031690632e1a7d4d90602401602060405180830381600087803b1580156140d957600080fd5b505af11580156140ed573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061411191906151d0565b905061412b6101805461017a6148d590919063ffffffff16565b506000610181548261413d91906156a0565b905061414a81600061474d565b61018081905561415d9061017a906136cf565b505060006101815550614290565b600061018054118015614200575061017c546101805460405163375542ed60e21b81526001600160a01b039092169163dd550bb4916141b09160040190815260200190565b60206040518083038186803b1580156141c857600080fd5b505afa1580156141dc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906142009190615174565b156142905761017c54610180546040516313dccf3d60e31b81526001600160a01b0390921691639ee679e89161423c9160040190815260200190565b602060405180830381600087803b15801561425657600080fd5b505af115801561426a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061428e919061527e565b505b50600061016655565b600081815261017760209081526040808320548084526101699092528220805483906142c4846147e1565b6142ce91906156b8565b90508061016560008282546142e391906156a0565b9091555050600282015461015f54614308916001600160a01b03918216911683613663565b60028201546040517f34fcbac0073d7c3d388e51312faf357774904998eeb8fca628b9e6f65ee1cbf79161434b916001600160a01b0390911690869085906153c2565b60405180910390a1949350505050565b60008181526101776020908152604080832054808452610169909252918290206101605460028201549351632142170760e11b8152929391926001600160a01b03918216926342842e0e926143ba923092909116908890600401615331565b600060405180830381600087803b1580156143d457600080fd5b505af11580156143e8573d6000803e3d6000fd5b50508254915082905060006143fc8361573a565b9091555050600083815261017760209081526040808320839055848352610178909152902061442b90846148d5565b506201518061016c546201518061444291906156d8565b6101635461445091906156a0565b61445a91906156f7565b8160010154101561447f57600161016e600082825461447991906156a0565b90915550505b80546144dd5760028101546001600160a01b03166000908152610167602052604090206144ac90836148d5565b50600082815261016960205260408120818155600181018290556002810180546001600160a01b0319169055600301555b60028101546040517ff279e6a1f5e320cca91135676d9cb6e44ca8a08c0b88342bcdb1144f6511b56891614520916001600160a01b0390911690859087906153c2565b60405180910390a1505050565b60008054610100900460ff1615614574578160ff1660011480156145505750303b155b61456c5760405162461bcd60e51b8152600401610a2490615557565b506000919050565b60005460ff80841691161061459b5760405162461bcd60e51b8152600401610a2490615557565b506000805460ff191660ff92909216919091179055600190565b600054610100900460ff166120615760405162461bcd60e51b8152600401610a24906155da565b600054610100900460ff166146035760405162461bcd60e51b8152600401610a24906155da565b612061614a96565b600054610100900460ff166146325760405162461bcd60e51b8152600401610a24906155da565b612061614ac6565b8015806146c35750604051636eb1769f60e11b81523060048201526001600160a01b03838116602483015284169063dd62ed3e9060440160206040518083038186803b15801561468957600080fd5b505afa15801561469d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906146c191906151d0565b155b61472e5760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b6064820152608401610a24565b610a9a8363095ea7b360e01b8484604051602401613682929190615355565b61017c5460405163654cfdff60e01b81526000916001600160a01b03169063654cfdff90614781908690869060040161565c565b602060405180830381600087803b15801561479b57600080fd5b505af11580156147af573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906147d391906151d0565b9050612e0061017a826136cf565b60008181526101696020908152604080832081516080810183528154815260018201549381019390935260028101546001600160a01b031691830191909152600301546060820181905261016c548392911415614842575060009392505050565b60008160600151600161485591906156a0565b90505b61016c54811161201f57600081815261016d6020526040902060010154158061488057508151155b1561488a576148c3565b8151600082815261016d60205260409020600181015490546148ac91906156b8565b6148b691906156d8565b6148c090846156a0565b92505b806148cd81615751565b915050614858565b60006135ea8383614af5565b60608160000180548060200260200160405190810160405280929190818152602001828054801561493157602002820191906000526020600020905b81548152602001906001019080831161491d575b50505050509050919050565b6000614992826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316614c129092919063ffffffff16565b805190915015610a9a57808060200190518101906149b09190615174565b610a9a5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610a24565b6000826000018281548110614a3457634e487b7160e01b600052603260045260246000fd5b9060005260206000200154905092915050565b6000818152600183016020526040812054614a8e575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556109cb565b5060006109cb565b600054610100900460ff16614abd5760405162461bcd60e51b8152600401610a24906155da565b612061336136db565b600054610100900460ff16614aed5760405162461bcd60e51b8152600401610a24906155da565b600161012d55565b60008181526001830160205260408120548015614c08576000614b196001836156f7565b8554909150600090614b2d906001906156f7565b9050818114614bae576000866000018281548110614b5b57634e487b7160e01b600052603260045260246000fd5b9060005260206000200154905080876000018481548110614b8c57634e487b7160e01b600052603260045260246000fd5b6000918252602080832090910192909255918252600188019052604090208390555b8554869080614bcd57634e487b7160e01b600052603160045260246000fd5b6001900381819060005260206000200160009055905585600101600086815260200190815260200160002060009055600193505050506109cb565b60009150506109cb565b6060610b8a8484600085856001600160a01b0385163b614c745760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610a24565b600080866001600160a01b03168587604051614c909190615301565b60006040518083038185875af1925050503d8060008114614ccd576040519150601f19603f3d011682016040523d82523d6000602084013e614cd2565b606091505b5091509150614ce2828286614ced565b979650505050505050565b60608315614cfc5750816135ea565b825115614d0c5782518084602001fd5b8160405162461bcd60e51b8152600401610a2491906154f6565b60405180604001604052806002906020820280368337509192915050565b5080546000825590600052602060002090810190610bfd91905b80821115614d725760008155600101614d5e565b5090565b80356001600160a01b038116811461134d57600080fd5b600082601f830112614d9d578081fd5b813560206001600160401b03821115614db857614db8615782565b8160051b614dc7828201615670565b838152828101908684018388018501891015614de1578687fd5b8693505b85841015614e03578035835260019390930192918401918401614de5565b50979650505050505050565b600082601f830112614e1f578081fd5b81356001600160401b03811115614e3857614e38615782565b614e4b601f8201601f1916602001615670565b818152846020838601011115614e5f578283fd5b816020850160208301379081016020019190915292915050565b600060208284031215614e8a578081fd5b6135ea82614d76565b600080600080600060a08688031215614eaa578081fd5b614eb386614d76565b9450614ec160208701614d76565b935060408601356001600160401b0380821115614edc578283fd5b614ee889838a01614d8d565b94506060880135915080821115614efd578283fd5b614f0989838a01614d8d565b93506080880135915080821115614f1e578283fd5b50614f2b88828901614e0f565b9150509295509295909350565b60008060008060008060008060006101208a8c031215614f56578384fd5b614f5f8a614d76565b9850614f6d60208b01614d76565b975060408a01359650614f8260608b01614d76565b955060808a01359450614f9760a08b01614d76565b935060c08a0135925060e08a013591506101008a013590509295985092959850929598565b60008060008060808587031215614fd1578384fd5b614fda85614d76565b9350614fe860208601614d76565b92506040850135915060608501356001600160401b03811115615009578182fd5b61501587828801614e0f565b91505092959194509250565b600080600080600060a08688031215615038578081fd5b61504186614d76565b945061504f60208701614d76565b9350604086013592506060860135915060808601356001600160401b03811115615077578182fd5b614f2b88828901614e0f565b60008060408385031215615095578182fd5b61509e83614d76565b915060208301356150ae81615798565b809150509250929050565b600080604083850312156150cb578182fd5b6150d483614d76565b946020939093013593505050565b6000602082840312156150f3578081fd5b81356001600160401b03811115615108578182fd5b610b8a84828501614d8d565b60008060408385031215615126578182fd5b82356001600160401b0381111561513b578283fd5b61514785828601614d8d565b92505060208301356150ae81615798565b600060208284031215615169578081fd5b81356135ea81615798565b600060208284031215615185578081fd5b81516135ea81615798565b6000602082840312156151a1578081fd5b81356001600160e01b0319811681146135ea578182fd5b6000602082840312156151c9578081fd5b5035919050565b6000602082840312156151e1578081fd5b5051919050565b600080604083850312156151fa578182fd5b823591506020830135600581106150ae578182fd5b60008060408385031215615221578182fd5b50508035926020909101359150565b60008060408385031215615242578182fd5b505080516020909101519092909150565b600080600060608486031215615267578081fd5b505081359360208301359350604090920135919050565b60006020828403121561528f578081fd5b81516001600160401b03811681146135ea578182fd5b6000815180845260208085019450808401835b838110156152d4578151875295820195908201906001016152b8565b509495945050505050565b600581106152fd57634e487b7160e01b600052602160045260246000fd5b9052565b6000825161531381846020870161570e565b9190910192915050565b6001600160a01b0391909116815260200190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6001600160a01b03929092168252602082015260400190565b60018060a01b038416815282602082015260606040820152600061539560608301846152a5565b95945050505050565b6001600160a01b03841681526020810183905260608101610b8a60408301846152df565b6001600160a01b039390931683526020830191909152604082015260600190565b6040808252835182820181905260009190606090818501906020808901865b83811015615442578151805186528381015184870152878101516001600160a01b0316888701528601518686015260809094019390820190600101615402565b505086830381880152875180845281840195509350600584901b830181019150878101865b8581101561549557601f198585030187526154838483516152a5565b96830196935090820190600101615467565b50919998505050505050505050565b60408101818360005b60028110156154cc5781518352602092830192909101906001016154ad565b50505092915050565b6020815260006135ea60208301846152a5565b602081016109cb82846152df565b602081526000825180602084015261551581604085016020870161570e565b601f01601f19169190910160400192915050565b602080825260149082015273526571756972652061646d696e2061636365737360601b604082015260600190565b6020808252602e908201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160408201526d191e481a5b9a5d1a585b1a5e995960921b606082015260800190565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b606082015260800190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b828152604081016135ea60208301846152df565b604051601f8201601f191681016001600160401b038111828210171561569857615698615782565b604052919050565b600082198211156156b3576156b361576c565b500190565b6000826156d357634e487b7160e01b81526012600452602481fd5b500490565b60008160001904831182151516156156f2576156f261576c565b500290565b6000828210156157095761570961576c565b500390565b60005b83811015615729578181015183820152602001615711565b8381111561374e5750506000910152565b6000816157495761574961576c565b506000190190565b60006000198214156157655761576561576c565b5060010190565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052604160045260246000fd5b8015158114610bfd57600080fdfea2646970667358221220ea67e1d96b116072c58c1b4ed9de50fafe8c64218a2fda14f14aa1877746eb4964736f6c63430008040033

Deployed ByteCode Sourcemap

1476:30398:71:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;661:254:14;;;;;;:::i;:::-;;:::i;:::-;;;15137:14:122;;15130:22;15112:41;;15100:2;15085:18;661:254:14;;;;;;;;3231:30:71;;;;;;;;;26896:25:122;;;26884:2;26869:18;3231:30:71;26851:76:122;25011:371:71;;;;;;:::i;:::-;;:::i;:::-;;4952:69;;;;;-1:-1:-1;;;;;4952:69:71;;;;;;;;;;:::i;5027:53::-;;;;;-1:-1:-1;;;;;5027:53:71;;;2381:30;;;;;-1:-1:-1;;;;;2381:30:71;;;30604:325;;;;;;:::i;:::-;;:::i;834:200:23:-;;;;;;:::i;:::-;;:::i;:::-;;;-1:-1:-1;;;;;;15326:33:122;;;15308:52;;15296:2;15281:18;834:200:23;15263:103:122;28118:164:71;;;:::i;21549:203::-;;;:::i;:::-;;;;;;;:::i;3194:31::-;;;;;;22683:286;;;:::i;28835:139::-;;;;;;:::i;:::-;;:::i;7147:565::-;;;:::i;5087:30::-;;;;;;10338:610;;;;;;:::i;:::-;;:::i;:::-;;;;;;;;:::i;2528:44::-;;;;;-1:-1:-1;;;;;2528:44:71;;;4897:49;;;;;-1:-1:-1;;;;;4897:49:71;;;9784:230;;;;;;:::i;:::-;;:::i;23502:109::-;;;:::i;23695:231::-;;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;11990:985::-;;;;;;:::i;:::-;;:::i;3267:29::-;;;;;;4775:28;;;;;;;;;3494:84;;3536:42;3494:84;;7798:1903;;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;28380:347::-;;;;;;:::i;:::-;;:::i;3641:82::-;;3699:24;3641:82;;;;;;;;;:::i;2452:28::-;;;;;;11466:459;;;;;;:::i;:::-;;:::i;1908:101:4:-;;;:::i;10099:108:71:-;;;;;;:::i;:::-;;:::i;3015:29::-;;;;;;22324:267;;;;;;:::i;:::-;;:::i;13736:544::-;;;:::i;1276:85:4:-;;;:::i;25497:1788:71:-;;;:::i;2417:29::-;;;;;-1:-1:-1;;;;;2417:29:71;;;5209:27;;;;;-1:-1:-1;;;;;5209:27:71;;;15613:551;;;;;;:::i;:::-;;:::i;24432:102::-;;;;;;:::i;:::-;;:::i;3584:51::-;;3630:5;3584:51;;5166:37;;;;;;30205:297;;;;;;:::i;:::-;;:::i;23057:391::-;;;;;;:::i;:::-;;:::i;3830:51::-;;;;;;:::i;:::-;;;;;;;;;;;;;;31034:386;;;;;;:::i;:::-;;:::i;24594:110::-;;;;;;:::i;:::-;;:::i;3050:69::-;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;27381:25:122;;;27437:2;27422:18;;27415:34;;;;27354:18;3050:69:71;27336:119:122;5496:1450:71;;;;;;:::i;:::-;;:::i;13041:89::-;13107:16;;;;13041:89;;27392:150;;;:::i;21944:293::-;;;;;;:::i;:::-;;:::i;972:247:13:-;;;;;;:::i;:::-;-1:-1:-1;;;972:247:13;;;;;;;;29654:186:71;;;;;;:::i;:::-;;:::i;3730:51::-;;;;;;:::i;:::-;;;;;;;;;;;;;;;;3787:37;;;;;;23984:118;;;;;;:::i;:::-;;:::i;17498:314::-;;;;;;:::i;:::-;;:::i;13227:433::-;;;:::i;29059:218::-;;;;;;:::i;:::-;;:::i;2881:53::-;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;2881:53:71;;;;;;;;;;;;;;;27691:25:122;;;27747:2;27732:18;;27725:34;;;;-1:-1:-1;;;;;27795:32:122;27790:2;27775:18;;27768:60;27859:2;27844:18;;27837:34;27678:3;27663:19;;27645:232;29342:209:71;;;;;;:::i;:::-;;:::i;10954:431::-;;;;;;:::i;:::-;;:::i;2578:29::-;;;;;;24766:112;;;;;;:::i;:::-;;:::i;27548:467::-;;;;;;:::i;:::-;;:::i;5123:37::-;;;;;;747:219:13;;;;;;:::i;:::-;-1:-1:-1;;;747:219:13;;;;;;;;2158:198:4;;;;;;:::i;:::-;;:::i;31491:113:71:-;;;;;;:::i;:::-;;:::i;24177:195::-;;;;;;:::i;:::-;;:::i;29942:158::-;;;;;;:::i;:::-;;:::i;661:254:14:-;785:4;-1:-1:-1;;;;;;808:60:14;;-1:-1:-1;;;808:60:14;;:100;;-1:-1:-1;;;;;;;;;;1168:51:30;;;872:36:14;801:107;661:254;-1:-1:-1;;661:254:14:o;25011:371:71:-;929:10:25;31655:25:71;;;;:11;:25;;;;;;;;;:52;;;31700:7;:5;:7::i;:::-;-1:-1:-1;;;;;31684:23:71;929:10:25;-1:-1:-1;;;;;31684:23:71;;31655:52;31647:85;;;;-1:-1:-1;;;31647:85:71;;;;;;;:::i;:::-;;;;;;;;;25100:12:::1;25115:33:::0;;;:21:::1;:33;::::0;;;;:47:::1;::::0;;::::1;::::0;;25100:12;25194:14:::1;::::0;25137:10;;25194:14:::1;:::i;:::-;25178:30;;25173:203;25219:14;;25210:5;:23;25173:203;;25258:34;25295:28:::0;;;:21:::1;:28;::::0;;;;25338:19:::1;::::0;::::1;:27:::0;;25295:28;;25361:4;;25338:27:::1;::::0;25361:4;;25338:27:::1;:::i;:::-;::::0;;;-1:-1:-1;25235:7:71;;-1:-1:-1;25235:7:71::1;::::0;-1:-1:-1;25235:7:71;::::1;:::i;:::-;;;;25173:203;;;;31742:1;25011:371:::0;:::o;30604:325::-;929:10:25;1488:7:4;:5;:7::i;:::-;-1:-1:-1;;;;;1488:23:4;;1480:68;;;;-1:-1:-1;;;1480:68:4;;;;;;;:::i;:::-;-1:-1:-1;;;;;30690:26:71;::::1;30682:35;;;::::0;::::1;;30727:23;:62:::0;;-1:-1:-1;;;;;;30727:62:71::1;-1:-1:-1::0;;;;;30727:62:71;;::::1;::::0;;::::1;::::0;;;30863:5:::1;::::0;:59:::1;::::0;-1:-1:-1;;;30863:59:71;;:5;::::1;::::0;:13:::1;::::0;:59:::1;::::0;-1:-1:-1;;30911:10:71;30863:59:::1;;;:::i;:::-;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;30604:325:::0;:::o;834:200:23:-;-1:-1:-1;;;834:200:23;;;;;;;:::o;28118:164:71:-;929:10:25;31655:25:71;;;;:11;:25;;;;;;;;;:52;;;31700:7;:5;:7::i;:::-;-1:-1:-1;;;;;31684:23:71;929:10:25;-1:-1:-1;;;;;31684:23:71;;31655:52;31647:85;;;;-1:-1:-1;;;31647:85:71;;;;;;;:::i;:::-;28193:20:::1;28216:19;:10;:17;:19::i;:::-;28193:42;;28245:30;28271:3;28245:25;:30::i;:::-;31742:1;28118:164::o:0;21549:203::-;21589:13;21618;;21635:3;21618:20;21614:132;;;-1:-1:-1;21654:25:71;;;;;;;;;;;;-1:-1:-1;;;21654:25:71;;;;;21549:203::o;21614:132::-;-1:-1:-1;21710:25:71;;;;;;;;;;;;-1:-1:-1;;;21710:25:71;;;;;21549:203::o;22683:286::-;929:10:25;31655:25:71;;;;:11;:25;;;;;;;;;:52;;;31700:7;:5;:7::i;:::-;-1:-1:-1;;;;;31684:23:71;929:10:25;-1:-1:-1;;;;;31684:23:71;;31655:52;31647:85;;;;-1:-1:-1;;;31647:85:71;;;;;;;:::i;:::-;1815:1:7::1;2569:7;;:19;;2561:63;;;;-1:-1:-1::0;;;2561:63:7::1;;;;;;;:::i;:::-;1815:1;2699:7;:18:::0;22781:11:71::2;::::0;22764:14:::2;22803:160;22828:6;22823:1;:11;22803:160;;22889:14;::::0;22855:16:::2;::::0;;;:13:::2;:16;::::0;;;;:31:::2;::::0;::::2;:48:::0;;;;22917:31:::2;::::0;;::::2;:35:::0;22869:1;22836:3:::2;22869:1:::0;22836:3:::2;:::i;:::-;;;;22803:160;;;-1:-1:-1::0;;1772:1:7::1;2872:7;:22:::0;22683:286:71:o;28835:139::-;929:10:25;31655:25:71;;;;:11;:25;;;;;;;;;:52;;;31700:7;:5;:7::i;:::-;-1:-1:-1;;;;;31684:23:71;929:10:25;-1:-1:-1;;;;;31684:23:71;;31655:52;31647:85;;;;-1:-1:-1;;;31647:85:71;;;;;;;:::i;:::-;28931:5:::1;::::0;:36:::1;::::0;-1:-1:-1;;;;;28931:5:71::1;28950:8:::0;28960:6;28931:18:::1;:36::i;7147:565::-:0;7203:22;;7195:73;;;;-1:-1:-1;;;7195:73:71;;24247:2:122;7195:73:71;;;24229:21:122;24286:2;24266:18;;;24259:30;24325:34;24305:18;;;24298:62;-1:-1:-1;;;24376:18:122;;;24369:31;24417:19;;7195:73:71;24219:223:122;7195:73:71;7278:25;7306:42;7337:10;7306:30;:42::i;:::-;7278:70;;7384:1;7366:8;:15;:19;7358:50;;;;-1:-1:-1;;;7358:50:71;;17806:2:122;7358:50:71;;;17788:21:122;17845:2;17825:18;;;17818:30;-1:-1:-1;;;17864:18:122;;;17857:48;17922:18;;7358:50:71;17778:168:122;7358:50:71;7423:9;7418:110;7442:8;:15;7438:1;:19;7418:110;;;7513:4;7478:19;:32;7498:8;7507:1;7498:11;;;;;;-1:-1:-1;;;7498:11:71;;;;;;;;;;;;;;;7478:32;;;;;;;;;;;;:39;;;;;;;;;;;;;;;;;;7459:3;;;;;:::i;:::-;;;;7418:110;;;;7537:72;7556:10;7593:8;:15;7568:22;;:40;;;;:::i;:::-;7537:5;;-1:-1:-1;;;;;7537:5:71;;:72;:18;:72::i;:::-;7624:81;7642:10;7679:8;:15;7654:22;;:40;;;;:::i;:::-;7696:8;7624:81;;;;;;;;:::i;:::-;;;;;;;;7147:565;:::o;10338:610::-;-1:-1:-1;;;;;10495:26:71;;10445:28;10495:26;;;:19;:26;;;;;10392:21;;;;10495:35;;:33;:35::i;:::-;-1:-1:-1;;;;;10476:55:71;;;;;-1:-1:-1;;;10476:55:71;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;10476:55:71;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;10597:26:71;;10541:37;10597:26;;;:19;:26;;;;;10445:86;;-1:-1:-1;10541:37:71;10597:35;;:33;:35::i;:::-;-1:-1:-1;;;;;10581:52:71;;;;;-1:-1:-1;;;10581:52:71;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;10541:92;;10648:9;10643:254;-1:-1:-1;;;;;10667:26:71;;;;;;:19;:26;;;;;:35;;:33;:35::i;:::-;10663:1;:39;10643:254;;;-1:-1:-1;;;;;10749:26:71;;10735:47;10749:26;;;:19;:26;;;;;10735:13;;:47;10749:32;;10779:1;10749:29;:32::i;:::-;10735:47;;;;;;;;;;;;;;-1:-1:-1;10735:47:71;10723:59;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;10723:59:71;;;;;;;;;;;;;;;:9;;:6;;10730:1;;10723:9;;;;-1:-1:-1;;;10723:9:71;;;;;;;;;;;;;;:59;;;;10820:66;:23;:57;10844:32;10874:1;10844:19;:26;10864:5;-1:-1:-1;;;;;10844:26:71;-1:-1:-1;;;;;10844:26:71;;;;;;;;;;;;:29;;:32;;;;:::i;:::-;10820:57;;;;;;;;;;;:64;:66::i;:::-;10796:18;10815:1;10796:21;;;;;;-1:-1:-1;;;10796:21:71;;;;;;;;;;;;;;:90;;;;10704:3;;;;;:::i;:::-;;;;10643:254;;;-1:-1:-1;10914:6:71;;10922:18;;-1:-1:-1;10338:610:71;-1:-1:-1;;10338:610:71:o;9784:230::-;929:10:25;1488:7:4;:5;:7::i;:::-;-1:-1:-1;;;;;1488:23:4;;1480:68;;;;-1:-1:-1;;;1480:68:4;;;;;;;:::i;:::-;9898:9:71::1;9893:115;9917:8;:15;9913:1;:19;9893:115;;;9988:9;9953:19;:32;9973:8;9982:1;9973:11;;;;;;-1:-1:-1::0;;;9973:11:71::1;;;;;;;;;;;;;;;9953:32;;;;;;;;;;;;:44;;;;;;;;;;;;;;;;;;9934:3;;;;;:::i;:::-;;;;9893:115;;23502:109:::0;929:10:25;23564:7:71;31655:25;;;:11;:25;;;;;;;;;:52;;;31700:7;:5;:7::i;:::-;-1:-1:-1;;;;;31684:23:71;929:10:25;-1:-1:-1;;;;;31684:23:71;;31655:52;31647:85;;;;-1:-1:-1;;;31647:85:71;;;;;;;:::i;:::-;-1:-1:-1;23590:14:71::1;::::0;23502:109;:::o;23695:231::-;23785:17;;:::i;:::-;929:10:25;31655:25:71;;;;:11;:25;;;;;;;;;:52;;;31700:7;:5;:7::i;:::-;-1:-1:-1;;;;;31684:23:71;929:10:25;-1:-1:-1;;;;;31684:23:71;;31655:52;31647:85;;;;-1:-1:-1;;;31647:85:71;;;;;;;:::i;:::-;-1:-1:-1;23814:105:71::1;::::0;;;;::::1;::::0;;-1:-1:-1;23822:33:71;;;:21:::1;:33;::::0;;;;;;:47;;23814:105;;23871:33;;;;;;:47:::1;;::::0;23814:105;;::::1;::::0;;;;31742:1:::1;23695:231:::0;;;:::o;11990:985::-;12064:10;;12056:68;;;;-1:-1:-1;;;12056:68:71;;20840:2:122;12056:68:71;;;20822:21:122;20879:2;20859:18;;;20852:30;20918:34;20898:18;;;20891:62;-1:-1:-1;;;20969:18:122;;;20962:38;21017:19;;12056:68:71;20812:230:122;12056:68:71;12139:9;12134:229;12158:3;:10;12154:1;:14;12134:229;;;12237:13;;12197:10;;12226:6;;-1:-1:-1;;;;;12197:10:71;;;;:28;;12226:3;;12230:1;;12226:6;;;;-1:-1:-1;;;12226:6:71;;;;;;;;;;;;;;;12197:36;;;;;;;;;;;;;26896:25:122;;26884:2;26869:18;;26851:76;12197:36:71;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;:53;12189:87;;;;-1:-1:-1;;;12189:87:71;;26179:2:122;12189:87:71;;;26161:21:122;26218:2;26198:18;;;26191:30;-1:-1:-1;;;26237:18:122;;;26230:51;26298:18;;12189:87:71;26151:171:122;12189:87:71;12290:10;;12345:6;;-1:-1:-1;;;;;12290:10:71;;;;:27;;12318:10;;12338:4;;12345:3;;12349:1;;12345:6;;;;-1:-1:-1;;;12345:6:71;;;;;;;;;;;;;;;12290:62;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;12170:3;;;;;:::i;:::-;;;;12134:229;;;-1:-1:-1;12393:14:71;;12417:11;:13;;;12372:18;12417:13;;;:::i;:::-;;;;-1:-1:-1;;12483:192:71;;;;;;;;12522:10;;12483:192;;12566:15;12483:192;;;;;;;12606:10;12483:192;;;;;;;;;;;;12454:11;;-1:-1:-1;12440:26:71;;;:13;:26;;;;;;:245;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;12440:245:71;-1:-1:-1;;;;;12440:245:71;;;;;;;;;;;;;;;;12695:166;12719:3;:10;12715:1;:14;12695:166;;;12777:11;;12750:16;:24;12767:3;12771:1;12767:6;;;;;;-1:-1:-1;;;12767:6:71;;;;;;;;;;;;;;;12750:24;;;;;;;;;;;:38;;;;12802:48;12843:3;12847:1;12843:6;;;;;;-1:-1:-1;;;12843:6:71;;;;;;;;;;;;;;;12802:23;:36;12826:11;;12802:36;;;;;;;;;;;:40;;:48;;;;:::i;:::-;-1:-1:-1;12731:3:71;;;;:::i;:::-;;;;12695:166;;;-1:-1:-1;12906:11:71;;12890:10;12870:31;;;;:19;:31;;;;;:48;;:35;:48::i;:::-;;12933:35;12939:10;12951:11;;12964:3;12933:35;;;;;;;;:::i;:::-;;;;;;;;11990:985;;:::o;7798:1903::-;7919:10;;:26;;-1:-1:-1;;;7919:26:71;;7873:16;;7901:15;;-1:-1:-1;;;;;7919:10:71;;;;:20;;:26;;7940:4;;7919:26;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;7901:44;;7955:25;7997:7;-1:-1:-1;;;;;7983:22:71;;;;;-1:-1:-1;;;7983:22:71;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;7983:22:71;;7955:50;;8015:22;8056:9;8051:378;8075:7;8071:1;:11;8051:378;;;8121:10;;:39;;-1:-1:-1;;;8121:39:71;;8103:15;;-1:-1:-1;;;;;8121:10:71;;:30;;:39;;8152:4;;8158:1;;8121:39;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;8194:10;;:37;;-1:-1:-1;;;8194:37:71;;;;;26896:25:122;;;8103:57:71;;-1:-1:-1;8174:17:71;;-1:-1:-1;;;;;8194:10:71;;;;:28;;26869:18:122;;8194:37:71;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;8174:57;;8262:13;;8249:9;:26;:67;;;;-1:-1:-1;8279:28:71;;;;:19;:28;;;;;;;;:37;8249:67;8245:174;;;8363:7;8336:8;8345:14;8336:24;;;;;;-1:-1:-1;;;8336:24:71;;;;;;;;;;;;;;;;;;:34;8388:16;;;;:::i;:::-;;;;8245:174;8051:378;;8084:3;;;;;:::i;:::-;;;;8051:378;;;;8441:30;8475:14;8484:4;8475:8;:14::i;:::-;8438:51;;;8499:28;8541:21;8581:9;8576:112;8600:11;:18;8596:1;:22;8576:112;;;8656:11;8668:1;8656:14;;;;;;-1:-1:-1;;;8656:14:71;;;;;;;;;;;;;;;:21;8639:38;;;;;:::i;:::-;;-1:-1:-1;8620:3:71;;;;:::i;:::-;;;;8576:112;;;;8697:32;8746:13;-1:-1:-1;;;;;8732:28:71;;;;;-1:-1:-1;;;8732:28:71;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;8732:28:71;;8697:63;;8775:9;8770:549;8794:11;:18;8790:1;:22;8770:549;;;8833:30;8866:11;8878:1;8866:14;;;;;;-1:-1:-1;;;8866:14:71;;;;;;;;;;;;;;;8833:47;;8899:9;8894:415;8918:13;:20;8914:1;:24;8894:415;;;8963:15;8981:13;8995:1;8981:16;;;;;;-1:-1:-1;;;8981:16:71;;;;;;;;;;;;;;;;;;;9035:10;;:37;;-1:-1:-1;;;9035:37:71;;;;;26896:25:122;;;8981:16:71;;-1:-1:-1;9015:17:71;;-1:-1:-1;;;;;9035:10:71;;;;:28;;26869:18:122;;9035:37:71;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;9015:57;;9107:13;;9094:9;:26;:67;;;;-1:-1:-1;9124:28:71;;;;:19;:28;;;;;;;;:37;9094:67;9090:205;;;9225:7;9185:15;9201:20;9185:37;;;;;;-1:-1:-1;;;9185:37:71;;;;;;;;;;;;;;;;;;:47;9254:22;;;;:::i;:::-;;;;9090:205;8894:415;;8940:3;;;;;:::i;:::-;;;;8894:415;;;;8770:549;8814:3;;;;;:::i;:::-;;;;8770:549;;;-1:-1:-1;9329:23:71;9369:37;9386:20;9369:14;:37;:::i;:::-;-1:-1:-1;;;;;9355:52:71;;;;;-1:-1:-1;;;9355:52:71;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;9355:52:71;;9329:78;;9422:9;9417:93;9441:14;9437:1;:18;9417:93;;;9488:8;9497:1;9488:11;;;;;;-1:-1:-1;;;9488:11:71;;;;;;;;;;;;;;;9476:6;9483:1;9476:9;;;;;;-1:-1:-1;;;9476:9:71;;;;;;;;;;;;;;;;;;:23;9457:3;;;;:::i;:::-;;;;9417:93;;;-1:-1:-1;9536:14:71;9519:153;9556:37;9573:20;9556:14;:37;:::i;:::-;9552:1;:41;9519:153;;;9626:15;9642:18;9646:14;9642:1;:18;:::i;:::-;9626:35;;;;;;-1:-1:-1;;;9626:35:71;;;;;;;;;;;;;;;9614:6;9621:1;9614:9;;;;;;-1:-1:-1;;;9614:9:71;;;;;;;;;;;;;;;;;;:47;9595:3;;;;:::i;:::-;;;;9519:153;;;-1:-1:-1;9688:6:71;7798:1903;-1:-1:-1;;;;;;;;;7798:1903:71:o;28380:347::-;929:10:25;31655:25:71;;;;:11;:25;;;;;;;;;:52;;;31700:7;:5;:7::i;:::-;-1:-1:-1;;;;;31684:23:71;929:10:25;-1:-1:-1;;;;;31684:23:71;;31655:52;31647:85;;;;-1:-1:-1;;;31647:85:71;;;;;;;:::i;:::-;28475:9:::1;28470:251;28494:3;:10;28490:1;:14;28470:251;;;28529:17;::::0;28568:6;;-1:-1:-1;;;;;28529:17:71;;::::1;::::0;:38:::1;::::0;28568:3;;28572:1;;28568:6;::::1;;;-1:-1:-1::0;;;28568:6:71::1;;;;;;;;;;;;;;;28529:46;;;;;;;;;;;;;26896:25:122::0;;26884:2;26869:18;;26851:76;28529:46:71::1;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;28525:186;;;28595:17;::::0;28631:6;;-1:-1:-1;;;;;28595:17:71;;::::1;::::0;:35:::1;::::0;28631:3;;28635:1;;28631:6;::::1;;;-1:-1:-1::0;;;28631:6:71::1;;;;;;;;;;;;;;;28595:43;;;;;;;;;;;;;26896:25:122::0;;26884:2;26869:18;;26851:76;28595:43:71::1;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;28661:35;28689:3;28693:1;28689:6;;;;;;-1:-1:-1::0;;;28689:6:71::1;;;;;;;;;;;;;;;28661:35;;;;26896:25:122::0;;26884:2;26869:18;;26851:76;28661:35:71::1;;;;;;;;28525:186;28506:3:::0;::::1;::::0;::::1;:::i;:::-;;;;28470:251;;11466:459:::0;11523:15;-1:-1:-1;;;;;11558:19:71;;11550:74;;;;-1:-1:-1;;;11550:74:71;;22006:2:122;11550:74:71;;;21988:21:122;22045:2;22025:18;;;22018:30;22084:34;22064:18;;;22057:62;-1:-1:-1;;;22135:18:122;;;22128:40;22185:19;;11550:74:71;21978:232:122;11550:74:71;-1:-1:-1;;;;;11698:26:71;;11635:21;11698:26;;;:19;:26;;;;;11635:21;;11698:35;;:33;:35::i;:::-;11670:63;;11749:9;11744:144;11768:17;11764:1;:21;11744:144;;;-1:-1:-1;;;;;11837:26:71;;11823:47;11837:26;;;:19;:26;;;;;11823:13;;:47;11837:32;;11867:1;11837:29;:32::i;:::-;11823:47;;;;;;;;;;;-1:-1:-1;11823:47:71;:54;11806:71;;;;:::i;:::-;;-1:-1:-1;11787:3:71;;;;:::i;:::-;;;;11744:144;;;-1:-1:-1;11905:13:71;;11466:459;-1:-1:-1;;;11466:459:71:o;1908:101:4:-;929:10:25;1488:7:4;:5;:7::i;:::-;-1:-1:-1;;;;;1488:23:4;;1480:68;;;;-1:-1:-1;;;1480:68:4;;;;;;;:::i;:::-;1972:30:::1;1999:1;1972:18;:30::i;:::-;1908:101::o:0;10099:108:71:-;929:10:25;1488:7:4;:5;:7::i;:::-;-1:-1:-1;;;;;1488:23:4;;1480:68;;;;-1:-1:-1;;;1480:68:4;;;;;;;:::i;:::-;10169:22:71::1;:31:::0;10099:108::o;22324:267::-;929:10:25;31655:25:71;;;;:11;:25;;;;;;;;;:52;;;31700:7;:5;:7::i;:::-;-1:-1:-1;;;;;31684:23:71;929:10:25;-1:-1:-1;;;;;31684:23:71;;31655:52;31647:85;;;;-1:-1:-1;;;31647:85:71;;;;;;;:::i;:::-;22420:1:::1;22411:6;:10;22403:19;;;::::0;::::1;;22432:5;::::0;:57:::1;::::0;-1:-1:-1;;;;;22432:5:71::1;22455:10;22475:4;22482:6:::0;22432:22:::1;:57::i;:::-;22525:6;22499:22;;:32;;;;;;;:::i;:::-;::::0;;;-1:-1:-1;;22546:38:71::1;::::0;::::1;::::0;::::1;::::0;22565:10:::1;::::0;22577:6;;22546:38:::1;:::i;13736:544::-:0;1815:1:7;2569:7;;:19;;2561:63;;;;-1:-1:-1;;;2561:63:7;;;;;;;:::i;:::-;1815:1;2699:7;:18;13819:10:71::1;13842:1;13799:31:::0;;;:19:::1;:31;::::0;;;;:40:::1;::::0;:38:::1;:40::i;:::-;:44;13791:77;;;::::0;-1:-1:-1;;;13791:77:71;;20084:2:122;13791:77:71::1;::::0;::::1;20066:21:122::0;20123:2;20103:18;;;20096:30;-1:-1:-1;;;20142:18:122;;;20135:50;20202:18;;13791:77:71::1;20056:170:122::0;13791:77:71::1;13961:10;13878:21;13941:31:::0;;;:19:::1;:31;::::0;;;;13878:21;;13941:40:::1;::::0;:38:::1;:40::i;:::-;13913:68;;13996:9;13991:239;14015:8;:15;14011:1;:19;13991:239;;;14051:15;14069:8;14078:1;14069:11;;;;;;-1:-1:-1::0;;;14069:11:71::1;;;;;;;;;;;;;;;14051:29;;14094:24;14110:7;14094:15;:24::i;:::-;-1:-1:-1::0;14149:22:71::1;::::0;;;:13:::1;:22;::::0;;;;:29;14132:46:::1;::::0;;::::1;:::i;:::-;;;14192:27;14211:7;14192:18;:27::i;:::-;-1:-1:-1::0;14032:3:71;::::1;::::0;::::1;:::i;:::-;;;;13991:239;;;;14239:34;14259:13;14239:19;:34::i;1276:85:4:-:0;1348:6;;-1:-1:-1;;;;;1348:6:4;;1276:85::o;25497:1788:71:-;31816:12;;-1:-1:-1;;;;;31816:12:71;31802:10;:26;31794:60;;;;-1:-1:-1;;;31794:60:71;;23539:2:122;31794:60:71;;;23521:21:122;23578:2;23558:18;;;23551:30;-1:-1:-1;;;23597:18:122;;;23590:51;23658:18;;31794:60:71;23511:171:122;31794:60:71;1815:1:7::1;2569:7;;:19;;2561:63;;;;-1:-1:-1::0;;;2561:63:7::1;;;;;;;:::i;:::-;1815:1;2699:7;:18:::0;25595:14:71::2;::::0;25574:18:::2;::::0;25595::::2;::::0;25612:1:::2;25595:18;:::i;:::-;25574:39;;25624:26;25653:21;:19;:21::i;:::-;25624:50;;25685:29;25717:39;25745:10;25717:27;:39::i;:::-;25685:71:::0;-1:-1:-1;25767:23:71::2;::::0;;;25900;;25896:905:::2;;3630:5;25980:16;;25959:18;:37;;;;:::i;:::-;25958:61;;;;:::i;:::-;25939:81;;26034:52;26063:22;;26045:15;:40;;;;:::i;:::-;26034:10;:52::i;:::-;3630:5;26139:14;;26118:18;:35;;;;:::i;:::-;26117:59;;;;:::i;:::-;26101:75:::0;-1:-1:-1;26194:18:71;;26190:81:::2;;26214:23;::::0;:57:::2;::::0;-1:-1:-1;;;26214:57:71;;::::2;::::0;::::2;26896:25:122::0;;;-1:-1:-1;;;;;26214:23:71;;::::2;::::0;:42:::2;::::0;26869:18:122;;26214:57:71::2;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;26190:81;3630:5;26325:15;;26304:18;:36;;;;:::i;:::-;26303:60;;;;:::i;:::-;26286:77:::0;-1:-1:-1;26381:19:71;;26377:171:::2;;26420:5;::::0;26442:14:::2;::::0;26420:54:::2;::::0;-1:-1:-1;;;26420:54:71;;-1:-1:-1;;;;;26420:5:71;;::::2;::::0;:13:::2;::::0;:54:::2;::::0;26442:14;::::2;::::0;26459;;26420:54:::2;;;:::i;:::-;;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1::0;26492:14:71::2;::::0;:41:::2;::::0;-1:-1:-1;;;26492:41:71;;::::2;::::0;::::2;26896:25:122::0;;;-1:-1:-1;;;;;26492:14:71;;::::2;::::0;:25:::2;::::0;26869:18:122;;26492:41:71::2;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;26377:171;26635:14:::0;26619:13;26580:36:::2;26601:15:::0;26580:18;:36:::2;:::i;:::-;:52;;;;:::i;:::-;:69;;;;:::i;:::-;26561:88;::::0;;::::2;:::i;:::-;;;25896:905;;;26670:22;::::0;:26;26666:135:::2;;26712:34;26723:22;;26712:10;:34::i;:::-;26666:135;;;26777:13;26788:1;26777:10;:13::i;:::-;26827:15;26810:13;;:32;;;;;;;:::i;:::-;::::0;;;-1:-1:-1;;26852:14:71::2;:27:::0;;;26929:126:::2;::::0;;;;::::2;::::0;;;;;::::2;::::0;;::::2;::::0;;;-1:-1:-1;26889:37:71;;;:21:::2;:37:::0;;;;;:166;;;;;::::2;::::0;;::::2;::::0;;;;27070:208;;28169:25:122;;;28210:18;;;28203:34;;;28253:18;;;28246:34;;;28311:2;28296:18;;28289:34;;;28354:3;28339:19;;28332:35;;;28398:3;28383:19;;28376:35;;;27070:208:71::2;::::0;28156:3:122;28141:19;27070:208:71::2;;;;;;;-1:-1:-1::0;;1772:1:7::1;2872:7;:22:::0;-1:-1:-1;;;;;25497:1788:71:o;15613:551::-;1815:1:7;2569:7;;:19;;2561:63;;;;-1:-1:-1;;;2561:63:7;;;;;;;:::i;:::-;1815:1;2699:7;:18;15700:17:71;;15692:56:::1;;;::::0;-1:-1:-1;;;15692:56:71;;21655:2:122;15692:56:71::1;::::0;::::1;21637:21:122::0;21694:2;21674:18;;;21667:30;-1:-1:-1;;;21713:18:122;;;21706:52;21775:18;;15692:56:71::1;21627:172:122::0;15692:56:71::1;15758:21;15798:9:::0;15793:321:::1;15817:10;:17;15813:1;:21;15793:321;;;15855:15;15873:16;:31;15890:10;15901:1;15890:13;;;;;;-1:-1:-1::0;;;15890:13:71::1;;;;;;;;;;::::0;;::::1;::::0;;;;;;;15873:31;;;;::::1;::::0;;;;;;;;-1:-1:-1;15873:31:71;;;;15926:22;;;:13:::1;:22:::0;;;;:28:::1;;::::0;15873:31;;-1:-1:-1;;;;;;15926:28:71::1;15958:10;15926:42;15918:69;;;::::0;-1:-1:-1;;;15918:69:71;;19392:2:122;15918:69:71::1;::::0;::::1;19374:21:122::0;19431:2;19411:18;;;19404:30;-1:-1:-1;;;19450:18:122;;;19443:44;19504:18;;15918:69:71::1;19364:164:122::0;15918:69:71::1;16001:35;16022:10;16033:1;16022:13;;;;;;-1:-1:-1::0;;;16022:13:71::1;;;;;;;;;;;;;;;16001:20;:35::i;:::-;;16050:24;16060:10;16071:1;16060:13;;;;;;-1:-1:-1::0;;;16060:13:71::1;;;;;;;;;;;;;;;16050:9;:24::i;:::-;16088:15:::0;::::1;::::0;::::1;:::i;:::-;;;;15793:321;15836:3;;;;;:::i;:::-;;;;15793:321;;;;16123:34;16143:13;16123:19;:34::i;24432:102::-:0;929:10:25;31655:25:71;;;;:11;:25;;;;;;;;;:52;;;31700:7;:5;:7::i;:::-;-1:-1:-1;;;;;31684:23:71;929:10:25;-1:-1:-1;;;;;31684:23:71;;31655:52;31647:85;;;;-1:-1:-1;;;31647:85:71;;;;;;;:::i;:::-;24501:16:::1;:26:::0;;-1:-1:-1;;24501:26:71::1;::::0;::::1;;::::0;;;::::1;::::0;;24432:102::o;30205:297::-;929:10:25;1488:7:4;:5;:7::i;:::-;-1:-1:-1;;;;;1488:23:4;;1480:68;;;;-1:-1:-1;;;1480:68:4;;;;;;;:::i;:::-;-1:-1:-1;;;;;30286:20:71;::::1;30278:29;;;::::0;::::1;;30317:17;:52:::0;;-1:-1:-1;;;;;;30317:52:71::1;-1:-1:-1::0;;;;;30317:52:71;;::::1;::::0;;::::1;::::0;;;30442:5:::1;::::0;:53:::1;::::0;-1:-1:-1;;;30442:53:71;;:5;::::1;::::0;:13:::1;::::0;:53:::1;::::0;-1:-1:-1;;30484:10:71;30442:53:::1;;;:::i;23057:391::-:0;929:10:25;31655:25:71;;;;:11;:25;;;;;;;;;:52;;;31700:7;:5;:7::i;:::-;-1:-1:-1;;;;;31684:23:71;929:10:25;-1:-1:-1;;;;;31684:23:71;;31655:52;31647:85;;;;-1:-1:-1;;;31647:85:71;;;;;;;:::i;:::-;23166:14:::1;:27:::0;;;23203:29:::1;23235:39;23183:10:::0;23235:27:::1;:39::i;:::-;23324:117;::::0;;;;::::1;::::0;;;;;::::1;::::0;;::::1;::::0;;;23306:14:::1;::::0;-1:-1:-1;23284:37:71;;;:21:::1;:37:::0;;;;:157;;;;;::::1;::::0;;::::1;::::0;;;;-1:-1:-1;;23057:391:71:o;31034:386::-;929:10:25;1488:7:4;:5;:7::i;:::-;-1:-1:-1;;;;;1488:23:4;;1480:68;;;;-1:-1:-1;;;1480:68:4;;;;;;;:::i;:::-;3630:5:71::1;31242:15:::0;31203:36:::1;31223:16:::0;31203:17;:36:::1;:::i;:::-;:54;;;;:::i;:::-;:77;;31195:86;;;::::0;::::1;;31291:16;:36:::0;;;;31337:15:::1;:34:::0;31381:14:::1;:32:::0;31034:386::o;24594:110::-;929:10:25;31655:25:71;;;;:11;:25;;;;;;;;;:52;;;31700:7;:5;:7::i;:::-;-1:-1:-1;;;;;31684:23:71;929:10:25;-1:-1:-1;;;;;31684:23:71;;31655:52;31647:85;;;;-1:-1:-1;;;31647:85:71;;;;;;;:::i;:::-;24691:6:::1;24674:13;;:23;;;;;;;:::i;:::-;::::0;;;-1:-1:-1;;;24594:110:71:o;5496:1450::-;3111:19:5;3133:25;3156:1;3133:22;:25::i;:::-;3111:47;;3172:14;3168:65;;;3202:13;:20;;-1:-1:-1;;3202:20:5;;;;;3168:65;5877:22:71::1;:20;:22::i;:::-;5909:21;:19;:21::i;:::-;5940:16;:14;:16::i;:::-;5966:24;:22;:24::i;:::-;6001:5;:40:::0;;-1:-1:-1;;;;;6001:40:71;;::::1;-1:-1:-1::0;;;;;;6001:40:71;;::::1;;::::0;;;6051:15:::1;:64:::0;;;;::::1;::::0;;::::1;;::::0;;6125:10:::1;:46:::0;;;;::::1;::::0;::::1;::::0;;;::::1;::::0;;6181:13:::1;:30:::0;;;6242:15:::1;6221:18;:36:::0;6001:5:::1;6267:11;:15:::0;;;6292:14:::1;:32:::0;;;6334:14:::1;:18:::0;6362:16:::1;:36:::0;;;6408:15:::1;:34:::0;;;6452:14:::1;:32:::0;;;6498:45;::::1;6494:222;;6557:23;:63:::0;;-1:-1:-1;;;;;;6557:63:71::1;6614:4;6557:63;::::0;;6494:222:::1;;;6635:23;:81:::0;;-1:-1:-1;;;;;;6635:81:71::1;-1:-1:-1::0;;;;;6635:81:71;::::1;;::::0;;6494:222:::1;3630:5;6772:14;;6754:15;;6735:16;;:34;;;;:::i;:::-;:51;;;;:::i;:::-;:74;;6727:83;;;::::0;::::1;;6910:15;::::0;6884:5:::1;::::0;:55:::1;::::0;-1:-1:-1;;;;;6884:5:71;;::::1;::::0;6910:15:::1;-1:-1:-1::0;;6884:17:71::1;:55::i;:::-;3257:14:5::0;3253:99;;;3303:5;3287:21;;-1:-1:-1;;3287:21:5;;;3327:14;;-1:-1:-1;17169:36:122;;3327:14:5;;17157:2:122;17142:18;3327:14:5;;;;;;;3253:99;5496:1450:71;;;;;;;;;;:::o;27392:150::-;929:10:25;31655:25:71;;;;:11;:25;;;;;;;;;:52;;;31700:7;:5;:7::i;:::-;-1:-1:-1;;;;;31684:23:71;929:10:25;-1:-1:-1;;;;;31684:23:71;;31655:52;31647:85;;;;-1:-1:-1;;;31647:85:71;;;;;;;:::i;:::-;27460:20:::1;27483:19;:10;:17;:19::i;:::-;27460:42;;27512:23;27531:3;27512:18;:23::i;21944:293::-:0;929:10:25;31655:25:71;;;;:11;:25;;;;;;;;;:52;;;31700:7;:5;:7::i;:::-;-1:-1:-1;;;;;31684:23:71;929:10:25;-1:-1:-1;;;;;31684:23:71;;31655:52;31647:85;;;;-1:-1:-1;;;31647:85:71;;;;;;;:::i;:::-;22062:1:::1;22053:6;:10;22045:19;;;::::0;::::1;;22074:5;::::0;:57:::1;::::0;-1:-1:-1;;;;;22074:5:71::1;22097:10;22117:4;22124:6:::0;22074:22:::1;:57::i;:::-;22141:30;22158:6;22166:4;22141:16;:30::i;:::-;;22186:44;22205:10;22217:6;22225:4;22186:44;;;;;;;;:::i;29654:186::-:0;929:10:25;1488:7:4;:5;:7::i;:::-;-1:-1:-1;;;;;1488:23:4;;1480:68;;;;-1:-1:-1;;;1480:68:4;;;;;;;:::i;:::-;-1:-1:-1;;;;;29732:19:71;::::1;29724:28;;;::::0;::::1;;29762:30;:71:::0;;-1:-1:-1;;;;;;29762:71:71::1;-1:-1:-1::0;;;;;29762:71:71;;;::::1;::::0;;;::::1;::::0;;29654:186::o;23984:118::-;929:10:25;31655:25:71;;;;:11;:25;;;;;;;;;:52;;;31700:7;:5;:7::i;:::-;-1:-1:-1;;;;;31684:23:71;929:10:25;-1:-1:-1;;;;;31684:23:71;;31655:52;31647:85;;;;-1:-1:-1;;;31647:85:71;;;;;;;:::i;:::-;24066:14:::1;:29:::0;23984:118::o;17498:314::-;17565:7;;;17617:161;-1:-1:-1;;;;;17641:25:71;;;;;;:19;:25;;;;;:34;;:32;:34::i;:::-;17637:1;:38;17617:161;;;-1:-1:-1;;;;;17735:25:71;;;;;;:19;:25;;;;;17711:56;;17735:31;;17764:1;17735:28;:31::i;:::-;17711:23;:56::i;:::-;17696:71;;;;:::i;:::-;;-1:-1:-1;17677:3:71;;;;:::i;:::-;;;;17617:161;;;-1:-1:-1;17794:11:71;17498:314;-1:-1:-1;;17498:314:71:o;13227:433::-;1815:1:7;2569:7;;:19;;2561:63;;;;-1:-1:-1;;;2561:63:7;;;;;;;:::i;:::-;1815:1;2699:7;:18;13283:16:71::1;::::0;::::1;;13279:109;;;13315:62;::::0;-1:-1:-1;;;13315:62:71;;18564:2:122;13315:62:71::1;::::0;::::1;18546:21:122::0;18603:2;18583:18;;;18576:30;18642:34;18622:18;;;18615:62;-1:-1:-1;;;18693:18:122;;;18686:50;18753:19;;13315:62:71::1;18536:242:122::0;13279:109:71::1;13398:19;13437:9:::0;13432:165:::1;13476:10;13456:31;::::0;;;:19:::1;:31;::::0;;;;:40:::1;::::0;:38:::1;:40::i;:::-;13452:1;:44;13432:165;;;13568:10;13548:31;::::0;;;:19:::1;:31;::::0;;;;13532:54:::1;::::0;13548:37:::1;::::0;13583:1;13548:34:::1;:37::i;:::-;13532:15;:54::i;:::-;13517:69;::::0;;::::1;:::i;:::-;::::0;-1:-1:-1;13498:3:71;::::1;::::0;::::1;:::i;:::-;;;;13432:165;;;;13629:1;13615:11;:15;13607:46;;;::::0;-1:-1:-1;;;13607:46:71;;25472:2:122;13607:46:71::1;::::0;::::1;25454:21:122::0;25511:2;25491:18;;;25484:30;-1:-1:-1;;;25530:18:122;;;25523:48;25588:18;;13607:46:71::1;25444:168:122::0;13607:46:71::1;-1:-1:-1::0;1772:1:7;2872:7;:22;13227:433:71:o;29059:218::-;929:10:25;31655:25:71;;;;:11;:25;;;;;;;;;:52;;;31700:7;:5;:7::i;:::-;-1:-1:-1;;;;;31684:23:71;929:10:25;-1:-1:-1;;;;;31684:23:71;;31655:52;31647:85;;;;-1:-1:-1;;;31647:85:71;;;;;;;:::i;:::-;29233:37:::1;::::0;;;;::::1;::::0;;;;;::::1;::::0;;::::1;::::0;;;-1:-1:-1;29204:26:71;;;:21:::1;:26:::0;;;;;:66;;;;;;::::1;::::0;;::::1;::::0;29059:218::o;29342:209::-;929:10:25;31655:25:71;;;;:11;:25;;;;;;;;;:52;;;31700:7;:5;:7::i;:::-;-1:-1:-1;;;;;31684:23:71;929:10:25;-1:-1:-1;;;;;31684:23:71;;31655:52;31647:85;;;;-1:-1:-1;;;31647:85:71;;;;;;;:::i;:::-;-1:-1:-1;;;;;29437:30:71;::::1;29429:39;;;::::0;::::1;;29478:14;:66:::0;;-1:-1:-1;;;;;;29478:66:71::1;-1:-1:-1::0;;;;;29478:66:71;;;::::1;::::0;;;::::1;::::0;;29342:209::o;10954:431::-;11024:4;11043:33;11080:15;11089:5;11080:8;:15::i;:::-;11040:55;;;11110:9;11105:252;11129:14;:21;11125:1;:25;11105:252;;;11176:9;11171:176;11195:14;11210:1;11195:17;;;;;;-1:-1:-1;;;11195:17:71;;;;;;;;;;;;;;;:24;11191:1;:28;11171:176;;;11272:7;11248:14;11263:1;11248:17;;;;;;-1:-1:-1;;;11248:17:71;;;;;;;;;;;;;;;11266:1;11248:20;;;;;;-1:-1:-1;;;11248:20:71;;;;;;;;;;;;;;;:31;11244:89;;;11310:4;11303:11;;;;;;;11244:89;11221:3;;;;:::i;:::-;;;;11171:176;;;-1:-1:-1;11152:3:71;;;;:::i;:::-;;;;11105:252;;;-1:-1:-1;11373:5:71;;10954:431;-1:-1:-1;;;;10954:431:71:o;24766:112::-;929:10:25;31655:25:71;;;;:11;:25;;;;;;;;;:52;;;31700:7;:5;:7::i;:::-;-1:-1:-1;;;;;31684:23:71;929:10:25;-1:-1:-1;;;;;31684:23:71;;31655:52;31647:85;;;;-1:-1:-1;;;31647:85:71;;;;;;;:::i;:::-;24865:6:::1;24848:13;;:23;;;;;;;:::i;27548:467::-:0;929:10:25;31655:25:71;;;;:11;:25;;;;;;;;;:52;;;31700:7;:5;:7::i;:::-;-1:-1:-1;;;;;31684:23:71;929:10:25;-1:-1:-1;;;;;31684:23:71;;31655:52;31647:85;;;;-1:-1:-1;;;31647:85:71;;;;;;;:::i;:::-;27631:20:::1;:18;:20::i;:::-;27682:1;27669:3;:10;:14;27661:69;;;::::0;-1:-1:-1;;;27661:69:71;;18153:2:122;27661:69:71::1;::::0;::::1;18135:21:122::0;18192:2;18172:18;;;18165:30;18231:34;18211:18;;;18204:62;-1:-1:-1;;;18282:18:122;;;18275:40;18332:19;;27661:69:71::1;18125:232:122::0;27661:69:71::1;27745:9;27740:269;27764:3;:10;27760:1;:14;27740:269;;;27799:17;::::0;27829:6;;-1:-1:-1;;;;;27799:17:71;;::::1;::::0;:29:::1;::::0;27829:3;;27833:1;;27829:6;::::1;;;-1:-1:-1::0;;;27829:6:71::1;;;;;;;;;;;;;;;27799:37;;;;;;;;;;;;;26896:25:122::0;;26884:2;26869:18;;26851:76;27799:37:71::1;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;27795:204;;;27856:17;::::0;27883:6;;-1:-1:-1;;;;;27856:17:71;;::::1;::::0;:26:::1;::::0;27883:3;;27887:1;;27883:6;::::1;;;-1:-1:-1::0;;;27883:6:71::1;;;;;;;;;;;;;;;27856:34;;;;;;;;;;;;;26896:25:122::0;;26884:2;26869:18;;26851:76;27856:34:71::1;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;27908:25;27926:3;27930:1;27926:6;;;;;;-1:-1:-1::0;;;27926:6:71::1;;;;;;;;;;;;;;;27908:10;:17;;:25;;;;:::i;:::-;;27956:28;27977:3;27981:1;27977:6;;;;;;-1:-1:-1::0;;;27977:6:71::1;;;;;;;;;;;;;;;27956:28;;;;26896:25:122::0;;26884:2;26869:18;;26851:76;27956:28:71::1;;;;;;;;27795:204;27776:3:::0;::::1;::::0;::::1;:::i;:::-;;;;27740:269;;2158:198:4::0;929:10:25;1488:7:4;:5;:7::i;:::-;-1:-1:-1;;;;;1488:23:4;;1480:68;;;;-1:-1:-1;;;1480:68:4;;;;;;;:::i;:::-;-1:-1:-1;;;;;2246:22:4;::::1;2238:73;;;::::0;-1:-1:-1;;;2238:73:4;;18985:2:122;2238:73:4::1;::::0;::::1;18967:21:122::0;19024:2;19004:18;;;18997:30;19063:34;19043:18;;;19036:62;-1:-1:-1;;;19114:18:122;;;19107:36;19160:19;;2238:73:4::1;18957:228:122::0;2238:73:4::1;2321:28;2340:8;2321:18;:28::i;31491:113:71:-:0;929:10:25;1488:7:4;:5;:7::i;:::-;-1:-1:-1;;;;;1488:23:4;;1480:68;;;;-1:-1:-1;;;1480:68:4;;;;;;;:::i;:::-;-1:-1:-1;;;;;31571:17:71;;;::::1;;::::0;;;:11:::1;:17;::::0;;;;:26;;-1:-1:-1;;31571:26:71::1;::::0;::::1;;::::0;;;::::1;::::0;;31491:113::o;24177:195::-;929:10:25;24254:7:71;31655:25;;;:11;:25;;;;;;;;;:52;;;31700:7;:5;:7::i;:::-;-1:-1:-1;;;;;31684:23:71;929:10:25;-1:-1:-1;;;;;31684:23:71;;31655:52;31647:85;;;;-1:-1:-1;;;31647:85:71;;;;;;;:::i;:::-;24273:15:::1;24291:25:::0;;;:16:::1;:25;::::0;;;;;24333:32:::1;24291:25:::0;24333:23:::1;:32::i;:::-;24326:39:::0;24177:195;-1:-1:-1;;;24177:195:71:o;29942:158::-;929:10:25;1488:7:4;:5;:7::i;:::-;-1:-1:-1;;;;;1488:23:4;;1480:68;;;;-1:-1:-1;;;1480:68:4;;;;;;;:::i;:::-;-1:-1:-1;;;;;30027:27:71;::::1;30019:36;;;::::0;::::1;;30065:12;:28:::0;;-1:-1:-1;;;;;;30065:28:71::1;-1:-1:-1::0;;;;;30065:28:71;;;::::1;::::0;;;::::1;::::0;;29942:158::o;11950:254:34:-;12010:16;12038:22;12063:19;12071:3;12063:7;:19::i;745:216:16:-;868:86;888:5;918:23;;;943:2;947:5;895:58;;;;;;;;;:::i;:::-;;;;-1:-1:-1;;895:58:16;;;;;;;;;;;;;;-1:-1:-1;;;;;895:58:16;-1:-1:-1;;;;;;895:58:16;;;;;;;;;;868:19;:86::i;10821:112:34:-;10881:7;10907:19;10915:3;4054:18;;3972:107;11275:135;11346:7;11380:22;11384:3;11396:5;11380:3;:22::i;10083:129::-;10150:4;10173:32;10178:3;10198:5;10173:4;:32::i;2510:187:4:-;2602:6;;;-1:-1:-1;;;;;2618:17:4;;;-1:-1:-1;;;;;;2618:17:4;;;;;;;2650:40;;2602:6;;;2618:17;2602:6;;2650:40;;2583:16;;2650:40;2510:187;;:::o;967:252:16:-;1116:96;1136:5;1166:27;;;1195:4;1201:2;1205:5;1143:68;;;;;;;;;;:::i;1116:96::-;967:252;;;;:::o;15068:477:71:-;15128:7;15155:12;15147:42;;;;-1:-1:-1;;;15147:42:71;;22832:2:122;15147:42:71;;;22814:21:122;22871:2;22851:18;;;22844:30;-1:-1:-1;;;22890:18:122;;;22883:47;22947:18;;15147:42:71;22804:167:122;15147:42:71;15199:26;15228:22;;;:13;:22;;;;;;15282:32;15242:7;15282:23;:32::i;:::-;15260:54;;15343:11;15324:15;;:30;;;;;;;:::i;:::-;;;;-1:-1:-1;;15387:14:71;;15364:20;;;:37;15430:11;;;;15411:5;;:44;;-1:-1:-1;;;;;15411:5:71;;;;15430:11;15443;15411:18;:44::i;:::-;15476:11;;;;15470:40;;;;;;-1:-1:-1;;;;;15476:11:71;;;;15489:7;;15498:11;;15470:40;:::i;:::-;;;;;;;;15527:11;15068:477;-1:-1:-1;;;15068:477:71:o;14286:776::-;14350:26;14379:22;;;:13;:22;;;;;14411:24;14393:7;14411:15;:24::i;:::-;;14450:9;14445:281;14469:32;;;;:23;:32;;;;;:41;;:39;:41::i;:::-;14465:1;:45;14445:281;;;14531:10;;14574:11;;;;14531:10;14587:32;;;:23;:32;;;;;-1:-1:-1;;;;;14531:10:71;;;;:27;;14567:4;;14574:11;;;14587:38;;14623:1;14587:35;:38::i;:::-;14531:95;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;14654:11:71;;;;;14676:32;;;:23;:32;;;;;14645:70;;-1:-1:-1;;;;;14654:11:71;;14676:32;;:38;;14712:1;14676:35;:38::i;:::-;14645:70;;;;;;;;:::i;:::-;;;;;;;;14512:3;;;;:::i;:::-;;;;14445:281;;;;14810:8;14781:14;;14799:8;14780:27;;;;:::i;:::-;14763:14;;:44;;;;:::i;:::-;:55;;;;:::i;:::-;14739:5;:20;;;:80;14735:175;;;14858:32;;;;:23;:32;;;;;:41;;:39;:41::i;:::-;14835:19;;:64;;;;;;;:::i;:::-;;;;-1:-1:-1;;14735:175:71;14939:11;;;;-1:-1:-1;;;;;14939:11:71;14919:32;;;;:19;:32;;;;;:48;;14959:7;14919:39;:48::i;:::-;-1:-1:-1;14984:22:71;;;;:13;:22;;;;;;;;14977:29;;;;;;;;;;;;;;-1:-1:-1;;;;;;14977:29:71;;;;;;;;15023:23;:32;;;;;;;14984:22;15016:39;15023:32;14984:22;15016:39;:::i;:::-;;;;;14286:776;;:::o;16170:777::-;16246:33;16282:13;;16299:3;16282:20;:148;;16381:30;;;;;;;;;-1:-1:-1;;;;;16381:30:71;-1:-1:-1;;;;;16381:47:71;;:49;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;16282:148;;;16317:30;;;;;;;;;-1:-1:-1;;;;;16317:30:71;-1:-1:-1;;;;;16317:47:71;;:49;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;16469:30;;:67;;-1:-1:-1;;;16469:67:71;;16246:184;;-1:-1:-1;16440:26:71;;-1:-1:-1;;;;;16469:30:71;;;;:55;;:67;;16525:10;;16469:67;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;16572:30;;:58;;-1:-1:-1;;;16572:58:71;;16440:96;;-1:-1:-1;16546:23:71;;-1:-1:-1;;;;;16572:30:71;;;;:46;;:58;;16619:10;;16572:58;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;16546:84;-1:-1:-1;16640:34:71;16724:18;16678:41;16706:13;16678:25;:41;:::i;:::-;16677:65;;:158;;16834:1;16677:158;;;16778:41;16806:13;16778:25;:41;:::i;:::-;16757:62;;:18;:62;:::i;:::-;16640:195;;16872:26;16853:15;:45;;16845:95;;;;-1:-1:-1;;;16845:95:71;;21249:2:122;16845:95:71;;;21231:21:122;21288:2;21268:18;;;21261:30;21327:34;21307:18;;;21300:62;-1:-1:-1;;;21378:18:122;;;21371:35;21423:19;;16845:95:71;21221:227:122;16845:95:71;16170:777;;;;;:::o;20051:370::-;20099:14;20125:20;20148:19;:10;:17;:19::i;:::-;20125:42;;20182:9;20177:238;20201:3;:10;20197:1;:14;20177:238;;;20254:17;;20293:6;;20233:15;;-1:-1:-1;;;;;20254:17:71;;:38;;20293:3;;20297:1;;20293:6;;;;-1:-1:-1;;;20293:6:71;;;;;;;;;;;;;;;20254:46;;;;;;;;;;;;;26896:25:122;;26884:2;26869:18;;26851:76;20254:46:71;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;20232:68:71;-1:-1:-1;20318:11:71;;20314:91;;20359:17;;20383:6;;-1:-1:-1;;;;;20359:17:71;;;;:23;;20383:3;;20387:1;;20383:6;;;;-1:-1:-1;;;20383:6:71;;;;;;;;;;;;;;;20359:31;;;;;;;;;;;;;26896:25:122;;26884:2;26869:18;;26851:76;20359:31:71;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;20349:41;;;;:::i;:::-;;;20314:91;-1:-1:-1;20213:3:71;;;;:::i;:::-;;;;20177:238;;;;20051:370;;:::o;19376:669::-;19540:19;;19508:14;;19450:7;19486:37;;;:21;:37;;;;;:51;;;19450:7;;;;19486:73;;19540:19;19486:73;:::i;:::-;19591:1;19569:19;:23;;;19469:90;;-1:-1:-1;19632:21:71;:10;19645:8;19632:21;:::i;:::-;19615:14;;:38;;;;:::i;:::-;19675:20;;19602:51;;-1:-1:-1;19705:277:71;19717:11;;19712:1;:16;19705:277;;19753:16;;;;:13;:16;;;;;:31;;;19749:83;;19809:8;;19749:83;19849:16;;;;:13;:16;;;;;:31;;;:36;-1:-1:-1;19845:80:71;;;19905:5;;19845:80;19948:16;;;;:13;:16;;;;;:23;19938:33;;;;:::i;:::-;;;19705:277;19730:3;;;;:::i;:::-;;;;19705:277;;;19991:20;:24;-1:-1:-1;20032:6:71;19376:669;-1:-1:-1;;19376:669:71:o;20427:1059::-;20517:15;20491:22;;:41;;;;;;;:::i;:::-;;;;-1:-1:-1;;20546:22:71;;:27;:57;;;;;20602:1;20577:22;;:26;20546:57;20542:902;;;20644:65;20661:22;;3699:24;20644:16;:65::i;:::-;20619:22;:90;20748:1;20723:22;:26;20542:902;;;20795:1;20770:22;;:26;:83;;;;-1:-1:-1;20800:17:71;;20830:22;;20800:53;;-1:-1:-1;;;20800:53:71;;-1:-1:-1;;;;;20800:17:71;;;;:29;;:53;;;;26896:25:122;;;26884:2;26869:18;;26851:76;20800:53:71;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;20766:678;;;20889:17;;20916:22;;20889:50;;-1:-1:-1;;;20889:50:71;;;;;26896:25:122;;;;20869:17:71;;-1:-1:-1;;;;;20889:17:71;;:26;;26869:18:122;;20889:50:71;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;20869:70;;20953:41;20971:22;;20953:10;:17;;:41;;;;:::i;:::-;;21008:17;21040:22;;21028:9;:34;;;;:::i;:::-;21008:54;;21101:52;21118:9;3699:24;21101:16;:52::i;:::-;21076:22;:77;;;21167:38;;:10;;:14;:38::i;:::-;-1:-1:-1;;21244:1:71;21219:22;:26;-1:-1:-1;20766:678:71;;;21291:1;21266:22;;:26;:92;;;;-1:-1:-1;21296:17:71;;21335:22;;21296:62;;-1:-1:-1;;;21296:62:71;;-1:-1:-1;;;;;21296:17:71;;;;:38;;:62;;;;26896:25:122;;;26884:2;26869:18;;26851:76;21296:62:71;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;21262:182;;;21374:17;;21410:22;;21374:59;;-1:-1:-1;;;21374:59:71;;-1:-1:-1;;;;;21374:17:71;;;;:35;;:59;;;;26896:25:122;;;26884:2;26869:18;;26851:76;21374:59:71;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;21262:182;-1:-1:-1;21478:1:71;21453:22;:26;20427:1059::o;16953:465::-;17020:7;17057:27;;;:16;:27;;;;;;;;;17123:22;;;:13;:22;;;;;17214:12;;17020:7;;17179:32;17057:27;17179:23;:32::i;:::-;:47;;;;:::i;:::-;17155:71;;17255:13;17236:15;;:32;;;;;;;:::i;:::-;;;;-1:-1:-1;;17297:11:71;;;;17278:5;;:46;;-1:-1:-1;;;;;17278:5:71;;;;17297:11;17310:13;17278:18;:46::i;:::-;17345:11;;;;17339:42;;;;;;-1:-1:-1;;;;;17345:11:71;;;;17358:7;;17367:13;;17339:42;:::i;:::-;;;;;;;;17398:13;16953:465;-1:-1:-1;;;;16953:465:71:o;18436:724::-;18493:15;18511:27;;;:16;:27;;;;;;;;;18577:22;;;:13;:22;;;;;;;18610:10;;18653:11;;;;18610:66;;-1:-1:-1;;;18610:66:71;;18511:27;;18577:22;;-1:-1:-1;;;;;18610:10:71;;;;:27;;:66;;18646:4;;18653:11;;;;18528:9;;18610:66;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;18687:14:71;;;-1:-1:-1;18687:5:71;;-1:-1:-1;18687:12:71;:14;;;:::i;:::-;;;;-1:-1:-1;;18718:27:71;;;;:16;:27;;;;;;;;18711:34;;;18755:32;;;:23;:32;;;;;:50;;18735:9;18755:39;:50::i;:::-;;18890:8;18861:14;;18879:8;18860:27;;;;:::i;:::-;18843:14;;:44;;;;:::i;:::-;:55;;;;:::i;:::-;18819:5;:20;;;:80;18815:135;;;18938:1;18915:19;;:24;;;;;;;:::i;:::-;;;;-1:-1:-1;;18815:135:71;18963:12;;18959:139;;19016:11;;;;-1:-1:-1;;;;;19016:11:71;18996:32;;;;:19;:32;;;;;:48;;19036:7;18996:39;:48::i;:::-;-1:-1:-1;19065:22:71;;;;:13;:22;;;;;19058:29;;;;;;;;;;;;;;-1:-1:-1;;;;;;19058:29:71;;;;;;18959:139;19121:11;;;;19112:41;;;;;;-1:-1:-1;;;;;19121:11:71;;;;19134:7;;19143:9;;19112:41;:::i;:::-;;;;;;;;18436:724;;;:::o;5287:808:5:-;5351:4;5684:13;;;;;;;5680:409;;;5738:7;:12;;5749:1;5738:12;:61;;;;-1:-1:-1;5793:4:5;1476:19:24;:23;5738:61:5;5713:166;;;;-1:-1:-1;;;5713:166:5;;;;;;;:::i;:::-;-1:-1:-1;5900:5:5;;5287:808;-1:-1:-1;5287:808:5:o;5680:409::-;5944:12;;:22;;;;:12;;:22;5936:81;;;;-1:-1:-1;;;5936:81:5;;;;;;;:::i;:::-;-1:-1:-1;6031:12:5;:22;;-1:-1:-1;;6031:22:5;;;;;;;;;;;;-1:-1:-1;;5287:808:5:o;596:65:13:-;4698:13:5;;;;;;;4690:69;;;;-1:-1:-1;;;4690:69:5;;;;;;;:::i;988:95:4:-;4698:13:5;;;;;;;4690:69;;;;-1:-1:-1;;;4690:69:5;;;;;;;:::i;:::-;1050:26:4::1;:24;:26::i;1853:111:7:-:0;4698:13:5;;;;;;;4690:69;;;;-1:-1:-1;;;4690:69:5;;;;;;;:::i;:::-;1923:34:7::1;:32;:34::i;1479:614:16:-:0;1845:10;;;1844:62;;-1:-1:-1;1861:39:16;;-1:-1:-1;;;1861:39:16;;1885:4;1861:39;;;10245:34:122;-1:-1:-1;;;;;10315:15:122;;;10295:18;;;10288:43;1861:15:16;;;;;10180:18:122;;1861:39:16;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;:44;1844:62;1823:163;;;;-1:-1:-1;;;1823:163:16;;26529:2:122;1823:163:16;;;26511:21:122;26568:2;26548:18;;;26541:30;26607:34;26587:18;;;26580:62;-1:-1:-1;;;26658:18:122;;;26651:52;26720:19;;1823:163:16;26501:244:122;1823:163:16;1996:90;2016:5;2046:22;;;2070:7;2079:5;2023:62;;;;;;;;;:::i;19166:204:71:-;19289:17;;:39;;-1:-1:-1;;;19289:39:71;;19248:17;;-1:-1:-1;;;;;19289:17:71;;:25;;:39;;19315:6;;19323:4;;19289:39;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;19277:51;-1:-1:-1;19338:25:71;:10;19277:51;19338:14;:25::i;17818:612::-;17891:7;17971:22;;;:13;:22;;;;;;;;17943:50;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;17943:50:71;;;;;;;;;;;;;;;;;18032:14;;17891:7;;17943:50;18008:38;18004:52;;;-1:-1:-1;18055:1:71;;17818:612;-1:-1:-1;;;17818:612:71:o;18004:52::-;18072:9;18084:5;:20;;;18107:1;18084:24;;;;:::i;:::-;18072:36;;18067:329;18115:14;;18110:1;:19;18067:329;;18154:24;;;;:21;:24;;;;;:38;;;:43;;:64;;-1:-1:-1;18201:12:71;;:17;18154:64;18150:78;;;18220:8;;18150:78;18373:12;;;18315:24;;;:21;:24;;;;;:38;;;;18274;;:79;;18315:38;18274:79;:::i;:::-;18273:112;;;;:::i;:::-;18242:143;;;;:::i;:::-;;;18067:329;18131:3;;;;:::i;:::-;;;;18067:329;;10380:135:34;10450:4;10473:35;10481:3;10501:5;10473:7;:35::i;5079:109::-;5135:16;5170:3;:11;;5163:18;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;5079:109;;;:::o;3306:717:16:-;3736:23;3762:69;3790:4;3762:69;;;;;;;;;;;;;;;;;3770:5;-1:-1:-1;;;;;3762:27:16;;;:69;;;;;:::i;:::-;3845:17;;3736:95;;-1:-1:-1;3845:21:16;3841:176;;3940:10;3929:30;;;;;;;;;;;;:::i;:::-;3921:85;;;;-1:-1:-1;;;3921:85:16;;25061:2:122;3921:85:16;;;25043:21:122;25100:2;25080:18;;;25073:30;25139:34;25119:18;;;25112:62;-1:-1:-1;;;25190:18:122;;;25183:40;25240:19;;3921:85:16;25033:232:122;4421:118:34;4488:7;4514:3;:11;;4526:5;4514:18;;;;;;-1:-1:-1;;;4514:18:34;;;;;;;;;;;;;;;;;4507:25;;4421:118;;;;:::o;1723:404::-;1786:4;3860:19;;;:12;;;:19;;;;;;1802:319;;-1:-1:-1;1844:23:34;;;;;;;;:11;:23;;;;;;;;;;;;;2024:18;;2002:19;;;:12;;;:19;;;;;;:40;;;;2056:11;;1802:319;-1:-1:-1;2105:5:34;2098:12;;1089:111:4;4698:13:5;;;;;;;4690:69;;;;-1:-1:-1;;;4690:69:5;;;;;;;:::i;:::-;1161:32:4::1;929:10:25::0;1161:18:4::1;:32::i;1970:109:7:-:0;4698:13:5;;;;;;;4690:69;;;;-1:-1:-1;;;4690:69:5;;;;;;;:::i;:::-;1772:1:7::1;2050:7;:22:::0;1970:109::o;2295:1388:34:-;2361:4;2498:19;;;:12;;;:19;;;;;;2532:15;;2528:1149;;2901:21;2925:14;2938:1;2925:10;:14;:::i;:::-;2973:18;;2901:38;;-1:-1:-1;2953:17:34;;2973:22;;2994:1;;2973:22;:::i;:::-;2953:42;;3027:13;3014:9;:26;3010:398;;3060:17;3080:3;:11;;3092:9;3080:22;;;;;;-1:-1:-1;;;3080:22:34;;;;;;;;;;;;;;;;;3060:42;;3231:9;3202:3;:11;;3214:13;3202:26;;;;;;-1:-1:-1;;;3202:26:34;;;;;;;;;;;;;;;;;;;;:38;;;;3314:23;;;:12;;;:23;;;;;:36;;;3010:398;3486:17;;:3;;:17;;;-1:-1:-1;;;3486:17:34;;;;;;;;;;;;;;;;;;;;;;;;;;3578:3;:12;;:19;3591:5;3578:19;;;;;;;;;;;3571:26;;;3619:4;3612:11;;;;;;;2528:1149;3661:5;3654:12;;;;;3872:223:24;4005:12;4036:52;4058:6;4066:4;4072:1;4075:12;4005;-1:-1:-1;;;;;1476:19:24;;;5239:60;;;;-1:-1:-1;;;5239:60:24;;23889:2:122;5239:60:24;;;23871:21:122;23928:2;23908:18;;;23901:30;23967:31;23947:18;;;23940:59;24016:18;;5239:60:24;23861:179:122;5239:60:24;5311:12;5325:23;5352:6;-1:-1:-1;;;;;5352:11:24;5371:5;5378:4;5352:31;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;5310:73;;;;5400:51;5417:7;5426:10;5438:12;5400:16;:51::i;:::-;5393:58;4959:499;-1:-1:-1;;;;;;;4959:499:24:o;6622:692::-;6768:12;6796:7;6792:516;;;-1:-1:-1;6826:10:24;6819:17;;6792:516;6937:17;;:21;6933:365;;7131:10;7125:17;7191:15;7178:10;7174:2;7170:19;7163:44;7080:145;7270:12;7263:20;;-1:-1:-1;;;7263:20:24;;;;;;;;:::i;-1:-1:-1:-;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;14:173:122:-;82:20;;-1:-1:-1;;;;;131:31:122;;121:42;;111:2;;177:1;174;167:12;192:743;246:5;299:3;292:4;284:6;280:17;276:27;266:2;;321:5;314;307:20;266:2;361:6;348:20;387:4;-1:-1:-1;;;;;406:2:122;403:26;400:2;;;432:18;;:::i;:::-;478:2;475:1;471:10;501:28;525:2;521;517:11;501:28;:::i;:::-;563:15;;;594:12;;;;626:15;;;660;;;656:24;;653:33;-1:-1:-1;650:2:122;;;703:5;696;689:20;650:2;729:5;720:14;;743:163;757:2;754:1;751:9;743:163;;;814:17;;802:30;;775:1;768:9;;;;;852:12;;;;884;;743:163;;;-1:-1:-1;924:5:122;256:679;-1:-1:-1;;;;;;;256:679:122:o;940:550::-;982:5;1035:3;1028:4;1020:6;1016:17;1012:27;1002:2;;1057:5;1050;1043:20;1002:2;1097:6;1084:20;-1:-1:-1;;;;;1119:2:122;1116:26;1113:2;;;1145:18;;:::i;:::-;1189:55;1232:2;1213:13;;-1:-1:-1;;1209:27:122;1238:4;1205:38;1189:55;:::i;:::-;1269:2;1260:7;1253:19;1315:3;1308:4;1303:2;1295:6;1291:15;1287:26;1284:35;1281:2;;;1336:5;1329;1322:20;1281:2;1405;1398:4;1390:6;1386:17;1379:4;1370:7;1366:18;1353:55;1428:16;;;1446:4;1424:27;1417:42;;;;1432:7;992:498;-1:-1:-1;;992:498:122:o;1495:196::-;1554:6;1607:2;1595:9;1586:7;1582:23;1578:32;1575:2;;;1628:6;1620;1613:22;1575:2;1656:29;1675:9;1656:29;:::i;1696:983::-;1850:6;1858;1866;1874;1882;1935:3;1923:9;1914:7;1910:23;1906:33;1903:2;;;1957:6;1949;1942:22;1903:2;1985:29;2004:9;1985:29;:::i;:::-;1975:39;;2033:38;2067:2;2056:9;2052:18;2033:38;:::i;:::-;2023:48;;2122:2;2111:9;2107:18;2094:32;-1:-1:-1;;;;;2186:2:122;2178:6;2175:14;2172:2;;;2207:6;2199;2192:22;2172:2;2235:61;2288:7;2279:6;2268:9;2264:22;2235:61;:::i;:::-;2225:71;;2349:2;2338:9;2334:18;2321:32;2305:48;;2378:2;2368:8;2365:16;2362:2;;;2399:6;2391;2384:22;2362:2;2427:63;2482:7;2471:8;2460:9;2456:24;2427:63;:::i;:::-;2417:73;;2543:3;2532:9;2528:19;2515:33;2499:49;;2573:2;2563:8;2560:16;2557:2;;;2594:6;2586;2579:22;2557:2;;2622:51;2665:7;2654:8;2643:9;2639:24;2622:51;:::i;:::-;2612:61;;;1893:786;;;;;;;;:::o;2684:764::-;2815:6;2823;2831;2839;2847;2855;2863;2871;2879;2932:3;2920:9;2911:7;2907:23;2903:33;2900:2;;;2954:6;2946;2939:22;2900:2;2982:29;3001:9;2982:29;:::i;:::-;2972:39;;3030:38;3064:2;3053:9;3049:18;3030:38;:::i;:::-;3020:48;;3115:2;3104:9;3100:18;3087:32;3077:42;;3138:38;3172:2;3161:9;3157:18;3138:38;:::i;:::-;3128:48;;3223:3;3212:9;3208:19;3195:33;3185:43;;3247:39;3281:3;3270:9;3266:19;3247:39;:::i;:::-;3237:49;;3333:3;3322:9;3318:19;3305:33;3295:43;;3385:3;3374:9;3370:19;3357:33;3347:43;;3437:3;3426:9;3422:19;3409:33;3399:43;;2890:558;;;;;;;;;;;:::o;3453:557::-;3548:6;3556;3564;3572;3625:3;3613:9;3604:7;3600:23;3596:33;3593:2;;;3647:6;3639;3632:22;3593:2;3675:29;3694:9;3675:29;:::i;:::-;3665:39;;3723:38;3757:2;3746:9;3742:18;3723:38;:::i;:::-;3713:48;;3808:2;3797:9;3793:18;3780:32;3770:42;;3863:2;3852:9;3848:18;3835:32;-1:-1:-1;;;;;3882:6:122;3879:30;3876:2;;;3927:6;3919;3912:22;3876:2;3955:49;3996:7;3987:6;3976:9;3972:22;3955:49;:::i;:::-;3945:59;;;3583:427;;;;;;;:::o;4015:626::-;4119:6;4127;4135;4143;4151;4204:3;4192:9;4183:7;4179:23;4175:33;4172:2;;;4226:6;4218;4211:22;4172:2;4254:29;4273:9;4254:29;:::i;:::-;4244:39;;4302:38;4336:2;4325:9;4321:18;4302:38;:::i;:::-;4292:48;;4387:2;4376:9;4372:18;4359:32;4349:42;;4438:2;4427:9;4423:18;4410:32;4400:42;;4493:3;4482:9;4478:19;4465:33;-1:-1:-1;;;;;4513:6:122;4510:30;4507:2;;;4558:6;4550;4543:22;4507:2;4586:49;4627:7;4618:6;4607:9;4603:22;4586:49;:::i;4646:325::-;4711:6;4719;4772:2;4760:9;4751:7;4747:23;4743:32;4740:2;;;4793:6;4785;4778:22;4740:2;4821:29;4840:9;4821:29;:::i;:::-;4811:39;;4900:2;4889:9;4885:18;4872:32;4913:28;4935:5;4913:28;:::i;:::-;4960:5;4950:15;;;4730:241;;;;;:::o;4976:264::-;5044:6;5052;5105:2;5093:9;5084:7;5080:23;5076:32;5073:2;;;5126:6;5118;5111:22;5073:2;5154:29;5173:9;5154:29;:::i;:::-;5144:39;5230:2;5215:18;;;;5202:32;;-1:-1:-1;;;5063:177:122:o;5245:368::-;5329:6;5382:2;5370:9;5361:7;5357:23;5353:32;5350:2;;;5403:6;5395;5388:22;5350:2;5448:9;5435:23;-1:-1:-1;;;;;5473:6:122;5470:30;5467:2;;;5518:6;5510;5503:22;5467:2;5546:61;5599:7;5590:6;5579:9;5575:22;5546:61;:::i;5618:497::-;5708:6;5716;5769:2;5757:9;5748:7;5744:23;5740:32;5737:2;;;5790:6;5782;5775:22;5737:2;5835:9;5822:23;-1:-1:-1;;;;;5860:6:122;5857:30;5854:2;;;5905:6;5897;5890:22;5854:2;5933:61;5986:7;5977:6;5966:9;5962:22;5933:61;:::i;:::-;5923:71;;;6044:2;6033:9;6029:18;6016:32;6057:28;6079:5;6057:28;:::i;6120:251::-;6176:6;6229:2;6217:9;6208:7;6204:23;6200:32;6197:2;;;6250:6;6242;6235:22;6197:2;6294:9;6281:23;6313:28;6335:5;6313:28;:::i;6376:255::-;6443:6;6496:2;6484:9;6475:7;6471:23;6467:32;6464:2;;;6517:6;6509;6502:22;6464:2;6554:9;6548:16;6573:28;6595:5;6573:28;:::i;6636:306::-;6694:6;6747:2;6735:9;6726:7;6722:23;6718:32;6715:2;;;6768:6;6760;6753:22;6715:2;6799:23;;-1:-1:-1;;;;;;6851:32:122;;6841:43;;6831:2;;6903:6;6895;6888:22;6947:190;7006:6;7059:2;7047:9;7038:7;7034:23;7030:32;7027:2;;;7080:6;7072;7065:22;7027:2;-1:-1:-1;7108:23:122;;7017:120;-1:-1:-1;7017:120:122:o;7142:194::-;7212:6;7265:2;7253:9;7244:7;7240:23;7236:32;7233:2;;;7286:6;7278;7271:22;7233:2;-1:-1:-1;7314:16:122;;7223:113;-1:-1:-1;7223:113:122:o;7341:354::-;7419:6;7427;7480:2;7468:9;7459:7;7455:23;7451:32;7448:2;;;7501:6;7493;7486:22;7448:2;7542:9;7529:23;7519:33;;7602:2;7591:9;7587:18;7574:32;7635:1;7628:5;7625:12;7615:2;;7656:6;7648;7641:22;7700:258;7768:6;7776;7829:2;7817:9;7808:7;7804:23;7800:32;7797:2;;;7850:6;7842;7835:22;7797:2;-1:-1:-1;;7878:23:122;;;7948:2;7933:18;;;7920:32;;-1:-1:-1;7787:171:122:o;7963:255::-;8042:6;8050;8103:2;8091:9;8082:7;8078:23;8074:32;8071:2;;;8124:6;8116;8109:22;8071:2;-1:-1:-1;;8152:16:122;;8208:2;8193:18;;;8187:25;8152:16;;8187:25;;-1:-1:-1;8061:157:122:o;8223:326::-;8300:6;8308;8316;8369:2;8357:9;8348:7;8344:23;8340:32;8337:2;;;8390:6;8382;8375:22;8337:2;-1:-1:-1;;8418:23:122;;;8488:2;8473:18;;8460:32;;-1:-1:-1;8539:2:122;8524:18;;;8511:32;;8327:222;-1:-1:-1;8327:222:122:o;8554:308::-;8623:6;8676:2;8664:9;8655:7;8651:23;8647:32;8644:2;;;8697:6;8689;8682:22;8644:2;8734:9;8728:16;-1:-1:-1;;;;;8777:5:122;8773:30;8766:5;8763:41;8753:2;;8823:6;8815;8808:22;8867:437;8920:3;8958:5;8952:12;8985:6;8980:3;8973:19;9011:4;9040:2;9035:3;9031:12;9024:19;;9077:2;9070:5;9066:14;9098:3;9110:169;9124:6;9121:1;9118:13;9110:169;;;9185:13;;9173:26;;9219:12;;;;9254:15;;;;9146:1;9139:9;9110:169;;;-1:-1:-1;9295:3:122;;8928:376;-1:-1:-1;;;;;8928:376:122:o;9309:232::-;9385:1;9378:5;9375:12;9365:2;;9430:10;9425:3;9421:20;9418:1;9411:31;9465:4;9462:1;9455:15;9493:4;9490:1;9483:15;9365:2;9517:18;;9355:186::o;9546:274::-;9675:3;9713:6;9707:13;9729:53;9775:6;9770:3;9763:4;9755:6;9751:17;9729:53;:::i;:::-;9798:16;;;;;9683:137;-1:-1:-1;;9683:137:122:o;9825:203::-;-1:-1:-1;;;;;9989:32:122;;;;9971:51;;9959:2;9944:18;;9926:102::o;10342:375::-;-1:-1:-1;;;;;10600:15:122;;;10582:34;;10652:15;;;;10647:2;10632:18;;10625:43;10699:2;10684:18;;10677:34;;;;10532:2;10517:18;;10499:218::o;10722:359::-;-1:-1:-1;;;;;10999:32:122;;;;10981:51;;11063:2;11048:18;;11041:34;10969:2;10954:18;;10936:145::o;11365:429::-;11629:1;11625;11620:3;11616:11;11612:19;11604:6;11600:32;11589:9;11582:51;11669:6;11664:2;11653:9;11649:18;11642:34;11712:2;11707;11696:9;11692:18;11685:30;11563:4;11732:56;11784:2;11773:9;11769:18;11761:6;11732:56;:::i;:::-;11724:64;11572:222;-1:-1:-1;;;;;11572:222:122:o;11799:367::-;-1:-1:-1;;;;;12027:32:122;;12009:51;;12091:2;12076:18;;12069:34;;;11997:2;11982:18;;12112:48;12156:2;12141:18;;12133:6;12112:48;:::i;12171:345::-;-1:-1:-1;;;;;12391:32:122;;;;12373:51;;12455:2;12440:18;;12433:34;;;;12498:2;12483:18;;12476:34;12361:2;12346:18;;12328:188::o;12521:1681::-;12882:2;12934:21;;;13004:13;;12907:18;;;13026:22;;;12853:4;;12882:2;13067;;13085:18;;;;13122:4;13149:15;;;12853:4;13195:388;13209:6;13206:1;13203:13;13195:388;;;13268:13;;13306:9;;13294:22;;13356:11;;;13350:18;13336:12;;;13329:40;13413:11;;;13407:18;-1:-1:-1;;;;;13403:44:122;13389:12;;;13382:66;13488:11;;13482:18;13468:12;;;13461:40;13530:4;13521:14;;;;13558:15;;;;13444:1;13224:9;13195:388;;;-1:-1:-1;;13619:19:122;;;13599:18;;;13592:47;13689:13;;13711:21;;;13750:12;;;;-1:-1:-1;13689:13:122;-1:-1:-1;13802:1:122;13798:16;;;13789:26;;13785:35;;;-1:-1:-1;13845:15:122;;;13880:4;13893:280;13909:8;13904:3;13901:17;13893:280;;;14004:2;14000:7;13994:3;13986:6;13982:16;13978:30;13971:5;13964:45;14032:53;14078:6;14067:8;14061:15;14032:53;:::i;:::-;14149:14;;;;14022:63;-1:-1:-1;14110:17:122;;;;13937:1;13928:11;13893:280;;;-1:-1:-1;14190:6:122;;12862:1340;-1:-1:-1;;;;;;;;;12862:1340:122:o;14207:494::-;14387:2;14372:18;;14376:9;14467:6;14345:4;14501:194;14515:4;14512:1;14509:11;14501:194;;;14574:13;;14562:26;;14611:4;14635:12;;;;14670:15;;;;14535:1;14528:9;14501:194;;;14505:3;;;14354:347;;;;:::o;14706:261::-;14885:2;14874:9;14867:21;14848:4;14905:56;14957:2;14946:9;14942:18;14934:6;14905:56;:::i;16813:199::-;16955:2;16940:18;;16967:39;16944:9;16988:6;16967:39;:::i;17216:383::-;17365:2;17354:9;17347:21;17328:4;17397:6;17391:13;17440:6;17435:2;17424:9;17420:18;17413:34;17456:66;17515:6;17510:2;17499:9;17495:18;17490:2;17482:6;17478:15;17456:66;:::i;:::-;17583:2;17562:15;-1:-1:-1;;17558:29:122;17543:45;;;;17590:2;17539:54;;17337:262;-1:-1:-1;;17337:262:122:o;19533:344::-;19735:2;19717:21;;;19774:2;19754:18;;;19747:30;-1:-1:-1;;;19808:2:122;19793: