Blast Sepolia Testnet

Contract

0xf4E23Ae6D7dEAA9B3c8292881e3bC3027268808a
Source Code Source Code

Overview

ETH Balance

0 ETH

More Info

Multichain Info

N/A
Transaction Hash
Method
Block
From
To
Amount

There are no matching entries

Please try again later

Parent Transaction Hash Block From To Amount
View All Internal Transactions

Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
OptimizedBTB

Compiler Version
v0.8.16+commit.07a7930e

Optimization Enabled:
Yes with 100 runs

Other Settings:
default evmVersion

Contract Source Code (Solidity Standard Json-Input format)

File 1 of 28 : OptimizedBTB.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

import "./interfaces/IBlast.sol";
import "./interfaces/IBlastPoints.sol";
import "./base/BlastTheBalloonBase.sol";
import "./BTBStorage.sol";

import "./interfaces/IBTBConfig.sol";

import {OptimizedBTBLogicLib} from "contracts/libraries/OptimizedBTBLogicLib.sol";

contract OptimizedBTB is BlastTheBalloonBase, BTBStorage {
    // ================================= INITIALIZE =================================

    /**
     * @notice Initialize Blast The Balloon Contract
     * @dev Called only once
     * @param _entropy Address of entropy
     * @param _provider Address of entropy provider
     */
    function initialize(
        address _entropy,
        address _provider,
        address _ballTreasury,
        address _pointsOperator,
        address _BlastPointsAddress,
        address _config
    ) external initializer {
        entropy = IEntropy(_entropy);
        provider = _provider;

        config = IBTBConfig(_config);

        ballTreasury = payable(_ballTreasury);
        adminAddr = payable(_msgSender());

        BlastPointsAddress = _BlastPointsAddress;

        pointsOperator = _pointsOperator;
        IBlastPoints(BlastPointsAddress).configurePointsOperator(_pointsOperator);

        IBlast(0x4300000000000000000000000000000000000002).configureClaimableGas();
        IBlast(0x4300000000000000000000000000000000000002).configureClaimableYield();

        _initVariables_();

        _initEmptyRound_();

        __Base_init(_msgSender());
    }

    // ============================== PRIVATE FUNCTION ==============================

    /**
     * @dev Create first empty round
     */
    function _initEmptyRound_() private {
        Round storage roundInfo = rounds[nonce];

        roundInfo.startTime = uint32(block.timestamp);
        roundInfo.roundId = nonce;
        roundInfo.totalWinningBlastMaster = maxBlastMasterWinners;
    }

    /**
     * @dev Init Variables
     */
    function _initVariables_() private {
        // Fee to spin
        spinFee = 4000000000000000;

        // Fee to ante
        anteFee = 4000000000000000;

        // Number of blast winners
        maxBlastMasterWinners = 10;

        failSpinLimit = 1 minutes;

        // Duration of a round
        roundDuration = 420;

        // Max time can increase
        roundMaxTime = 420;

        // prize portion
        ballTreasuryPortion = 2000; // 2000 / 100_000 = 0.02 <=> 2%
        majorJackpotNextRoundPortion = 0; // 0%
        majorJackpotPortion = 83000; // 83%
        minorJackpotPortion = 15000; // 15%

        // jackpot chance to win prize
        kingBlasterChancePercentage = 1500; // 1.5%
        blastMasterChancePercentage = 15000; // 15.0%
        minorJackpotChancePercentage = 100; // 0.1%

        // reward percentage of each type when user win
        kingBlasterPrizePercentage = 35000; // 35%
        blastMasterPrizePercentage = 65000; // 65%
        minorJackpotPrizePercentage = 70000; // 70%

        // adjust percentage of minor jackpot
        minorJackpotAdjustPercentage = 5000;
        maxMinorJackpotProbability =
            PERCENTAGE_DENOMINATOR -
            (kingBlasterChancePercentage + blastMasterChancePercentage);

        seededBack = 5;
        numOfSeed = 5;
        guaranteeSlot = 3;

        multipleProbability = 1000; // 1%
    }

    // ============================== EXTERNAL UPDATE FUNCTION ==============================

    /**
     * @dev Update Pyth address
     * @param _entropy new entropy
     * @param _provider new provider
     * @param _newTreasury new treasury
     * @param _newAdmin new admin
     */
    function updateSystemAddress(
        address _entropy,
        address _provider,
        address _newTreasury,
        address _newAdmin
    ) external onlyAdmin {
        require(_entropy != address(0), BlastTheBalloonErrors.NOT_ALLOWED_ZERO_ADDRESS);
        require(_provider != address(0), BlastTheBalloonErrors.NOT_ALLOWED_ZERO_ADDRESS);
        require(_newTreasury != address(0), BlastTheBalloonErrors.NOT_ALLOWED_ZERO_ADDRESS);
        require(_newAdmin != address(0), BlastTheBalloonErrors.NOT_ALLOWED_ZERO_ADDRESS);

        entropy = IEntropy(_entropy);
        provider = _provider;
        ballTreasury = payable(_newTreasury);
        adminAddr = payable(_newAdmin);

        // emit UpdatePyth(_entropy, _provider);
    }

    /**
     * @dev update number of seed
     * @param _newSeed new duration
     * @param _seedBack seed back
     * @param _newGuaranteeSeat new guarantee seat
     */
    function updateSeed(uint32 _newSeed, uint _seedBack, uint _newGuaranteeSeat) external onlyAdmin {
        Round storage roundInfo = rounds[nonce];

        require(
            block.timestamp > roundInfo.startTime + roundInfo.duration || block.timestamp < roundInfo.startTime,
            BlastTheBalloonErrors.ROUND_STILL_OPEN
        );
        require(_newSeed > 0, BlastTheBalloonErrors.CANT_BE_ZERO);
        require(_newGuaranteeSeat > 0, BlastTheBalloonErrors.CANT_BE_ZERO);

        numOfSeed = _newSeed;
        seededBack = _seedBack;
        guaranteeSlot = _newGuaranteeSeat;

        // emit UpdateRoundDuration(_newDuration);
    }

    /**
     * @dev update Round Config
     * @param _newMaxBlastMasters new max blast masters
     * @param _newSpinFee new spin fee
     * @param _newDuration new duration
     * @param _newMaxTime new max time
     * @param _anteFee new ante fee
     * @param _multipleProbability new multiple probability
     */
    function updateRoundConfig(
        uint _newMaxBlastMasters,
        uint _newSpinFee,
        uint _anteFee,
        uint32 _newDuration,
        uint32 _newMaxTime,
        uint24 _multipleProbability
    ) external onlyAdmin {
        Round storage roundInfo = rounds[nonce];

        require(
            block.timestamp > roundInfo.startTime + roundInfo.duration || block.timestamp < roundInfo.startTime,
            BlastTheBalloonErrors.ROUND_STILL_OPEN
        );
        require(_newDuration <= _newMaxTime, BlastTheBalloonErrors.INVALID_TIME);
        require(_newMaxBlastMasters > 0, BlastTheBalloonErrors.CANT_BE_ZERO);

        spinFee = _newSpinFee;
        anteFee = _anteFee;
        roundDuration = _newDuration;
        roundMaxTime = _newMaxTime;
        maxBlastMasterWinners = _newMaxBlastMasters;
        multipleProbability = _multipleProbability;
    }

    /**
     * @dev update jackpot prize distribution
     * @param portions Array of prize portion includes:
     * 0. % goes to minor
     * 1. % goes to major,
     * 2. % goes to next round major,
     * 3. % goes to treasury,
     */
    function updatePrizePortion(uint24[4] calldata portions) external onlyAdmin {
        uint total = portions[0] + portions[1] + portions[2] + portions[3];

        require(total == PERCENTAGE_DENOMINATOR, BlastTheBalloonErrors.INVALID_PERCENTAGE);

        minorJackpotPortion = portions[0];
        majorJackpotPortion = portions[1];
        majorJackpotNextRoundPortion = portions[2];
        ballTreasuryPortion = portions[3];

        // emit UpdatePrizePortion(portions);
    }

    /**
     * @dev update jackpot chances
     * @param jackpotChances Array of jackpot chance includes:
     * 0. kingBlasterChancePercentage
     * 1. blastMasterChancePercentage
     * 2. minorJackpotChancePercentage
     */
    function updateJackpotChance(uint24[3] calldata jackpotChances) external onlyAdmin {
        require(
            jackpotChances[0] + jackpotChances[1] + jackpotChances[2] <= PERCENTAGE_DENOMINATOR,
            BlastTheBalloonErrors.INVALID_PERCENTAGE
        );

        kingBlasterChancePercentage = jackpotChances[0];
        blastMasterChancePercentage = jackpotChances[1];
        minorJackpotChancePercentage = jackpotChances[2];

        maxMinorJackpotProbability =
            PERCENTAGE_DENOMINATOR -
            (kingBlasterChancePercentage + blastMasterChancePercentage);

        // emit UpdateJackpotChance(jackpotChances);
    }

    /**
     * @dev update reward percentage of jackpot
     * @param rewardPercentages Array of jackpot chance includes:
     * 0. kingBlasterPrizePercentage
     * 1. blastMasterPrizePercentage
     * 2. minorJackpotPrizePercentage
     */
    function updateRewardPercentage(uint24[3] calldata rewardPercentages) external onlyAdmin {
        uint total = rewardPercentages[0] + rewardPercentages[1];

        require(total <= PERCENTAGE_DENOMINATOR, BlastTheBalloonErrors.INVALID_PERCENTAGE);
        require(rewardPercentages[2] <= PERCENTAGE_DENOMINATOR, BlastTheBalloonErrors.INVALID_PERCENTAGE);

        kingBlasterPrizePercentage = rewardPercentages[0];
        blastMasterPrizePercentage = rewardPercentages[1];
        minorJackpotPrizePercentage = rewardPercentages[2];

        // emit UpdateRewardPercentage(rewardPercentages);
    }

    /**
     * @dev update jackpot adjust percentage
     * @param newAdjustPercentage new adjust percentage
     */
    function updateJackpotAdjustPercentage(uint newAdjustPercentage) external onlyAdmin {
        require(newAdjustPercentage <= PERCENTAGE_DENOMINATOR, BlastTheBalloonErrors.INVALID_PERCENTAGE);

        minorJackpotAdjustPercentage = newAdjustPercentage;

        // emit UpdateJackpotAdjustPercentage(newAdjustPercentage);
    }

    /**
     * @dev update spin fail time limit
     * @param _newDuration new duration
     */
    function updateFailSpinLimit(uint32 _newDuration) external onlyAdmin {
        require(_newDuration > 5 seconds, BlastTheBalloonErrors.INVALID_FAIL_SPIN_LIMIT);

        failSpinLimit = _newDuration;
    }

    // ============================== EXTERNAL FUNCTION ==============================

    // Function to receive native token msg.data is empty
    receive() external payable {}

    // Fallback function is called when msg.data is not empty
    fallback() external payable {}

    /**
     * @dev Create a new round and only admin can do it
     */
    function createNewRound(uint _startRound) external payable onlyAdmin {
        Round storage previousRoundInfo = rounds[nonce];

        OptimizedBTBLogicLib.newRoundCondition(_startRound, roundDuration, previousRoundInfo);

        (uint previousPrizeLeft, uint seedPrize) = OptimizedBTBLogicLib.getRemainingPrize(
            nonce,
            numOfSeed,
            spinFee,
            majorJackpotPortion,
            previousRoundInfo,
            kingBlasters
        );

        // update number of spin using seed, back to 0 if someone win jackpot
        seedCount = seedPrize != 0 ? 0 : seedCount;

        nonce += 1;
        guaranteeSlotCount = 0;

        Round storage roundInfo = rounds[nonce];

        IBTBConfig.Point memory levelPoints = config.getLevelPoint();

        IBTBConfig.TimeImpact memory timeImpacts = config.getTimeImpact();

        uint[40] memory params = [
            nonce,
            roundDuration,
            roundMaxTime,
            maxBlastMasterWinners,
            timeImpacts.firstGameTimerChancePercentage,
            timeImpacts.secondGameTimerChancePercentage,
            timeImpacts.thirdGameTimerChancePercentage,
            timeImpacts.firstGameTimerImpact,
            timeImpacts.secondGameTimerImpact,
            timeImpacts.thirdGameTimerImpact,
            kingBlasterChancePercentage,
            blastMasterChancePercentage,
            minorJackpotChancePercentage,
            kingBlasterPrizePercentage,
            blastMasterPrizePercentage,
            minorJackpotPrizePercentage,
            minorJackpotAdjustPercentage,
            maxMinorJackpotProbability,
            levelPoints.firstLevelPointP,
            levelPoints.secondLevelPointP,
            levelPoints.thirdLevelPointP,
            levelPoints.fourthLevelPointP,
            levelPoints.fifthLevelPointP,
            levelPoints.sixthLevelPointP,
            levelPoints.firstLevelPointA,
            levelPoints.secondLevelPointA,
            levelPoints.thirdLevelPointA,
            levelPoints.fourthLevelPointA,
            levelPoints.fifthLevelPointA,
            levelPoints.sixthLevelPointA,
            levelPoints.firstBonusTime,
            levelPoints.secondBonusTime,
            levelPoints.thirdBonusTime,
            levelPoints.firstBonusMultiplier,
            levelPoints.secondBonusMultiplier,
            levelPoints.thirdBonusMultiplier,
            ballTreasuryPortion,
            majorJackpotNextRoundPortion,
            majorJackpotPortion,
            minorJackpotPortion
        ];

        OptimizedBTBLogicLib.updateRoundInfo(
            roundInfo,
            previousRoundInfo,
            previousPrizeLeft,
            seedPrize,
            uint32(_startRound),
            params
        );

        emit CreateNewRound(_msgSender(), nonce, roundInfo.startTime, roundInfo.duration);
    }

    function addAnte(address player) external payable nonReentrant {
        OptimizedBTBLogicLib.addAnte(nonce, anteFee, player, ballTreasury, rounds, ante);
    }

    /**
     * @notice Request to get a random number. The caller should generate a random number prior to calling this method, then
     * submit the hash of that number as userCommitment.
     * @param userRandomNumber user generate random number to get random number from pyth
     */
    function buySpin(address player, bytes32 userRandomNumber) external payable nonReentrant {
        Round storage roundInfo = rounds[nonce];
        Spin storage spinInfo = lastSpins[nonce][player];

        _refundSpin(player);

        OptimizedBTBLogicLib.buySpinCondition(roundInfo, spinInfo);

        _calculateLastPrize(player);

        require(ante[nonce][player], BlastTheBalloonErrors.INVALID_ANTE);

        uint64 sequenceNumber = OptimizedBTBLogicLib.buySpin(
            nonce,
            msg.value,
            spinFee,
            player,
            provider,
            spinInfo,
            entropy,
            requestedRandomNumber,
            userRandomNumber
        );

        emit BuySpin(player, nonce, sequenceNumber);
    }

    /**
     * @notice Execute Spin
     * @param sequenceNumber Sequence Number
     * @param _providerAddress Created Random by Pyth. This can be retrieved from them in a provider-dependent manner.
     * @param randomNumber Generated random number by Pyth. Used to calculate prize
     */
    function entropyCallback(
        uint64 sequenceNumber,
        // If your app uses multiple providers, you can use this argument
        // to distinguish which one is calling the app back. This app only
        // uses one provider so this argument is not used.
        address _providerAddress,
        bytes32 randomNumber
    ) internal override {
        require(_msgSender() == address(entropy), BlastTheBalloonErrors.CALLER_NOT_ENTROPY);

        Round storage roundInfo = rounds[nonce];

        uint guaranteedSeatBitMask = guaranteedSeatBitMaskByRound[nonce];

        // get player address
        address playerAddress = requestedRandomNumber[sequenceNumber];

        Spin storage spinInfo = lastSpins[nonce][playerAddress];

        OptimizedBTBLogicLib.executeSpinCondition(roundInfo, spinInfo, failSpinLimit);

        _updatePrizePool(roundInfo, spinInfo);

        uint userRandom = uint(spinInfo.userCommitment);

        // Calculate exact jackpot percentage
        (
            uint jackpotWinningPosition,
            uint24 minorProbability,
            uint24 kingProbability,
            uint24 blastProbability
        ) = _calculateProbabilityOfWinning(uint(randomNumber));

        // Calculate time impact for current round duration
        string memory gameTimeImpact = _gameTimeImpact(uint(randomNumber), userRandom);
        MajorJackWinnerReturn memory majorWinnerReturn = _majorJackpotWinner(
            jackpotWinningPosition,
            kingProbability,
            blastProbability,
            uint(randomNumber),
            guaranteedSeatBitMask,
            playerAddress
        );

        {
            uint prizeAmount = majorWinnerReturn.prize;
            WinType jackpotType = majorWinnerReturn.wintype;

            // Choose minor winner if none then choose major
            if (jackpotType == WinType.NONE) {
                (prizeAmount, jackpotType) = _minorJackpotWinner(
                    jackpotWinningPosition,
                    minorProbability,
                    playerAddress
                );
            }

            // set executed status
            spinInfo.status = SpinStatus.EXECUTED;
            guaranteedSeatBitMaskByRound[nonce] = majorWinnerReturn.guaranteedSeatBitMask;

            // Emit execute Spin event
            // emit ExecuteSpin(playerAddress, uint256(randomNumber), nonce, prizeAmount, gameTimeImpact, jackpotType);
            emitEvent(playerAddress, uint256(randomNumber), nonce, prizeAmount, gameTimeImpact, jackpotType);
        }
    }

    function emitEvent(
        address playerAddress,
        uint randomUmber,
        uint nonce,
        uint prizeAmount,
        string memory gameTimeImpact,
        WinType jackpotType
    ) internal {
        emit ExecuteSpin(playerAddress, randomUmber, nonce, prizeAmount, gameTimeImpact, jackpotType);
    }

    /**
     * @dev Withdraw winning prize of the last round
     */
    function claimPrize() external nonReentrant {
        Round storage roundInfo = rounds[nonce];

        _refundSpin(_msgSender());
        _calculateLastPrize(_msgSender());

        uint pendingAmount;

        if (block.timestamp > roundInfo.startTime + roundInfo.duration && !pendingClaimed[nonce][_msgSender()]) {
            pendingAmount = getCurrentClaimableAmounts(_msgSender());
            pendingClaimed[nonce][_msgSender()] = true;
        }

        uint allPrize = claimableAmounts[_msgSender()] + pendingAmount;

        require(address(this).balance >= allPrize, BlastTheBalloonErrors.NOT_ENOUGH_TOKEN);

        claimableAmounts[_msgSender()] = 0;

        (bool success, ) = payable(_msgSender()).call{value: allPrize}("");
        require(success, "Failed to send Ether to blast winner");

        emit ClaimPrize(_msgSender(), allPrize);
    }

    /**
     * @dev Emergency Withdraw
     */
    function emergencyWithdraw(uint _amount) external nonReentrant onlyAdmin {
        require(emergencyStatus, BlastTheBalloonErrors.NOT_EMERGENCY);
        require(address(this).balance >= _amount, BlastTheBalloonErrors.INVALID_AMOUNT);

        (bool success, ) = payable(_msgSender()).call{value: _amount}("");
        require(success, "Failed to send Ether to blast winner");
    }

    /**
     * @dev Set Emergency status
     */
    function setEmergencyStatus(bool _status) external onlyAdmin {
        emergencyStatus = _status;
    }

    /**
     * @dev Refund fail spin
     */
    function refundSpin() external nonReentrant {
        _refundSpin(_msgSender());

        require(refundClaimable[_msgSender()] > 0, BlastTheBalloonErrors.INVALID_AMOUNT);

        uint refundAmount = refundClaimable[_msgSender()];
        refundClaimable[_msgSender()] = 0;

        require(address(this).balance >= refundAmount, BlastTheBalloonErrors.INVALID_AMOUNT);

        (bool success, ) = payable(_msgSender()).call{value: refundAmount}("");
        require(success, "Failed to send Ether to blast winner");
    }

    /**
     * @dev Claim Contract Gas
     */
    function claimAllGas(address _recipient) external onlyAdmin {
        IBlast(0x4300000000000000000000000000000000000002).claimAllGas(address(this), _recipient);
    }

    /**
     * @dev Claim Contract Max Gas
     */
    function claimMaxGas(address _recipient) external onlyAdmin {
        IBlast(0x4300000000000000000000000000000000000002).claimMaxGas(address(this), _recipient);
    }

    /**
     * @dev Claim Contract Yield
     */
    function claimAllYield(address _recipient) external onlyAdmin {
        IBlast(0x4300000000000000000000000000000000000002).claimAllYield(address(this), _recipient);
    }

    // ============================== INTERNAL FUNCTION ==============================

    /**
     * @dev Refund last fail spin
     */
    function _refundSpin(address player) internal {
        OptimizedBTBLogicLib.refundSpin(players, lastSpins, refundClaimable, failSpinLimit, player);
    }

    /**
     * @dev calculates the user's previous awards
     */
    function _calculateLastPrize(address player) internal {
        Player storage playerInfo = players[player];

        if (playerInfo.lastPlayRound >= nonce) {
            return;
        }

        if (!pendingClaimed[playerInfo.lastPlayRound][player]) {
            claimableAmounts[player] = getActualClaimableAmounts(player);
            pendingClaimed[playerInfo.lastPlayRound][player] = true;
        }

        playerInfo.lastPlayRound = nonce;
    }

    /**
     * @dev update Prize Information of the round
     */
    function _updatePrizePool(Round storage roundInfo, Spin storage spinInfo) internal {
        uint majorPortion = roundInfo.prizePortion.majorJackpotPortion;

        // send eth to treasury
        // uint ballTreasuryAmount = (spinInfo.spinFee * ballTreasuryPortion) / PERCENTAGE_DENOMINATOR;
        uint ballTreasuryAmount = (spinInfo.spinFee * roundInfo.prizePortion.ballTreasuryPortion) / PERCENTAGE_DENOMINATOR;
        (bool success, ) = ballTreasury.call{value: ballTreasuryAmount}("");
        require(success, "Failed to send Ether to treasury");

        if (seedCount < seededBack) {
            seedCount += 1;

            // send seed eth to treasury
            uint seedRefund = (spinInfo.spinFee * majorPortion) / PERCENTAGE_DENOMINATOR;
            (success, ) = adminAddr.call{value: seedRefund}("");
            require(success, "Failed to send Seed Ether to prize pool wallet");

            majorPortion = 0;
        }

        OptimizedBTBLogicLib.updatePrizePool(
            roundInfo,
            spinInfo,
            majorPortion,
            roundInfo.prizePortion.minorJackpotPortion,
            roundInfo.prizePortion.majorJackpotNextRoundPortion
        );
    }

    /**
     * @dev Calculate timing affects the game
     * @param randomNumber random number
     */
    function _gameTimeImpact(uint randomNumber, uint userRandom) internal returns (string memory) {
        Round storage roundInfo = rounds[nonce];

        return OptimizedBTBLogicLib.gameTimeImpact(randomNumber, userRandom, roundInfo);
    }

    /**
     * @dev Calculate jackpot probability
     */
    function _calculateProbabilityOfWinning(uint randomNumber) internal view returns (uint, uint24, uint24, uint24) {
        Round storage roundInfo = rounds[nonce];

        /// Calculate probability of king and blast
        uint kingBlasterWinningPercentage = roundInfo.jackpot.kingBlasterChancePercentage;
        uint blastMasterWinningPercentage = kingBlasterWinningPercentage +
            roundInfo.jackpot.blastMasterChancePercentage;

        // Calculate winning probability for minor
        uint currentProbability = (roundInfo.jackpot.minorJackpotChancePercentage +
            ((minorJackpotEntryCount * PERCENTAGE_DENOMINATOR) / roundInfo.jackpot.minorJackpotAdjustPercentage));

        uint minorJackpotWinningPercentage = currentProbability < roundInfo.jackpot.maxMinorJackpotProbability
            ? currentProbability
            : roundInfo.jackpot.maxMinorJackpotProbability;

        minorJackpotWinningPercentage += blastMasterWinningPercentage;

        /// Calculate winning position to choose winner
        uint winningPosition = randomNumber % PERCENTAGE_DENOMINATOR;

        return (
            winningPosition,
            uint24(minorJackpotWinningPercentage),
            uint24(kingBlasterWinningPercentage),
            uint24(blastMasterWinningPercentage)
        );
    }

    /**
     * @dev Check to see if won a major, king or blast
     * @param winningPosition winning probability to check if won king or blast or nothing
     * @param kingProbability percentage to win king
     * @param blastProbability percentage to win blast
     * @param randomNumber used to calculate position if won blast
     */
    function _majorJackpotWinner(
        uint winningPosition,
        uint kingProbability,
        uint blastProbability,
        uint randomNumber,
        uint currentGuaranteedSeatBitMask,
        address player
    ) internal returns (MajorJackWinnerReturn memory) {
        uint[7] memory positionUints = [
            nonce,
            randomNumber,
            guaranteeSlot,
            guaranteeSlotCount,
            winningPosition,
            kingProbability,
            blastProbability
        ];

        (uint blastMasterWinningPosition, uint guaranteedSeatBitMask) = OptimizedBTBLogicLib.calculateWinningPosition(
            positionUints,
            player,
            rounds[nonce],
            currentGuaranteedSeatBitMask,
            guaranteedSeat
        );

        uint[6] memory uints = [
            nonce,
            winningPosition,
            kingProbability,
            blastProbability,
            blastMasterWinningPosition,
            multipleProbability
        ];

        uint prize;
        WinType winType;
        uint winPos;

        (prize, winType, winPos) = OptimizedBTBLogicLib.kingWinner(uints, player, rounds, kingBlasters);

        if (winType == WinType.KING_BLASTER) {
            minorJackpotEntryCount += 1;

            emit MajorWinner(player, nonce, true, 0);

            // return (prize, winType, guaranteedSeatBitMask);
            return MajorJackWinnerReturn(prize, winType, guaranteedSeatBitMask);
        }

        (prize, winType, winPos) = OptimizedBTBLogicLib.blastWinner(
            uints,
            player,
            rounds,
            blastMasters,
            blastMasterPositions,
            numberOfBlasts
        );

        if (winType == WinType.BLAST_MASTER) {
            minorJackpotEntryCount += 1;
            guaranteeSlotCount = guaranteeSlotCount < guaranteeSlot ? guaranteeSlotCount + 1 : guaranteeSlotCount;

            emit MajorWinner(player, nonce, false, winPos);
        }

        return MajorJackWinnerReturn(prize, winType, guaranteedSeatBitMask);
    }

    /**
     * @dev Check to see if won a minor jackpot
     * @param winningPosition winning position to check if won or not
     * @param minorProbability minor winning probability
     */
    function _minorJackpotWinner(
        uint winningPosition,
        uint24 minorProbability,
        address player
    ) internal returns (uint, WinType) {
        if (winningPosition < minorProbability) {
            uint entries;
            uint minorPrize;

            (entries, minorPrize) = OptimizedBTBLogicLib.minorWinner(
                nonce,
                minorJackpotEntryCount,
                player,
                rounds,
                claimableAmounts
            );

            minorJackpotEntryCount = 0;

            emit MinorWinner(player, nonce, minorPrize, entries);

            return (minorPrize, WinType.MINOR_JACKPOT);
        }

        minorJackpotEntryCount += 1;

        return (0, WinType.NONE);
    }

    // ============================== VIEW FUNCTION ==============================

    // This method is required by the IEntropyConsumer interface
    function getEntropy() internal view override returns (address) {
        return address(entropy);
    }

    // Get the fee to spin . See the comment above about fees.
    function getFee() public view returns (uint256 fee) {
        fee = entropy.getFee(provider);
    }

    // This method is used to get current claimable amount
    function getCurrentClaimableAmounts(address player) public view returns (uint) {
        Round storage roundInfo = rounds[nonce];

        uint totalClaimableAmount;

        if (pendingClaimed[nonce][player]) {
            return totalClaimableAmount;
        }

        uint totalBlasts = numberOfBlasts[nonce][player];
        uint kingPrize = ((roundInfo.jackpot.kingBlasterPrizePercentage * roundInfo.majorJackpot) /
            PERCENTAGE_DENOMINATOR);
        uint blastPrize = roundInfo.totalWinningBlastMaster == 0
            ? 0
            : (roundInfo.majorJackpot - kingPrize) / roundInfo.totalWinningBlastMaster;

        if (kingBlasters[nonce] == player) {
            if (roundInfo.totalWinningBlastMaster == 0) {
                totalClaimableAmount += (roundInfo.majorJackpot - kingPrize);
            }

            totalClaimableAmount += kingPrize;
        }

        if (totalBlasts > 0) {
            if (kingBlasters[nonce] == address(0)) {
                totalClaimableAmount += ((kingPrize / roundInfo.totalWinningBlastMaster) * totalBlasts);
            }

            totalClaimableAmount += blastPrize * totalBlasts;
        }

        return totalClaimableAmount;
    }

    // This method is used to get actual claimable amount
    function getActualClaimableAmounts(address player) public view returns (uint) {
        Player storage playerInfo = players[player];
        Round storage roundInfo = rounds[playerInfo.lastPlayRound];

        if (playerInfo.lastPlayRound >= nonce || pendingClaimed[playerInfo.lastPlayRound][player]) {
            return claimableAmounts[player];
        }

        uint totalClaimableAmount = claimableAmounts[player];
        uint totalBlasts = numberOfBlasts[playerInfo.lastPlayRound][player];

        uint kingPrize = ((roundInfo.jackpot.kingBlasterPrizePercentage * roundInfo.majorJackpot) /
            PERCENTAGE_DENOMINATOR);
        uint blastPrize = roundInfo.totalWinningBlastMaster == 0
            ? 0
            : (roundInfo.majorJackpot - kingPrize) / roundInfo.totalWinningBlastMaster;

        if (kingBlasters[playerInfo.lastPlayRound] == player) {
            if (roundInfo.totalWinningBlastMaster == 0) {
                totalClaimableAmount += (roundInfo.majorJackpot - kingPrize);
            }

            totalClaimableAmount += kingPrize;
        }

        if (totalBlasts > 0) {
            if (kingBlasters[playerInfo.lastPlayRound] == address(0)) {
                totalClaimableAmount += ((kingPrize / roundInfo.totalWinningBlastMaster) * totalBlasts);
            }

            totalClaimableAmount += blastPrize * totalBlasts;
        }

        return totalClaimableAmount;
    }

    function getClaimableAmount(address player) public view returns (uint) {
        Round storage roundInfo = rounds[nonce];

        uint currentAmount;
        uint previousAmount = getActualClaimableAmounts(player);

        if (block.timestamp > roundInfo.startTime + roundInfo.duration) {
            currentAmount = getCurrentClaimableAmounts(player);
        }

        return previousAmount + currentAmount;
    }

    // This method is used to get current probability of minor
    function getMinorProbability(uint _nonce) public view returns (uint) {
        Round storage roundInfo = rounds[_nonce];

        if (_nonce > nonce) {
            return 0;
        }

        // Calculate winning probability for minor
        uint currentProbability = (roundInfo.jackpot.minorJackpotChancePercentage +
            ((minorJackpotEntryCount * PERCENTAGE_DENOMINATOR) / roundInfo.jackpot.minorJackpotAdjustPercentage));

        uint minorJackpotWinningPercentage = currentProbability < roundInfo.jackpot.maxMinorJackpotProbability
            ? currentProbability
            : roundInfo.jackpot.maxMinorJackpotProbability;

        return minorJackpotWinningPercentage;
    }

    // This method is used to get blast winners
    function getBlastWinners(uint _nonce) public view returns (address[] memory) {
        Round storage roundInfo = rounds[_nonce];

        if (_nonce > nonce) {
            return new address[](1);
        }

        address[] memory blastWinners = new address[](roundInfo.maxBlastMasterWinners);

        if (roundInfo.totalWinningBlastMaster == 0) {
            return blastWinners;
        }

        for (uint i = 0; i < roundInfo.maxBlastMasterWinners; i++) {
            blastWinners[i] = blastMasters[_nonce][i];
        }

        return blastWinners;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.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 {Initializable} from "../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:
 *
 * ```solidity
 * 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}:
 *
 * ```solidity
 * 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. We recommend using {AccessControlDefaultAdminRules}
 * to enforce additional security measures for this role.
 */
abstract contract AccessControlUpgradeable is Initializable, ContextUpgradeable, IAccessControlUpgradeable, ERC165Upgradeable {
    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);
        _;
    }

    function __AccessControl_init() internal onlyInitializing {
    }

    function __AccessControl_init_unchained() internal onlyInitializing {
    }
    /**
     * @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(account),
                        " 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.
     *
     * May emit a {RoleGranted} event.
     */
    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.
     *
     * May emit a {RoleRevoked} event.
     */
    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`.
     *
     * May emit a {RoleRevoked} event.
     */
    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.
     *
     * May emit a {RoleGranted} event.
     *
     * [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.
     *
     * May emit a {RoleGranted} event.
     */
    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.
     *
     * May emit a {RoleRevoked} event.
     */
    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;
}

// 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;
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.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]
 * ```solidity
 * 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.
     *
     * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
     * constructor.
     *
     * Emits an {Initialized} event.
     */
    modifier initializer() {
        bool isTopLevelCall = !_initializing;
        require(
            (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
            "Initializable: contract is already initialized"
        );
        _initialized = 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.
     *
     * 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.
     *
     * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
     * cannot be nested. If one is invoked in the context of another, execution will revert.
     *
     * 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.
     *
     * WARNING: setting the version to 255 will prevent any future reinitialization.
     *
     * Emits an {Initialized} event.
     */
    modifier reinitializer(uint8 version) {
        require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
        _initialized = version;
        _initializing = true;
        _;
        _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.
     *
     * Emits an {Initialized} event the first time it is successfully executed.
     */
    function _disableInitializers() internal virtual {
        require(!_initializing, "Initializable: contract is initializing");
        if (_initialized != type(uint8).max) {
            _initialized = type(uint8).max;
            emit Initialized(type(uint8).max);
        }
    }

    /**
     * @dev Returns the highest version that has been initialized. See {reinitializer}.
     */
    function _getInitializedVersion() internal view returns (uint8) {
        return _initialized;
    }

    /**
     * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
     */
    function _isInitializing() internal view returns (bool) {
        return _initializing;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol)

pragma solidity ^0.8.0;

import "../utils/ContextUpgradeable.sol";
import {Initializable} from "../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 Modifier to make a function callable only when the contract is not paused.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    modifier whenNotPaused() {
        _requireNotPaused();
        _;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is paused.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    modifier whenPaused() {
        _requirePaused();
        _;
    }

    /**
     * @dev Returns true if the contract is paused, and false otherwise.
     */
    function paused() public view virtual returns (bool) {
        return _paused;
    }

    /**
     * @dev Throws if the contract is paused.
     */
    function _requireNotPaused() internal view virtual {
        require(!paused(), "Pausable: paused");
    }

    /**
     * @dev Throws if the contract is not paused.
     */
    function _requirePaused() internal view virtual {
        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;
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)

pragma solidity ^0.8.0;
import {Initializable} from "../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() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be _NOT_ENTERED
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

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

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

    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        return _status == _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;
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.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
     *
     * Furthermore, `isContract` will also return true if the target contract within
     * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
     * which only has an effect at the end of a transaction.
     * ====
     *
     * [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://consensys.net/diligence/blog/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.8.0/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 functionCallWithValue(target, data, 0, "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");
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, 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) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, 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) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or 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 {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // 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
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert(errorMessage);
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol)

pragma solidity ^0.8.0;
import {Initializable} from "../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;
    }

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)

pragma solidity ^0.8.0;

import "./IERC165Upgradeable.sol";
import {Initializable} from "../../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;
}

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library MathUpgradeable {
    enum Rounding {
        Down, // Toward negative infinity
        Up, // Toward infinity
        Zero // Toward zero
    }

    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
     * with further edits by Uniswap Labs also under MIT license.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod0 := mul(x, y)
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                // The surrounding unchecked block does not change this fact.
                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1, "Math: mulDiv overflow");

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
            // See https://cs.stackexchange.com/q/138556/92363.

            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 twos = denominator & (~denominator + 1);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)

                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;

            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
            // in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10 ** 64) {
                value /= 10 ** 64;
                result += 64;
            }
            if (value >= 10 ** 32) {
                value /= 10 ** 32;
                result += 32;
            }
            if (value >= 10 ** 16) {
                value /= 10 ** 16;
                result += 16;
            }
            if (value >= 10 ** 8) {
                value /= 10 ** 8;
                result += 8;
            }
            if (value >= 10 ** 4) {
                value /= 10 ** 4;
                result += 4;
            }
            if (value >= 10 ** 2) {
                value /= 10 ** 2;
                result += 2;
            }
            if (value >= 10 ** 1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256, rounded down, of a positive value.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard signed math utilities missing in the Solidity language.
 */
library SignedMathUpgradeable {
    /**
     * @dev Returns the largest of two signed numbers.
     */
    function max(int256 a, int256 b) internal pure returns (int256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two signed numbers.
     */
    function min(int256 a, int256 b) internal pure returns (int256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two signed numbers without overflow.
     * The result is rounded towards zero.
     */
    function average(int256 a, int256 b) internal pure returns (int256) {
        // Formula from the book "Hacker's Delight"
        int256 x = (a & b) + ((a ^ b) >> 1);
        return x + (int256(uint256(x) >> 255) & (a ^ b));
    }

    /**
     * @dev Returns the absolute unsigned value of a signed value.
     */
    function abs(int256 n) internal pure returns (uint256) {
        unchecked {
            // must be unchecked in order to support `n = type(int256).min`
            return uint256(n >= 0 ? n : -n);
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)

pragma solidity ^0.8.0;

import "./math/MathUpgradeable.sol";
import "./math/SignedMathUpgradeable.sol";

/**
 * @dev String operations.
 */
library StringsUpgradeable {
    bytes16 private constant _SYMBOLS = "0123456789abcdef";
    uint8 private constant _ADDRESS_LENGTH = 20;

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        unchecked {
            uint256 length = MathUpgradeable.log10(value) + 1;
            string memory buffer = new string(length);
            uint256 ptr;
            /// @solidity memory-safe-assembly
            assembly {
                ptr := add(buffer, add(32, length))
            }
            while (true) {
                ptr--;
                /// @solidity memory-safe-assembly
                assembly {
                    mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                }
                value /= 10;
                if (value == 0) break;
            }
            return buffer;
        }
    }

    /**
     * @dev Converts a `int256` to its ASCII `string` decimal representation.
     */
    function toString(int256 value) internal pure returns (string memory) {
        return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMathUpgradeable.abs(value))));
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        unchecked {
            return toHexString(value, MathUpgradeable.log256(value) + 1);
        }
    }

    /**
     * @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] = _SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
     */
    function toHexString(address addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
    }

    /**
     * @dev Returns true if the two strings are equal.
     */
    function equal(string memory a, string memory b) internal pure returns (bool) {
        return keccak256(bytes(a)) == keccak256(bytes(b));
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) external returns (bool);

    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../../../utils/Address.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 SafeERC20 {
    using Address for address;

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

    function safeTransferFrom(
        IERC20 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(
        IERC20 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(
        IERC20 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(
        IERC20 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(IERC20 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");
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)

pragma solidity ^0.8.1;

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

        return account.code.length > 0;
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

    /**
     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly

                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/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 18 of 28 : EntropyEvents.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;

import "./EntropyStructs.sol";

interface EntropyEvents {
    event Registered(EntropyStructs.ProviderInfo provider);

    event Requested(EntropyStructs.Request request);
    event RequestedWithCallback(
        address indexed provider,
        address indexed requestor,
        uint64 indexed sequenceNumber,
        bytes32 userRandomNumber,
        EntropyStructs.Request request
    );

    event Revealed(
        EntropyStructs.Request request,
        bytes32 userRevelation,
        bytes32 providerRevelation,
        bytes32 blockHash,
        bytes32 randomNumber
    );
    event RevealedWithCallback(
        EntropyStructs.Request request,
        bytes32 userRandomNumber,
        bytes32 providerRevelation,
        bytes32 randomNumber
    );

    event ProviderFeeUpdated(address provider, uint128 oldFee, uint128 newFee);

    event ProviderUriUpdated(address provider, bytes oldUri, bytes newUri);
}

File 19 of 28 : EntropyStructs.sol
// SPDX-License-Identifier: Apache 2

pragma solidity ^0.8.0;

contract EntropyStructs {
    struct ProviderInfo {
        uint128 feeInWei;
        uint128 accruedFeesInWei;
        // The commitment that the provider posted to the blockchain, and the sequence number
        // where they committed to this. This value is not advanced after the provider commits,
        // and instead is stored to help providers track where they are in the hash chain.
        bytes32 originalCommitment;
        uint64 originalCommitmentSequenceNumber;
        // Metadata for the current commitment. Providers may optionally use this field to to help
        // manage rotations (i.e., to pick the sequence number from the correct hash chain).
        bytes commitmentMetadata;
        // Optional URI where clients can retrieve revelations for the provider.
        // Client SDKs can use this field to automatically determine how to retrieve random values for each provider.
        // TODO: specify the API that must be implemented at this URI
        bytes uri;
        // The first sequence number that is *not* included in the current commitment (i.e., an exclusive end index).
        // The contract maintains the invariant that sequenceNumber <= endSequenceNumber.
        // If sequenceNumber == endSequenceNumber, the provider must rotate their commitment to add additional random values.
        uint64 endSequenceNumber;
        // The sequence number that will be assigned to the next inbound user request.
        uint64 sequenceNumber;
        // The current commitment represents an index/value in the provider's hash chain.
        // These values are used to verify requests for future sequence numbers. Note that
        // currentCommitmentSequenceNumber < sequenceNumber.
        //
        // The currentCommitment advances forward through the provider's hash chain as values
        // are revealed on-chain.
        bytes32 currentCommitment;
        uint64 currentCommitmentSequenceNumber;
    }

    struct Request {
        // Storage slot 1 //
        address provider;
        uint64 sequenceNumber;
        // The number of hashes required to verify the provider revelation.
        uint32 numHashes;
        // Storage slot 2 //
        // The commitment is keccak256(userCommitment, providerCommitment). Storing the hash instead of both saves 20k gas by
        // eliminating 1 store.
        bytes32 commitment;
        // Storage slot 3 //
        // The number of the block where this request was created.
        // Note that we're using a uint64 such that we have an additional space for an address and other fields in
        // this storage slot. Although block.number returns a uint256, 64 bits should be plenty to index all of the
        // blocks ever generated.
        uint64 blockNumber;
        // The address that requested this random number.
        address requester;
        // If true, incorporate the blockhash of blockNumber into the generated random value.
        bool useBlockhash;
        // If true, the requester will be called back with the generated random value.
        bool isRequestWithCallback;
        // There are 2 remaining bytes of free space in this slot.
    }
}

// SPDX-License-Identifier: Apache 2
pragma solidity ^0.8.0;

import "./EntropyEvents.sol";

interface IEntropy is EntropyEvents {
    // Register msg.sender as a randomness provider. The arguments are the provider's configuration parameters
    // and initial commitment. Re-registering the same provider rotates the provider's commitment (and updates
    // the feeInWei).
    //
    // chainLength is the number of values in the hash chain *including* the commitment, that is, chainLength >= 1.
    function register(
        uint128 feeInWei,
        bytes32 commitment,
        bytes calldata commitmentMetadata,
        uint64 chainLength,
        bytes calldata uri
    ) external;

    // Withdraw a portion of the accumulated fees for the provider msg.sender.
    // Calling this function will transfer `amount` wei to the caller (provided that they have accrued a sufficient
    // balance of fees in the contract).
    function withdraw(uint128 amount) external;

    // As a user, request a random number from `provider`. Prior to calling this method, the user should
    // generate a random number x and keep it secret. The user should then compute hash(x) and pass that
    // as the userCommitment argument. (You may call the constructUserCommitment method to compute the hash.)
    //
    // This method returns a sequence number. The user should pass this sequence number to
    // their chosen provider (the exact method for doing so will depend on the provider) to retrieve the provider's
    // number. The user should then call fulfillRequest to construct the final random number.
    //
    // This method will revert unless the caller provides a sufficient fee (at least getFee(provider)) as msg.value.
    // Note that excess value is *not* refunded to the caller.
    function request(
        address provider,
        bytes32 userCommitment,
        bool useBlockHash
    ) external payable returns (uint64 assignedSequenceNumber);

    // Request a random number. The method expects the provider address and a secret random number
    // in the arguments. It returns a sequence number.
    //
    // The address calling this function should be a contract that inherits from the IEntropyConsumer interface.
    // The `entropyCallback` method on that interface will receive a callback with the generated random number.
    //
    // This method will revert unless the caller provides a sufficient fee (at least getFee(provider)) as msg.value.
    // Note that excess value is *not* refunded to the caller.
    function requestWithCallback(
        address provider,
        bytes32 userRandomNumber
    ) external payable returns (uint64 assignedSequenceNumber);

    // Fulfill a request for a random number. This method validates the provided userRandomness and provider's proof
    // against the corresponding commitments in the in-flight request. If both values are validated, this function returns
    // the corresponding random number.
    //
    // Note that this function can only be called once per in-flight request. Calling this function deletes the stored
    // request information (so that the contract doesn't use a linear amount of storage in the number of requests).
    // If you need to use the returned random number more than once, you are responsible for storing it.
    function reveal(
        address provider,
        uint64 sequenceNumber,
        bytes32 userRevelation,
        bytes32 providerRevelation
    ) external returns (bytes32 randomNumber);

    // Fulfill a request for a random number. This method validates the provided userRandomness
    // and provider's revelation against the corresponding commitment in the in-flight request. If both values are validated
    // and the requestor address is a contract address, this function calls the requester's entropyCallback method with the
    // sequence number, provider address and the random number as arguments. Else if the requestor is an EOA, it won't call it.
    //
    // Note that this function can only be called once per in-flight request. Calling this function deletes the stored
    // request information (so that the contract doesn't use a linear amount of storage in the number of requests).
    // If you need to use the returned random number more than once, you are responsible for storing it.
    //
    // Anyone can call this method to fulfill a request, but the callback will only be made to the original requester.
    function revealWithCallback(
        address provider,
        uint64 sequenceNumber,
        bytes32 userRandomNumber,
        bytes32 providerRevelation
    ) external;

    function getProviderInfo(
        address provider
    ) external view returns (EntropyStructs.ProviderInfo memory info);

    function getDefaultProvider() external view returns (address provider);

    function getRequest(
        address provider,
        uint64 sequenceNumber
    ) external view returns (EntropyStructs.Request memory req);

    function getFee(address provider) external view returns (uint128 feeAmount);

    function getAccruedPythFees()
        external
        view
        returns (uint128 accruedPythFeesInWei);

    function setProviderFee(uint128 newFeeInWei) external;

    function setProviderUri(bytes calldata newUri) external;

    function constructUserCommitment(
        bytes32 userRandomness
    ) external pure returns (bytes32 userCommitment);

    function combineRandomValues(
        bytes32 userRandomness,
        bytes32 providerRandomness,
        bytes32 blockHash
    ) external pure returns (bytes32 combinedRandomness);
}

// SPDX-License-Identifier: Apache 2
pragma solidity ^0.8.0;

abstract contract IEntropyConsumer {
    // This method is called by Entropy to provide the random number to the consumer.
    // It asserts that the msg.sender is the Entropy contract. It is not meant to be
    // override by the consumer.
    function _entropyCallback(
        uint64 sequence,
        address provider,
        bytes32 randomNumber
    ) external {
        address entropy = getEntropy();
        require(entropy != address(0), "Entropy address not set");
        require(msg.sender == entropy, "Only Entropy can call this function");

        entropyCallback(sequence, provider, randomNumber);
    }

    // getEntropy returns Entropy contract address. The method is being used to check that the
    // callback is indeed from Entropy contract. The consumer is expected to implement this method.
    // Entropy address can be found here - https://docs.pyth.network/entropy/contract-addresses
    function getEntropy() internal view virtual returns (address);

    // This method is expected to be implemented by the consumer to handle the random number.
    // It will be called by _entropyCallback after _entropyCallback ensures that the call is
    // indeed from Entropy contract.
    function entropyCallback(
        uint64 sequence,
        address provider,
        bytes32 randomNumber
    ) internal virtual;
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;


import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";

import {BlastTheBalloonErrors} from "../libraries/BlastTheBalloonErrors.sol";

/**
 * @title Blast The Balloon contract
 * @notice This is our Base contract that most other contracts inherit from. It includes many standard
 *  useful abilities like upgradeability, pausability, access control, and re-entrancy guards.
 * @author Blast The Balloon
 */

contract BlastTheBalloonBase is AccessControlUpgradeable, PausableUpgradeable, ReentrancyGuardUpgradeable {
    bytes32 public constant OWNER_ROLE = keccak256("OWNER_ROLE");
    bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
    // Pre-reserving a few slots in the base contract in case we need to add things in the future.
    // This does not actually take up gas cost or storage cost, but it does reserve the storage slots.
    // See OpenZeppelin's use of this pattern here:
    // https://github.com/OpenZeppelin/openzeppelin-contracts-ethereum-package/blob/master/contracts/GSN/Context.sol#L37
    uint256[50] private __gap1;
    uint256[50] private __gap2;
    uint256[50] private __gap3;
    uint256[50] private __gap4;

    constructor() {
        _disableInitializers();
    }

    // solhint-disable-next-line func-name-mixedcase
    function __Base_init(address owner) public onlyInitializing {
        require(owner != address(0), "Owner cannot be the zero address");
        __AccessControl_init();
        __Pausable_init();
        __ReentrancyGuard_init();

        _setupRole(OWNER_ROLE, owner);
        _setupRole(PAUSER_ROLE, owner);

        _setRoleAdmin(PAUSER_ROLE, OWNER_ROLE);
        _setRoleAdmin(OWNER_ROLE, OWNER_ROLE);
    }

    function isAdmin() public view returns (bool) {
        return hasRole(OWNER_ROLE, _msgSender());
    }

    function isPauser() public view returns (bool) {
        return hasRole(PAUSER_ROLE, _msgSender());
    }

    modifier onlyAdmin() {
        require(isAdmin(), "Must have admin role to perform this action");
        _;
    }

    modifier onlyPauser() {
        require(isPauser(), "Must have pauser role to perform this action");
        _;
    }
}

File 23 of 28 : BTBStorage.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@pythnetwork/entropy-sdk-solidity/IEntropy.sol";
import "@pythnetwork/entropy-sdk-solidity/IEntropyConsumer.sol";

import "./interfaces/IBTBConfig.sol";

abstract contract BTBStorage is IEntropyConsumer {
    enum WinType {
        NONE,
        MINOR_JACKPOT,
        KING_BLASTER,
        BLAST_MASTER
    }

    enum SpinStatus {
        NONE,
        WAITING_FOR_EXECUTE,
        EXECUTED
    }

    /// @dev Spin Information
    struct Spin {
        uint roundId;
        uint currentSpinId;
        uint lastPlayTime;
        uint spinFee; // the spin fee of user
        bytes32 userCommitment; /// user commitment Of a Spin
        uint64 sequenceNumber; /// sequence number Of a Spin
        SpinStatus status; /// status of spin
    }

    struct TimeImpact {
        uint24 firstGameTimerChancePercentage;
        uint24 secondGameTimerChancePercentage;
        uint24 thirdGameTimerChancePercentage;
        uint32 firstGameTimerImpact;
        uint32 secondGameTimerImpact;
        uint32 thirdGameTimerImpact;
    }

    struct Jackpot {
        uint24 kingBlasterChancePercentage;
        uint24 blastMasterChancePercentage;
        uint24 minorJackpotChancePercentage;
        uint24 kingBlasterPrizePercentage;
        uint24 blastMasterPrizePercentage;
        uint24 minorJackpotPrizePercentage;
        uint minorJackpotAdjustPercentage;
        uint maxMinorJackpotProbability;
    }

    struct PrizePortion {
        uint ballTreasuryPortion;
        uint majorJackpotNextRoundPortion;
        uint majorJackpotPortion;
        uint minorJackpotPortion;
    }

    struct Point {
        uint firstLevelPointP;
        uint secondLevelPointP;
        uint thirdLevelPointP;
        uint fourthLevelPointP;
        uint fifthLevelPointP;
        uint sixthLevelPointP;
        uint firstLevelPointA;
        uint secondLevelPointA;
        uint thirdLevelPointA;
        uint fourthLevelPointA;
        uint fifthLevelPointA;
        uint sixthLevelPointA;
        uint32 firstBonusTime;
        uint32 secondBonusTime;
        uint32 thirdBonusTime;
        uint firstBonusMultiplier;
        uint secondBonusMultiplier;
        uint thirdBonusMultiplier;
    }

    /// @dev Round Information
    struct Round {
        uint roundId;
        uint32 startTime;
        uint32 duration;
        uint32 maxTime;
        uint majorJackpotNextRound; // total amount of eth for next round major jackpot
        uint majorJackpot; // total amount of eth for major jackpot
        uint minorJackpot; // total amount of eth for minor jackpot
        uint totalSpins; // total spin used in a round
        uint totalWinningBlastMaster; // total winning blast master
        uint maxBlastMasterWinners; // total winning blast master
        TimeImpact timeImpact;
        Jackpot jackpot;
        PrizePortion prizePortion;
        Point point;
    }
    struct MajorJackWinnerReturn {
        uint prize;
        WinType wintype;
        uint guaranteedSeatBitMask;
    }

    /// @dev Players Information
    struct Player {
        uint lastPlayRound;
    }

    // address public constant BLAST = 0x4300000000000000000000000000000000000002;

    /// @dev Percentage denominator
    uint32 public constant PERCENTAGE_DENOMINATOR = 100000;

    uint32 public failSpinLimit;

    /// @dev Emergency status
    bool public emergencyStatus;

    /// @dev Treasury Address take 1% of Spin fee
    address payable public ballTreasury;

    /// mainnet: 0x2536FE9ab3F511540F2f9e2eC2A805005C3Dd800
    /// testnet: 0x2fc95838c71e76ec69ff817983BFf17c710F34E0
    /// @dev BlastPoints contract address. Use this address to calculate points and then distribute to users
    address public BlastPointsAddress;

    /// @dev Points Operator address
    address public pointsOperator;

    /// @notice Get HelixConfig contract's address
    IBTBConfig public config;

    /// @dev the nonce number, used to Identify round, this variable will +1 each new round
    uint public nonce;

    /// @dev the price of a spin
    uint public spinFee;

    /// @dev the percentage chance of winning king blaster
    uint24 public kingBlasterChancePercentage;

    /// @dev the percentage chance of winning blast master
    uint24 public blastMasterChancePercentage;

    /// @dev the percentage chance of winning Minor Jackpot
    uint24 public minorJackpotChancePercentage;

    /// @dev the denominator of [1%] + ([n / minorJackpotAdjustPercentage] * 100)%. where n is number of entries, eg: n = 1 then adjust = 1% + (1/100 * 100)% = 2%
    uint public minorJackpotAdjustPercentage;

    /// @dev the max probability of minor jackpot
    uint public maxMinorJackpotProbability;

    /// @dev the max number of winning king blaster
    uint public maxKingBasterWinners;

    /// @dev the max number of winning blast master
    uint public maxBlastMasterWinners;

    /// @dev king's prize percentage in the major jackpot
    uint24 public kingBlasterPrizePercentage;

    /// @dev blast's prize percentage in the major jackpot
    uint24 public blastMasterPrizePercentage;

    /// @dev minor prize percentage in the minor jackpot
    uint24 public minorJackpotPrizePercentage;

    /// @dev Number of entries since last win minor
    uint public minorJackpotEntryCount;

    /// @dev the duration of a round
    uint32 public roundDuration;

    /// @dev the max time can increase
    uint32 public roundMaxTime;

    /// @dev Percentage of treasury's ETH received when users buy spin
    uint24 public ballTreasuryPortion;

    /// @dev Percentage of majorJackpot's ETH received when users buy spin
    uint24 public majorJackpotPortion;

    /// @dev Percentage of minorJackpot's ETH received when users buy spin
    uint24 public minorJackpotPortion;

    /// @dev Percentage of next round majorJackpot's ETH received when users buy spin
    uint24 public majorJackpotNextRoundPortion;

    // /// @dev first Game timer chance percentage
    // uint24 public firstGameTimerChancePercentage;

    // /// @dev second Game timer chance percentage
    // uint24 public secondGameTimerChancePercentage;

    // /// @dev third Game timer chance percentage
    // uint24 public thirdGameTimerChancePercentage;

    // /// @dev first Game timer impact duration
    // uint32 public firstGameTimerImpact;

    // /// @dev second Game timer impact duration
    // uint32 public secondGameTimerImpact;

    // /// @dev second Game timer impact duration
    // uint32 public thirdGameTimerImpact;

    // /// @dev first level point probability
    // uint public firstLevelPointP;

    // /// @dev second level point probability
    // uint public secondLevelPointP;

    // /// @dev third level point probability
    // uint public thirdLevelPointP;

    // /// @dev fourth level point probability
    // uint public fourthLevelPointP;

    // /// @dev fifth level point probability
    // uint public fifthLevelPointP;

    // /// @dev sixth level point probability
    // uint public sixthLevelPointP;

    // /// @dev first level point amount
    // uint public firstLevelPointA;

    // /// @dev second level point amount
    // uint public secondLevelPointA;

    // /// @dev third level point amount
    // uint public thirdLevelPointA;

    // /// @dev fourth level point amount
    // uint public fourthLevelPointA;

    // /// @dev fifth level point amount
    // uint public fifthLevelPointA;

    // /// @dev sixth level point amount
    // uint public sixthLevelPointA;

    /// @dev level 1 bonus time
    // uint32 public firstBonusTime;

    // /// @dev level 2 bonus time
    // uint32 public secondBonusTime;

    // /// @dev level 3 bonus time
    // uint32 public thirdBonusTime;

    // /// @dev level 1 multiplier
    // uint public firstBonusMultiplier;

    // /// @dev level 2 multiplier
    // uint public secondBonusMultiplier;

    // /// @dev level 3 multiplier
    // uint public thirdBonusMultiplier;

    /// @dev guaranteed Slot
    uint public guaranteeSlot;

    /// @dev guaranteed Slot Count
    uint public guaranteeSlotCount;

    /// @dev the multiple probability
    uint24 public multipleProbability;

    /// @dev the number of seed back to treasury
    uint public seededBack;

    /// @dev seatposition left when new player win
    uint[] public arrayPosLeft;

    /// @dev the ante fee
    uint public anteFee;

    // uint public testVar;

    /// @dev Mapping claimeable refund amounts. address --> claimed amounts
    mapping(address => uint) public refundClaimable;

    /// @dev Mapping claimed amounts. roundId (nonce) --> address --> claimed amounts
    mapping(uint => mapping(address => uint)) public claimed;

    /// @dev Mapping pending claimed amounts. roundId (nonce) --> address --> bool claimed
    mapping(uint => mapping(address => bool)) public pendingClaimed;

    /// @dev total winning prize from all round
    mapping(address => uint) public claimableAmounts;

    /// @dev Mapping winning kingBlaster. roundId (nonce) --> winned king address
    mapping(uint => address) public kingBlasters;

    /// @dev Mapping number of blast wins in 1 round. round nonce --> user address --> number of blast wins in 1 round
    mapping(uint => mapping(address => uint)) public numberOfBlasts;

    /// @dev Mapping winning blastMaster (default is 20 blast master). round nonce --> position id --> winner address
    mapping(uint => mapping(uint => address)) public blastMasters;

    /// @dev Mapping winning blastMaster with position (default is 10 blast master). round nonce --> winner address --> position
    mapping(uint => mapping(address => uint)) public blastMasterPositions;

    /// @dev Mapping Round Information
    mapping(uint => Round) public rounds;

    /// @dev Mapping Player Information
    mapping(address => Player) public players;

    /// @dev Mapping last spin information. round nonce --> player address --> lastSpin
    mapping(uint => mapping(address => Spin)) public lastSpins;

    /// @dev Mapping guaranteed address. round nonce --> seat number --> address
    mapping(uint => mapping(uint => address)) public guaranteedSeat;

    mapping(uint => uint) public guaranteedSeatBitMaskByRound;

    /// @dev Mapping Ante
    mapping(uint => mapping(address => bool)) public ante;

    // ============================================ EVENT ============================================

    // Event emitted when a buy spin is requested. The sequence number is required to reveal the result of the flip.
    event BuySpin(address indexed player, uint roundId, uint64 sequenceNumber);

    // Event emitted when executing a new spin
    event ExecuteSpin(
        address indexed player,
        uint256 randomNumber,
        uint nonce,
        uint prizeAmount,
        string timeImpact,
        WinType indexed winType
    );
    // event ExecuteSpin(address indexed player, uint[3] uints, bool isIncrease, WinType indexed winType);

    // Event emitted when creating a new round
    event CreateNewRound(address indexed creator, uint nonce, uint startTime, uint roundDuration);

    // Event emitted when claiming prize
    event ClaimPrize(address indexed player, uint prize);

    // Event emitted when someone wins a minor prize
    event MinorWinner(address indexed player, uint roundId, uint prize, uint numberOfEntries);

    // Event emitted when someone wins a king major prize
    event MajorWinner(address indexed player, uint roundId, bool isKing, uint position);

    // ================================= PYTH GENERATE RANDOM NUMBER =================================

    // Contracts using Pyth Entropy should import the solidity SDK and then store both the Entropy contract
    // and a specific entropy provider to use for requests. Each provider commits to a sequence of random numbers.
    // Providers are then responsible for two things:
    // 1. Operating an off-chain service that reveals their random numbers once they've been committed to on-chain
    // 2. Maintaining the secrecy of the other random numbers
    // Users should choose a reliable provider who they trust to uphold these commitments.
    // (For the moment, the only available provider is 0x6CC14824Ea2918f5De5C2f75A9Da968ad4BD6344)
    IEntropy public entropy;
    address public provider;

    // The contract is required to maintain a collection of in-flight requests. This mapping allows the contract
    // to match the revealed random numbers against the original requests. The key of the map can be the
    // sequence number provided by the Entropy protocol, and the value can be whatever information the protocol
    // needs to resolve in-flight requests.
    mapping(uint64 => address) public requestedRandomNumber;

    // ================================= PYTH GENERATE RANDOM NUMBER =================================

    /// @dev Number of seed prize when init new round
    uint public numOfSeed;

    /// @dev Number of time using seed prize when user buy spin
    uint public seedCount;

    /// @dev Admin Address take refund seed
    address payable public adminAddr;
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

enum YieldMode {
    AUTOMATIC,
    VOID,
    CLAIMABLE
}

enum GasMode {
    VOID,
    CLAIMABLE 
}

interface IBlast{
    // configure
    function configureContract(address contractAddress, YieldMode _yield, GasMode gasMode, address governor) external;
    function configure(YieldMode _yield, GasMode gasMode, address governor) external;

    // base configuration options
    function configureClaimableYield() external;
    function configureClaimableYieldOnBehalf(address contractAddress) external;
    function configureAutomaticYield() external;
    function configureAutomaticYieldOnBehalf(address contractAddress) external;
    function configureVoidYield() external;
    function configureVoidYieldOnBehalf(address contractAddress) external;
    function configureClaimableGas() external;
    function configureClaimableGasOnBehalf(address contractAddress) external;
    function configureVoidGas() external;
    function configureVoidGasOnBehalf(address contractAddress) external;
    function configureGovernor(address _governor) external;
    function configureGovernorOnBehalf(address _newGovernor, address contractAddress) external;

    // claim yield
    function claimYield(address contractAddress, address recipientOfYield, uint256 amount) external returns (uint256);
    function claimAllYield(address contractAddress, address recipientOfYield) external returns (uint256);

    // claim gas
    function claimAllGas(address contractAddress, address recipientOfGas) external returns (uint256);
    function claimGasAtMinClaimRate(address contractAddress, address recipientOfGas, uint256 minClaimRateBips) external returns (uint256);
    function claimMaxGas(address contractAddress, address recipientOfGas) external returns (uint256);
    function claimGas(address contractAddress, address recipientOfGas, uint256 gasToClaim, uint256 gasSecondsToConsume) external returns (uint256);

    // read functions
    function readClaimableYield(address contractAddress) external view returns (uint256);
    function readYieldConfiguration(address contractAddress) external view returns (uint8);
    function readGasParams(address contractAddress) external view returns (uint256 etherSeconds, uint256 etherBalance, uint256 lastUpdated, GasMode);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

interface IBlastPoints {
  function configurePointsOperator(address operator) external;
  function configurePointsOperatorOnBehalf(address contractAddress, address operator) external;
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

interface IBTBConfig {

    struct Point {
        uint firstLevelPointP;
        uint secondLevelPointP;
        uint thirdLevelPointP;
        uint fourthLevelPointP;
        uint fifthLevelPointP;
        uint sixthLevelPointP;
        uint firstLevelPointA;
        uint secondLevelPointA;
        uint thirdLevelPointA;
        uint fourthLevelPointA;
        uint fifthLevelPointA;
        uint sixthLevelPointA;
        uint32 firstBonusTime;
        uint32 secondBonusTime;
        uint32 thirdBonusTime;
        uint firstBonusMultiplier;
        uint secondBonusMultiplier;
        uint thirdBonusMultiplier;
    }

    struct TimeImpact {
        uint24 firstGameTimerChancePercentage;
        uint24 secondGameTimerChancePercentage;
        uint24 thirdGameTimerChancePercentage;
        uint32 firstGameTimerImpact;
        uint32 secondGameTimerImpact;
        uint32 thirdGameTimerImpact;
    }

    function getLevelPoint() external view returns(Point memory);

    function getTimeImpact() external view returns(TimeImpact memory);
}

File 27 of 28 : BlastTheBalloonErrors.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

library BlastTheBalloonErrors {
    string public constant NOT_ALLOWED_ZERO_ADDRESS = '0';
    string public constant CALLER_NOT_MANAGER = '1'; // 'The caller of the function is not a manager'
    string public constant INVALID_SPIN = '2';
    string public constant INVALID_ROUND_DURATION = '3';
    string public constant ROUND_STILL_OPEN = '4';
    string public constant ROUND_ENDED = '5';
    string public constant ROUND_NOT_START = '6';
    string public constant SPIN_IS_NOT_EXECUTED = '7';
    string public constant INVALID_FEE = '8';
    string public constant SPIN_IS_EXECUTED = '9';
    string public constant CANT_BE_ZERO = '10';
    string public constant INVALID_PERCENTAGE = '11';
    string public constant CALLER_NOT_ENTROPY = '12';
    string public constant NOT_ENOUGH_TOKEN = '13';
    string public constant NOT_EMERGENCY = '14';
    string public constant INVALID_TIMESTAMP = '15';
    string public constant INVALID_TIME = '16';
    string public constant CLAIMED = '17';
    string public constant INVALID_AMOUNT = '18';
    string public constant INVALID_FAIL_SPIN_LIMIT = '19';
    string public constant INVALID_ANTE = '20';
    string public constant ALREADY_ANTE = '21';
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

import "../BTBStorage.sol";
import "@pythnetwork/entropy-sdk-solidity/IEntropy.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
import {BlastTheBalloonErrors} from "../libraries/BlastTheBalloonErrors.sol";

library OptimizedBTBLogicLib {
    uint32 constant PERCENTAGE_DENOMINATOR = 100000;

    /// @dev The longest possible time to start a new round
    uint32 public constant LONGEST_START_DURATION = 7 days;

    /// @dev Used to end a round a few seconds early
    uint16 public constant DURATION_TOLERANCE = 3;

    function newRoundCondition(
        uint _startRound,
        uint32 roundDuration,
        BTBStorage.Round storage previousRoundInfo
    ) external view {
        /// Check invalid round duration
        require(roundDuration > 0, BlastTheBalloonErrors.INVALID_ROUND_DURATION);

        /// Check if old round finished?
        require(
            block.timestamp > previousRoundInfo.startTime + previousRoundInfo.duration,
            BlastTheBalloonErrors.ROUND_STILL_OPEN
        );

        /// Check start round timestamp
        require(block.timestamp <= _startRound, BlastTheBalloonErrors.INVALID_TIMESTAMP);

        /// Check if timestamp start new round longer than 7 days
        require(_startRound <= block.timestamp + LONGEST_START_DURATION, BlastTheBalloonErrors.INVALID_TIMESTAMP);
    }

    function getRemainingPrize(
        uint nonce,
        uint numOfSeed,
        uint spinFee,
        uint majorJackpotPortion,
        BTBStorage.Round storage previousRoundInfo,
        mapping(uint => address) storage kingBlasters
    ) external view returns (uint, uint) {
        /// Check to see if the previous round had a king winner and blast winner if none then add previous prize to current round
        uint previousPrizeLeft = kingBlasters[nonce] == address(0) && previousRoundInfo.totalWinningBlastMaster == 0
            ? previousRoundInfo.majorJackpot
            : 0;

        uint seedPrize = kingBlasters[nonce] != address(0) || previousRoundInfo.totalWinningBlastMaster != 0
            ? (numOfSeed * spinFee * majorJackpotPortion) / PERCENTAGE_DENOMINATOR
            : 0;

        return (previousPrizeLeft, seedPrize);
    }

    /**
     * @dev Refund last fail spin
     */
    function refundSpin(
        mapping(address => BTBStorage.Player) storage players,
        mapping(uint => mapping(address => BTBStorage.Spin)) storage lastSpins,
        mapping(address => uint) storage refundClaimable,
        uint32 failSpinLimit,
        address player
    ) external {
        BTBStorage.Player storage playerInfo = players[player];
        BTBStorage.Spin storage spinInfo = lastSpins[playerInfo.lastPlayRound][player];

        if (
            block.timestamp > spinInfo.lastPlayTime + failSpinLimit &&
            spinInfo.status == BTBStorage.SpinStatus.WAITING_FOR_EXECUTE
        ) {
            spinInfo.status = BTBStorage.SpinStatus.EXECUTED;

            refundClaimable[player] += spinInfo.spinFee;

            // emit RefundSpin(player, nonce, spinInfo.spinFee);
        }
    }

    function addAnte(
        uint _nonce,
        uint anteFee,
        address player,
        address ballTreasury,
        mapping(uint => BTBStorage.Round) storage rounds,
        mapping(uint => mapping(address => bool)) storage ante
    ) external {
        uint nonce = _nonce;

        BTBStorage.Round storage roundInfo = rounds[nonce];

        roundInfo.majorJackpotNextRound += ((msg.value * roundInfo.prizePortion.majorJackpotNextRoundPortion) /
            PERCENTAGE_DENOMINATOR);
        roundInfo.majorJackpot += ((msg.value * roundInfo.prizePortion.majorJackpotPortion) / PERCENTAGE_DENOMINATOR);
        roundInfo.minorJackpot += ((msg.value * roundInfo.prizePortion.minorJackpotPortion) / PERCENTAGE_DENOMINATOR);
        uint ballTreasuryAmount = (msg.value * roundInfo.prizePortion.ballTreasuryPortion) / PERCENTAGE_DENOMINATOR;

        if (block.timestamp >= roundInfo.startTime) {
            nonce = _nonce + 1;
            roundInfo = rounds[nonce];
        }

        require(ante[nonce][player] != true, BlastTheBalloonErrors.ALREADY_ANTE);
        require(msg.value >= anteFee, BlastTheBalloonErrors.INVALID_FEE);

        (bool success, ) = ballTreasury.call{value: ballTreasuryAmount}("");
        require(success, "Failed to send Ether to treasury");

        ante[nonce][player] = true;
    }

    /**
     * @dev check buy spin condition
     */
    function buySpinCondition(BTBStorage.Round storage roundInfo, BTBStorage.Spin storage spinInfo) external view {
        uint32 adjustedDuration = roundInfo.duration < DURATION_TOLERANCE ? 0 : roundInfo.duration - DURATION_TOLERANCE;

        require(block.timestamp >= roundInfo.startTime, BlastTheBalloonErrors.ROUND_NOT_START);

        require(block.timestamp <= roundInfo.startTime + adjustedDuration, BlastTheBalloonErrors.ROUND_ENDED);

        require(
            spinInfo.status == BTBStorage.SpinStatus.NONE || spinInfo.status == BTBStorage.SpinStatus.EXECUTED,
            BlastTheBalloonErrors.SPIN_IS_NOT_EXECUTED
        );
    }

    /**
     * @dev check execute spin condition
     */
    function executeSpinCondition(
        BTBStorage.Round storage roundInfo,
        BTBStorage.Spin storage spinInfo,
        uint32 failSpinLimit
    ) external view {
        require(block.timestamp >= roundInfo.startTime, BlastTheBalloonErrors.ROUND_NOT_START);

        require(block.timestamp <= roundInfo.startTime + roundInfo.duration, BlastTheBalloonErrors.ROUND_ENDED);

        require(spinInfo.status == BTBStorage.SpinStatus.WAITING_FOR_EXECUTE, BlastTheBalloonErrors.SPIN_IS_EXECUTED);

        require(block.timestamp < spinInfo.lastPlayTime + failSpinLimit, BlastTheBalloonErrors.INVALID_SPIN);
    }

    /**
     * @dev check buy spin condition
     */
    function buySpin(
        uint nonce,
        uint totalValue,
        uint spinFee,
        address player,
        address provider,
        BTBStorage.Spin storage spinInfo,
        IEntropy entropy,
        mapping(uint64 => address) storage requestedRandomNumber,
        bytes32 userRandomNumber
    ) external returns (uint64) {
        // The entropy protocol requires the caller to pay a fee (in native gas tokens) per requested random number.
        // This fee can either be paid by the contract itself or passed on to the end user.
        // This implementation of the requestFlip method passes on the fee to the end user.
        uint pythFee = entropy.getFee(provider);
        uint totalFee = pythFee + spinFee;

        require(totalValue >= totalFee, BlastTheBalloonErrors.INVALID_FEE);

        uint acutalSpinFee = totalValue - pythFee;

        // pay the fees and request a random number from entropy
        uint64 sequenceNumber = entropy.requestWithCallback{value: pythFee}(provider, userRandomNumber);

        requestedRandomNumber[sequenceNumber] = player;

        spinInfo.roundId = nonce;
        spinInfo.lastPlayTime = block.timestamp;
        spinInfo.currentSpinId += 1;
        spinInfo.spinFee = acutalSpinFee;
        spinInfo.userCommitment = userRandomNumber;
        spinInfo.sequenceNumber = sequenceNumber;

        spinInfo.status = BTBStorage.SpinStatus.WAITING_FOR_EXECUTE;

        return sequenceNumber;
    }

    function gameTimeImpact(
        uint randomNumber,
        uint userRandom,
        BTBStorage.Round storage roundInfo
    ) external returns (string memory) {
        // Round storage roundInfo = rounds[nonce];

        uint winningPosition = randomNumber > userRandom
            ? (randomNumber % userRandom) % PERCENTAGE_DENOMINATOR
            : (userRandom % randomNumber) % PERCENTAGE_DENOMINATOR;

        uint24 firstLevelChance = roundInfo.timeImpact.firstGameTimerChancePercentage;
        uint24 secondLevelChance = roundInfo.timeImpact.secondGameTimerChancePercentage;
        uint24 thirdLevelChance = roundInfo.timeImpact.thirdGameTimerChancePercentage;

        uint32 firstLevelImpact = roundInfo.timeImpact.firstGameTimerImpact;
        uint32 secondLevelImpact = roundInfo.timeImpact.secondGameTimerImpact;
        uint32 thirdLevelImpact = roundInfo.timeImpact.thirdGameTimerImpact;

        if (winningPosition < firstLevelChance) {
            roundInfo.duration = roundInfo.duration + firstLevelImpact >= roundInfo.maxTime
                ? roundInfo.maxTime
                : roundInfo.duration + firstLevelImpact;

            return string.concat("+", Strings.toString(firstLevelImpact));
        } else if (winningPosition < firstLevelChance + secondLevelChance) {
            roundInfo.duration = (secondLevelImpact >= roundInfo.duration) ? 0 : roundInfo.duration - secondLevelImpact;

            return string.concat("-", Strings.toString(secondLevelImpact));
        } else if (winningPosition < firstLevelChance + secondLevelChance + thirdLevelChance) {
            roundInfo.duration = (thirdLevelImpact >= roundInfo.duration) ? 0 : roundInfo.duration - thirdLevelImpact;

            return string.concat("-", Strings.toString(thirdLevelImpact));
        }
    }

    /**
     * @dev Minor Winner Logic
     */
    function minorWinner(
        uint nonce,
        uint minorJackpotEntryCount,
        address player,
        mapping(uint => BTBStorage.Round) storage rounds,
        mapping(address => uint) storage claimableAmounts
    ) external returns (uint, uint) {
        BTBStorage.Round storage roundInfo = rounds[nonce];

        uint entries = minorJackpotEntryCount;
        uint minorPrize = (roundInfo.jackpot.minorJackpotPrizePercentage * roundInfo.minorJackpot) /
            PERCENTAGE_DENOMINATOR;

        roundInfo.minorJackpot = minorPrize < roundInfo.minorJackpot ? roundInfo.minorJackpot - minorPrize : 0;

        claimableAmounts[player] += minorPrize;

        return (entries, minorPrize);
    }

    /**
     * @dev Check to see if won a major, king or blast
     * @param uints an array contains:
     * 0. nonce
     * 1. winningPosition
     * 2. kingProbability
     * 3. blastProbability
     * 4. blastMasterWinningPosition
     * 5. multipleProbability
     */
    function kingWinner(
        uint[6] memory uints,
        address player,
        mapping(uint => BTBStorage.Round) storage rounds,
        mapping(uint => address) storage kingBlasters
    ) external returns (uint, BTBStorage.WinType, uint) {
        BTBStorage.Round storage roundInfo = rounds[uints[0]];

        if (uints[1] < uints[2]) {
            uint kingPrize = (roundInfo.majorJackpot * roundInfo.jackpot.kingBlasterPrizePercentage) /
                PERCENTAGE_DENOMINATOR;

            kingBlasters[uints[0]] = player;

            return (kingPrize, BTBStorage.WinType.KING_BLASTER, 0);
        }

        return (0, BTBStorage.WinType.NONE, 0);
    }

    /**
     * @param uints an array contains:
     * 0. nonce
     * 1. winningPosition
     * 2. kingProbability
     * 3. blastProbability
     * 4. blastMasterWinningPosition
     * 5. multipleProbability
     */
    function replaceBlast(
        address player,
        uint[6] memory uints,
        uint blastMasterWinningPosition,
        BTBStorage.Round storage roundInfo,
        mapping(uint => mapping(uint => address)) storage blastMasters,
        mapping(uint => mapping(address => uint)) storage blastMasterPositions,
        mapping(uint => mapping(address => uint)) storage numberOfBlasts
    ) internal returns (uint, BTBStorage.WinType, uint) {
        if (uints[1] >= uints[3]) {
            return (0, BTBStorage.WinType.NONE, 0);
        }

        address previousWinner = blastMasters[uints[0]][blastMasterWinningPosition];
        roundInfo.jackpot.blastMasterChancePercentage = roundInfo.totalWinningBlastMaster <
            roundInfo.maxBlastMasterWinners
            ? roundInfo.jackpot.blastMasterChancePercentage - uint24(uints[5])
            : roundInfo.jackpot.blastMasterChancePercentage;

        if (previousWinner == address(0)) {
            roundInfo.totalWinningBlastMaster = (roundInfo.totalWinningBlastMaster < roundInfo.maxBlastMasterWinners)
                ? roundInfo.totalWinningBlastMaster + 1
                : roundInfo.maxBlastMasterWinners;
        } else {
            blastMasterPositions[uints[0]][previousWinner] = roundInfo.maxBlastMasterWinners;
            numberOfBlasts[uints[0]][previousWinner] = numberOfBlasts[uints[0]][previousWinner] == 0
                ? 0
                : numberOfBlasts[uints[0]][previousWinner] - 1;
        }

        blastMasters[uints[0]][blastMasterWinningPosition] = player;
        blastMasterPositions[uints[0]][player] = blastMasterWinningPosition;
        numberOfBlasts[uints[0]][player] += 1;

        uint blastPrize = ((roundInfo.majorJackpot * roundInfo.jackpot.blastMasterPrizePercentage) /
            roundInfo.totalWinningBlastMaster) / PERCENTAGE_DENOMINATOR;

        return (blastPrize, BTBStorage.WinType.BLAST_MASTER, blastMasterWinningPosition);
    }

    /**
     * @dev Check to see if won a major, king or blast
     * @param uints an array contains:
     * 0. nonce
     * 1. winningPosition
     * 2. kingProbability
     * 3. blastProbability
     * 4. blastMasterWinningPosition
     * 5. multipleProbability
     */
    function blastWinner(
        uint[6] memory uints,
        address player,
        mapping(uint => BTBStorage.Round) storage rounds,
        mapping(uint => mapping(uint => address)) storage blastMasters,
        mapping(uint => mapping(address => uint)) storage blastMasterPositions,
        mapping(uint => mapping(address => uint)) storage numberOfBlasts
    ) external returns (uint, BTBStorage.WinType, uint) {
        BTBStorage.Round storage roundInfo = rounds[uints[0]];

        uint blastMasterWinningPosition = uints[4];

        if (uints[1] < uints[3]) {
            return
                replaceBlast(
                    player,
                    uints,
                    blastMasterWinningPosition,
                    roundInfo,
                    blastMasters,
                    blastMasterPositions,
                    numberOfBlasts
                );
        }

        return (0, BTBStorage.WinType.NONE, 0);
    }

    /**
     * @dev Calculate Winning Position
     * @param uints an array contains:
     * 0. nonce
     * 1. randomNumber
     * 2. guaranteeSlot
     * 3. guaranteeSlotCount
     * 4. winningPosition
     * 5. kingProbability
     * 6. blastProbability
     */
    function calculateWinningPosition(
        uint[7] memory uints,
        address player,
        BTBStorage.Round memory roundInfo,
        uint guaranteedSeatBitMask,
        mapping(uint => mapping(uint => address)) storage guaranteedSeat
    ) external returns (uint, uint) {
        uint index = 0;
        uint maxBlastMasterWinners = roundInfo.maxBlastMasterWinners;
        uint[] memory arrayPosLeft = new uint[](maxBlastMasterWinners);

        for (uint i = 0; i < maxBlastMasterWinners; ) {
            if ((guaranteedSeatBitMask & (1 << i)) == 0) {
                arrayPosLeft[index] = i;
                index += 1;
            }

            unchecked {
                ++i;
            }
        }

        uint randomNumber = type(uint).max % uints[1];
        uint blastMasterWinningPosition = arrayPosLeft[randomNumber % index];

        if (uints[4] >= uints[5] && uints[4] < uints[6]) {
            if (blastMasterWinningPosition < uints[2]) {
                guaranteedSeat[uints[0]][blastMasterWinningPosition] = player;
                guaranteedSeatBitMask ^= (1 << blastMasterWinningPosition);
            }
        }

        return (blastMasterWinningPosition, guaranteedSeatBitMask);
    }

    /**
     * @dev update Prize Information of the round
     */
    function updatePrizePool(
        BTBStorage.Round storage roundInfo,
        BTBStorage.Spin storage spinInfo,
        // uint ballTreasuryPortion,
        uint majorJackpotPortion,
        uint minorJackpotPortion,
        uint majorJackpotNextRoundPortion // address ballTreasury
    ) external {
        // update round info
        roundInfo.totalSpins += 1;
        roundInfo.majorJackpot += ((spinInfo.spinFee * majorJackpotPortion) / PERCENTAGE_DENOMINATOR);
        roundInfo.minorJackpot += ((spinInfo.spinFee * minorJackpotPortion) / PERCENTAGE_DENOMINATOR);
        roundInfo.majorJackpotNextRound += ((spinInfo.spinFee * majorJackpotNextRoundPortion) / PERCENTAGE_DENOMINATOR);
    }

    /**
     * @dev update new round Information
     */
    function updateRoundInfo(
        BTBStorage.Round storage roundInfo,
        BTBStorage.Round storage previousRoundInfo,
        uint previousPrizeLeft,
        uint seedPrize,
        uint32 _startRound,
        uint[40] memory params
    ) external {
        roundInfo.startTime = _startRound;
        roundInfo.roundId = params[0];
        roundInfo.duration = uint32(params[1]);
        roundInfo.maxTime = uint32(params[2]);
        roundInfo.majorJackpot += (previousRoundInfo.majorJackpotNextRound + previousPrizeLeft + seedPrize);
        roundInfo.minorJackpot = previousRoundInfo.minorJackpot;
        roundInfo.maxBlastMasterWinners = params[3];

        roundInfo.timeImpact.firstGameTimerChancePercentage = uint24(params[4]);
        roundInfo.timeImpact.secondGameTimerChancePercentage = uint24(params[5]);
        roundInfo.timeImpact.thirdGameTimerChancePercentage = uint24(params[6]);

        roundInfo.timeImpact.firstGameTimerImpact = uint32(params[7]);
        roundInfo.timeImpact.secondGameTimerImpact = uint32(params[8]);
        roundInfo.timeImpact.thirdGameTimerImpact = uint32(params[9]);

        roundInfo.jackpot.kingBlasterChancePercentage = uint24(params[10]);
        roundInfo.jackpot.blastMasterChancePercentage = uint24(params[11]);
        roundInfo.jackpot.minorJackpotChancePercentage = uint24(params[12]);

        roundInfo.jackpot.kingBlasterPrizePercentage = uint24(params[13]);
        roundInfo.jackpot.blastMasterPrizePercentage = uint24(params[14]);
        roundInfo.jackpot.minorJackpotPrizePercentage = uint24(params[15]);

        roundInfo.jackpot.minorJackpotAdjustPercentage = params[16];
        roundInfo.jackpot.maxMinorJackpotProbability = params[17];

        roundInfo.point.firstLevelPointP = params[18];
        roundInfo.point.secondLevelPointP = params[19];
        roundInfo.point.thirdLevelPointP = params[20];
        roundInfo.point.fourthLevelPointP = params[21];
        roundInfo.point.fifthLevelPointP = params[22];
        roundInfo.point.sixthLevelPointP = params[23];

        roundInfo.point.firstLevelPointA = params[24];
        roundInfo.point.secondLevelPointA = params[25];
        roundInfo.point.thirdLevelPointA = params[26];
        roundInfo.point.fourthLevelPointA = params[27];
        roundInfo.point.fifthLevelPointA = params[28];
        roundInfo.point.sixthLevelPointA = params[29];

        roundInfo.point.firstBonusTime = uint32(params[30]);
        roundInfo.point.secondBonusTime = uint32(params[31]);
        roundInfo.point.thirdBonusTime = uint32(params[32]);

        roundInfo.point.firstBonusMultiplier = params[33];
        roundInfo.point.secondBonusMultiplier = params[34];
        roundInfo.point.thirdBonusMultiplier = params[35];

        roundInfo.prizePortion.ballTreasuryPortion = params[36];
        roundInfo.prizePortion.majorJackpotNextRoundPortion = params[37];
        roundInfo.prizePortion.majorJackpotPortion = params[38];
        roundInfo.prizePortion.minorJackpotPortion = params[39];
    }
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 100
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "metadata": {
    "useLiteralContent": true
  },
  "libraries": {
    "contracts/libraries/OptimizedBTBLogicLib.sol": {
      "OptimizedBTBLogicLib": "0xd5cc46388c7046fb9770ae1191c14686eaec63c6"
    }
  }
}

Contract ABI

API
[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"player","type":"address"},{"indexed":false,"internalType":"uint256","name":"roundId","type":"uint256"},{"indexed":false,"internalType":"uint64","name":"sequenceNumber","type":"uint64"}],"name":"BuySpin","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"player","type":"address"},{"indexed":false,"internalType":"uint256","name":"prize","type":"uint256"}],"name":"ClaimPrize","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"creator","type":"address"},{"indexed":false,"internalType":"uint256","name":"nonce","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"startTime","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"roundDuration","type":"uint256"}],"name":"CreateNewRound","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"player","type":"address"},{"indexed":false,"internalType":"uint256","name":"randomNumber","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"nonce","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"prizeAmount","type":"uint256"},{"indexed":false,"internalType":"string","name":"timeImpact","type":"string"},{"indexed":true,"internalType":"enum BTBStorage.WinType","name":"winType","type":"uint8"}],"name":"ExecuteSpin","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":"player","type":"address"},{"indexed":false,"internalType":"uint256","name":"roundId","type":"uint256"},{"indexed":false,"internalType":"bool","name":"isKing","type":"bool"},{"indexed":false,"internalType":"uint256","name":"position","type":"uint256"}],"name":"MajorWinner","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"player","type":"address"},{"indexed":false,"internalType":"uint256","name":"roundId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"prize","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"numberOfEntries","type":"uint256"}],"name":"MinorWinner","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[],"name":"BlastPointsAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"OWNER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PAUSER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERCENTAGE_DENOMINATOR","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"__Base_init","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"sequence","type":"uint64"},{"internalType":"address","name":"provider","type":"address"},{"internalType":"bytes32","name":"randomNumber","type":"bytes32"}],"name":"_entropyCallback","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"player","type":"address"}],"name":"addAnte","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"adminAddr","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"ante","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"anteFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"arrayPosLeft","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ballTreasury","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ballTreasuryPortion","outputs":[{"internalType":"uint24","name":"","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"blastMasterChancePercentage","outputs":[{"internalType":"uint24","name":"","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"blastMasterPositions","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"blastMasterPrizePercentage","outputs":[{"internalType":"uint24","name":"","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"blastMasters","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"player","type":"address"},{"internalType":"bytes32","name":"userRandomNumber","type":"bytes32"}],"name":"buySpin","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_recipient","type":"address"}],"name":"claimAllGas","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_recipient","type":"address"}],"name":"claimAllYield","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_recipient","type":"address"}],"name":"claimMaxGas","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimPrize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"claimableAmounts","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"claimed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"config","outputs":[{"internalType":"contract IBTBConfig","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_startRound","type":"uint256"}],"name":"createNewRound","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"emergencyStatus","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"emergencyWithdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"entropy","outputs":[{"internalType":"contract IEntropy","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"failSpinLimit","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"player","type":"address"}],"name":"getActualClaimableAmounts","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_nonce","type":"uint256"}],"name":"getBlastWinners","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"player","type":"address"}],"name":"getClaimableAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"player","type":"address"}],"name":"getCurrentClaimableAmounts","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getFee","outputs":[{"internalType":"uint256","name":"fee","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_nonce","type":"uint256"}],"name":"getMinorProbability","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"guaranteeSlot","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"guaranteeSlotCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"guaranteedSeat","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"guaranteedSeatBitMaskByRound","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_entropy","type":"address"},{"internalType":"address","name":"_provider","type":"address"},{"internalType":"address","name":"_ballTreasury","type":"address"},{"internalType":"address","name":"_pointsOperator","type":"address"},{"internalType":"address","name":"_BlastPointsAddress","type":"address"},{"internalType":"address","name":"_config","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"isAdmin","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isPauser","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"kingBlasterChancePercentage","outputs":[{"internalType":"uint24","name":"","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"kingBlasterPrizePercentage","outputs":[{"internalType":"uint24","name":"","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"kingBlasters","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"lastSpins","outputs":[{"internalType":"uint256","name":"roundId","type":"uint256"},{"internalType":"uint256","name":"currentSpinId","type":"uint256"},{"internalType":"uint256","name":"lastPlayTime","type":"uint256"},{"internalType":"uint256","name":"spinFee","type":"uint256"},{"internalType":"bytes32","name":"userCommitment","type":"bytes32"},{"internalType":"uint64","name":"sequenceNumber","type":"uint64"},{"internalType":"enum BTBStorage.SpinStatus","name":"status","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"majorJackpotNextRoundPortion","outputs":[{"internalType":"uint24","name":"","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"majorJackpotPortion","outputs":[{"internalType":"uint24","name":"","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxBlastMasterWinners","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxKingBasterWinners","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxMinorJackpotProbability","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minorJackpotAdjustPercentage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minorJackpotChancePercentage","outputs":[{"internalType":"uint24","name":"","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minorJackpotEntryCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minorJackpotPortion","outputs":[{"internalType":"uint24","name":"","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minorJackpotPrizePercentage","outputs":[{"internalType":"uint24","name":"","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"multipleProbability","outputs":[{"internalType":"uint24","name":"","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nonce","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"numOfSeed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"numberOfBlasts","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"pendingClaimed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"players","outputs":[{"internalType":"uint256","name":"lastPlayRound","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pointsOperator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"provider","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"refundClaimable","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"refundSpin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"","type":"uint64"}],"name":"requestedRandomNumber","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"roundDuration","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"roundMaxTime","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"rounds","outputs":[{"internalType":"uint256","name":"roundId","type":"uint256"},{"internalType":"uint32","name":"startTime","type":"uint32"},{"internalType":"uint32","name":"duration","type":"uint32"},{"internalType":"uint32","name":"maxTime","type":"uint32"},{"internalType":"uint256","name":"majorJackpotNextRound","type":"uint256"},{"internalType":"uint256","name":"majorJackpot","type":"uint256"},{"internalType":"uint256","name":"minorJackpot","type":"uint256"},{"internalType":"uint256","name":"totalSpins","type":"uint256"},{"internalType":"uint256","name":"totalWinningBlastMaster","type":"uint256"},{"internalType":"uint256","name":"maxBlastMasterWinners","type":"uint256"},{"components":[{"internalType":"uint24","name":"firstGameTimerChancePercentage","type":"uint24"},{"internalType":"uint24","name":"secondGameTimerChancePercentage","type":"uint24"},{"internalType":"uint24","name":"thirdGameTimerChancePercentage","type":"uint24"},{"internalType":"uint32","name":"firstGameTimerImpact","type":"uint32"},{"internalType":"uint32","name":"secondGameTimerImpact","type":"uint32"},{"internalType":"uint32","name":"thirdGameTimerImpact","type":"uint32"}],"internalType":"struct BTBStorage.TimeImpact","name":"timeImpact","type":"tuple"},{"components":[{"internalType":"uint24","name":"kingBlasterChancePercentage","type":"uint24"},{"internalType":"uint24","name":"blastMasterChancePercentage","type":"uint24"},{"internalType":"uint24","name":"minorJackpotChancePercentage","type":"uint24"},{"internalType":"uint24","name":"kingBlasterPrizePercentage","type":"uint24"},{"internalType":"uint24","name":"blastMasterPrizePercentage","type":"uint24"},{"internalType":"uint24","name":"minorJackpotPrizePercentage","type":"uint24"},{"internalType":"uint256","name":"minorJackpotAdjustPercentage","type":"uint256"},{"internalType":"uint256","name":"maxMinorJackpotProbability","type":"uint256"}],"internalType":"struct BTBStorage.Jackpot","name":"jackpot","type":"tuple"},{"components":[{"internalType":"uint256","name":"ballTreasuryPortion","type":"uint256"},{"internalType":"uint256","name":"majorJackpotNextRoundPortion","type":"uint256"},{"internalType":"uint256","name":"majorJackpotPortion","type":"uint256"},{"internalType":"uint256","name":"minorJackpotPortion","type":"uint256"}],"internalType":"struct BTBStorage.PrizePortion","name":"prizePortion","type":"tuple"},{"components":[{"internalType":"uint256","name":"firstLevelPointP","type":"uint256"},{"internalType":"uint256","name":"secondLevelPointP","type":"uint256"},{"internalType":"uint256","name":"thirdLevelPointP","type":"uint256"},{"internalType":"uint256","name":"fourthLevelPointP","type":"uint256"},{"internalType":"uint256","name":"fifthLevelPointP","type":"uint256"},{"internalType":"uint256","name":"sixthLevelPointP","type":"uint256"},{"internalType":"uint256","name":"firstLevelPointA","type":"uint256"},{"internalType":"uint256","name":"secondLevelPointA","type":"uint256"},{"internalType":"uint256","name":"thirdLevelPointA","type":"uint256"},{"internalType":"uint256","name":"fourthLevelPointA","type":"uint256"},{"internalType":"uint256","name":"fifthLevelPointA","type":"uint256"},{"internalType":"uint256","name":"sixthLevelPointA","type":"uint256"},{"internalType":"uint32","name":"firstBonusTime","type":"uint32"},{"internalType":"uint32","name":"secondBonusTime","type":"uint32"},{"internalType":"uint32","name":"thirdBonusTime","type":"uint32"},{"internalType":"uint256","name":"firstBonusMultiplier","type":"uint256"},{"internalType":"uint256","name":"secondBonusMultiplier","type":"uint256"},{"internalType":"uint256","name":"thirdBonusMultiplier","type":"uint256"}],"internalType":"struct BTBStorage.Point","name":"point","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"seedCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"seededBack","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"_status","type":"bool"}],"name":"setEmergencyStatus","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"spinFee","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":"uint32","name":"_newDuration","type":"uint32"}],"name":"updateFailSpinLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newAdjustPercentage","type":"uint256"}],"name":"updateJackpotAdjustPercentage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint24[3]","name":"jackpotChances","type":"uint24[3]"}],"name":"updateJackpotChance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint24[4]","name":"portions","type":"uint24[4]"}],"name":"updatePrizePortion","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint24[3]","name":"rewardPercentages","type":"uint24[3]"}],"name":"updateRewardPercentage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newMaxBlastMasters","type":"uint256"},{"internalType":"uint256","name":"_newSpinFee","type":"uint256"},{"internalType":"uint256","name":"_anteFee","type":"uint256"},{"internalType":"uint32","name":"_newDuration","type":"uint32"},{"internalType":"uint32","name":"_newMaxTime","type":"uint32"},{"internalType":"uint24","name":"_multipleProbability","type":"uint24"}],"name":"updateRoundConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_newSeed","type":"uint32"},{"internalType":"uint256","name":"_seedBack","type":"uint256"},{"internalType":"uint256","name":"_newGuaranteeSeat","type":"uint256"}],"name":"updateSeed","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_entropy","type":"address"},{"internalType":"address","name":"_provider","type":"address"},{"internalType":"address","name":"_newTreasury","type":"address"},{"internalType":"address","name":"_newAdmin","type":"address"}],"name":"updateSystemAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]

60806040523480156200001157600080fd5b506200001c62000022565b620000e3565b600054610100900460ff16156200008f5760405162461bcd60e51b815260206004820152602760248201527f496e697469616c697a61626c653a20636f6e747261637420697320696e697469604482015266616c697a696e6760c81b606482015260840160405180910390fd5b60005460ff90811614620000e1576000805460ff191660ff9081179091556040519081527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b565b615dfa80620000f36000396000f3fe6080604052600436106104c05760003560e01c80638bed5bd211610274578063bb0c48531161014e578063d731907a116100c6578063e58378bb11610082578063e58378bb1461145f578063e63ab1e914611481578063e7487eb5146114a3578063edcc8461146114df578063f7cb789a146114ff578063f9b3d1ad1461151d57005b8063d731907a14611328578063dce54178146113ac578063e12f3a61146113cc578063e2eb41ff146113ec578063e3ec42c61461141a578063e45ed31e1461143157005b8063cc2a9a5b11610115578063cc2a9a5b1461126f578063cd2002f51461128f578063ced72f87146112af578063d00c9179146112c4578063d0efe3c7146112e8578063d547741f1461130857005b8063bb0c4853146111c0578063c18e8ac8146111d5578063c2d94aec146111fa578063c91360061461121a578063cabb1ae41461125157005b8063a178a472116101ec578063a97223ed116101a8578063a97223ed14611100578063affed0e014611124578063b3cd42541461113b578063b6db75a014611167578063b7a52bfb1461117c578063b80163a9146111a057005b8063a178a47214611026578063a217fddf1461104a578063a2ee631b1461105f578063a420f7b21461108c578063a65d60af146110b0578063a86b11b8146110c757005b8063936c8a511161023b578063936c8a5114610f5a57806399221b2714610f9c5780639b6bb91d14610fbc5780639bbe239114610fdc5780639df6efd214610ff3578063a170dfc91461100657005b80638bed5bd214610ae35780638c65c81f14610afa578063906e3d9f14610ee75780639119ddc514610efe57806391d1485414610f3a57005b806344599e34116103a55780635c975abb1161031d57806370740ac9116102d957806370740ac914610a3157806375715ce714610a4657806379502c5514610a6657806380c2c89714610a875780638183059314610a9e57806387f9798914610abf57005b80635c975abb146109785780635ead5a321461099057806367cabd66146109c957806368e9f954146109e95780636919ed7214610a095780636a8a3b9614610a1c57005b80634dbb78b71161036c5780634dbb78b71461089b5780634f1d7f6e146108bf5780635237fa55146108d657806352a5f1f8146108f65780635312ea8e146109165780635508edf11461093657005b806344599e34146107fc57806346eacf1b1461081957806347ce07cc1461083957806347fb45531461085a5780634b750bd51461087b57005b806322bc2852116104385780632c4106bd116103ff5780632c4106bd146107405780632cc5d624146107575780632f2ff15d1461077757806330858ae21461079757806336568abe146107c55780633de3786a146107e557005b806322bc285214610694578063248a9ca3146106ab578063272b1323146106db5780632a3d5741146106fb5780632bc79c121461071257005b80630ecb3c89116104875780630ecb3c89146105b1578063120aa877146105c457806316cfaa16146105fd5780631aba27301461061e5780631b43987f146106555780631f0f01ef1461067757005b806301ffc9a7146104c9578063085d4883146104fe578063088803b41461052c5780630a1d562f146105645780630e9d0c321461058c57005b366104c757005b005b3480156104d557600080fd5b506104e96104e4366004614cb5565b61153a565b60405190151581526020015b60405180910390f35b34801561050a57600080fd5b506101e65461051f906001600160a01b031681565b6040516104f59190614cdf565b34801561053857600080fd5b506101ce5461055090600160301b900462ffffff1681565b60405162ffffff90911681526020016104f5565b34801561057057600080fd5b506101c35461051f90600160281b90046001600160a01b031681565b34801561059857600080fd5b506105a36101cc5481565b6040519081526020016104f5565b6104c76105bf366004614d0f565b611571565b3480156105d057600080fd5b506105a36105df366004614d39565b6101d860209081526000928352604080842090915290825290205481565b34801561060957600080fd5b506101c45461051f906001600160a01b031681565b34801561062a57600080fd5b5061051f610639366004614d7a565b6101e7602052600090815260409020546001600160a01b031681565b34801561066157600080fd5b506101c3546104e990600160201b900460ff1681565b34801561068357600080fd5b506101d3546105509062ffffff1681565b3480156106a057600080fd5b506105a36101cb5481565b3480156106b757600080fd5b506105a36106c6366004614d97565b60009081526065602052604090206001015490565b3480156106e757600080fd5b506104c76106f6366004614db0565b6117c1565b34801561070757600080fd5b506105a36101d15481565b34801561071e57600080fd5b506105a361072d366004614db0565b6101da6020526000908152604090205481565b34801561074c57600080fd5b506105a36101c85481565b34801561076357600080fd5b506105a3610772366004614d97565b611854565b34801561078357600080fd5b506104c7610792366004614d39565b611876565b3480156107a357600080fd5b506105a36107b2366004614d97565b6101e36020526000908152604090205481565b3480156107d157600080fd5b506104c76107e0366004614d39565b6118a0565b3480156107f157600080fd5b506105a36101cd5481565b34801561080857600080fd5b506101ce546105509062ffffff1681565b34801561082557600080fd5b506105a3610834366004614db0565b61191a565b34801561084557600080fd5b506101e55461051f906001600160a01b031681565b34801561086657600080fd5b506101c55461051f906001600160a01b031681565b34801561088757600080fd5b506104c7610896366004614d97565b611abb565b3480156108a757600080fd5b506101c954610550906301000000900462ffffff1681565b3480156108cb57600080fd5b506105a36101d45481565b3480156108e257600080fd5b506104c76108f1366004614dcb565b611b23565b34801561090257600080fd5b506104c7610911366004614df3565b611ca0565b34801561092257600080fd5b506104c7610931366004614d97565b611d7c565b34801561094257600080fd5b5061051f610951366004614e31565b6101dd6020908152600092835260408084209091529082529020546001600160a01b031681565b34801561098457600080fd5b5060975460ff166104e9565b34801561099c57600080fd5b506105a36109ab366004614d39565b6101dc60209081526000928352604080842090915290825290205481565b3480156109d557600080fd5b506104c76109e4366004614e53565b611ea2565b3480156109f557600080fd5b506104c7610a04366004614eb9565b61202c565b6104c7610a17366004614d97565b612163565b348015610a2857600080fd5b506104e96127b2565b348015610a3d57600080fd5b506104c76127d1565b348015610a5257600080fd5b506104c7610a61366004614db0565b6129a5565b348015610a7257600080fd5b506101c65461051f906001600160a01b031681565b348015610a9357600080fd5b506105a36101ca5481565b348015610aaa57600080fd5b506101ea5461051f906001600160a01b031681565b348015610acb57600080fd5b506101c95461055090600160301b900462ffffff1681565b348015610aef57600080fd5b506105a36101e95481565b348015610b0657600080fd5b50610ecd610b15366004614d97565b6101df6020528060005260406000206000915090508060000154908060010160009054906101000a900463ffffffff16908060010160049054906101000a900463ffffffff16908060010160089054906101000a900463ffffffff1690806002015490806003015490806004015490806005015490806006015490806007015490806008016040518060c00160405290816000820160009054906101000a900462ffffff1662ffffff1662ffffff1681526020016000820160039054906101000a900462ffffff1662ffffff1662ffffff1681526020016000820160069054906101000a900462ffffff1662ffffff1662ffffff1681526020016000820160099054906101000a900463ffffffff1663ffffffff1663ffffffff16815260200160008201600d9054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016000820160119054906101000a900463ffffffff1663ffffffff1663ffffffff16815250509080600901604051806101000160405290816000820160009054906101000a900462ffffff1662ffffff1662ffffff1681526020016000820160039054906101000a900462ffffff1662ffffff1662ffffff1681526020016000820160069054906101000a900462ffffff1662ffffff1662ffffff1681526020016000820160099054906101000a900462ffffff1662ffffff1662ffffff16815260200160008201600c9054906101000a900462ffffff1662ffffff1662ffffff16815260200160008201600f9054906101000a900462ffffff1662ffffff1662ffffff168152602001600182015481526020016002820154815250509080600c0160405180608001604052908160008201548152602001600182015481526020016002820154815260200160038201548152505090806010016040518061024001604052908160008201548152602001600182015481526020016002820154815260200160038201548152602001600482015481526020016005820154815260200160068201548152602001600782015481526020016008820154815260200160098201548152602001600a8201548152602001600b8201548152602001600c820160009054906101000a900463ffffffff1663ffffffff1663ffffffff168152602001600c820160049054906101000a900463ffffffff1663ffffffff1663ffffffff168152602001600c820160089054906101000a900463ffffffff1663ffffffff1663ffffffff168152602001600d8201548152602001600e8201548152602001600f8201548152505090508e565b6040516104f59e9d9c9b9a99989796959493929190614fcc565b348015610ef357600080fd5b506105a36101cf5481565b348015610f0a57600080fd5b506104e9610f19366004614d39565b6101d960209081526000928352604080842090915290825290205460ff1681565b348015610f4657600080fd5b506104e9610f55366004614d39565b612aa8565b348015610f6657600080fd5b5061051f610f75366004614e31565b6101e26020908152600092835260408084209091529082529020546001600160a01b031681565b348015610fa857600080fd5b506104c7610fb736600461512d565b612ad3565b348015610fc857600080fd5b506104c7610fd7366004614dcb565b612b55565b348015610fe857600080fd5b506105a36101d65481565b6104c7611001366004614db0565b612cc7565b34801561101257600080fd5b506105a3611021366004614d97565b612d7f565b34801561103257600080fd5b506101d05461055090600160581b900462ffffff1681565b34801561105657600080fd5b506105a3600081565b34801561106b57600080fd5b5061107f61107a366004614d97565b612e09565b6040516104f5919061514a565b34801561109857600080fd5b506101d05461055090600160881b900462ffffff1681565b3480156110bc57600080fd5b506105a36101e85481565b3480156110d357600080fd5b506105a36110e2366004614d39565b6101de60209081526000928352604080842090915290825290205481565b34801561110c57600080fd5b506101d05461055090600160401b900462ffffff1681565b34801561113057600080fd5b506105a36101c75481565b34801561114757600080fd5b50611152620186a081565b60405163ffffffff90911681526020016104f5565b34801561117357600080fd5b506104e9612f1e565b34801561118857600080fd5b506101ce54610550906301000000900462ffffff1681565b3480156111ac57600080fd5b506104c76111bb366004615197565b612f38565b3480156111cc57600080fd5b506104c7612f7c565b3480156111e157600080fd5b506101d05461115290600160201b900463ffffffff1681565b34801561120657600080fd5b506104c7611215366004614db0565b6130a0565b34801561122657600080fd5b5061051f611235366004614d97565b6101db602052600090815260409020546001600160a01b031681565b34801561125d57600080fd5b506101c3546111529063ffffffff1681565b34801561127b57600080fd5b506104c761128a3660046151b9565b6130f0565b34801561129b57600080fd5b506104c76112aa366004614db0565b6133f9565b3480156112bb57600080fd5b506105a3613449565b3480156112d057600080fd5b506101d05461055090600160701b900462ffffff1681565b3480156112f457600080fd5b506104c761130336600461523e565b6134d2565b34801561131457600080fd5b506104c7611323366004614d39565b61365b565b34801561133457600080fd5b50611399611343366004614d39565b6101e160209081526000928352604080842090915290825290208054600182015460028301546003840154600485015460059095015493949293919290916001600160401b03811690600160401b900460ff1687565b6040516104f597969594939291906152bb565b3480156113b857600080fd5b506104c76113c736600461531d565b613680565b3480156113d857600080fd5b506105a36113e7366004614db0565b61381a565b3480156113f857600080fd5b506105a3611407366004614db0565b6101e06020526000908152604090205481565b34801561142657600080fd5b506105a36101d25481565b34801561143d57600080fd5b506105a361144c366004614db0565b6101d76020526000908152604090205481565b34801561146b57600080fd5b506105a3600080516020615d8583398151915281565b34801561148d57600080fd5b506105a3600080516020615da583398151915281565b3480156114af57600080fd5b506104e96114be366004614d39565b6101e460209081526000928352604080842090915290825290205460ff1681565b3480156114eb57600080fd5b506105a36114fa366004614db0565b61387c565b34801561150b57600080fd5b506101d0546111529063ffffffff1681565b34801561152957600080fd5b506101c9546105509062ffffff1681565b60006001600160e01b03198216637965db0b60e01b148061156b57506301ffc9a760e01b6001600160e01b03198316145b92915050565b611579613a6c565b6101c75460009081526101df602090815260408083206101e183528184206001600160a01b03871685529092529091206115b284613ac5565b604051638f4fa25160e01b8152600481018390526024810182905273d5cc46388c7046fb9770ae1191c14686eaec63c690638f4fa2519060440160006040518083038186803b15801561160457600080fd5b505af4158015611618573d6000803e3d6000fd5b5050505061162584613b5c565b6101c75460009081526101e4602090815260408083206001600160a01b03881684528252918290205482518084019093526002835261032360f41b9183019190915260ff166116905760405162461bcd60e51b8152600401611687919061538f565b60405180910390fd5b506101c7546101c8546101e6546101e554604051631295edbd60e21b8152600481019490945234602485015260448401929092526001600160a01b038088166064850152908116608484015260a483018490521660c48201526101e760e4820152610104810184905260009073d5cc46388c7046fb9770ae1191c14686eaec63c690634a57b6f49061012401602060405180830381865af4158015611739573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061175d91906153a2565b6101c754604080519182526001600160401b03831660208301529192506001600160a01b038716917f5a0920da7c4df7f0e5da0ce23542a31a806ddda495b3f8da7ca95e92ad030dbc910160405180910390a25050506117bd600160c955565b5050565b6117c9612f1e565b6117e55760405162461bcd60e51b8152600401611687906153bf565b604051634aa7d2f760e11b81526002604360981b019063954fa5ee90611811903090859060040161540a565b6020604051808303816000875af1158015611830573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117bd9190615424565b6101d5818154811061186557600080fd5b600091825260209091200154905081565b60008281526065602052604090206001015461189181613c0a565b61189b8383613c14565b505050565b6001600160a01b03811633146119105760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b6064820152608401611687565b6117bd8282613c9a565b6101c75460009081526101df602090815260408083206101d983528184206001600160a01b0386168552909252822054829060ff161561195b579392505050565b6101c75460009081526101dc602090815260408083206001600160a01b038816845290915281205460038401546009850154919291620186a0916119aa91600160481b900462ffffff16615453565b6119b49190615488565b9050600084600601546000146119e85784600601548286600301546119d9919061549c565b6119e39190615488565b6119eb565b60005b6101c75460009081526101db60205260409020549091506001600160a01b03808916911603611a4a578460060154600003611a3d57818560030154611a30919061549c565b611a3a90856154af565b93505b611a4782856154af565b93505b8215611ab0576101c75460009081526101db60205260409020546001600160a01b0316611a995782856006015483611a829190615488565b611a8c9190615453565b611a9690856154af565b93505b611aa38382615453565b611aad90856154af565b93505b509195945050505050565b611ac3612f1e565b611adf5760405162461bcd60e51b8152600401611687906153bf565b604080518082019091526002815261313160f01b6020820152620186a0821115611b1c5760405162461bcd60e51b8152600401611687919061538f565b506101ca55565b611b2b612f1e565b611b475760405162461bcd60e51b8152600401611687906153bf565b6000611b5960408301602084016154d8565b611b6660208401846154d8565b611b7091906154f5565b62ffffff169050620186a063ffffffff1681111560405180604001604052806002815260200161313160f01b81525090611bbd5760405162461bcd60e51b8152600401611687919061538f565b50620186a0611bd260608401604085016154d8565b62ffffff16111560405180604001604052806002815260200161313160f01b81525090611c125760405162461bcd60e51b8152600401611687919061538f565b50611c2060208301836154d8565b6101ce805462ffffff191662ffffff92909216919091179055611c4960408301602084016154d8565b6101ce805462ffffff9290921663010000000265ffffff00000019909216919091179055611c7d60608301604084016154d8565b6101ce60066101000a81548162ffffff021916908362ffffff1602179055505050565b6000611cb56101e5546001600160a01b031690565b90506001600160a01b038116611d075760405162461bcd60e51b8152602060048201526017602482015276115b9d1c9bdc1e481859191c995cdcc81b9bdd081cd95d604a1b6044820152606401611687565b336001600160a01b03821614611d6b5760405162461bcd60e51b815260206004820152602360248201527f4f6e6c7920456e74726f70792063616e2063616c6c20746869732066756e637460448201526234b7b760e91b6064820152608401611687565b611d76848484613d01565b50505050565b611d84613a6c565b611d8c612f1e565b611da85760405162461bcd60e51b8152600401611687906153bf565b6101c3546040805180820190915260028152610c4d60f21b602082015290600160201b900460ff16611ded5760405162461bcd60e51b8152600401611687919061538f565b508047101560405180604001604052806002815260200161062760f31b81525090611e2b5760405162461bcd60e51b8152600401611687919061538f565b50604051600090339083908381818185875af1925050503d8060008114611e6e576040519150601f19603f3d011682016040523d82523d6000602084013e611e73565b606091505b5050905080611e945760405162461bcd60e51b815260040161168790615518565b50611e9f600160c955565b50565b611eaa612f1e565b611ec65760405162461bcd60e51b8152600401611687906153bf565b6040805180820190915260018152600360fc1b60208201526001600160a01b038516611f055760405162461bcd60e51b8152600401611687919061538f565b506040805180820190915260018152600360fc1b60208201526001600160a01b038416611f455760405162461bcd60e51b8152600401611687919061538f565b506040805180820190915260018152600360fc1b60208201526001600160a01b038316611f855760405162461bcd60e51b8152600401611687919061538f565b506040805180820190915260018152600360fc1b60208201526001600160a01b038216611fc55760405162461bcd60e51b8152600401611687919061538f565b506101e580546001600160a01b039586166001600160a01b0319918216179091556101e68054948616948216949094179093556101c38054928516600160281b02600160281b600160c81b0319909316929092179091556101ea8054919093169116179055565b612034612f1e565b6120505760405162461bcd60e51b8152600401611687906153bf565b6101c75460009081526101df6020526040902060018101546120819063ffffffff600160201b82048116911661555c565b63ffffffff1642118061209d5750600181015463ffffffff1642105b604051806040016040528060018152602001600d60fa1b815250906120d55760405162461bcd60e51b8152600401611687919061538f565b50604080518082019091526002815261031360f41b602082015263ffffffff85166121135760405162461bcd60e51b8152600401611687919061538f565b50604080518082019091526002815261031360f41b60208201528261214b5760405162461bcd60e51b8152600401611687919061538f565b505063ffffffff9092166101e8556101d4556101d155565b61216b612f1e565b6121875760405162461bcd60e51b8152600401611687906153bf565b6101c75460009081526101df6020526040908190206101d054915163063f43a360e41b81526004810184905263ffffffff9092166024830152604482018190529073d5cc46388c7046fb9770ae1191c14686eaec63c6906363f43a309060640160006040518083038186803b1580156121ff57600080fd5b505af4158015612213573d6000803e3d6000fd5b50506101c7546101e8546101c8546101d05460405163fce0536160e01b8152600481019490945260248401929092526044830152600160581b900462ffffff166064820152608481018490526101db60a48201526000925082915073d5cc46388c7046fb9770ae1191c14686eaec63c69063fce053619060c4016040805180830381865af41580156122a9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122cd9190615579565b91509150806000036122e2576101e9546122e5565b60005b6101e98190555060016101c7600082825461230091906154af565b909155505060006101d28190556101c75481526101df60205260408082206101c65482516315cef56d60e11b815292519193926001600160a01b0390911691632b9deada91600480820192610240929091908290030181865afa15801561236b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061238f91906155e7565b905060006101c660009054906101000a90046001600160a01b03166001600160a01b031663a8bcf2ab6040518163ffffffff1660e01b815260040160c060405180830381865afa1580156123e7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061240b91906156dc565b905060006040518061050001604052806101c75481526020016101d060009054906101000a900463ffffffff1663ffffffff1681526020016101d060049054906101000a900463ffffffff1663ffffffff1681526020016101cd548152602001836000015162ffffff168152602001836020015162ffffff168152602001836040015162ffffff168152602001836060015163ffffffff168152602001836080015163ffffffff1681526020018360a0015163ffffffff1681526020016101c960009054906101000a900462ffffff1662ffffff1681526020016101c960039054906101000a900462ffffff1662ffffff1681526020016101c960069054906101000a900462ffffff1662ffffff1681526020016101ce60009054906101000a900462ffffff1662ffffff1681526020016101ce60039054906101000a900462ffffff1662ffffff1681526020016101ce60069054906101000a900462ffffff1662ffffff1681526020016101ca5481526020016101cb54815260200184600001518152602001846020015181526020018460400151815260200184606001518152602001846080015181526020018460a0015181526020018460c0015181526020018460e001518152602001846101000151815260200184610120015181526020018461014001518152602001846101600151815260200184610180015163ffffffff168152602001846101a0015163ffffffff168152602001846101c0015163ffffffff168152602001846101e001518152602001846102000151815260200184610220015181526020016101d060089054906101000a900462ffffff1662ffffff1681526020016101d060119054906101000a900462ffffff1662ffffff1681526020016101d0600b9054906101000a900462ffffff1662ffffff1681526020016101d0600e9054906101000a900462ffffff1662ffffff16815250905073d5cc46388c7046fb9770ae1191c14686eaec63c663353fd196858989898d876040518763ffffffff1660e01b815260040161270d96959493929190615786565b60006040518083038186803b15801561272557600080fd5b505af4158015612739573d6000803e3d6000fd5b505050506127443390565b6101c75460018601546040805192835263ffffffff8083166020850152600160201b90920490911682820152516001600160a01b0392909216917fd4f4eda7fb0fd42c44eceae5e61ca3734aa4c9843303a9bed659b38d6872b26a9181900360600190a25050505050505050565b60006127cc600080516020615da583398151915233612aa8565b905090565b6127d9613a6c565b6101c75460009081526101df602052604090206127f533613ac5565b6127fe33613b5c565b600181015460009061281f9063ffffffff600160201b82048116911661555c565b63ffffffff164211801561285257506101c75460009081526101d96020908152604080832033845290915290205460ff16155b1561288c576128603361191a565b6101c75460009081526101d9602090815260408083203384529091529020805460ff1916600117905590505b3360009081526101da60205260408120546128a89083906154af565b90508047101560405180604001604052806002815260200161313360f01b815250906128e75760405162461bcd60e51b8152600401611687919061538f565b503360008181526101da60205260408082208290555190919083908381818185875af1925050503d806000811461293a576040519150601f19603f3d011682016040523d82523d6000602084013e61293f565b606091505b50509050806129605760405162461bcd60e51b815260040161168790615518565b60405182815233907f0ba90eb685b3b0006a3c394dd506a37e5e136aa5340c16793a2d546bd6721b8f9060200160405180910390a2505050506129a3600160c955565b565b600054610100900460ff166129cc5760405162461bcd60e51b8152600401611687906157e5565b6001600160a01b038116612a225760405162461bcd60e51b815260206004820181905260248201527f4f776e65722063616e6e6f7420626520746865207a65726f20616464726573736044820152606401611687565b612a2a613f0c565b612a32613f33565b612a3a613f62565b612a52600080516020615d8583398151915282613f91565b612a6a600080516020615da583398151915282613f91565b612a90600080516020615da5833981519152600080516020615d85833981519152613f9b565b611e9f600080516020615d8583398151915280613f9b565b60009182526065602090815260408084206001600160a01b0393909316845291905290205460ff1690565b612adb612f1e565b612af75760405162461bcd60e51b8152600401611687906153bf565b604080518082019091526002815261313960f01b6020820152600563ffffffff831611612b375760405162461bcd60e51b8152600401611687919061538f565b506101c3805463ffffffff191663ffffffff92909216919091179055565b612b5d612f1e565b612b795760405162461bcd60e51b8152600401611687906153bf565b620186a0612b8d60608301604084016154d8565b612b9d60408401602085016154d8565b612baa60208501856154d8565b612bb491906154f5565b612bbe91906154f5565b62ffffff16111560405180604001604052806002815260200161313160f01b81525090612bfe5760405162461bcd60e51b8152600401611687919061538f565b50612c0c60208201826154d8565b6101c9805462ffffff191662ffffff92909216919091179055612c3560408201602083016154d8565b6101c9805462ffffff9290921663010000000265ffffff00000019909216919091179055612c6960608201604083016154d8565b6101c9805462ffffff928316600160301b0268ffffff000000000000198216811792839055612ca89363010000009093048316929081169116176154f5565b612cba9062ffffff16620186a0615830565b63ffffffff166101cb5550565b612ccf613a6c565b6101c7546101d6546101c3546040516362d764b560e01b8152600481019390935260248301919091526001600160a01b038084166044840152600160281b9091041660648201526101df60848201526101e460a482015273d5cc46388c7046fb9770ae1191c14686eaec63c6906362d764b59060c40160006040518083038186803b158015612d5d57600080fd5b505af4158015612d71573d6000803e3d6000fd5b50505050611e9f600160c955565b60008181526101df602052604081206101c754831115612da25750600092915050565b600a8101546101cf5460009190612dbd90620186a090615453565b612dc79190615488565b6009830154612de29190600160301b900462ffffff166154af565b9050600082600901600201548210612dfe57600b830154612e00565b815b95945050505050565b60008181526101df602052604090206101c75460609190831115612e4d5760408051600180825281830190925290602080830190803683370190505b509392505050565b600081600701546001600160401b03811115612e6b57612e6b61559d565b604051908082528060200260200182016040528015612e94578160200160208202803683370190505b5090508160060154600003612eaa579392505050565b60005b8260070154811015612e455760008581526101dd6020908152604080832084845290915290205482516001600160a01b0390911690839083908110612ef457612ef46154c2565b6001600160a01b039092166020928302919091019091015280612f168161584d565b915050612ead565b60006127cc600080516020615d8583398151915233612aa8565b612f40612f1e565b612f5c5760405162461bcd60e51b8152600401611687906153bf565b6101c38054911515600160201b0264ff0000000019909216919091179055565b612f84613a6c565b612f8d33613ac5565b3360009081526101d760209081526040918290205482518084019093526002835261062760f31b91830191909152612fd85760405162461bcd60e51b8152600401611687919061538f565b503360009081526101d760209081526040808320805493905580518082019091526002815261062760f31b918101919091524782111561302b5760405162461bcd60e51b8152600401611687919061538f565b50604051600090339083908381818185875af1925050503d806000811461306e576040519150601f19603f3d011682016040523d82523d6000602084013e613073565b606091505b50509050806130945760405162461bcd60e51b815260040161168790615518565b50506129a3600160c955565b6130a8612f1e565b6130c45760405162461bcd60e51b8152600401611687906153bf565b60405163430021db60e11b81526002604360981b019063860043b690611811903090859060040161540a565b600054610100900460ff16158080156131105750600054600160ff909116105b8061312a5750303b15801561312a575060005460ff166001145b61318d5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401611687565b6000805460ff1916600117905580156131b0576000805461ff0019166101001790555b6101e580546001600160a01b03808a166001600160a01b0319928316179092556101e680548984169083161790556101c6805485841692169190911790556101c38054918716600160281b02600160281b600160c81b03199092169190911790556132183390565b6101ea80546001600160a01b03199081166001600160a01b03938416179091556101c4805482168684169081179091556101c580549092169287169290921790556040516336b91f2b60e01b81526336b91f2b9061327a908790600401614cdf565b600060405180830381600087803b15801561329457600080fd5b505af11580156132a8573d6000803e3d6000fd5b505050506002604360981b016001600160a01b0316634e606c476040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156132ee57600080fd5b505af1158015613302573d6000803e3d6000fd5b505050506002604360981b016001600160a01b031663f098767a6040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561334857600080fd5b505af115801561335c573d6000803e3d6000fd5b50505050613368613fe6565b6101c7805460009081526101df6020526040902060018101805463ffffffff19164263ffffffff16179055905481556101cd546006909101556133aa336129a5565b80156133f0576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b50505050505050565b613401612f1e565b61341d5760405162461bcd60e51b8152600401611687906153bf565b60405163662aa11d60e01b81526002604360981b019063662aa11d90611811903090859060040161540a565b6101e5546101e654604051631711922960e31b81526000926001600160a01b039081169263b88c9148926134839290911690600401614cdf565b602060405180830381865afa1580156134a0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134c49190615866565b6001600160801b0316905090565b6134da612f1e565b6134f65760405162461bcd60e51b8152600401611687906153bf565b6101c75460009081526101df6020526040902060018101546135279063ffffffff600160201b82048116911661555c565b63ffffffff164211806135435750600181015463ffffffff1642105b604051806040016040528060018152602001600d60fa1b8152509061357b5760405162461bcd60e51b8152600401611687919061538f565b508263ffffffff168463ffffffff16111560405180604001604052806002815260200161189b60f11b815250906135c55760405162461bcd60e51b8152600401611687919061538f565b50604080518082019091526002815261031360f41b6020820152876135fd5760405162461bcd60e51b8152600401611687919061538f565b50506101c8949094556101d6929092556101d0805463ffffffff938416600160201b0267ffffffffffffffff1990911693909216929092171790556101cd919091556101d3805462ffffff90921662ffffff19909216919091179055565b60008281526065602052604090206001015461367681613c0a565b61189b8383613c9a565b613688612f1e565b6136a45760405162461bcd60e51b8152600401611687906153bf565b60006136b660808301606084016154d8565b6136c660608401604085016154d8565b6136d660408501602086016154d8565b6136e360208601866154d8565b6136ed91906154f5565b6136f791906154f5565b61370191906154f5565b62ffffff169050620186a063ffffffff16811460405180604001604052806002815260200161313160f01b8152509061374d5760405162461bcd60e51b8152600401611687919061538f565b5061375b60208301836154d8565b6101d0805462ffffff92909216600160701b0262ffffff60701b1990921691909117905561378f60408301602084016154d8565b6101d0805462ffffff92909216600160581b0262ffffff60581b199092169190911790556137c360608301604084016154d8565b6101d0805462ffffff92909216600160881b0262ffffff60881b199092169190911790556137f760808301606084016154d8565b6101d060086101000a81548162ffffff021916908362ffffff1602179055505050565b6101c75460009081526101df6020526040812081806138388561387c565b60018401549091506138599063ffffffff600160201b82048116911661555c565b63ffffffff164211156138725761386f8561191a565b91505b612e0082826154af565b6001600160a01b03811660009081526101e06020908152604080832080548085526101df9093529083206101c754919290911115806138e05750815460009081526101d9602090815260408083206001600160a01b038816845290915290205460ff165b15613904575050506001600160a01b031660009081526101da602052604090205490565b6001600160a01b03841660008181526101da6020908152604080832054865484526101dc83528184209484529390915281205460038401546009850154919291620186a09161395e91600160481b900462ffffff16615453565b6139689190615488565b90506000846006015460001461399c57846006015482866003015461398d919061549c565b6139979190615488565b61399f565b60005b865460009081526101db60205260409020549091506001600160a01b03808a169116036139fc5784600601546000036139ef578185600301546139e2919061549c565b6139ec90856154af565b93505b6139f982856154af565b93505b8215613a6057855460009081526101db60205260409020546001600160a01b0316613a495782856006015483613a329190615488565b613a3c9190615453565b613a4690856154af565b93505b613a538382615453565b613a5d90856154af565b93505b50919695505050505050565b600260c95403613abe5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401611687565b600260c955565b6101c354604051632992684f60e01b81526101e060048201526101e160248201526101d7604482015263ffffffff90911660648201526001600160a01b038216608482015273d5cc46388c7046fb9770ae1191c14686eaec63c690632992684f9060a40160006040518083038186803b158015613b4157600080fd5b505af4158015613b55573d6000803e3d6000fd5b5050505050565b6001600160a01b03811660009081526101e0602052604090206101c754815410613b84575050565b805460009081526101d9602090815260408083206001600160a01b038616845290915290205460ff16613bfa57613bba8261387c565b6001600160a01b03831660008181526101da6020908152604080832094909455845482526101d981528382209282529190915220805460ff191660011790555b6101c754905550565b600160c955565b611e9f81336140ca565b613c1e8282612aa8565b6117bd5760008281526065602090815260408083206001600160a01b03851684529091529020805460ff19166001179055613c563390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b613ca48282612aa8565b156117bd5760008281526065602090815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b6101e5546001600160a01b0316336001600160a01b03161460405180604001604052806002815260200161189960f11b81525090613d525760405162461bcd60e51b8152600401611687919061538f565b506101c75460008181526101df602090815260408083206101e38352818420546001600160401b03891685526101e78452828520549585526101e184528285206001600160a01b0390961680865295909352928190206101c35491516310d481af60e11b8152600481018590526024810182905263ffffffff9092166044830152929391929073d5cc46388c7046fb9770ae1191c14686eaec63c6906321a9035e9060640160006040518083038186803b158015613e0f57600080fd5b505af4158015613e23573d6000803e3d6000fd5b50505050613e318482614123565b60048101546000808080613e448a61439c565b93509350935093506000613e5b8b60001c87614467565b90506000613e758662ffffff8087169086168f8e8e61450d565b80516020820151919250906000816003811115613e9457613e946152a5565b03613eaa57613ea488888d614901565b90925090505b60058a0180546002919060ff60401b1916600160401b83021790555082604001516101e360006101c754815260200190815260200160002081905550613efa8b8f60001c6101c754858886614a46565b50505050505050505050505050505050565b600054610100900460ff166129a35760405162461bcd60e51b8152600401611687906157e5565b600054610100900460ff16613f5a5760405162461bcd60e51b8152600401611687906157e5565b6129a3614aa7565b600054610100900460ff16613f895760405162461bcd60e51b8152600401611687906157e5565b6129a3614ada565b6117bd8282613c14565b600082815260656020526040808220600101805490849055905190918391839186917fbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff9190a4505050565b660e35fa931a00006101c88190556101d655600a6101cd556101c38054603c63ffffffff199091161790556101d080546001600160a01b0319166f3a980144380007d0000001a4000001a41790556101c9805468ffffffffffffffffff199081166664003a980005dc17918290556101ce80549091166801117000fde80088b81790556113886101ca556140899062ffffff6301000000909104166105dc6154f5565b61409b9062ffffff16620186a0615830565b63ffffffff166101cb5560056101d48190556101e85560036101d1556101d3805462ffffff19166103e8179055565b6140d48282612aa8565b6117bd576140e181614b01565b6140ec836020614b13565b6040516020016140fd92919061588f565b60408051601f198184030181529082905262461bcd60e51b82526116879160040161538f565b600e820154600c8301546003830154600091620186a0916141449190615453565b61414e9190615488565b6101c354604051919250600091600160281b9091046001600160a01b03169083908381818185875af1925050503d80600081146141a7576040519150601f19603f3d011682016040523d82523d6000602084013e6141ac565b606091505b50509050806141fd5760405162461bcd60e51b815260206004820181905260248201527f4661696c656420746f2073656e6420457468657220746f2074726561737572796044820152606401611687565b6101d4546101e954101561430a5760016101e9600082825461421f91906154af565b90915550506003840154600090620186a09061423c908690615453565b6142469190615488565b6101ea546040519192506001600160a01b0316908290600081818185875af1925050503d8060008114614295576040519150601f19603f3d011682016040523d82523d6000602084013e61429a565b606091505b505080925050816143045760405162461bcd60e51b815260206004820152602e60248201527f4661696c656420746f2073656e64205365656420457468657220746f2070726960448201526d1e99481c1bdbdb081dd85b1b195d60921b6064820152608401611687565b60009350505b600f850154600d860154604051600162beb8ff60e01b031981526004810188905260248101879052604481018690526064810192909252608482015273d5cc46388c7046fb9770ae1191c14686eaec63c69063ff4147019060a40160006040518083038186803b15801561437d57600080fd5b505af4158015614391573d6000803e3d6000fd5b505050505050505050565b6101c75460009081526101df6020526040812060098101548291829182919062ffffff8082169184916143d7916301000000900416836154af565b600a8401546101cf549192506000916143f490620186a090615453565b6143fe9190615488565b60098501546144199190600160301b900462ffffff166154af565b905060008460090160020154821061443557600b850154614437565b815b905061444383826154af565b90506000614454620186a08c6158fe565b9b919a5093985091965090945050505050565b6101c75460009081526101df60205260409081902090516327dba2ff60e11b81526004810184905260248101839052604481018290526060919073d5cc46388c7046fb9770ae1191c14686eaec63c690634fb745fe90606401600060405180830381865af41580156144dd573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526145059190810190615912565b949350505050565b60408051606081018252600080825260208201819052918101919091526040805160e0810182526101c75480825260208083018890526101d154838501526101d2546060840152608083018b905260a083018a905260c0830189905260009182526101df9052828120925163108c9e0360e11b815291929091829173d5cc46388c7046fb9770ae1191c14686eaec63c6916321193c06916145ba9187918a918c906101e290600401615b0c565b6040805180830381865af41580156145d6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906145fa9190615579565b6040805160c0810182526101c7548152602081018e90528082018d9052606081018c9052608081018490526101d35462ffffff1660a08201529051631585364760e31b8152929450909250906000908190819073d5cc46388c7046fb9770ae1191c14686eaec63c69063ac29b238906146819087908d906101df906101db90600401615c94565b606060405180830381865af415801561469e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906146c29190615cc8565b9194509250905060028260038111156146dd576146dd6152a5565b036147855760016101cf60008282546146f691906154af565b90915550506101c75460408051918252600160208301526000908201526001600160a01b038a16907fe74013133fb67b03a3cba620837d933f19e9141c42fbf501232300c7f981fc639060600160405180910390a2604051806060016040528084815260200183600381111561476e5761476e6152a5565b8152602001868152509750505050505050506148f7565b60405163d984928f60e01b815273d5cc46388c7046fb9770ae1191c14686eaec63c69063d984928f906147ce9087908d906101df906101dd906101de906101dc90600401615d05565b606060405180830381865af41580156147eb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061480f9190615cc8565b91945092509050600382600381111561482a5761482a6152a5565b036148c15760016101cf600082825461484391906154af565b90915550506101d1546101d2541061485e576101d25461486d565b6101d25461486d9060016154af565b6101d2556101c754604080519182526000602083015281018290526001600160a01b038a16907fe74013133fb67b03a3cba620837d933f19e9141c42fbf501232300c7f981fc639060600160405180910390a25b60405180606001604052808481526020018360038111156148e4576148e46152a5565b8152602001868152509750505050505050505b9695505050505050565b6000808362ffffff16851015614a1d576101c7546101cf54604051630633166360e01b8152600481019290925260248201526001600160a01b03841660448201526101df60648201526101da6084820152600090819073d5cc46388c7046fb9770ae1191c14686eaec63c69063063316639060a4016040805180830381865af4158015614992573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906149b69190615579565b60006101cf556101c7546040805191825260208201839052810183905291935091506001600160a01b038616907f3f88442eb3441ef91803cd74336f2222c81a2d7f987cb61024e76b1b02eeecfb9060600160405180910390a2925060019150614a3e9050565b60016101cf6000828254614a3191906154af565b9091555060009250829150505b935093915050565b806003811115614a5857614a586152a5565b866001600160a01b03167fc338cfe35c3d868fb81af4e8541c0468d009b25f0849de36101d3369ee3667ef87878787604051614a979493929190615d48565b60405180910390a3505050505050565b600054610100900460ff16614ace5760405162461bcd60e51b8152600401611687906157e5565b6097805460ff19169055565b600054610100900460ff16613c035760405162461bcd60e51b8152600401611687906157e5565b606061156b6001600160a01b03831660145b60606000614b22836002615453565b614b2d9060026154af565b6001600160401b03811115614b4457614b4461559d565b6040519080825280601f01601f191660200182016040528015614b6e576020820181803683370190505b509050600360fc1b81600081518110614b8957614b896154c2565b60200101906001600160f81b031916908160001a905350600f60fb1b81600181518110614bb857614bb86154c2565b60200101906001600160f81b031916908160001a9053506000614bdc846002615453565b614be79060016154af565b90505b6001811115614c5f576f181899199a1a9b1b9c1cb0b131b232b360811b85600f1660108110614c1b57614c1b6154c2565b1a60f81b828281518110614c3157614c316154c2565b60200101906001600160f81b031916908160001a90535060049490941c93614c5881615d6d565b9050614bea565b508315614cae5760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401611687565b9392505050565b600060208284031215614cc757600080fd5b81356001600160e01b031981168114614cae57600080fd5b6001600160a01b0391909116815260200190565b80356001600160a01b0381168114614d0a57600080fd5b919050565b60008060408385031215614d2257600080fd5b614d2b83614cf3565b946020939093013593505050565b60008060408385031215614d4c57600080fd5b82359150614d5c60208401614cf3565b90509250929050565b6001600160401b0381168114611e9f57600080fd5b600060208284031215614d8c57600080fd5b8135614cae81614d65565b600060208284031215614da957600080fd5b5035919050565b600060208284031215614dc257600080fd5b614cae82614cf3565b600060608284031215614ddd57600080fd5b82606083011115614ded57600080fd5b50919050565b600080600060608486031215614e0857600080fd5b8335614e1381614d65565b9250614e2160208501614cf3565b9150604084013590509250925092565b60008060408385031215614e4457600080fd5b50508035926020909101359150565b60008060008060808587031215614e6957600080fd5b614e7285614cf3565b9350614e8060208601614cf3565b9250614e8e60408601614cf3565b9150614e9c60608601614cf3565b905092959194509250565b63ffffffff81168114611e9f57600080fd5b600080600060608486031215614ece57600080fd5b8335614ed981614ea7565b95602085013595506040909401359392505050565b805182526020810151602083015260408101516040830152606081015160608301526080810151608083015260a081015160a083015260c081015160c083015260e081015160e083015261010080820151818401525061012080820151818401525061014080820151818401525061016080820151818401525061018080820151614f808285018263ffffffff169052565b50506101a08181015163ffffffff81168483015250506101c08181015163ffffffff81168483015250506101e08181015190830152610200808201519083015261022090810151910152565b60006105c0820190508f825263ffffffff8f16602083015263ffffffff8e16604083015263ffffffff8d1660608301528b60808301528a60a08301528960c08301528860e0830152876101008301528661012083015261507e61014083018762ffffff80825116835280602083015116602084015280604083015116604084015250606081015163ffffffff80821660608501528060808401511660808501528060a08401511660a085015250505050565b845162ffffff9081166102008401526020860151811661022084015260408601518116610240840152606086015181166102608401526080860151811661028084015260a0860151166102a083015260c08501516102c083015260e08501516102e0830152835161030083015260208401516103208301526040840151610340830152606084015161036083015261511a610380830184614eee565b9f9e505050505050505050505050505050565b60006020828403121561513f57600080fd5b8135614cae81614ea7565b6020808252825182820181905260009190848201906040850190845b8181101561518b5783516001600160a01b031683529284019291840191600101615166565b50909695505050505050565b6000602082840312156151a957600080fd5b81358015158114614cae57600080fd5b60008060008060008060c087890312156151d257600080fd5b6151db87614cf3565b95506151e960208801614cf3565b94506151f760408801614cf3565b935061520560608801614cf3565b925061521360808801614cf3565b915061522160a08801614cf3565b90509295509295509295565b62ffffff81168114611e9f57600080fd5b60008060008060008060c0878903121561525757600080fd5b863595506020870135945060408701359350606087013561527781614ea7565b9250608087013561528781614ea7565b915060a08701356152978161522d565b809150509295509295509295565b634e487b7160e01b600052602160045260246000fd5b600060e0820190508882528760208301528660408301528560608301528460808301526001600160401b03841660a08301526003831061530b57634e487b7160e01b600052602160045260246000fd5b8260c083015298975050505050505050565b60006080828403121561532f57600080fd5b82608083011115614ded57600080fd5b60005b8381101561535a578181015183820152602001615342565b50506000910152565b6000815180845261537b81602086016020860161533f565b601f01601f19169290920160200192915050565b602081526000614cae6020830184615363565b6000602082840312156153b457600080fd5b8151614cae81614d65565b6020808252602b908201527f4d75737420686176652061646d696e20726f6c6520746f20706572666f726d2060408201526a3a3434b99030b1ba34b7b760a91b606082015260800190565b6001600160a01b0392831681529116602082015260400190565b60006020828403121561543657600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b600081600019048311821515161561546d5761546d61543d565b500290565b634e487b7160e01b600052601260045260246000fd5b60008261549757615497615472565b500490565b8181038181111561156b5761156b61543d565b8082018082111561156b5761156b61543d565b634e487b7160e01b600052603260045260246000fd5b6000602082840312156154ea57600080fd5b8135614cae8161522d565b62ffffff8181168382160190808211156155115761551161543d565b5092915050565b60208082526024908201527f4661696c656420746f2073656e6420457468657220746f20626c617374207769604082015263373732b960e11b606082015260800190565b63ffffffff8181168382160190808211156155115761551161543d565b6000806040838503121561558c57600080fd5b505080516020909101519092909150565b634e487b7160e01b600052604160045260246000fd5b60405161024081016001600160401b03811182821017156155d6576155d661559d565b60405290565b8051614d0a81614ea7565b600061024082840312156155fa57600080fd5b6156026155b3565b825181526020830151602082015260408301516040820152606083015160608201526080830151608082015260a083015160a082015260c083015160c082015260e083015160e082015261010080840151818301525061012080840151818301525061014080840151818301525061016080840151818301525061018061568a8185016155dc565b908201526101a061569c8482016155dc565b908201526101c06156ae8482016155dc565b908201526101e083810151908201526102008084015190820152610220928301519281019290925250919050565b600060c082840312156156ee57600080fd5b60405160c081018181106001600160401b03821117156157105761571061559d565b604052825161571e8161522d565b8152602083015161572e8161522d565b602082015260408301516157418161522d565b6040820152606083015161575481614ea7565b6060820152608083015161576781614ea7565b608082015260a083015161577a81614ea7565b60a08201529392505050565b60006105a0820190508782526020878184015286604084015285606084015263ffffffff8516608084015260a083018460005b60288110156157d6578151835291830191908301906001016157b9565b50505050979650505050505050565b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b606082015260800190565b63ffffffff8281168282160390808211156155115761551161543d565b60006001820161585f5761585f61543d565b5060010190565b60006020828403121561587857600080fd5b81516001600160801b0381168114614cae57600080fd5b76020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b8152600083516158c181601785016020880161533f565b7001034b99036b4b9b9b4b733903937b6329607d1b60179184019182015283516158f281602884016020880161533f565b01602801949350505050565b60008261590d5761590d615472565b500690565b60006020828403121561592457600080fd5b81516001600160401b038082111561593b57600080fd5b818401915084601f83011261594f57600080fd5b8151818111156159615761596161559d565b604051601f8201601f19908116603f011681019083821181831017156159895761598961559d565b816040528281528760208487010111156159a257600080fd5b6159b383602083016020880161533f565b979650505050505050565b805462ffffff8082168452808260181c166020850152808260301c166040850152808260481c166060850152615a0060808501828460601c1662ffffff169052565b615a1660a08501828460781c1662ffffff169052565b5050600181015460c08301526002015460e090910152565b8054825260018101546020830152600281015460408301526003810154606083015260048101546080830152600581015460a0830152600681015460c0830152600781015460e083015260088101546101008301526009810154610120830152600a810154610140830152600b810154610160830152600c81015463ffffffff808216610180850152615acf6101a08501828460201c1663ffffffff169052565b615ae76101c08501828460401c1663ffffffff169052565b5050600d8101546101e0830152600e810154610200830152600f015461022090910152565b6107008101818760005b6007811015615b35578151835260209283019290910190600101615b16565b5050506001600160a01b03861660e08301528454610100830152600185015463ffffffff808216610120850152602082901c811661014085015260409190911c8116610160840152600286015461018084015260038601546101a084015260048601546101c084015260058601546101e084015260068601546102008401526007860154610220840152600886015462ffffff808216610240860152601882901c8116610260860152603082901c16610280850152604881901c82166102a0850152606881901c82166102c085015260881c166102e0830152615c1f6103008301600987016159be565b600c850154610400830152600d850154610420830152600e850154610440830152600f850154610460830152615c5c610480830160108701615a2e565b6106c08201939093526106e001529392505050565b8060005b6006811015611d76578151845260209384019390910190600101615c75565b6101208101615ca38287615c71565b6001600160a01b039490941660c082015260e081019290925261010090910152919050565b600080600060608486031215615cdd57600080fd5b83519250602084015160048110615cf357600080fd5b80925050604084015190509250925092565b6101608101615d148289615c71565b6001600160a01b039690961660c082015260e081019490945261010084019290925261012083015261014090910152919050565b8481528360208201528260408201526080606082015260006148f76080830184615363565b600081615d7c57615d7c61543d565b50600019019056feb19546dff01e856fb3f010c267a7b1c60363cf8a4664e21cc89c26224620214e65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862aa2646970667358221220d8d037784a19318b8b19324601143d915adb1ac064c74a871d0b79a547729ef764736f6c63430008100033

Deployed Bytecode

0x6080604052600436106104c05760003560e01c80638bed5bd211610274578063bb0c48531161014e578063d731907a116100c6578063e58378bb11610082578063e58378bb1461145f578063e63ab1e914611481578063e7487eb5146114a3578063edcc8461146114df578063f7cb789a146114ff578063f9b3d1ad1461151d57005b8063d731907a14611328578063dce54178146113ac578063e12f3a61146113cc578063e2eb41ff146113ec578063e3ec42c61461141a578063e45ed31e1461143157005b8063cc2a9a5b11610115578063cc2a9a5b1461126f578063cd2002f51461128f578063ced72f87146112af578063d00c9179146112c4578063d0efe3c7146112e8578063d547741f1461130857005b8063bb0c4853146111c0578063c18e8ac8146111d5578063c2d94aec146111fa578063c91360061461121a578063cabb1ae41461125157005b8063a178a472116101ec578063a97223ed116101a8578063a97223ed14611100578063affed0e014611124578063b3cd42541461113b578063b6db75a014611167578063b7a52bfb1461117c578063b80163a9146111a057005b8063a178a47214611026578063a217fddf1461104a578063a2ee631b1461105f578063a420f7b21461108c578063a65d60af146110b0578063a86b11b8146110c757005b8063936c8a511161023b578063936c8a5114610f5a57806399221b2714610f9c5780639b6bb91d14610fbc5780639bbe239114610fdc5780639df6efd214610ff3578063a170dfc91461100657005b80638bed5bd214610ae35780638c65c81f14610afa578063906e3d9f14610ee75780639119ddc514610efe57806391d1485414610f3a57005b806344599e34116103a55780635c975abb1161031d57806370740ac9116102d957806370740ac914610a3157806375715ce714610a4657806379502c5514610a6657806380c2c89714610a875780638183059314610a9e57806387f9798914610abf57005b80635c975abb146109785780635ead5a321461099057806367cabd66146109c957806368e9f954146109e95780636919ed7214610a095780636a8a3b9614610a1c57005b80634dbb78b71161036c5780634dbb78b71461089b5780634f1d7f6e146108bf5780635237fa55146108d657806352a5f1f8146108f65780635312ea8e146109165780635508edf11461093657005b806344599e34146107fc57806346eacf1b1461081957806347ce07cc1461083957806347fb45531461085a5780634b750bd51461087b57005b806322bc2852116104385780632c4106bd116103ff5780632c4106bd146107405780632cc5d624146107575780632f2ff15d1461077757806330858ae21461079757806336568abe146107c55780633de3786a146107e557005b806322bc285214610694578063248a9ca3146106ab578063272b1323146106db5780632a3d5741146106fb5780632bc79c121461071257005b80630ecb3c89116104875780630ecb3c89146105b1578063120aa877146105c457806316cfaa16146105fd5780631aba27301461061e5780631b43987f146106555780631f0f01ef1461067757005b806301ffc9a7146104c9578063085d4883146104fe578063088803b41461052c5780630a1d562f146105645780630e9d0c321461058c57005b366104c757005b005b3480156104d557600080fd5b506104e96104e4366004614cb5565b61153a565b60405190151581526020015b60405180910390f35b34801561050a57600080fd5b506101e65461051f906001600160a01b031681565b6040516104f59190614cdf565b34801561053857600080fd5b506101ce5461055090600160301b900462ffffff1681565b60405162ffffff90911681526020016104f5565b34801561057057600080fd5b506101c35461051f90600160281b90046001600160a01b031681565b34801561059857600080fd5b506105a36101cc5481565b6040519081526020016104f5565b6104c76105bf366004614d0f565b611571565b3480156105d057600080fd5b506105a36105df366004614d39565b6101d860209081526000928352604080842090915290825290205481565b34801561060957600080fd5b506101c45461051f906001600160a01b031681565b34801561062a57600080fd5b5061051f610639366004614d7a565b6101e7602052600090815260409020546001600160a01b031681565b34801561066157600080fd5b506101c3546104e990600160201b900460ff1681565b34801561068357600080fd5b506101d3546105509062ffffff1681565b3480156106a057600080fd5b506105a36101cb5481565b3480156106b757600080fd5b506105a36106c6366004614d97565b60009081526065602052604090206001015490565b3480156106e757600080fd5b506104c76106f6366004614db0565b6117c1565b34801561070757600080fd5b506105a36101d15481565b34801561071e57600080fd5b506105a361072d366004614db0565b6101da6020526000908152604090205481565b34801561074c57600080fd5b506105a36101c85481565b34801561076357600080fd5b506105a3610772366004614d97565b611854565b34801561078357600080fd5b506104c7610792366004614d39565b611876565b3480156107a357600080fd5b506105a36107b2366004614d97565b6101e36020526000908152604090205481565b3480156107d157600080fd5b506104c76107e0366004614d39565b6118a0565b3480156107f157600080fd5b506105a36101cd5481565b34801561080857600080fd5b506101ce546105509062ffffff1681565b34801561082557600080fd5b506105a3610834366004614db0565b61191a565b34801561084557600080fd5b506101e55461051f906001600160a01b031681565b34801561086657600080fd5b506101c55461051f906001600160a01b031681565b34801561088757600080fd5b506104c7610896366004614d97565b611abb565b3480156108a757600080fd5b506101c954610550906301000000900462ffffff1681565b3480156108cb57600080fd5b506105a36101d45481565b3480156108e257600080fd5b506104c76108f1366004614dcb565b611b23565b34801561090257600080fd5b506104c7610911366004614df3565b611ca0565b34801561092257600080fd5b506104c7610931366004614d97565b611d7c565b34801561094257600080fd5b5061051f610951366004614e31565b6101dd6020908152600092835260408084209091529082529020546001600160a01b031681565b34801561098457600080fd5b5060975460ff166104e9565b34801561099c57600080fd5b506105a36109ab366004614d39565b6101dc60209081526000928352604080842090915290825290205481565b3480156109d557600080fd5b506104c76109e4366004614e53565b611ea2565b3480156109f557600080fd5b506104c7610a04366004614eb9565b61202c565b6104c7610a17366004614d97565b612163565b348015610a2857600080fd5b506104e96127b2565b348015610a3d57600080fd5b506104c76127d1565b348015610a5257600080fd5b506104c7610a61366004614db0565b6129a5565b348015610a7257600080fd5b506101c65461051f906001600160a01b031681565b348015610a9357600080fd5b506105a36101ca5481565b348015610aaa57600080fd5b506101ea5461051f906001600160a01b031681565b348015610acb57600080fd5b506101c95461055090600160301b900462ffffff1681565b348015610aef57600080fd5b506105a36101e95481565b348015610b0657600080fd5b50610ecd610b15366004614d97565b6101df6020528060005260406000206000915090508060000154908060010160009054906101000a900463ffffffff16908060010160049054906101000a900463ffffffff16908060010160089054906101000a900463ffffffff1690806002015490806003015490806004015490806005015490806006015490806007015490806008016040518060c00160405290816000820160009054906101000a900462ffffff1662ffffff1662ffffff1681526020016000820160039054906101000a900462ffffff1662ffffff1662ffffff1681526020016000820160069054906101000a900462ffffff1662ffffff1662ffffff1681526020016000820160099054906101000a900463ffffffff1663ffffffff1663ffffffff16815260200160008201600d9054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016000820160119054906101000a900463ffffffff1663ffffffff1663ffffffff16815250509080600901604051806101000160405290816000820160009054906101000a900462ffffff1662ffffff1662ffffff1681526020016000820160039054906101000a900462ffffff1662ffffff1662ffffff1681526020016000820160069054906101000a900462ffffff1662ffffff1662ffffff1681526020016000820160099054906101000a900462ffffff1662ffffff1662ffffff16815260200160008201600c9054906101000a900462ffffff1662ffffff1662ffffff16815260200160008201600f9054906101000a900462ffffff1662ffffff1662ffffff168152602001600182015481526020016002820154815250509080600c0160405180608001604052908160008201548152602001600182015481526020016002820154815260200160038201548152505090806010016040518061024001604052908160008201548152602001600182015481526020016002820154815260200160038201548152602001600482015481526020016005820154815260200160068201548152602001600782015481526020016008820154815260200160098201548152602001600a8201548152602001600b8201548152602001600c820160009054906101000a900463ffffffff1663ffffffff1663ffffffff168152602001600c820160049054906101000a900463ffffffff1663ffffffff1663ffffffff168152602001600c820160089054906101000a900463ffffffff1663ffffffff1663ffffffff168152602001600d8201548152602001600e8201548152602001600f8201548152505090508e565b6040516104f59e9d9c9b9a99989796959493929190614fcc565b348015610ef357600080fd5b506105a36101cf5481565b348015610f0a57600080fd5b506104e9610f19366004614d39565b6101d960209081526000928352604080842090915290825290205460ff1681565b348015610f4657600080fd5b506104e9610f55366004614d39565b612aa8565b348015610f6657600080fd5b5061051f610f75366004614e31565b6101e26020908152600092835260408084209091529082529020546001600160a01b031681565b348015610fa857600080fd5b506104c7610fb736600461512d565b612ad3565b348015610fc857600080fd5b506104c7610fd7366004614dcb565b612b55565b348015610fe857600080fd5b506105a36101d65481565b6104c7611001366004614db0565b612cc7565b34801561101257600080fd5b506105a3611021366004614d97565b612d7f565b34801561103257600080fd5b506101d05461055090600160581b900462ffffff1681565b34801561105657600080fd5b506105a3600081565b34801561106b57600080fd5b5061107f61107a366004614d97565b612e09565b6040516104f5919061514a565b34801561109857600080fd5b506101d05461055090600160881b900462ffffff1681565b3480156110bc57600080fd5b506105a36101e85481565b3480156110d357600080fd5b506105a36110e2366004614d39565b6101de60209081526000928352604080842090915290825290205481565b34801561110c57600080fd5b506101d05461055090600160401b900462ffffff1681565b34801561113057600080fd5b506105a36101c75481565b34801561114757600080fd5b50611152620186a081565b60405163ffffffff90911681526020016104f5565b34801561117357600080fd5b506104e9612f1e565b34801561118857600080fd5b506101ce54610550906301000000900462ffffff1681565b3480156111ac57600080fd5b506104c76111bb366004615197565b612f38565b3480156111cc57600080fd5b506104c7612f7c565b3480156111e157600080fd5b506101d05461115290600160201b900463ffffffff1681565b34801561120657600080fd5b506104c7611215366004614db0565b6130a0565b34801561122657600080fd5b5061051f611235366004614d97565b6101db602052600090815260409020546001600160a01b031681565b34801561125d57600080fd5b506101c3546111529063ffffffff1681565b34801561127b57600080fd5b506104c761128a3660046151b9565b6130f0565b34801561129b57600080fd5b506104c76112aa366004614db0565b6133f9565b3480156112bb57600080fd5b506105a3613449565b3480156112d057600080fd5b506101d05461055090600160701b900462ffffff1681565b3480156112f457600080fd5b506104c761130336600461523e565b6134d2565b34801561131457600080fd5b506104c7611323366004614d39565b61365b565b34801561133457600080fd5b50611399611343366004614d39565b6101e160209081526000928352604080842090915290825290208054600182015460028301546003840154600485015460059095015493949293919290916001600160401b03811690600160401b900460ff1687565b6040516104f597969594939291906152bb565b3480156113b857600080fd5b506104c76113c736600461531d565b613680565b3480156113d857600080fd5b506105a36113e7366004614db0565b61381a565b3480156113f857600080fd5b506105a3611407366004614db0565b6101e06020526000908152604090205481565b34801561142657600080fd5b506105a36101d25481565b34801561143d57600080fd5b506105a361144c366004614db0565b6101d76020526000908152604090205481565b34801561146b57600080fd5b506105a3600080516020615d8583398151915281565b34801561148d57600080fd5b506105a3600080516020615da583398151915281565b3480156114af57600080fd5b506104e96114be366004614d39565b6101e460209081526000928352604080842090915290825290205460ff1681565b3480156114eb57600080fd5b506105a36114fa366004614db0565b61387c565b34801561150b57600080fd5b506101d0546111529063ffffffff1681565b34801561152957600080fd5b506101c9546105509062ffffff1681565b60006001600160e01b03198216637965db0b60e01b148061156b57506301ffc9a760e01b6001600160e01b03198316145b92915050565b611579613a6c565b6101c75460009081526101df602090815260408083206101e183528184206001600160a01b03871685529092529091206115b284613ac5565b604051638f4fa25160e01b8152600481018390526024810182905273d5cc46388c7046fb9770ae1191c14686eaec63c690638f4fa2519060440160006040518083038186803b15801561160457600080fd5b505af4158015611618573d6000803e3d6000fd5b5050505061162584613b5c565b6101c75460009081526101e4602090815260408083206001600160a01b03881684528252918290205482518084019093526002835261032360f41b9183019190915260ff166116905760405162461bcd60e51b8152600401611687919061538f565b60405180910390fd5b506101c7546101c8546101e6546101e554604051631295edbd60e21b8152600481019490945234602485015260448401929092526001600160a01b038088166064850152908116608484015260a483018490521660c48201526101e760e4820152610104810184905260009073d5cc46388c7046fb9770ae1191c14686eaec63c690634a57b6f49061012401602060405180830381865af4158015611739573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061175d91906153a2565b6101c754604080519182526001600160401b03831660208301529192506001600160a01b038716917f5a0920da7c4df7f0e5da0ce23542a31a806ddda495b3f8da7ca95e92ad030dbc910160405180910390a25050506117bd600160c955565b5050565b6117c9612f1e565b6117e55760405162461bcd60e51b8152600401611687906153bf565b604051634aa7d2f760e11b81526002604360981b019063954fa5ee90611811903090859060040161540a565b6020604051808303816000875af1158015611830573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117bd9190615424565b6101d5818154811061186557600080fd5b600091825260209091200154905081565b60008281526065602052604090206001015461189181613c0a565b61189b8383613c14565b505050565b6001600160a01b03811633146119105760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b6064820152608401611687565b6117bd8282613c9a565b6101c75460009081526101df602090815260408083206101d983528184206001600160a01b0386168552909252822054829060ff161561195b579392505050565b6101c75460009081526101dc602090815260408083206001600160a01b038816845290915281205460038401546009850154919291620186a0916119aa91600160481b900462ffffff16615453565b6119b49190615488565b9050600084600601546000146119e85784600601548286600301546119d9919061549c565b6119e39190615488565b6119eb565b60005b6101c75460009081526101db60205260409020549091506001600160a01b03808916911603611a4a578460060154600003611a3d57818560030154611a30919061549c565b611a3a90856154af565b93505b611a4782856154af565b93505b8215611ab0576101c75460009081526101db60205260409020546001600160a01b0316611a995782856006015483611a829190615488565b611a8c9190615453565b611a9690856154af565b93505b611aa38382615453565b611aad90856154af565b93505b509195945050505050565b611ac3612f1e565b611adf5760405162461bcd60e51b8152600401611687906153bf565b604080518082019091526002815261313160f01b6020820152620186a0821115611b1c5760405162461bcd60e51b8152600401611687919061538f565b506101ca55565b611b2b612f1e565b611b475760405162461bcd60e51b8152600401611687906153bf565b6000611b5960408301602084016154d8565b611b6660208401846154d8565b611b7091906154f5565b62ffffff169050620186a063ffffffff1681111560405180604001604052806002815260200161313160f01b81525090611bbd5760405162461bcd60e51b8152600401611687919061538f565b50620186a0611bd260608401604085016154d8565b62ffffff16111560405180604001604052806002815260200161313160f01b81525090611c125760405162461bcd60e51b8152600401611687919061538f565b50611c2060208301836154d8565b6101ce805462ffffff191662ffffff92909216919091179055611c4960408301602084016154d8565b6101ce805462ffffff9290921663010000000265ffffff00000019909216919091179055611c7d60608301604084016154d8565b6101ce60066101000a81548162ffffff021916908362ffffff1602179055505050565b6000611cb56101e5546001600160a01b031690565b90506001600160a01b038116611d075760405162461bcd60e51b8152602060048201526017602482015276115b9d1c9bdc1e481859191c995cdcc81b9bdd081cd95d604a1b6044820152606401611687565b336001600160a01b03821614611d6b5760405162461bcd60e51b815260206004820152602360248201527f4f6e6c7920456e74726f70792063616e2063616c6c20746869732066756e637460448201526234b7b760e91b6064820152608401611687565b611d76848484613d01565b50505050565b611d84613a6c565b611d8c612f1e565b611da85760405162461bcd60e51b8152600401611687906153bf565b6101c3546040805180820190915260028152610c4d60f21b602082015290600160201b900460ff16611ded5760405162461bcd60e51b8152600401611687919061538f565b508047101560405180604001604052806002815260200161062760f31b81525090611e2b5760405162461bcd60e51b8152600401611687919061538f565b50604051600090339083908381818185875af1925050503d8060008114611e6e576040519150601f19603f3d011682016040523d82523d6000602084013e611e73565b606091505b5050905080611e945760405162461bcd60e51b815260040161168790615518565b50611e9f600160c955565b50565b611eaa612f1e565b611ec65760405162461bcd60e51b8152600401611687906153bf565b6040805180820190915260018152600360fc1b60208201526001600160a01b038516611f055760405162461bcd60e51b8152600401611687919061538f565b506040805180820190915260018152600360fc1b60208201526001600160a01b038416611f455760405162461bcd60e51b8152600401611687919061538f565b506040805180820190915260018152600360fc1b60208201526001600160a01b038316611f855760405162461bcd60e51b8152600401611687919061538f565b506040805180820190915260018152600360fc1b60208201526001600160a01b038216611fc55760405162461bcd60e51b8152600401611687919061538f565b506101e580546001600160a01b039586166001600160a01b0319918216179091556101e68054948616948216949094179093556101c38054928516600160281b02600160281b600160c81b0319909316929092179091556101ea8054919093169116179055565b612034612f1e565b6120505760405162461bcd60e51b8152600401611687906153bf565b6101c75460009081526101df6020526040902060018101546120819063ffffffff600160201b82048116911661555c565b63ffffffff1642118061209d5750600181015463ffffffff1642105b604051806040016040528060018152602001600d60fa1b815250906120d55760405162461bcd60e51b8152600401611687919061538f565b50604080518082019091526002815261031360f41b602082015263ffffffff85166121135760405162461bcd60e51b8152600401611687919061538f565b50604080518082019091526002815261031360f41b60208201528261214b5760405162461bcd60e51b8152600401611687919061538f565b505063ffffffff9092166101e8556101d4556101d155565b61216b612f1e565b6121875760405162461bcd60e51b8152600401611687906153bf565b6101c75460009081526101df6020526040908190206101d054915163063f43a360e41b81526004810184905263ffffffff9092166024830152604482018190529073d5cc46388c7046fb9770ae1191c14686eaec63c6906363f43a309060640160006040518083038186803b1580156121ff57600080fd5b505af4158015612213573d6000803e3d6000fd5b50506101c7546101e8546101c8546101d05460405163fce0536160e01b8152600481019490945260248401929092526044830152600160581b900462ffffff166064820152608481018490526101db60a48201526000925082915073d5cc46388c7046fb9770ae1191c14686eaec63c69063fce053619060c4016040805180830381865af41580156122a9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122cd9190615579565b91509150806000036122e2576101e9546122e5565b60005b6101e98190555060016101c7600082825461230091906154af565b909155505060006101d28190556101c75481526101df60205260408082206101c65482516315cef56d60e11b815292519193926001600160a01b0390911691632b9deada91600480820192610240929091908290030181865afa15801561236b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061238f91906155e7565b905060006101c660009054906101000a90046001600160a01b03166001600160a01b031663a8bcf2ab6040518163ffffffff1660e01b815260040160c060405180830381865afa1580156123e7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061240b91906156dc565b905060006040518061050001604052806101c75481526020016101d060009054906101000a900463ffffffff1663ffffffff1681526020016101d060049054906101000a900463ffffffff1663ffffffff1681526020016101cd548152602001836000015162ffffff168152602001836020015162ffffff168152602001836040015162ffffff168152602001836060015163ffffffff168152602001836080015163ffffffff1681526020018360a0015163ffffffff1681526020016101c960009054906101000a900462ffffff1662ffffff1681526020016101c960039054906101000a900462ffffff1662ffffff1681526020016101c960069054906101000a900462ffffff1662ffffff1681526020016101ce60009054906101000a900462ffffff1662ffffff1681526020016101ce60039054906101000a900462ffffff1662ffffff1681526020016101ce60069054906101000a900462ffffff1662ffffff1681526020016101ca5481526020016101cb54815260200184600001518152602001846020015181526020018460400151815260200184606001518152602001846080015181526020018460a0015181526020018460c0015181526020018460e001518152602001846101000151815260200184610120015181526020018461014001518152602001846101600151815260200184610180015163ffffffff168152602001846101a0015163ffffffff168152602001846101c0015163ffffffff168152602001846101e001518152602001846102000151815260200184610220015181526020016101d060089054906101000a900462ffffff1662ffffff1681526020016101d060119054906101000a900462ffffff1662ffffff1681526020016101d0600b9054906101000a900462ffffff1662ffffff1681526020016101d0600e9054906101000a900462ffffff1662ffffff16815250905073d5cc46388c7046fb9770ae1191c14686eaec63c663353fd196858989898d876040518763ffffffff1660e01b815260040161270d96959493929190615786565b60006040518083038186803b15801561272557600080fd5b505af4158015612739573d6000803e3d6000fd5b505050506127443390565b6101c75460018601546040805192835263ffffffff8083166020850152600160201b90920490911682820152516001600160a01b0392909216917fd4f4eda7fb0fd42c44eceae5e61ca3734aa4c9843303a9bed659b38d6872b26a9181900360600190a25050505050505050565b60006127cc600080516020615da583398151915233612aa8565b905090565b6127d9613a6c565b6101c75460009081526101df602052604090206127f533613ac5565b6127fe33613b5c565b600181015460009061281f9063ffffffff600160201b82048116911661555c565b63ffffffff164211801561285257506101c75460009081526101d96020908152604080832033845290915290205460ff16155b1561288c576128603361191a565b6101c75460009081526101d9602090815260408083203384529091529020805460ff1916600117905590505b3360009081526101da60205260408120546128a89083906154af565b90508047101560405180604001604052806002815260200161313360f01b815250906128e75760405162461bcd60e51b8152600401611687919061538f565b503360008181526101da60205260408082208290555190919083908381818185875af1925050503d806000811461293a576040519150601f19603f3d011682016040523d82523d6000602084013e61293f565b606091505b50509050806129605760405162461bcd60e51b815260040161168790615518565b60405182815233907f0ba90eb685b3b0006a3c394dd506a37e5e136aa5340c16793a2d546bd6721b8f9060200160405180910390a2505050506129a3600160c955565b565b600054610100900460ff166129cc5760405162461bcd60e51b8152600401611687906157e5565b6001600160a01b038116612a225760405162461bcd60e51b815260206004820181905260248201527f4f776e65722063616e6e6f7420626520746865207a65726f20616464726573736044820152606401611687565b612a2a613f0c565b612a32613f33565b612a3a613f62565b612a52600080516020615d8583398151915282613f91565b612a6a600080516020615da583398151915282613f91565b612a90600080516020615da5833981519152600080516020615d85833981519152613f9b565b611e9f600080516020615d8583398151915280613f9b565b60009182526065602090815260408084206001600160a01b0393909316845291905290205460ff1690565b612adb612f1e565b612af75760405162461bcd60e51b8152600401611687906153bf565b604080518082019091526002815261313960f01b6020820152600563ffffffff831611612b375760405162461bcd60e51b8152600401611687919061538f565b506101c3805463ffffffff191663ffffffff92909216919091179055565b612b5d612f1e565b612b795760405162461bcd60e51b8152600401611687906153bf565b620186a0612b8d60608301604084016154d8565b612b9d60408401602085016154d8565b612baa60208501856154d8565b612bb491906154f5565b612bbe91906154f5565b62ffffff16111560405180604001604052806002815260200161313160f01b81525090612bfe5760405162461bcd60e51b8152600401611687919061538f565b50612c0c60208201826154d8565b6101c9805462ffffff191662ffffff92909216919091179055612c3560408201602083016154d8565b6101c9805462ffffff9290921663010000000265ffffff00000019909216919091179055612c6960608201604083016154d8565b6101c9805462ffffff928316600160301b0268ffffff000000000000198216811792839055612ca89363010000009093048316929081169116176154f5565b612cba9062ffffff16620186a0615830565b63ffffffff166101cb5550565b612ccf613a6c565b6101c7546101d6546101c3546040516362d764b560e01b8152600481019390935260248301919091526001600160a01b038084166044840152600160281b9091041660648201526101df60848201526101e460a482015273d5cc46388c7046fb9770ae1191c14686eaec63c6906362d764b59060c40160006040518083038186803b158015612d5d57600080fd5b505af4158015612d71573d6000803e3d6000fd5b50505050611e9f600160c955565b60008181526101df602052604081206101c754831115612da25750600092915050565b600a8101546101cf5460009190612dbd90620186a090615453565b612dc79190615488565b6009830154612de29190600160301b900462ffffff166154af565b9050600082600901600201548210612dfe57600b830154612e00565b815b95945050505050565b60008181526101df602052604090206101c75460609190831115612e4d5760408051600180825281830190925290602080830190803683370190505b509392505050565b600081600701546001600160401b03811115612e6b57612e6b61559d565b604051908082528060200260200182016040528015612e94578160200160208202803683370190505b5090508160060154600003612eaa579392505050565b60005b8260070154811015612e455760008581526101dd6020908152604080832084845290915290205482516001600160a01b0390911690839083908110612ef457612ef46154c2565b6001600160a01b039092166020928302919091019091015280612f168161584d565b915050612ead565b60006127cc600080516020615d8583398151915233612aa8565b612f40612f1e565b612f5c5760405162461bcd60e51b8152600401611687906153bf565b6101c38054911515600160201b0264ff0000000019909216919091179055565b612f84613a6c565b612f8d33613ac5565b3360009081526101d760209081526040918290205482518084019093526002835261062760f31b91830191909152612fd85760405162461bcd60e51b8152600401611687919061538f565b503360009081526101d760209081526040808320805493905580518082019091526002815261062760f31b918101919091524782111561302b5760405162461bcd60e51b8152600401611687919061538f565b50604051600090339083908381818185875af1925050503d806000811461306e576040519150601f19603f3d011682016040523d82523d6000602084013e613073565b606091505b50509050806130945760405162461bcd60e51b815260040161168790615518565b50506129a3600160c955565b6130a8612f1e565b6130c45760405162461bcd60e51b8152600401611687906153bf565b60405163430021db60e11b81526002604360981b019063860043b690611811903090859060040161540a565b600054610100900460ff16158080156131105750600054600160ff909116105b8061312a5750303b15801561312a575060005460ff166001145b61318d5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401611687565b6000805460ff1916600117905580156131b0576000805461ff0019166101001790555b6101e580546001600160a01b03808a166001600160a01b0319928316179092556101e680548984169083161790556101c6805485841692169190911790556101c38054918716600160281b02600160281b600160c81b03199092169190911790556132183390565b6101ea80546001600160a01b03199081166001600160a01b03938416179091556101c4805482168684169081179091556101c580549092169287169290921790556040516336b91f2b60e01b81526336b91f2b9061327a908790600401614cdf565b600060405180830381600087803b15801561329457600080fd5b505af11580156132a8573d6000803e3d6000fd5b505050506002604360981b016001600160a01b0316634e606c476040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156132ee57600080fd5b505af1158015613302573d6000803e3d6000fd5b505050506002604360981b016001600160a01b031663f098767a6040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561334857600080fd5b505af115801561335c573d6000803e3d6000fd5b50505050613368613fe6565b6101c7805460009081526101df6020526040902060018101805463ffffffff19164263ffffffff16179055905481556101cd546006909101556133aa336129a5565b80156133f0576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b50505050505050565b613401612f1e565b61341d5760405162461bcd60e51b8152600401611687906153bf565b60405163662aa11d60e01b81526002604360981b019063662aa11d90611811903090859060040161540a565b6101e5546101e654604051631711922960e31b81526000926001600160a01b039081169263b88c9148926134839290911690600401614cdf565b602060405180830381865afa1580156134a0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134c49190615866565b6001600160801b0316905090565b6134da612f1e565b6134f65760405162461bcd60e51b8152600401611687906153bf565b6101c75460009081526101df6020526040902060018101546135279063ffffffff600160201b82048116911661555c565b63ffffffff164211806135435750600181015463ffffffff1642105b604051806040016040528060018152602001600d60fa1b8152509061357b5760405162461bcd60e51b8152600401611687919061538f565b508263ffffffff168463ffffffff16111560405180604001604052806002815260200161189b60f11b815250906135c55760405162461bcd60e51b8152600401611687919061538f565b50604080518082019091526002815261031360f41b6020820152876135fd5760405162461bcd60e51b8152600401611687919061538f565b50506101c8949094556101d6929092556101d0805463ffffffff938416600160201b0267ffffffffffffffff1990911693909216929092171790556101cd919091556101d3805462ffffff90921662ffffff19909216919091179055565b60008281526065602052604090206001015461367681613c0a565b61189b8383613c9a565b613688612f1e565b6136a45760405162461bcd60e51b8152600401611687906153bf565b60006136b660808301606084016154d8565b6136c660608401604085016154d8565b6136d660408501602086016154d8565b6136e360208601866154d8565b6136ed91906154f5565b6136f791906154f5565b61370191906154f5565b62ffffff169050620186a063ffffffff16811460405180604001604052806002815260200161313160f01b8152509061374d5760405162461bcd60e51b8152600401611687919061538f565b5061375b60208301836154d8565b6101d0805462ffffff92909216600160701b0262ffffff60701b1990921691909117905561378f60408301602084016154d8565b6101d0805462ffffff92909216600160581b0262ffffff60581b199092169190911790556137c360608301604084016154d8565b6101d0805462ffffff92909216600160881b0262ffffff60881b199092169190911790556137f760808301606084016154d8565b6101d060086101000a81548162ffffff021916908362ffffff1602179055505050565b6101c75460009081526101df6020526040812081806138388561387c565b60018401549091506138599063ffffffff600160201b82048116911661555c565b63ffffffff164211156138725761386f8561191a565b91505b612e0082826154af565b6001600160a01b03811660009081526101e06020908152604080832080548085526101df9093529083206101c754919290911115806138e05750815460009081526101d9602090815260408083206001600160a01b038816845290915290205460ff165b15613904575050506001600160a01b031660009081526101da602052604090205490565b6001600160a01b03841660008181526101da6020908152604080832054865484526101dc83528184209484529390915281205460038401546009850154919291620186a09161395e91600160481b900462ffffff16615453565b6139689190615488565b90506000846006015460001461399c57846006015482866003015461398d919061549c565b6139979190615488565b61399f565b60005b865460009081526101db60205260409020549091506001600160a01b03808a169116036139fc5784600601546000036139ef578185600301546139e2919061549c565b6139ec90856154af565b93505b6139f982856154af565b93505b8215613a6057855460009081526101db60205260409020546001600160a01b0316613a495782856006015483613a329190615488565b613a3c9190615453565b613a4690856154af565b93505b613a538382615453565b613a5d90856154af565b93505b50919695505050505050565b600260c95403613abe5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401611687565b600260c955565b6101c354604051632992684f60e01b81526101e060048201526101e160248201526101d7604482015263ffffffff90911660648201526001600160a01b038216608482015273d5cc46388c7046fb9770ae1191c14686eaec63c690632992684f9060a40160006040518083038186803b158015613b4157600080fd5b505af4158015613b55573d6000803e3d6000fd5b5050505050565b6001600160a01b03811660009081526101e0602052604090206101c754815410613b84575050565b805460009081526101d9602090815260408083206001600160a01b038616845290915290205460ff16613bfa57613bba8261387c565b6001600160a01b03831660008181526101da6020908152604080832094909455845482526101d981528382209282529190915220805460ff191660011790555b6101c754905550565b600160c955565b611e9f81336140ca565b613c1e8282612aa8565b6117bd5760008281526065602090815260408083206001600160a01b03851684529091529020805460ff19166001179055613c563390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b613ca48282612aa8565b156117bd5760008281526065602090815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b6101e5546001600160a01b0316336001600160a01b03161460405180604001604052806002815260200161189960f11b81525090613d525760405162461bcd60e51b8152600401611687919061538f565b506101c75460008181526101df602090815260408083206101e38352818420546001600160401b03891685526101e78452828520549585526101e184528285206001600160a01b0390961680865295909352928190206101c35491516310d481af60e11b8152600481018590526024810182905263ffffffff9092166044830152929391929073d5cc46388c7046fb9770ae1191c14686eaec63c6906321a9035e9060640160006040518083038186803b158015613e0f57600080fd5b505af4158015613e23573d6000803e3d6000fd5b50505050613e318482614123565b60048101546000808080613e448a61439c565b93509350935093506000613e5b8b60001c87614467565b90506000613e758662ffffff8087169086168f8e8e61450d565b80516020820151919250906000816003811115613e9457613e946152a5565b03613eaa57613ea488888d614901565b90925090505b60058a0180546002919060ff60401b1916600160401b83021790555082604001516101e360006101c754815260200190815260200160002081905550613efa8b8f60001c6101c754858886614a46565b50505050505050505050505050505050565b600054610100900460ff166129a35760405162461bcd60e51b8152600401611687906157e5565b600054610100900460ff16613f5a5760405162461bcd60e51b8152600401611687906157e5565b6129a3614aa7565b600054610100900460ff16613f895760405162461bcd60e51b8152600401611687906157e5565b6129a3614ada565b6117bd8282613c14565b600082815260656020526040808220600101805490849055905190918391839186917fbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff9190a4505050565b660e35fa931a00006101c88190556101d655600a6101cd556101c38054603c63ffffffff199091161790556101d080546001600160a01b0319166f3a980144380007d0000001a4000001a41790556101c9805468ffffffffffffffffff199081166664003a980005dc17918290556101ce80549091166801117000fde80088b81790556113886101ca556140899062ffffff6301000000909104166105dc6154f5565b61409b9062ffffff16620186a0615830565b63ffffffff166101cb5560056101d48190556101e85560036101d1556101d3805462ffffff19166103e8179055565b6140d48282612aa8565b6117bd576140e181614b01565b6140ec836020614b13565b6040516020016140fd92919061588f565b60408051601f198184030181529082905262461bcd60e51b82526116879160040161538f565b600e820154600c8301546003830154600091620186a0916141449190615453565b61414e9190615488565b6101c354604051919250600091600160281b9091046001600160a01b03169083908381818185875af1925050503d80600081146141a7576040519150601f19603f3d011682016040523d82523d6000602084013e6141ac565b606091505b50509050806141fd5760405162461bcd60e51b815260206004820181905260248201527f4661696c656420746f2073656e6420457468657220746f2074726561737572796044820152606401611687565b6101d4546101e954101561430a5760016101e9600082825461421f91906154af565b90915550506003840154600090620186a09061423c908690615453565b6142469190615488565b6101ea546040519192506001600160a01b0316908290600081818185875af1925050503d8060008114614295576040519150601f19603f3d011682016040523d82523d6000602084013e61429a565b606091505b505080925050816143045760405162461bcd60e51b815260206004820152602e60248201527f4661696c656420746f2073656e64205365656420457468657220746f2070726960448201526d1e99481c1bdbdb081dd85b1b195d60921b6064820152608401611687565b60009350505b600f850154600d860154604051600162beb8ff60e01b031981526004810188905260248101879052604481018690526064810192909252608482015273d5cc46388c7046fb9770ae1191c14686eaec63c69063ff4147019060a40160006040518083038186803b15801561437d57600080fd5b505af4158015614391573d6000803e3d6000fd5b505050505050505050565b6101c75460009081526101df6020526040812060098101548291829182919062ffffff8082169184916143d7916301000000900416836154af565b600a8401546101cf549192506000916143f490620186a090615453565b6143fe9190615488565b60098501546144199190600160301b900462ffffff166154af565b905060008460090160020154821061443557600b850154614437565b815b905061444383826154af565b90506000614454620186a08c6158fe565b9b919a5093985091965090945050505050565b6101c75460009081526101df60205260409081902090516327dba2ff60e11b81526004810184905260248101839052604481018290526060919073d5cc46388c7046fb9770ae1191c14686eaec63c690634fb745fe90606401600060405180830381865af41580156144dd573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526145059190810190615912565b949350505050565b60408051606081018252600080825260208201819052918101919091526040805160e0810182526101c75480825260208083018890526101d154838501526101d2546060840152608083018b905260a083018a905260c0830189905260009182526101df9052828120925163108c9e0360e11b815291929091829173d5cc46388c7046fb9770ae1191c14686eaec63c6916321193c06916145ba9187918a918c906101e290600401615b0c565b6040805180830381865af41580156145d6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906145fa9190615579565b6040805160c0810182526101c7548152602081018e90528082018d9052606081018c9052608081018490526101d35462ffffff1660a08201529051631585364760e31b8152929450909250906000908190819073d5cc46388c7046fb9770ae1191c14686eaec63c69063ac29b238906146819087908d906101df906101db90600401615c94565b606060405180830381865af415801561469e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906146c29190615cc8565b9194509250905060028260038111156146dd576146dd6152a5565b036147855760016101cf60008282546146f691906154af565b90915550506101c75460408051918252600160208301526000908201526001600160a01b038a16907fe74013133fb67b03a3cba620837d933f19e9141c42fbf501232300c7f981fc639060600160405180910390a2604051806060016040528084815260200183600381111561476e5761476e6152a5565b8152602001868152509750505050505050506148f7565b60405163d984928f60e01b815273d5cc46388c7046fb9770ae1191c14686eaec63c69063d984928f906147ce9087908d906101df906101dd906101de906101dc90600401615d05565b606060405180830381865af41580156147eb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061480f9190615cc8565b91945092509050600382600381111561482a5761482a6152a5565b036148c15760016101cf600082825461484391906154af565b90915550506101d1546101d2541061485e576101d25461486d565b6101d25461486d9060016154af565b6101d2556101c754604080519182526000602083015281018290526001600160a01b038a16907fe74013133fb67b03a3cba620837d933f19e9141c42fbf501232300c7f981fc639060600160405180910390a25b60405180606001604052808481526020018360038111156148e4576148e46152a5565b8152602001868152509750505050505050505b9695505050505050565b6000808362ffffff16851015614a1d576101c7546101cf54604051630633166360e01b8152600481019290925260248201526001600160a01b03841660448201526101df60648201526101da6084820152600090819073d5cc46388c7046fb9770ae1191c14686eaec63c69063063316639060a4016040805180830381865af4158015614992573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906149b69190615579565b60006101cf556101c7546040805191825260208201839052810183905291935091506001600160a01b038616907f3f88442eb3441ef91803cd74336f2222c81a2d7f987cb61024e76b1b02eeecfb9060600160405180910390a2925060019150614a3e9050565b60016101cf6000828254614a3191906154af565b9091555060009250829150505b935093915050565b806003811115614a5857614a586152a5565b866001600160a01b03167fc338cfe35c3d868fb81af4e8541c0468d009b25f0849de36101d3369ee3667ef87878787604051614a979493929190615d48565b60405180910390a3505050505050565b600054610100900460ff16614ace5760405162461bcd60e51b8152600401611687906157e5565b6097805460ff19169055565b600054610100900460ff16613c035760405162461bcd60e51b8152600401611687906157e5565b606061156b6001600160a01b03831660145b60606000614b22836002615453565b614b2d9060026154af565b6001600160401b03811115614b4457614b4461559d565b6040519080825280601f01601f191660200182016040528015614b6e576020820181803683370190505b509050600360fc1b81600081518110614b8957614b896154c2565b60200101906001600160f81b031916908160001a905350600f60fb1b81600181518110614bb857614bb86154c2565b60200101906001600160f81b031916908160001a9053506000614bdc846002615453565b614be79060016154af565b90505b6001811115614c5f576f181899199a1a9b1b9c1cb0b131b232b360811b85600f1660108110614c1b57614c1b6154c2565b1a60f81b828281518110614c3157614c316154c2565b60200101906001600160f81b031916908160001a90535060049490941c93614c5881615d6d565b9050614bea565b508315614cae5760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401611687565b9392505050565b600060208284031215614cc757600080fd5b81356001600160e01b031981168114614cae57600080fd5b6001600160a01b0391909116815260200190565b80356001600160a01b0381168114614d0a57600080fd5b919050565b60008060408385031215614d2257600080fd5b614d2b83614cf3565b946020939093013593505050565b60008060408385031215614d4c57600080fd5b82359150614d5c60208401614cf3565b90509250929050565b6001600160401b0381168114611e9f57600080fd5b600060208284031215614d8c57600080fd5b8135614cae81614d65565b600060208284031215614da957600080fd5b5035919050565b600060208284031215614dc257600080fd5b614cae82614cf3565b600060608284031215614ddd57600080fd5b82606083011115614ded57600080fd5b50919050565b600080600060608486031215614e0857600080fd5b8335614e1381614d65565b9250614e2160208501614cf3565b9150604084013590509250925092565b60008060408385031215614e4457600080fd5b50508035926020909101359150565b60008060008060808587031215614e6957600080fd5b614e7285614cf3565b9350614e8060208601614cf3565b9250614e8e60408601614cf3565b9150614e9c60608601614cf3565b905092959194509250565b63ffffffff81168114611e9f57600080fd5b600080600060608486031215614ece57600080fd5b8335614ed981614ea7565b95602085013595506040909401359392505050565b805182526020810151602083015260408101516040830152606081015160608301526080810151608083015260a081015160a083015260c081015160c083015260e081015160e083015261010080820151818401525061012080820151818401525061014080820151818401525061016080820151818401525061018080820151614f808285018263ffffffff169052565b50506101a08181015163ffffffff81168483015250506101c08181015163ffffffff81168483015250506101e08181015190830152610200808201519083015261022090810151910152565b60006105c0820190508f825263ffffffff8f16602083015263ffffffff8e16604083015263ffffffff8d1660608301528b60808301528a60a08301528960c08301528860e0830152876101008301528661012083015261507e61014083018762ffffff80825116835280602083015116602084015280604083015116604084015250606081015163ffffffff80821660608501528060808401511660808501528060a08401511660a085015250505050565b845162ffffff9081166102008401526020860151811661022084015260408601518116610240840152606086015181166102608401526080860151811661028084015260a0860151166102a083015260c08501516102c083015260e08501516102e0830152835161030083015260208401516103208301526040840151610340830152606084015161036083015261511a610380830184614eee565b9f9e505050505050505050505050505050565b60006020828403121561513f57600080fd5b8135614cae81614ea7565b6020808252825182820181905260009190848201906040850190845b8181101561518b5783516001600160a01b031683529284019291840191600101615166565b50909695505050505050565b6000602082840312156151a957600080fd5b81358015158114614cae57600080fd5b60008060008060008060c087890312156151d257600080fd5b6151db87614cf3565b95506151e960208801614cf3565b94506151f760408801614cf3565b935061520560608801614cf3565b925061521360808801614cf3565b915061522160a08801614cf3565b90509295509295509295565b62ffffff81168114611e9f57600080fd5b60008060008060008060c0878903121561525757600080fd5b863595506020870135945060408701359350606087013561527781614ea7565b9250608087013561528781614ea7565b915060a08701356152978161522d565b809150509295509295509295565b634e487b7160e01b600052602160045260246000fd5b600060e0820190508882528760208301528660408301528560608301528460808301526001600160401b03841660a08301526003831061530b57634e487b7160e01b600052602160045260246000fd5b8260c083015298975050505050505050565b60006080828403121561532f57600080fd5b82608083011115614ded57600080fd5b60005b8381101561535a578181015183820152602001615342565b50506000910152565b6000815180845261537b81602086016020860161533f565b601f01601f19169290920160200192915050565b602081526000614cae6020830184615363565b6000602082840312156153b457600080fd5b8151614cae81614d65565b6020808252602b908201527f4d75737420686176652061646d696e20726f6c6520746f20706572666f726d2060408201526a3a3434b99030b1ba34b7b760a91b606082015260800190565b6001600160a01b0392831681529116602082015260400190565b60006020828403121561543657600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b600081600019048311821515161561546d5761546d61543d565b500290565b634e487b7160e01b600052601260045260246000fd5b60008261549757615497615472565b500490565b8181038181111561156b5761156b61543d565b8082018082111561156b5761156b61543d565b634e487b7160e01b600052603260045260246000fd5b6000602082840312156154ea57600080fd5b8135614cae8161522d565b62ffffff8181168382160190808211156155115761551161543d565b5092915050565b60208082526024908201527f4661696c656420746f2073656e6420457468657220746f20626c617374207769604082015263373732b960e11b606082015260800190565b63ffffffff8181168382160190808211156155115761551161543d565b6000806040838503121561558c57600080fd5b505080516020909101519092909150565b634e487b7160e01b600052604160045260246000fd5b60405161024081016001600160401b03811182821017156155d6576155d661559d565b60405290565b8051614d0a81614ea7565b600061024082840312156155fa57600080fd5b6156026155b3565b825181526020830151602082015260408301516040820152606083015160608201526080830151608082015260a083015160a082015260c083015160c082015260e083015160e082015261010080840151818301525061012080840151818301525061014080840151818301525061016080840151818301525061018061568a8185016155dc565b908201526101a061569c8482016155dc565b908201526101c06156ae8482016155dc565b908201526101e083810151908201526102008084015190820152610220928301519281019290925250919050565b600060c082840312156156ee57600080fd5b60405160c081018181106001600160401b03821117156157105761571061559d565b604052825161571e8161522d565b8152602083015161572e8161522d565b602082015260408301516157418161522d565b6040820152606083015161575481614ea7565b6060820152608083015161576781614ea7565b608082015260a083015161577a81614ea7565b60a08201529392505050565b60006105a0820190508782526020878184015286604084015285606084015263ffffffff8516608084015260a083018460005b60288110156157d6578151835291830191908301906001016157b9565b50505050979650505050505050565b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b606082015260800190565b63ffffffff8281168282160390808211156155115761551161543d565b60006001820161585f5761585f61543d565b5060010190565b60006020828403121561587857600080fd5b81516001600160801b0381168114614cae57600080fd5b76020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b8152600083516158c181601785016020880161533f565b7001034b99036b4b9b9b4b733903937b6329607d1b60179184019182015283516158f281602884016020880161533f565b01602801949350505050565b60008261590d5761590d615472565b500690565b60006020828403121561592457600080fd5b81516001600160401b038082111561593b57600080fd5b818401915084601f83011261594f57600080fd5b8151818111156159615761596161559d565b604051601f8201601f19908116603f011681019083821181831017156159895761598961559d565b816040528281528760208487010111156159a257600080fd5b6159b383602083016020880161533f565b979650505050505050565b805462ffffff8082168452808260181c166020850152808260301c166040850152808260481c166060850152615a0060808501828460601c1662ffffff169052565b615a1660a08501828460781c1662ffffff169052565b5050600181015460c08301526002015460e090910152565b8054825260018101546020830152600281015460408301526003810154606083015260048101546080830152600581015460a0830152600681015460c0830152600781015460e083015260088101546101008301526009810154610120830152600a810154610140830152600b810154610160830152600c81015463ffffffff808216610180850152615acf6101a08501828460201c1663ffffffff169052565b615ae76101c08501828460401c1663ffffffff169052565b5050600d8101546101e0830152600e810154610200830152600f015461022090910152565b6107008101818760005b6007811015615b35578151835260209283019290910190600101615b16565b5050506001600160a01b03861660e08301528454610100830152600185015463ffffffff808216610120850152602082901c811661014085015260409190911c8116610160840152600286015461018084015260038601546101a084015260048601546101c084015260058601546101e084015260068601546102008401526007860154610220840152600886015462ffffff808216610240860152601882901c8116610260860152603082901c16610280850152604881901c82166102a0850152606881901c82166102c085015260881c166102e0830152615c1f6103008301600987016159be565b600c850154610400830152600d850154610420830152600e850154610440830152600f850154610460830152615c5c610480830160108701615a2e565b6106c08201939093526106e001529392505050565b8060005b6006811015611d76578151845260209384019390910190600101615c75565b6101208101615ca38287615c71565b6001600160a01b039490941660c082015260e081019290925261010090910152919050565b600080600060608486031215615cdd57600080fd5b83519250602084015160048110615cf357600080fd5b80925050604084015190509250925092565b6101608101615d148289615c71565b6001600160a01b039690961660c082015260e081019490945261010084019290925261012083015261014090910152919050565b8481528360208201528260408201526080606082015260006148f76080830184615363565b600081615d7c57615d7c61543d565b50600019019056feb19546dff01e856fb3f010c267a7b1c60363cf8a4664e21cc89c26224620214e65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862aa2646970667358221220d8d037784a19318b8b19324601143d915adb1ac064c74a871d0b79a547729ef764736f6c63430008100033

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
0xf4E23Ae6D7dEAA9B3c8292881e3bC3027268808a
Loading...
Loading
Loading...
Loading

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.